Passing list of Enum as path variable - Spring/Kotlin - spring

Trying to pass list of Enum values as path variable
#GetMapping(path = ["/days"], produces = ["application/json;charset=UTF-8"])
#ApiOperation("Process by days of week")
fun processByDaysOfWeek(#PathVariable day: List<DayOfWeek>): {
...
}
enum class DayOfWeek {
MONDAY, WEDNESDAY, THURSDAY, FRIDAY;
}
When testing with RestAssured
val days = listOf(DayOfWeek.MONDAY, DayOfWeek.THURSDAY)
RestAssured
.given()
.spec(spec)
.pathParam("days", days)
.get("/engine/workday/{days}")
.then()
.statusCode(200)
.extract().`as`(JsonNode::class.java)
Request method: GET
Request URI: http://localhost:62938/engine/workday/%3D%5B%22MONDAY%22%2C%22THURSDAY%22%5D
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: <none>
Path params: days=["MONDAY","THURSDAY"]
Headers: Accept=*/*
Content-Type=application/json; charset=UTF-8
Cookies: <none>
Multiparts: <none>
Body: <none>
What is the best way to test this? I want the URL to look like
http://localhost:62938/engine/workday/MONDAY,THURSDAY

You can use joinToString on your list.
assertEquals("MONDAY,THURSDAY", listOf(DayOfWeek.MONDAY, DayOfWeek.THURSDAY).joinToString(","))

Related

How to define a contract/stub for a particular test on the consumer side

Stubs on the consumer side overlap due to the same url and response body, WireMochk doesn't understand which stub to choose
I have to write contracts for an endpoint (api/topic/add/project/{projectName}), that has in the request body a DTO, that has some validation on each its field:
#Value
public class TopicCreationDto {
#NotNull
#Pattern(regexp = "^[a-zA-Z0-9.\\-_]+$")
String name;
#Min(1)
int partitions;
#Min(1)
int replication;
I also have to write contracts for each response of this endpoint, for 201, 422, 404, 409 request codes. I wanted to use regular expressions in my contracts on the request side of the consumer:
body:
name: testTopic
partitions: 1
replication: 1
matchers:
url:
regex: api/topic/add/project/[a-zA-Z0-9.\-_]+
body:
- path: $.name
type: by_regex
value: ^[a-zA-Z0-9.\-_]+$
- path: $.replication
type: by_regex
value: ^[1-9][0-9]*$
- path: $.partitions
type: by_regex
value: ^[1-9][0-9]*$
The problem is that these regular expressions are identical for every case, and the WireMock on the cosumer side doesn't know which stub should be used. Can you help me to solve this problem, maybe is there a way to define specific stub for a specific test method or maybe am I doing something wrong?

How can I query an internally deployed API from my Jelastic environment?

I am writing a Jelastic manifest where I deploy two nodes. On one node, I have an API that I need to query in order to setup the second. Something along these lines:
type: install
name: My test
nodes:
- count: 1
cloudlets: 4
nodeGroup: auth
nodeType: docker
image: my-api:latest
- count: 1
cloudlets: 16
nodeGroup: cp
nodeType: docker
image: some-service:latest
onInstall:
- script: |
import com.hivext.api.core.utils.Transport;
try {
const body = new Transport().get("http://${nodes.auth.master.intIP}:9011/api/key", {
"Authorization": "my-api-key"
});
return { result: 0, body: body };
} catch (e) {
return {
type: "error",
message: "unknown error: " + e
};
}
In my script, when I do
const body = new Transport().get("http://www.google.com");
it works, I get the body content of the google page. However,
const body = new Transport().get("http://${nodes.auth.master.intIP}:9011/api/key", {
"Authorization": "my-api-key"
});
returns
ERROR: script.response: {"type":"error","message":"unknown error: JavaException: java.io.IOException: Failed to select a proxy"}
What am I doing wrong? How can I query my service in a script like in the above snippet? When I curl it through regular cmd, then it works:
curl -s -H "Authorization: my-api-key" http://${nodes.auth.master.intIP}:9011/api/key
Also, incidentally, where can I find the documentation of com.hivext.api.core.utils.Transport?
You can't access your environment node via internal IP from the script (at least it's not guaranteed).
As a workaround, you can access your node via public IP or endpoint.
If public IP or endpoint is not applicable in your case and your service must be accessed only internally, you can try to access your node via curl and ExecCmd API. For example:
type: install
name: My test
nodes:
- count: 1
cloudlets: 4
nodeGroup: auth
nodeType: docker
image: my-api:latest
- count: 1
cloudlets: 16
nodeGroup: cp
nodeType: docker
image: some-service:latest
onInstall:
- script: |
function execInternalApi(nodeId, url) {
let resp = api.env.control.ExecCmdById({
envName: '${env.name}',
nodeId: nodeId,
commandList: toJSON([{
command: 'curl -fSsl -H "Authorization: my-api-key" \'' + url + '\''
}])
})
if (resp.result != 0) return resp
return { result: 0, out: resp.responses[0].out }
}
let body = execInternalApi(${nodes.auth.master.id}, 'http://localhost:9011/api/key');
return { result: 0, body: body };

In a serverless.yml file, how does one customize the default http response template?

Looking at the documentation here https://serverless.com/framework/docs/providers/aws/events/apigateway/#custom-response-templates, there doesn't seem to be much detail at all about setting up these templates.
I'm currently looking to remove the default application/json content-type, which is generated during creation of the handler's integration response (pictured below), and replace it with text/html. Is there defined syntax for how to do this hidden somewhere? Or is this level of customization not possible within the current scope of the framework?
Here is my endpoint as defined in serverless.yml
events:
- http:
method: any
path: /
integration: lambda
request:
region: ${env:AwsRegion}
response:
headers:
Content-Type: "'text/html'"
template: $input.path('body')
- http:
method: any
path: /{proxy+}
Which produces the following configuration on AWS Api Gateway:
I did try modifying the specification like this, as a guess, but it threw a syntax error:
template:
text\html: $input.path('body')
Make It Work
It looks like this isn't really supported by the framework, but it can be hacked together by (ab)using statusCodes in your serverless template.
Moving the response template to under a status code, and providing a pattern for that status code, accomplishes what I think you are after. The syntax:
- http:
method: any
path: /
integration: lambda
request:
region: us-east-1
response:
headers:
Content-Type: "'text/html'"
statusCodes:
200:
pattern: ""
template:
text/html: $input.path('body')
Note: Both the pattern and the template must be present.
Is It Really That Bad?
That's up to you, ultimately. I'm calling it a hack because:
It would be much nicer to be able to supply this at the response.template level, rather than at response.statusCodes.200.template.
Specifying one or more statusCodes removes the set of default lambda error regexes that you get when you don't specify any.
It feels like working around the fact that response.template will only accept a string, whereas it could (should?) accept a string or an object (like it does under statusCodes).
Fix It?
The offending code, from /lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js:
if (http.response.template) {
_.merge(integrationResponse.ResponseTemplates, {
'application/json': http.response.template,
});
}
if (config.template) {
const template =
typeof config.template === 'string'
? { 'application/json': config.template }
: config.template;
_.merge(integrationResponse.ResponseTemplates, template);
}
I think for this to work under response.template, the code in the first if() would need to behave more like the code in the second if().

Register multiple nodes with consul

The question is, how do I register multiple nodes with consul under the same ID. I'm running a consul server in docker, and in my machine localhost I run two processes of the same HelloWorld nodejs app on my mac.
Problem: the entry for the process running at 3000 gets replaced by the process running at 3001 hence I'm ending up with one node only.
Question 2 Where do I download this GUI Client (not Web UI) from for Mac as shown in the screenshot.
Payload for node 1 port 3000
{
HTTP: 'http://My-Mac-Pro.local:3000/health',
Interval: '15s',
Name: 'My-Mac-Pro.local',
ID: 'user1'
}
Payload for node 2 port 3001
{
HTTP: 'http://My-Mac-Pro.local:3001/health',
Interval: '15s',
Name: 'My-Mac-Pro.local',
ID: 'user2'
}
nodeJS code
let http = require("http");
http.request({
method: "PUT",
hostname: env.CONSUL_HOST,
port: 8500,
path: "/v1/agent/check/register",
headers: {
"content-type": "application/json; charset=utf-8"
}
}, function(response){
if (response.statusCode == 200) {
resolve();
}
}).on("error", reject).end(JSON.stringify(body));
Expectation: See the multiple nodes under web
When you register services, each of services should register with unique service's ID.
It could be something as : ${serviceName}-${hostname}-{ip}-${port}-${process.pid()}-${uuid.v4()} or any combination of those to ensure that your service ID is unique. Different ID in registration payload is what sets consul to differ instances of same app/serviceIdentity running and they wont "override" one another.
Example of registration payload:
const id = `${ip}-${hostname}-${serviceIdentity}-${port}`;
const registrationDetails ={
Name: serviceIdentity,
ID: id,
Address: ip,
Port: parseInt(port),
Check: {
CheckID: `http-${id}`,
Name: `http-${id}`,
TLSSkipVerify: true,
HTTP: `http://${host}:${port}/health`,
Interval: '10s',
Notes: `Service http health`,
DeregisterCriticalServiceAfter: '60s',
},
};

How to Access Athena QueryString From CloudFormation in Lambda?

AWS-loaded question, but does anyone know what the proper way to access an Athena Query String (in CloudFormation) in Lambda?
I have set up both the Athena NamedQuery and the Lambda in CloudFormation. Abstracting out some of the more project-specific details, the general form I have is:
MyQuery:
Type: AWS::Athena::NamedQuery
Properties:
Database: "mydatabase"
Name: "DataQuery"
QueryString: SELECT * FROM mydatabase
MyLambda:
Type: AWS::Serverless::Function
Properties:
Handler: 'handlers.migration_handler'
Runtime: python3.6
CodeUri:
Bucket: BATS::SAM::CodeS3Bucket
Key: BATS::SAM::CodeS3Key
Description: Migrates data from output of Athena query to S3
Policies:
- AmazonS3FullAccess
- AWSLambdaExecute
- AmazonAthenaFullAccess
Environment:
Variables:
MY_QUERY:
Ref: MyQuery
When I'm writing the handler for the lambda, I want to call:
athena_client = boto3.client('athena')
response = athena_client.start_query_execution(
QueryString = os.environ['MY_QUERY']
ResultConfiguration = {'OutputLocation: 's3://my-bucket'}
)
However, QueryString needs to be a string, so this currently isn't working. I want to access the QueryString property in MY_QUERY, and I feel like I'm so close but I'm not quite sure how to get that last step. Any help here would be greatly appreciated.
Figured it out yesterday (or more specifically, my teammate figured it out).
Boto3 happens to have another method called get_named_query(NamedQueryId), and that returns a dictionary in the form of:
{
'NamedQuery': {
'Name': 'string',
'Description': 'string',
'Database': 'string',
'QueryString': 'string',
'NamedQueryId': 'string'
}
Thus, my code worked when I modified my lambda handler to:
athena_client = boto3.client('athena')
query_info = athena_client.get_named_query(
NamedQueryId = os.environ['MY_QUERY']
)
response = athena_client.start_query_execution(
QueryString = query_info['NamedQuery']['QueryString']
ResultConfiguration = {'OutputLocation: 's3://my-bucket'}
)

Resources