How to get entire request headers/querystrings in Serverless framework? - amazon-web-services

I started to try Serverless framework, but it looks little confusable for some points...
One of them is request headers/querystrings,
I made request template like this:
s-templates.json
{
"apiRequestTemplate": {
"application/json": {
"httpMethod": "$context.httpMethod",
"body": "$input.json('$')",
"queryParams" : "$input.params().querystring",
"headerParams" : "$input.params().header",
"headerParamNames" : "$input.params().header.keySet()",
"contentTypeValue" : "$input.params().header.get('Content-Type')"
}
}
}
s-function.json
"requestParameters": {},
"requestTemplates": "$${apiRequestTemplate}",
With this setting, I expected to get the request something like this:
{
"body" : {}
"contentTypeValue" : ""
"headerParamNames" : ["Accept", "Accept-Encoding", ... ],
"headerParams" : {
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding" : "gzip, deflate, sdch, br, Accept-Language=ja,en-US;q=0.8,en;q=0.6",
...
},
"httpMethod" : "GET",
"queryParams" : {
"category" : "Some Category"
}
}
But in real, what I get is:
{
"body" : {}
"contentTypeValue" : ""
"headerParamNames" : "[Accept,Accept-Encoding, ... ]",
"headerParams" : "{Accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", Accept-Encoding=gzip, deflate, sdch, br, Accept-Language=ja,en-US;q=0.8,en;q=0.6", ...}",
"httpMethod" : "GET",
"queryParams" : "{category=Some Category}"
}
This results inconvenient to handle.
I know also, method like below:
s-function.json
"requestParameters": {},
"requestTemplates": {
"application/json": "{\"category\":\"$input.params('category')\"}"
},
But this is also inconvenient need to specify all parameters in configuration..
Is there any way to get entire request-headers / query-strings as json object in lambda function?
Modified after answer
I tried to change s-template.json to
"queryParams" : "$util.parseJson($input.params().querystring)",
"headerParams" : "$util.parseJson($input.params().header)",
But result was same...
And, in AWS document, what I want can be seen here: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#input-variable-reference
#set($allParams = $input.params())
{
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
}
}
But I don't know how to set this setting to Serverless framework's s-templates.json...

I use following request template. It will wrap data, path, headers,params,query into a JSON object and pass it to the function.
{
"application/json": {
"data": "$input.json('$')",
"path": "$context.resourcePath",
"method": "$context.httpMethod",
"headers": "{#foreach($header in $input.params().header.keySet())\"$header\": \"$util.escapeJavaScript($input.params().header.get($header))\" #if($foreach.hasNext),#end#end}",
"params": "{#foreach($param in $input.params().path.keySet())\"$param\": \"$util.escapeJavaScript($input.params().path.get($param))\" #if($foreach.hasNext),#end#end}",
"query": "{#foreach($queryParam in $input.params().querystring.keySet())\"$queryParam\": \"$util.escapeJavaScript($input.params().querystring.get($queryParam))\" #if($foreach.hasNext),#end#end}"
}
}
You can refer Apache Velocity Templates to get better understanding about the inner syntax such as #foreach($header in .....).

Have you tried $util.parseJson()? It takes the json as a string and turns it into a traditional json object.

Related

Trying to remove item from a list in AWS AppSync in VTL

I am trying to make a custom resolver in VTL that will remove a certain string from a dynamodb list. I was trying to use $util.list.copyAndRemoveAll to remove the friend from the list. But I am not quite sure how to use it and the documentation isn't very clear to me and I don't even know if I am using the function correctly within VTL. https://docs.aws.amazon.com/appsync/latest/devguide/list-helpers-in-util-list.html
Thank you in advance.
GraphQL mutation
type Mutation {
removeFromIncomingFriendList(pk: String!, sk: String!, friend: String!): String!
}
#user_incoming_friend_requests is a list of usernames of users that have sent friend requests.
{
"version" : "2018-05-29",
"operation" : "UpdateItem",
"key" : {
"pk" : { "S" : "USER#USERNAME" },
"sk" : { "S" : "METADATA#USERNAME" },
},
"update": {
"expression" : "SET #user_incoming_friend_requests = :newList ADD version :plusOne",
"expressionNames": {
"#user_incoming_friend_requests" : "user_incoming_friend_requests",
},
"expressionValues" : {
":newList": $util.list.copyAndRemoveAll("#user_incoming_friend_requests", [${context.arguments.friend}])
":plusOne" : {"N":1}
}
}
}

how to update an attribute in a nested array object DynamoDb AWS

I want to update the choices attribute directly through AWS API Gateway.
{
"id" : "1",
"general" : {
"questions : [
"choices" : ["1","2","3"]
]
}
}
Here is my resolver mapping template
#set($inputRoot = $input.path('$'))
{
"TableName" : "models",
"Key" : {
"accountId" : {
"S": "$inputRoot.accountId"
},
"category" : {
"S" : "model"
}
},
"UpdateExpression" : "SET general.questions = :questions",
"ExpressionAttributeValues" : {
":questions" : {
"L" : [
#foreach($elem in $inputRoot.questions)
{
"M" : {
"choices" : {
"L" : [
#foreach($elem1 in $elem.choices)
{"S" : "$elem1"}
#if(foreach.hasNext),#end
#end
]
}
}
}
#if($foreach.hasNext),#end
#end
]
}
}
}
But I am getting Internal server error 500 on execution.
Gateway response body: {"message": "Internal server error"}
Does DynamoDb support updating this expression or is there any error in mapping template ? If so what should be the mapping template for the object I am trying to update.

Wrong SQS AWS message when I'm subscribed from a SNS Topic

I'm having problems with the next design:
When I'm receiving the message in my SQS Subscriber, the model of message it's wrong, example:
{
"Type" : "Notification",
"MessageId" : "7a6789f0-02f0-5ed3-8a11-deebcd08f145",
"TopicArn" : "arn:aws:sns:us-east-2:167186109795:name_sns_topic",
"Message" : "My JSON message",
"Timestamp" : "1987-04-23T17:17:44.897Z",
"SignatureVersion" : "1",
"Signature" : "string",
"SigningCertURL" : "url",
"UnsubscribeURL" : "url",
"MessageAttributes" : {
"X-Header1" : {"Type":"String","Value":"value1"},
"X-Header2" : {"Type":"String","Value":"value2"},
"X-Header3" : {"Type":"String","Value":"value3"},
"X-HeaderN" : {"Type":"String","Value":"value4"}
}
}
The common model when recieve message from SQS should be:
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "Hello from SQS!",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {},
"md5OfBody": "7b270e59b47ff90a553787216d55d91d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:{partition}:sqs:{region}:123456789012:MyQueue",
"awsRegion": "{region}"
}
]
}
In my handler Java Lambda (example code) is throwing an exception because the estructure of de message received is not SQS Event:
public class MyHandler implements RequestHandler<SQSEvent, String> {
#Override
public String handleRequest(SQSEvent event, Context context) {
LambdaLogger logger = context.getLogger();
for (SQSEvent.SQSMessage msg : event.getRecords()) {
logger.log("SQS message body: " + msg.getBody());
logger.log("Get attributes: " + msg.getMessageAttributes().toString());
msg.getMessageAttributes()
.forEach(
(k, v) -> {
logger.log("key: " + k + "value: " + v.getStringValue());
});
}
return "Successful";
}
}
How can I do for handle the message thats its receiving ?
In my opinion this isn't documented too well but it's not bad once you figure it out.
The first thing is that I don't use the predefined Lambda objects. I read everything into a String and take it from there. So the base of my Lamda function is:
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
// copy InputStream to String, avoiding 3rd party libraries
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
String jsonString = result.toString();
}
When you "go direct" from SNS to Lambda the message looks something like (some fields removed for sake of length):
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"Sns": {
"Type": "Notification",
"Subject": "the message subject",
"Message": "{\"message\": \"this is the message\", \"value\": 100}",
"Timestamp": "2020-04-24T21:44:28.220Z",
"SignatureVersion": "1"
}
}
]
}
I had sent in a test message in JSON with two simple fields. Using JsonPath the "message" field inside of everything is read with:
String snsMessage = JsonPath.read(jsonString, "$.Records[0].Sns.Message");
String realMessage = JsonPath.read(snsMessage, "$.message");
But when it goes SNS -> SQS -> Lambda (or, indeed any SNS -> SQS path) the SNS message is now mostly wrapped and escaped in an SQS message:
{
"Records": [
{
"messageId": "ca8c53e5-8417-4479-a720-d4ecf970ca68",
"body": "{\n \"Type\" : \"Notification\",\n \"Subject\" : \"the message subject\",\n \"Message\" : \"{\\\"message\\\": \\\"this is the message\\\", \\\"value\\\": 100}\"\n}",
"attributes": {
"ApproximateReceiveCount": "1"
},
"md5OfBody": "6a4840230aca6a7bf7934bf191a529b8",
"eventSource": "aws:sqs"
}
]
}
So in this case, the value is in Records[0].body but that contains another JSON object. I'll admit that there is likely an easier way but from what I found I had to parse 3 times:
String sqsBody = <as read in lambda>;
String recordBody = JsonPath.read(sqsBody, "$.Records[0].body");
String internalMessage = JsonPath.read(recordBody, "$.Message");
// now read out of the sns message
String theSnsMessage = JsonPath.read(message, "$.message");

How I can update boolean field in DynamoDb with Mapping template?

I have next code for update item in DB with mapping template:
$!{expSet.put("available", ":available")}
$!{expValues.put(":available", { "BOOL": $ctx.args.available })}
when I send available = false - it's ok, but if available = true I get error
"Unable to parse the JSON document: 'Unexpected character (':' (code
58)): was expecting double-quote to start field name
schema in GraphQl
type Item {
....
available: Boolean!
....
}
What I do wrong ?
Your UpdateItem request mapping template should look something like:
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id" : { "S" : "${context.arguments.id}" }
},
"update" : {
"expression" : "SET #available = :available",
"expressionNames": {
"#available" : "available"
},
"expressionValues": {
":available" : { "BOOL": ${context.arguments.available} }
}
}
}

WireMock bodyPatterns XML Match regex

I am using WireMock to mock SOA service, but I have an issue with the bodyPattern XML, can I use regex inside XML?
My request header is changing based on the request time, I just want to match anything inside the header.
{
"request" : {
"url" : "/service/v1/WebService",
"method" : "POST",
"bodyPatterns" : [ {
"equalToXml" :"\\Q<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header>
<SOAP-ENV:Header>
I want to match whatever inside header.
</SOAP-ENV:Header>
} ]
},
For example your request is something like below,
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ex="http://example.com">
<soapenv:Header/>
<soapenv:Body>
<ex:somebody>
<ex:input>
<ex:name>John</ex:name>
<ex:phone>7414444</ex:phone>
<ex:role>teacher</ex:role>
</ex:input>
</ex:somebody>
</soapenv:Body>
then your JSON file could be,
{
"request" : {
"url" : "/service/v1/WebService",
"bodyPatterns" : [ {
"matchesXPath": "//ex:input[ex:name=\"John\" and xw:phone=\"7414444\"]",
"xPathNamespaces" : {
"ex" : "http://example.com"
}
}]
},
"response" : {
"status" : 200,
"headers": {
"Content-Type": "text/xml;charset=UTF-8"
},
"body" : "<Abody>"
}}
You can use Xpath to arrive at same result
"bodyPatterns": [
{
"matchesXPath": "your xpath"
}