The goal of this project is to facilitate validation of HTTP queries. It provides some end points that can be called with well known behavior and response.
You will java 11 and maven to build the project.
First clone the repository:
$ git clone [email protected]:ypiel-talend/validation-server.gitThen build it:
$ mvn clean installAnd start the server:
$ java -jar target/validation-server-0.0.2-SNAPSHOT.jar --server.port=9098The --server.port option let you define the port you want to listen to, 8080 is the default.
Once the server is running, you can access its API swagger documentation: http://127.0.0.1:9098/swagger-ui/index.html
You have to build the porject first, then create the docker image:
$ docker build -t validation-server .
[...]
Successfully tagged validation-server:latestThen execute the container:
$ docker run -p 8080:8080 validation-server
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.9)
2023-12-11 14:27:30.779 INFO 1 --- [ main] o.t.c.t.v.ValidationServerApplication : Starting ValidationServerApplication v0.0.2-SNAPSHOT using Java 11.0.16 on ae2f258eaccb with PID 1 (/validation-server-0.0.2-SNAPSHOT.jar started by root in /)
2023-12-11 14:27:30.780 INFO 1 --- [ main] o.t.c.t.v.ValidationServerApplication : No active profile set, falling back to 1 default profile: "default"
2023-12-11 14:27:31.310 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-12-11 14:27:31.316 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-12-11 14:27:31.316 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.71]
2023-12-11 14:27:31.359 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-12-11 14:27:31.359 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 547 ms
2023-12-11 14:27:31.549 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-12-11 14:27:31.556 INFO 1 --- [ main] o.t.c.t.v.ValidationServerApplication : Started ValidationServerApplication in 1.025 seconds (JVM running for 1.276)The container port 8080 is mapped on the host also on 8080, so, the server is ready to be queried from your localhost.
To enable another container to access the web server, initiate it within the same Docker network. First, identify the network's name of the container that will call the server:
$ docker inspect my_container --format "{{range \$key, \$value := .NetworkSettings.Networks}}{{\$key}} {{end}}"
my_networkSo, you have to start the validation server container in this network:
$ docker run --network my_network -p 8080:8080 --name my-validation-server validation-server
[...]Then, you can identify the server IP in this network:
$ docker network inspect my_network | jq -r '.[0].Containers[] | select(.Name == "my-validation-server").IPv4Address'
www.xxx.yyy.zzz/mmWhere www.xxx.yyy.zzz is hte IP address of the validation server in the network and mm its subnet mask.
So you can query the web server using this address http://www.xxx.yyy.zzz:8080/....
http://<ip>>:<port>/swagger-ui/index.html
The /ping endpoint always responds with a pong. payload. It is just to check if the server is alive.
This endpoint support Accept header. If Accept: text/plain then it responds with a plain text response payload:
$ curl -H "Accept: text/plain" http://172.26.0.4:8080/ping
pong.This endpoint support Accept header. If Accept: application/json then it responds with a simple json document:
$ curl -H "Accept: application/json" http://172.26.0.4:8080/ping
{"message":"pong."}It only supports GET verb. With any other verb, an error message generated by Sprint will be returned:
$ curl -X POST -H "Accept: application/json" http://127.0.0.1:8080/ping
{"timestamp":"2024-03-06T20:51:39.177+00:00","status":405,"error":"Method Not Allowed","path":"/ping"}The /loadfile endpoint load the file designed by the file query param and return its content as its payload:
$ echo "Hello" > /tmp/hello.txt
$ echo "the" >> /tmp/hello.txt
$ echo "world!" >> /tmp/hello.txt
$ curl http://127.0.0.1:8080/loadfile?file=/tmp/hello.txt
Hello
the
world!The /post endpoint accepts POST verb and a body. It will return exactly the body it received as plain text or in
a json document, depending the Accept header:
$ curl -X POST http://127.0.0.1:8080/post --data "Hello world !" -H "Accept: text/plain" -H "Content-Type: text/plain"
Hello world !The /echo endpoint do quite the same but returns the received payload in a json object:
$ curl -X POST http://127.0.0.1:8080/post --data "Hello world !" -H "Accept: text/plain" -H "Content-Type: text/plain"
{"post_body":"Hello world !"}There are two endpoints that deserve paginated elements.
The /paginate one return a a JSON array that contains some json objects {"id": i, "label": "element_" + i}. It takes some parameters:
total: The total number of generated elements.offset: From which index of element do you want to retrieve them ?limit: How many elements do you want to retrieve ?
$ curl 'http://127.0.0.1:8080/paginate?total=100&offset=0&limit=5' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 146 0 146 0 0 48666 0 --:--:-- --:--:-- --:--:-- 48666
[
{
"id": 1,
"label": "element_1"
},
{
"id": 2,
"label": "element_2"
},
{
"id": 3,
"label": "element_3"
},
{
"id": 4,
"label": "element_4"
},
{
"id": 5,
"label": "element_5"
}
]
$ curl 'http://127.0.0.1:8080/paginate?total=100&offset=5&limit=5' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 148 0 148 0 0 37000 0 --:--:-- --:--:-- --:--:-- 49333
[
{
"id": 6,
"label": "element_6"
},
{
"id": 7,
"label": "element_7"
},
{
"id": 8,
"label": "element_8"
},
{
"id": 9,
"label": "element_9"
},
{
"id": 10,
"label": "element_10"
}
]The second enpoint is paginateNestedArray. It takes the same parameters and returns the same elements, but, the element array is a nested array in a root object:
$ curl 'http://127.0.0.1:8080/paginateNestedArray?total=100&offset=0&limit=5' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 201 0 201 0 0 15461 0 --:--:-- --:--:-- --:--:-- 15461
{
"offset": 0,
"limit": 5,
"total": 100,
"size": 5,
"elements": [
{
"id": 1,
"label": "element_1"
},
{
"id": 2,
"label": "element_2"
},
{
"id": 3,
"label": "element_3"
},
{
"id": 4,
"label": "element_4"
},
{
"id": 5,
"label": "element_5"
}
]
}
$ curl 'http://127.0.0.1:8080/paginateNestedArray?total=100&offset=5&limit=5' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 203 0 203 0 0 29000 0 --:--:-- --:--:-- --:--:-- 29000
{
"offset": 5,
"limit": 5,
"total": 100,
"size": 5,
"elements": [
{
"id": 6,
"label": "element_6"
},
{
"id": 7,
"label": "element_7"
},
{
"id": 8,
"label": "element_8"
},
{
"id": 9,
"label": "element_9"
},
{
"id": 10,
"label": "element_10"
}
]
}There are two endpoints to test HTTP client retry with backoff.
The /retry503 endpoint responds three out of four times a HTTP 503 error with this payload:
{
"error": "You have still to retry '%s' times."
}The fourth call will return a 200 success HTTP with this one:
{
"success": "true"
}If you prefer to return a success response before or after the fourth call you can overwrite this with this property -Dvalidation-server.noauth-controller.retry-503-attempts-success=<nb attempts>.
In the same way, the /retryTimeout will wait for 3000 milliseconds three out of four
time before responding with a 200 successful HTTP with this payload:
{
"message": "Wait for timeout will be disable in '%s' attempts."
}The fourth call will not wait before to respond.
The 3000ms default delay can be overwritten with this propery -Dvalidation-server.noauth-controller.retry-timeout-attempts-delay=<delay>.
If you prefer to skip the delay before or after the fourth call you can overwrite this with this property -Dvalidation-server.noauth-controller.retry-timeout-attempts-success=<nb attempts>.
To retrieve a token the query has to be as POST with header Content-type: x-www-form-urlencoded and those form key/values:
- client_id = 1234567890
- client_secret = secret_1234567890_
- grant_type = client_credentials
- scope = scA scB scC
$ curl -X POST http://127.0.0.1:8080/oauth2/client-credentials/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=1234567890' \
-d 'client_secret=secret_1234567890_' \
-d 'grant_type=client_credentials' \
-d 'scope=scA scB scC'
{"access_token":"_success_token_","token_type":"Bearer","expires_in":1702306621631}The returned token is always the same. On expires_in is computed dynamically.
If one of those value is wrong, then you should receive such answer:
$ curl -X POST http://127.0.0.1:8080/oauth2/client-credentials/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=xxxxx' \
-d 'client_secret=xxxxxx' \
-d 'grant_type=client_credentials' \
-d 'scope=scA scB scC'
{"message":"OAuth2 security issue.","cause":"Wrong credentials, can't provide token."}You can also force the type of expires_in to be long (as expected in the OAUTH specification) or String (as returned by some server):
$ curl -X POST http://127.0.0.1:8080/oauth2/client-credentials/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=1234567890' \
-d 'client_secret=secret_1234567890_' \
-d 'grant_type=client_credentials' \
-d 'scope=scA scB scC'
-H 'expected_expires_as_string: true'
{"access_token":"_success_token_","token_type":"Bearer","expires_in":"1721731343667"}You can ask for user entity in oauth section. It will expect the token:
$ curl -X GET http://127.0.0.1:8080/oauth2/get/user \
-H 'Authorization: Bearer _success_token_'
{"id":1,"name":"Peter","active":true,"alternative":false}You can give id, name and/or active HTTP query parameter to overwrite the User attribute value:
$ curl -X GET 'http://127.0.0.1:8080/oauth2/get/user?id=3&name=John&active=false' \
-H 'Authorization: Bearer _success_token_'
{"id":3,"name":"John","active":false,"alternative":false}If the given token is wrong, an error is returned:
$ curl -X GET http://127.0.0.1:8080/oauth2/get/user \
-H 'Authorization: Bearer _xxxxx_'
{"message":"OAuth2 security issue.","cause":"Unrecognized token."}The alternative endpoint oauth2/alternative/get/user is the same as oauth2/get/user except that the oauth token has to be set in the AlternativeAuthorization header instead of Authorization, and, the token prefix is AlternativeTokenPrefix instead of Bearer.
If this endpoint is called, the alternative attribute of the user is set to true:
$ curl -X GET http://127.0.0.1:9098/oauth2/alternative/get/user \
-H 'AlternativeAuthorization: AlternativeTokenPrefix _success_token_'
{"id":1,"name":"Peter","active":true,"alternative":true}This project is based on spring boot. You can easily add new endpoints that will suit to your own needs. Then build the docker image and deploy it in your test environment.