requestParameters returning "Invalid mapping expression specified: true" - amazon-web-services

I'm configuring a lambda function's API gateway integration with the Serverless Framework version 0.4.2.
My problem is with defining an endpoint's request parameters. The AWS docs for API gateway entry says:
requestParameters
Represents request parameters that can be accepted by Amazon API Gateway. Request parameters are represented as a key/value map, with a source as the key and a Boolean flag as the value. The Boolean flag is used to specify whether the parameter is required. A source must match the pattern method.request.{location}.{name}, where location is either querystring, path, or header. name is a valid, unique parameter name. Sources specified here are available to the integration for mapping to integration request parameters or templates.
As I understand it, the config in the s-function.json is given directly to the AWS CLI, so I've specified the request parameters in the format:
"method.request.querystring.startYear": true. However, I'm receiving an Invalid mapping expression specified: true error. I've also tried specifying the config as "method.request.querystring.startYear": "true" with the same result.
s-function.json:
{
"name": "myname",
// etc...
"endpoints": [
{
"path": "mypath",
"method": "GET",
"type": "AWS",
"authorizationType": "none",
"apiKeyRequired": false,
"requestParameters": {
"method.request.querystring.startYear": true,
"method.request.querystring.startMonth": true,
"method.request.querystring.startDay": true,
"method.request.querystring.currentYear": true,
"method.request.querystring.currentMonth": true,
"method.request.querystring.currentDay": true,
"method.request.querystring.totalDays": true,
"method.request.querystring.volume": true,
"method.request.querystring.userId": true
},
// etc...
}
],
"events": []
}
Any ideas? Thanks in advance!

It looks like the requestParameters in the s-function.json file is meant for configuring the integration request section, so I ended up using:
"requestParameters": {
"integration.request.querystring.startYear" : "method.request.querystring.startYear",
"integration.request.querystring.startMonth" : "method.request.querystring.startMonth",
"integration.request.querystring.startDay" : "method.request.querystring.startDay",
"integration.request.querystring.currentYear" : "method.request.querystring.currentYear",
"integration.request.querystring.currentMonth" : "method.request.querystring.currentMonth",
"integration.request.querystring.currentDay" : "method.request.querystring.currentDay",
"integration.request.querystring.totalDays" : "method.request.querystring.totalDays",
"integration.request.querystring.volume" : "method.request.querystring.volume",
"integration.request.querystring.userId" : "method.request.querystring.userId"
},
This ended up adding them automatically to the method request section on the dashboard as well:
I could then use them in the mapping template to turn them into a method post that would be sent as the event into my Lambda function. Right now I have a specific mapping template that I'm using, but I may in the future use Alua K's suggested method for mapping all of the inputs in a generic way so that I don't have to configure a separate mapping template for each function.

You can pass query params to your lambda like
"requestTemplates": {
"application/json": {
"querystring": "$input.params().querystring"
}
}
In lambda function access querystring like this event.querystring

First, you need to execute a put-method command for creating the Method- Request with query parameters:
aws apigateway put-method --rest-api-id "yourAPI-ID" --resource-id "yourResource-ID" --http-method GET --authorization-type "NONE" --no-api-key-required --request-parameters "method.request.querystring.paramname1=true","method.request.querystring.paramname2=true"
After this you can execute the put-integration command then only this will work. Otherwise it will give invalid mapping error
"requestParameters": {
"integration.request.querystring.paramname1" : "method.request.querystring.paramname1",
"integration.request.querystring.paramname2" : "method.request.querystring.paramname2",

Make sure you're using the right end points as well. There are two types or some such in AWS.. friend of mine got caught out with that in the past.

Related

How can i get parameters from Construct before finishing of creation stack

Can you help me please?
I create DatabaseInstanceEngine and eks secret and want to put db_instance_endpoint_address to this secret
self.cluster.add_manifest('my-secret-env', {
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "my-secret-env",
'namespace': 'default'
},
"data": {
"DATA_HOST": self._base64encode(self.db.db_instance_endpoint_address),
}
})
after execution I see ${Token[TOKEN.475]} instead of value
When i create SSM parameter (only for test)
ssm_parameter = ssm.StringParameter(self, "mySsmParameter",
parameter_name="mySsmParameter",
string_value=self.db.db_instance_endpoint_address,
type=ssm.ParameterType.STRING
)
I see correct value
How can i resolve this token?
secretenv.node.add_dependency(self.db) did not help
Thank you
Try without self._base64encode(.....)
explain:
when you reference some value in CDK it puts a reference string in the cloudformation template and resolves the value at deploy time, when you encode this value as base64 it doesn't resolve at deploy time.
Take a look a the cloudformation template after you run synth.
Solved.
I changed python's base64 to Fn.base64 and problem was solved because resolve Fn.base64 tokens.
Unfortunately this way is not good for DatabaseInstance .secret.secret_value_from_json('password').unsafe_unwrap()
I recieve this using Customresource

How do I map a value from the method request body into an API gateway mapping template?

I have a lambda, written in Java, that accepts a Request Object of the structure
{
"id": "1",
"value": "foobar"
}
When I call this Lambda through the test interface with such an object, it works fine.
I want to create an API where a PUT request to /items/1 (i.e. of the form /items/{id}), with a request body of
{
"value": "foobar"
}
calls this Lambda.
I have created the API resourcesitems, and {id} appropriately.
And I have created the PUT method (on /items/{id}) and associated it to the lambda.
I have created a mapping template that maps the id from the path to the object.
{
"id": "$method.request.path.id"
}
However, how do I map the value from the request body into the template so that I get an integration request of the form
{
"id": "1", // came from path
"value": "foobar" // came from HTTP request body
}
How do I achieve this mapping?
Try this application/json mapping template:
{
"id": "$method.request.path.id",
"body" : $input.json('$')
}
Then in your lambda: console.log(event.body)
API Gateway mapping template and access logging variable reference
I found that this in the template works.
#set($inputRoot = $input.path('$'))
{
"id": "$method.request.path.id",
"value": $inputRoot.value
}

AWS SNS Subscription Filter policy checking a key in Message Attributes does NOT exist - possible?

We have two types of SNS messages coming in:
1. has MessageAttributes empty like this:
"MessageAttributes": {}
2. has MessageAttributes coming in like this:
"MessageAttributes": {
"Generator": {
"Type": "String",
"Value": "some-service"
}
}
I would like to use a filter subscription policy that ignores the second type but passes the first type to the subscriber.
So I tried this for the policy:
{
"Generator": [
{
"exists": false
}
]
}
I thought this would mean it will only pass along messages that do NOT contain the Generator key in MessageAttributes
However I am seeing now that no messages are getting passed along.
The AWS Subscription Filter docs seem to support this as a solution, but they only show the opposite way of checking that a key does exist, so I'm not sure if they support checking a key doesn't exist: https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html#attribute-key-matching
Is this possible?
The answer from #David Adams is out of date. See the Attribute key matching docs.
Use "exists": false to return incoming messages that don't include the specified attribute.
It is now possible to exclude any messages that have a particular key by using the policy:
{
"key": [
{
"exists": false
}
]
}
Late response but may be helpful to someone.
Filtering out by lack of existance is not possible. See the bottom of https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html#attribute-key-matching
Note: You cannot use the exists operator to match messages in which an attribute does not exist. Filtering will NOT match any messages if you set [{"exists": false}].
You could pass a string 'null' or similar to the generator attribute if it is non existant maybe?

AWS API Gateway - Configuring Integration Request Mapping Templates to accept String body rather JSON

I have a spring boot application with GET method like below. This method has input parameter as String that is mapped with path variable {userId}.
#GetMapping("/users/{userId}")
public String get(#PathVariable ("userId") String userId) {
return userId;
}
I created AWS lambda function and uploaded my spring boot JAR. I am able to test lambda fuction with test event after passing string example "userId1". Lambda function worked fine.
Using API gateway, created API, defined resource and GET method. URL looks like below:
/users/{userId} - GET - Integration Request
Also, followed below steps to define Mapping Templates.
Opened the Integration Request settings and then Body Mapping Templates.
Selected the option: When there are no templates defined (recommended)
Added a mapping template for: application/json
Added the following template to map the userId to the Lambda input
{
"userId": "$input.params('userId')"
}
When I test my API, it gives me below exception. I am not sure, how I should define Mapping Templates so that it accepts only String rather JSON. Because my Lambda function and corresponding implemented method accepts only String. Thanks in advance for help.
{
"errorMessage": "An error occurred during JSON parsing",
"errorType": "java.lang.RuntimeException",
"stackTrace": [],
"cause": {
"errorMessage": "com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token\n at [Source: lambdainternal.util.NativeMemoryAsInputStream#69b2283a; line: 1, column: 1]",
"errorType": "java.io.UncheckedIOException",
"stackTrace": [],
"cause": {
"errorMessage": "Can not deserialize instance of java.lang.String out of START_OBJECT token\n at [Source: lambdainternal.util.NativeMemoryAsInputStream#69b2283a; line: 1, column: 1]",
"errorType": "com.fasterxml.jackson.databind.JsonMappingException",
"stackTrace": [
"com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)",
"com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)",
"com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62)",
"com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)",
"com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1511)",
"com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1102)"
]
}
}
}
I think that your problem occurred because you are mapping a JSON like:
{
"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": {
"sub_key1": "value4"
}
}
This containt a key key4 with and JSON object as value. When your are trying to deserialize this element, it won't work directly because it sees a JSON_OBJECT, not a String.
You need to create a class like the following one:
public class Name { // the name doesn't matter
#JsonProperty("sub_key1")
private String sub_key1;
// getter and setter
}
to solve your problem.
My problem is resolved with simple change. As, I mentioned in my above question, I followed below steps.
Opened the Integration Request settings and then Body Mapping Templates.
Selected the option: When there are no templates defined (recommended)
Added a mapping template for: application/json
Added the following template to map the userId to the Lambda input
{ "userId": "$input.params('userId')" }
In last step #4, I replaced below JSON format with String format.
{ "userId": "$input.params('userId')" }
With below, it worked. My API could pass String parameter properly to my lambda function which has handler method with one single input of type String.
$input.params('userId')

In AWS API Gateway, How do I include a stage parameter as part of the event variable in Lambda (Node)?

I have a stage variable set up called "environment".
I would like to pass it through in a POST request as part of the JSON.
Example:
Stage Variables
environment : "development"
JSON
{
"name": "Toli",
"company": "SomeCompany"
}
event variable should look like;
{
"name": "Toli",
"company": "SomeCompany",
"environment": "development"
}
So far the best I could come up with was the following mapping template (under Integration Request):
{
"body" : $input.json('$'),
"environment" : "$stageVariables.environment"
}
Then in node I do
exports.handler = function(event, context) {
var environment = event.environment;
// hack to merge stage and JSON
event = _.extend(event.body, {
environment : environment
});
....
If your API Gateway method use Lambda Proxy integration, all your stage variables will be available via the event.stageVariables object.
For the project I'm currently working on, I created a simple function that goes over all the properties in event.stageVariables and appends them to process.env (e.g.: Object.assign(process.env, event.stageVariables);)
Your suggestion of using a mapping template to pass-through the variable would be the recommended solution for this type of workflow.
You can also access the stage name in the $context object.
Integration Request:
{
"environment" : "$context.stage"
}