AWS JavaScript resolver, Unable to convert - amazon-web-services

Trying JS resolver for the first time, Getting the Unable to convert error,
// Query-listItems-request.js
import { util } from '#aws-appsync/utils';
export function request(ctx) {
const { args: { userId } } = ctx;
return {
operation: 'Query',
query: {
expression: '#id = :id',
expressionValues: util.dynamodb.toMapValues({ ':id': `${userId}` }),
expressionNames: { '#id': 'id' },
},
};
}
export function response(ctx) {
return ctx.result;
}
AWS Sam tempalte:
AppGraphqlApiQueryListBalanceLogsResolver:
Type: AWS::AppSync::Resolver
Properties:
TypeName: Query
DataSourceName: !GetAtt AppGraphqlApiToListItemDataSource.Name
RequestMappingTemplateS3Location: Query-listItems-request.js
ResponseMappingTemplateS3Location: AppGraphqlApi/response.vtl
ApiId: !Ref AppGraphqlApi
FieldName: listItems
DependsOn: AppGraphqlBOApiSchema

Related

502 Bad Gateway Error on Serverless Framework Express Rest-API

I am trying to build a express rest-api with the serverless framework with the following code. I have a working POST request method to the path /fruits but the following GET request method throws a 502 Bad Gateway error.
const serverless = require('serverless-http');
const express = require('express');
const app = express();
const AWS = require('aws-sdk');
...
const dynamoDB = new AWS.DynamoDB.DocumentClient();
app.get('/fruits/:fruitName', (req, res) => {
const params = {
TableName: TABLE_NAME,
Key: {
fruitName: req.params.fruitName,
},
}
dynamoDB.get(params, (err, res) => {
if (err) {
console.log(error);
res.status(400).json({ error: 'Could not get fruit' });
}
if (res.Item) {
const { fruitName, biName} = res.Item;
res.json({ fruitName, biName});
} else {
res.status(404).json({ error: "Fruit not found" });
}
})
})
...
module.exports.handler = serverless(app);
I have set up a serverless.yml as follows
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: us-west-2
iamRoleStatements:
- Effect: 'Allow'
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["FruitsTable", "Arn" ] }
environment:
TABLE_NAME: 'fruits'
resources:
Resources:
FruitsTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: fruitName
AttributeType: S
KeySchema:
- AttributeName: fruitName
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: 'fruits'
functions:
app:
handler: index.handler
events:
- httpApi: 'GET /fruits/{fruitName}'
- httpApi: 'POST /fruits'
Any help is much appreciated.
The issue was identical variable naming causing a overwrite. And the following would fix that.
app.get('/fruits/:fruitName', (req, res) => {
const params = {
TableName: TABLE_NAME,
Key: {
fruitName: req.params.fruitName,
},
}
dynamoDB.get(params, (err, result) => {
if (err) {
console.log(error);
res.status(400).json({ error: 'Could not get fruit' });
}
if (result.Item) {
const { fruitName, biName} = result.Item;
res.json({ fruitName, biName});
} else {
res.status(404).json({ error: "Fruit not found" });
}
})
})

define API mappings for apigateway in CDK

how can I deploy the following setting to my apigateway using cdk?
relevant part of my CDK-stack for my API:
....
const restApi = new apigateway.LambdaRestApi(this, "dyndns-api", {
handler: dyndnsLambda,
proxy: false,
domainName: {
securityPolicy: apigateway.SecurityPolicy.TLS_1_2,
domainName: siteDomain,
certificate: certificate,
endpointType: apigateway.EndpointType.REGIONAL
}
});
const methodResponse: apigateway.MethodResponse = {
statusCode: "200",
responseModels: {"application/json": apigateway.Model.EMPTY_MODEL}
}
const integrationResponse: apigateway.IntegrationResponse = {
statusCode: "200",
contentHandling: apigateway.ContentHandling.CONVERT_TO_TEXT
}
new route53.ARecord(this, "apiDNS", {
zone: zone,
recordName: siteDomain,
target: route53.RecordTarget.fromAlias(new route53Targets.ApiGateway(restApi)),
});
const requestTemplate = {
"execution_mode" : "$input.params('mode')",
"source_ip" : "$context.identity.sourceIp",
"set_hostname" : "$input.params('hostname')",
"validation_hash" : "$input.params('hash')"
}
const dnydnsIntegration = new apigateway.LambdaIntegration(dyndnsLambda, {
allowTestInvoke: true,
proxy: false,
integrationResponses: [integrationResponse],
passthroughBehavior: apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES,
requestTemplates: { "application/json": JSON.stringify(requestTemplate) },
});
restApi.root.addMethod("GET", dnydnsIntegration, {
methodResponses: [methodResponse]
});
I know, that there is apigatewayv2.ApiMapping, but I have trouble implementing that into my stack. Also tried the deprecated class CfnApiMappingV2 with no success either.

CDK add mapping templates to LambdaIntegration

I have a Lambda function which can be accessed by api gateway.
How can I make CDK to add an Mapping template like in this Screenshot:
I tried multiple variants of this:
....
const restApi = new apigateway.LambdaRestApi(this, "dyndns-api", {
handler: dyndnsLambda,
proxy: false,
domainName: {
domainName: siteDomain,
certificate: certificate,
endpointType: apigateway.EndpointType.REGIONAL
}
});
const methodResponse: apigateway.MethodResponse = {
statusCode: "200",
responseModels: {"application/json": apigateway.Model.EMPTY_MODEL}
}
const requestTemplate = {
"execution_mode" : "$input.params('mode')",
"source_ip" : "$context.identity.sourceIp",
"set_hostname" : "$input.params('hostname')",
"validation_hash" : "$input.params('hash')"
}
const dnydnsIntegration = new apigateway.LambdaIntegration(dyndnsLambda, {
allowTestInvoke: true,
passthroughBehavior: apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES,
requestTemplates: { "application/json": JSON.stringify(requestTemplate) },
});
restApi.root.addMethod("GET", dnydnsIntegration, {
methodResponses: [methodResponse]
});
But with not effect, it does not seem to arrive in the console, as I would expect.
solved it like this:
needed to add proxy setting to restApi, so the requestTemplate gets accepted. also needed integrationResponse.
const restApi = new apigateway.LambdaRestApi(this, "dyndns-api", {
handler: dyndnsLambda,
proxy: false,
domainName: {
securityPolicy: apigateway.SecurityPolicy.TLS_1_2,
domainName: siteDomain,
certificate: certificate,
endpointType: apigateway.EndpointType.REGIONAL
}
});
const methodResponse: apigateway.MethodResponse = {
statusCode: "200",
responseModels: {"application/json": apigateway.Model.EMPTY_MODEL}
}
const integrationResponse: apigateway.IntegrationResponse = {
statusCode: "200",
contentHandling: apigateway.ContentHandling.CONVERT_TO_TEXT
}
const requestTemplate = {
"execution_mode" : "$input.params('mode')",
"source_ip" : "$context.identity.sourceIp",
"set_hostname" : "$input.params('hostname')",
"validation_hash" : "$input.params('hash')"
}
const dnydnsIntegration = new apigateway.LambdaIntegration(dyndnsLambda, {
allowTestInvoke: true,
proxy: false,
integrationResponses: [integrationResponse],
passthroughBehavior: apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES,
requestTemplates: { "application/json": JSON.stringify(requestTemplate) },
});
restApi.root.addMethod("GET", dnydnsIntegration, {
methodResponses: [methodResponse]
});

AWS CDK event bridge and api gateway AWS example does not work

I am following the instructions here to setup an event bridge: https://eventbus-cdk.workshop.aws/en/04-api-gateway-service-integrations/01-rest-api/rest-apis.html
Based on the error message, the error is coming from this line of code: languageResource.addMethod("POST", new apigw.Integration({
I am not sure what is causing this issue because this is an example given by AWS and should work, but it does not.
I can build it but it fails with the following error on cdk deploy:
CREATE_FAILED | AWS::ApiGateway::Method | MyRestAPI/Default/{language}/POST (MyRestAPIlanguagePOSTB787D51A) Invalid Resource identifier specified (Service: AmazonApiGateway; Status Code: 404; Error Code: NotFoundException;
The code is below:
const myLambda = new lambda.Function(this, "MyEventProcessor", {
code: new lambda.InlineCode("def main(event, context):\n\tprint(event)\n\treturn {'statusCode': 200, 'body': 'Hello, World'}"),
handler: "index.main",
runtime: lambda.Runtime.PYTHON_3_7
})
const bus = new events.EventBus(this, `pwm-${this.stage}-MdpEventBus`)
new cdk.CfnOutput(this, "PwmMdpEventBus", {value: bus.eventBusName})
new events.Rule(this, `PwmMdpEventBusRule`, {
eventBus: bus,
eventPattern: {source: [`com.amazon.alexa.english`]},
targets: [new targets.LambdaFunction(myLambda)]
})
const apigwRole = new iam.Role(this, "MYAPIGWRole", {
assumedBy: new iam.ServicePrincipal("apigateway"),
inlinePolicies: {
"putEvents": new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: ["events:PutEvents"],
resources: [bus.eventBusArn]
})]
})
}
});
const options = {
credentialsRole: apigwRole,
requestParameters: {
"integration.request.header.X-Amz-Target": "'AWSEvents.PutEvents'",
"integration.request.header.Content-Type": "'application/x-amz-json-1.1'"
},
requestTemplates: {
"application/json": `#set($language=$input.params('language'))\n{"Entries": [{"Source": "com.amazon.alexa.$language", "Detail": "$util.escapeJavaScript($input.body)", "Resources": ["resource1", "resource2"], "DetailType": "myDetailType", "EventBusName": "${bus.eventBusName}"}]}`
},
integrationResponses: [{
statusCode: "200",
responseTemplates: {
"application/json": ""
}
}]
}
const myRestAPI = new apigw.RestApi(this, "MyRestAPI");
const languageResource = myRestAPI.root.addResource("{language}");
languageResource.addMethod("POST", new apigw.Integration({
type: apigw.IntegrationType.AWS,
uri: `arn:aws:apigateway:${cdk.Aws.REGION}:events:path//`,
integrationHttpMethod: "POST",
options: options,
}),
{
methodResponses: [{
statusCode: "200"
}],
requestModels: {"application/json": model.getModel(this, myRestAPI) },
requestValidator: new apigw.RequestValidator(this, "myValidator", {
restApi: myRestAPI,
validateRequestBody: true
})
})
In the AWS example, they are encapsulating your code inside
export class MyCdkAppStack extends cdk.Stack {
...
}
Are you missing that encapsulation? I noticed your sample code didn't include it. Because when you execute const myRestAPI = new apigw.RestApi(this, "MyRestAPI"); the this should refer to the MyCdkAppStack instance.

How to handle failures in AWS cloudformation CUSTOM resources?

I Have a lambda function created using CFN which looks like this:
InitializeDynamoDBLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
const AWS = require("aws-sdk");
const response = require("cfn-response");
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = function(event, context) {
let DynamoTableName = event.ResourceProperties.DynamoTable;
let KeyJSON = JSON.parse(event.ResourceProperties.KeyJSON);
let ValueJSON = JSON.parse(event.ResourceProperties.ValueJSON);
for(key in KeyJSON){
var params = {
TableName: DynamoTableName,
Item: {
'Key': KeyJSON[key],
'Value': JSON.stringify(ValueJSON[key])
}
};
docClient.put(params, function(err, data) {
if (err) {
console.log(err);
response.send(event, context, response.FAILED, {});
}
else {
response.send(event, context, response.SUCCESS, {});
}
});
}
};
Handler: index.handler
Role: !GetAtt 'LambdaExecutionRole.Arn'
Runtime: nodejs12.x
Timeout: 60
This Lambda is initialized using a CUSTOM resource like this:
InitializeDB:
Type: Custom::InitializeDynamoDBLambda
Properties:
ServiceToken:
Fn::GetAtt: [ InitializeDynamoDBLambda , "Arn" ]
DynamoTable: !Ref TenantLevelDBname
KeyJSON: !Ref KeyJSON
ValueJSON: !Ref ValueJSON
The problem is when there is an error, the Cloudformation stack gets stuck in a state like UPDATE_IN_PROGRESS etc.
How do I handle failures in such scenarios?