Closed Andrea-Scuderi closed 5 months ago
Hi @Andrea-Scuderi, thanks for bringing this up. This is interesting. In my testing it has always been the case that both headers have been set and the documentation does not indicate otherwise...
https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
Would you mind posting your example event here?
Just to be sure, did you use the RestAPI integration (v1) and not the recently released, cheaper HttpAPI integration (v2)? In case you are using the new HttpAPI integration you must use the events from (APIGateway.V2.Request
and APIGateway.V2.Response
) except otherwise enforced at the APIGateway level.
Hi,
I made a branch with the issue:
https://github.com/swift-sprinter/aws-serverless-swift-api-template/tree/APIGateway.Response
In this branch I made there is the issue with APIGateway.Request
I used at first APIGateway.V2.Request
but I got another error as well.
Note that the integration configuration is made by the Serverless
framework.
I'll check better, and send the full log.
Here the logs:
Sun May 31 21:07:47 UTC 2020 : Endpoint response body before transformations: {"errorType":"FunctionError","errorMessage":"requestDecoding(Swift.DecodingError.valueNotFound(Swift.Dictionary<Swift.String, Swift.String>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: \"headers\", intValue: nil)], debugDescription: \"Expected Dictionary<String, String> value but found null instead.\", underlyingError: nil)))"}
Sun May 31 21:07:47 UTC 2020 : Lambda execution failed with status 200 due to customer function error: requestDecoding(Swift.DecodingError.valueNotFound(Swift.Dictionary<Swift.String, Swift.String>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "headers", intValue: nil)], debugDescription: "Expected Dictionary<String, String> value but found null instead.", underlyingError: nil))). Lambda request id: bf4a2226-87e4-4f0e-a53f-b641c885a0f2
Sun May 31 21:07:47 UTC 2020 : Method completed with status: 502
Here the Cloud Formation created by the Serverless framework:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
},
"CreateProductLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/swift-sprinter-rest-api-swift-dev-createProduct"
}
},
"ReadProductLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/swift-sprinter-rest-api-swift-dev-readProduct"
}
},
"UpdateProductLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/swift-sprinter-rest-api-swift-dev-updateProduct"
}
},
"DeleteProductLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/swift-sprinter-rest-api-swift-dev-deleteProduct"
}
},
"ListProductsLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/swift-sprinter-rest-api-swift-dev-listProducts"
}
},
"IamRoleLambdaExecution": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": {
"Fn::Join": [
"-",
[
"dev",
"swift-sprinter-rest-api-swift",
"lambda"
]
]
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/swift-sprinter-rest-api-swift-dev*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/swift-sprinter-rest-api-swift-dev*:*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:UpdateItem",
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:DescribeTable"
],
"Resource": [
{
"Fn::GetAtt": [
"ProductsTable",
"Arn"
]
}
]
}
]
}
}
],
"Path": "/",
"RoleName": {
"Fn::Join": [
"-",
[
"swift-sprinter-rest-api-swift",
"dev",
{
"Ref": "AWS::Region"
},
"lambdaRole"
]
]
}
}
},
"SwiftDashlambdaDashruntimeLambdaLayer": {
"Type": "AWS::Lambda::LayerVersion",
"Properties": {
"Content": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/swift-lambda-runtime.zip"
},
"LayerName": "aws-swift-sprinter-lambda-runtime",
"Description": "AWS Lambda Custom Runtime for Swift-Sprinter"
}
},
"CreateProductLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/createProduct.zip"
},
"FunctionName": "swift-sprinter-rest-api-swift-dev-createProduct",
"Handler": "build/Products.create",
"MemorySize": 256,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "provided",
"Timeout": 6,
"Description": "[dev] Create Product",
"Environment": {
"Variables": {
"PRODUCTS_TABLE_NAME": "swift-sprinter-products-table-dev"
}
},
"Layers": [
{
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
]
},
"DependsOn": [
"CreateProductLogGroup",
"IamRoleLambdaExecution"
]
},
"CreateProductLambdaVersionFcAKu9BmWmV6XcT6zYMXiTOTzaPApEwvNt7kOFw25Q": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "CreateProductLambdaFunction"
},
"CodeSha256": "gefvnn7eGPkN6JLAHNRTxy6cB8BmmvpFJZ/W7ilyluM=",
"Description": "[dev] Create Product"
}
},
"ReadProductLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/readProduct.zip"
},
"FunctionName": "swift-sprinter-rest-api-swift-dev-readProduct",
"Handler": "build/Products.read",
"MemorySize": 256,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "provided",
"Timeout": 6,
"Description": "[dev] Get Product",
"Environment": {
"Variables": {
"PRODUCTS_TABLE_NAME": "swift-sprinter-products-table-dev"
}
},
"Layers": [
{
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
]
},
"DependsOn": [
"ReadProductLogGroup",
"IamRoleLambdaExecution"
]
},
"ReadProductLambdaVersionClu1XVGJNfVHTNn9CHbI4lADKenPdpXIa8xYenH5Y": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "ReadProductLambdaFunction"
},
"CodeSha256": "gefvnn7eGPkN6JLAHNRTxy6cB8BmmvpFJZ/W7ilyluM=",
"Description": "[dev] Get Product"
}
},
"UpdateProductLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/updateProduct.zip"
},
"FunctionName": "swift-sprinter-rest-api-swift-dev-updateProduct",
"Handler": "build/Products.update",
"MemorySize": 256,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "provided",
"Timeout": 6,
"Description": "[dev] Update Product",
"Environment": {
"Variables": {
"PRODUCTS_TABLE_NAME": "swift-sprinter-products-table-dev"
}
},
"Layers": [
{
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
]
},
"DependsOn": [
"UpdateProductLogGroup",
"IamRoleLambdaExecution"
]
},
"UpdateProductLambdaVersionVEf8POcBJrzwD3lJNxbquVWI9W7Bw5hH3EupbComduI": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "UpdateProductLambdaFunction"
},
"CodeSha256": "gefvnn7eGPkN6JLAHNRTxy6cB8BmmvpFJZ/W7ilyluM=",
"Description": "[dev] Update Product"
}
},
"DeleteProductLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/deleteProduct.zip"
},
"FunctionName": "swift-sprinter-rest-api-swift-dev-deleteProduct",
"Handler": "build/Products.delete",
"MemorySize": 256,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "provided",
"Timeout": 6,
"Description": "[dev] Delete Product",
"Environment": {
"Variables": {
"PRODUCTS_TABLE_NAME": "swift-sprinter-products-table-dev"
}
},
"Layers": [
{
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
]
},
"DependsOn": [
"DeleteProductLogGroup",
"IamRoleLambdaExecution"
]
},
"DeleteProductLambdaVersionYhN6H7XkSm7QdCjQiJAw7MV2vQOLS2FEuYh0nwwmus": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "DeleteProductLambdaFunction"
},
"CodeSha256": "gefvnn7eGPkN6JLAHNRTxy6cB8BmmvpFJZ/W7ilyluM=",
"Description": "[dev] Delete Product"
}
},
"ListProductsLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/swift-sprinter-rest-api-swift/dev/1590958619571-2020-05-31T20:56:59.571Z/listProducts.zip"
},
"FunctionName": "swift-sprinter-rest-api-swift-dev-listProducts",
"Handler": "build/Products.list",
"MemorySize": 256,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "provided",
"Timeout": 6,
"Description": "[dev] List Products",
"Environment": {
"Variables": {
"PRODUCTS_TABLE_NAME": "swift-sprinter-products-table-dev"
}
},
"Layers": [
{
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
]
},
"DependsOn": [
"ListProductsLogGroup",
"IamRoleLambdaExecution"
]
},
"ListProductsLambdaVersionDmh2m13atNPTocuEI0Vz9o1jkL12SByEJd3ocgWn48": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "ListProductsLambdaFunction"
},
"CodeSha256": "gefvnn7eGPkN6JLAHNRTxy6cB8BmmvpFJZ/W7ilyluM=",
"Description": "[dev] List Products"
}
},
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "dev-swift-sprinter-rest-api-swift",
"EndpointConfiguration": {
"Types": [
"EDGE"
]
},
"Policy": ""
}
},
"ApiGatewayResourceProducts": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"PathPart": "products",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayResourceProductsSkuVar": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Ref": "ApiGatewayResourceProducts"
},
"PathPart": "{sku}",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodProductsOptions": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"AuthorizationType": "NONE",
"HttpMethod": "OPTIONS",
"MethodResponses": [
{
"StatusCode": "200",
"ResponseParameters": {
"method.response.header.Access-Control-Allow-Origin": true,
"method.response.header.Access-Control-Allow-Headers": true,
"method.response.header.Access-Control-Allow-Methods": true
},
"ResponseModels": {}
}
],
"RequestParameters": {},
"Integration": {
"Type": "MOCK",
"RequestTemplates": {
"application/json": "{statusCode:200}"
},
"ContentHandling": "CONVERT_TO_TEXT",
"IntegrationResponses": [
{
"StatusCode": "200",
"ResponseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
"method.response.header.Access-Control-Allow-Methods": "'OPTIONS,GET,PUT,POST'"
},
"ResponseTemplates": {
"application/json": "#set($origin = $input.params(\"Origin\"))\n#if($origin == \"\") #set($origin = $input.params(\"origin\")) #end\n#if($origin.matches(\".*\")) #set($context.responseOverride.header.Access-Control-Allow-Origin = $origin) #end"
}
}
]
},
"ResourceId": {
"Ref": "ApiGatewayResourceProducts"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodProductsSkuVarOptions": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"AuthorizationType": "NONE",
"HttpMethod": "OPTIONS",
"MethodResponses": [
{
"StatusCode": "200",
"ResponseParameters": {
"method.response.header.Access-Control-Allow-Origin": true,
"method.response.header.Access-Control-Allow-Headers": true,
"method.response.header.Access-Control-Allow-Methods": true
},
"ResponseModels": {}
}
],
"RequestParameters": {},
"Integration": {
"Type": "MOCK",
"RequestTemplates": {
"application/json": "{statusCode:200}"
},
"ContentHandling": "CONVERT_TO_TEXT",
"IntegrationResponses": [
{
"StatusCode": "200",
"ResponseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'",
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
"method.response.header.Access-Control-Allow-Methods": "'OPTIONS,DELETE,GET'"
},
"ResponseTemplates": {
"application/json": "#set($origin = $input.params(\"Origin\"))\n#if($origin == \"\") #set($origin = $input.params(\"origin\")) #end\n#if($origin.matches(\".*\")) #set($context.responseOverride.header.Access-Control-Allow-Origin = $origin) #end"
}
}
]
},
"ResourceId": {
"Ref": "ApiGatewayResourceProductsSkuVar"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodProductsPost": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "POST",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProducts"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"CreateProductLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProductsSkuVarGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "GET",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProductsSkuVar"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"ReadProductLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProductsPut": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "PUT",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProducts"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"UpdateProductLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProductsSkuVarDelete": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "DELETE",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProductsSkuVar"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"DeleteProductLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProductsGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "GET",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProducts"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"ListProductsLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayDeployment1590958611566": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"StageName": "dev"
},
"DependsOn": [
"ApiGatewayMethodProductsOptions",
"ApiGatewayMethodProductsSkuVarOptions",
"ApiGatewayMethodProductsPost",
"ApiGatewayMethodProductsSkuVarGet",
"ApiGatewayMethodProductsPut",
"ApiGatewayMethodProductsSkuVarDelete",
"ApiGatewayMethodProductsGet"
]
},
"CreateProductLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"CreateProductLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"ReadProductLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"ReadProductLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"UpdateProductLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"UpdateProductLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"DeleteProductLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"DeleteProductLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"ListProductsLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"ListProductsLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"ProductsTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "swift-sprinter-products-table-dev",
"AttributeDefinitions": [
{
"AttributeName": "sku",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "sku",
"KeyType": "HASH"
}
],
"BillingMode": "PAY_PER_REQUEST"
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
}
},
"SwiftDashlambdaDashruntimeLambdaLayerQualifiedArn": {
"Description": "Current Lambda layer version",
"Value": {
"Ref": "SwiftDashlambdaDashruntimeLambdaLayer"
}
},
"CreateProductLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "CreateProductLambdaVersionFcAKu9BmWmV6XcT6zYMXiTOTzaPApEwvNt7kOFw25Q"
}
},
"ReadProductLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "ReadProductLambdaVersionClu1XVGJNfVHTNn9CHbI4lADKenPdpXIa8xYenH5Y"
}
},
"UpdateProductLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "UpdateProductLambdaVersionVEf8POcBJrzwD3lJNxbquVWI9W7Bw5hH3EupbComduI"
}
},
"DeleteProductLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "DeleteProductLambdaVersionYhN6H7XkSm7QdCjQiJAw7MV2vQOLS2FEuYh0nwwmus"
}
},
"ListProductsLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "ListProductsLambdaVersionDmh2m13atNPTocuEI0Vz9o1jkL12SByEJd3ocgWn48"
}
},
"ServiceEndpoint": {
"Description": "URL of the service endpoint",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "ApiGatewayRestApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/dev"
]
]
}
}
}
}
Okay, would you mind to deploy a simple String Lambda to your endpoint so that we can print the original event? This way we can have a look at what matched and what did not match.
import AWSLambdaRuntime
Lambda.run { (context, event: String, callback) in
context.logger.warning("event: \(event)")
callback(.success(#"{"statusCode": 200, "body": "event logged"}"#))
}
Sure, note that the error is clear. The 'header' dictionary is nil In fact in latest code I solved by just creating the event I needed:
public extension APIGateway {
struct SimpleRequest: Codable {
public let body: String?
public let pathParameters: [String: String]?
}
}
@Andrea-Scuderi I've seen the error. Yes, there is a problem... But if we just remove properties if there is a problem with one, we might end up with no properties overall...
I suggest to check with the AWS guys for the right specification of those events. The official documentation is quite unclear.
cc @bmoffatt
@Andrea-Scuderi Is this still an issue? If so do you have the incoming request payload handy?
@fabianfett Yes, it is still an issue.
Here the payload:
2020-08-08T12:31:34+0000 warning: lifecycleIteration=1 awsRequestID=acd877c6-e54f-47aa-bdc0-467bfa537b04 awsTraceID=Root=1-5f2e9b26-3d35d2de2ebafebee4ce1390;Parent=1dcca0a36cbb8e11;Sampled=0 event: {"resource":"/products","path":"/products","httpMethod":"GET","headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Encoding":"gzip, deflate, br","Accept-Language":"en-gb","CloudFront-Forwarded-Proto":"https","CloudFront-Is-Desktop-Viewer":"true","CloudFront-Is-Mobile-Viewer":"false","CloudFront-Is-SmartTV-Viewer":"false","CloudFront-Is-Tablet-Viewer":"false","CloudFront-Viewer-Country":"IT","Host":"tth4frgaw0.execute-api.us-east-1.amazonaws.com","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15","Via":"2.0 3a2b7bab76093d39e8da0874d82ee34d.cloudfront.net (CloudFront)","X-Amz-Cf-Id":"jYCwSHUHZ4FzIhvuv6ZiNmhAoROo607LWSFL7xJIZnGTvhZtlRMwjQ==","X-Amzn-Trace-Id":"Root=1-5f2e9b26-3d35d2de2ebafebee4ce1390","X-Forwarded-For":"37.179.128.97, 130.176.90.132","X-Forwarded-Port":"443","X-Forwarded-Proto":"https"},"multiValueHeaders":{"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],"Accept-Encoding":["gzip, deflate, br"],"Accept-Language":["en-gb"],"CloudFront-Forwarded-Proto":["https"],"CloudFront-Is-Desktop-Viewer":["true"],"CloudFront-Is-Mobile-Viewer":["false"],"CloudFront-Is-SmartTV-Viewer":["false"],"CloudFront-Is-Tablet-Viewer":["false"],"CloudFront-Viewer-Country":["IT"],"Host":["tth4frgaw0.execute-api.us-east-1.amazonaws.com"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15"],"Via":["2.0 3a2b7bab76093d39e8da0874d82ee34d.cloudfront.net (CloudFront)"],"X-Amz-Cf-Id":["jYCwSHUHZ4FzIhvuv6ZiNmhAoROo607LWSFL7xJIZnGTvhZtlRMwjQ=="],"X-Amzn-Trace-Id":["Root=1-5f2e9b26-3d35d2de2ebafebee4ce1390"],"X-Forwarded-For":["37.179.128.97, 130.176.90.132"],"X-Forwarded-Port":["443"],"X-Forwarded-Proto":["https"]},"queryStringParameters":null,"multiValueQueryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"qimgur","resourcePath":"/products","httpMethod":"GET","extendedRequestId":"Q80t-FPLoAMF7OQ=","requestTime":"08/Aug/2020:12:31:34 +0000","path":"/dev/products","accountId":"339065908707","protocol":"HTTP/1.1","stage":"dev","domainPrefix":"tth4frgaw0","requestTimeEpoch":1596889894141,"requestId":"6217a5c2-ff35-42bd-866c-f2b1ceefe6a6","identity":{"cognitoIdentityPoolId":null,"accountId":null,"cognitoIdentityId":null,"caller":null,"sourceIp":"37.179.128.97","principalOrgId":null,"accessKey":null,"cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":null,"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15","user":null},"domainName":"tth4frgaw0.execute-api.us-east-1.amazonaws.com","apiId":"tth4frgaw0"},"body":null,"isBase64Encoded":false}
printed by:
import AWSLambdaRuntime
let lambda: Lambda.StringClosure = { (context, event: String, callback) in
context.logger.warning("event: \(event)")
let result: Result<String, Error> = .success(#"{"statusCode": 200, "body": "event logged"}"#)
callback(result)
}
Lambda.run(lambda)
Hi @Andrea-Scuderi,
the payload, you uploaded is an APIGateway v1
payload. I've just created a test case for it, to verify that it is working... See attached code.
Does this solve your problem?
static let otherEventBody = """
{
"resource":"/products",
"path":"/products",
"httpMethod":"GET",
"headers":{
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding":"gzip, deflate, br",
"Accept-Language":"en-gb",
"CloudFront-Forwarded-Proto":"https",
"CloudFront-Is-Desktop-Viewer":"true",
"CloudFront-Is-Mobile-Viewer":"false",
"CloudFront-Is-SmartTV-Viewer":"false",
"CloudFront-Is-Tablet-Viewer":"false",
"CloudFront-Viewer-Country":"IT",
"Host":"tth4frgaw0.execute-api.us-east-1.amazonaws.com",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15",
"Via":"2.0 3a2b7bab76093d39e8da0874d82ee34d.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id":"jYCwSHUHZ4FzIhvuv6ZiNmhAoROo607LWSFL7xJIZnGTvhZtlRMwjQ==",
"X-Amzn-Trace-Id":"Root=1-5f2e9b26-3d35d2de2ebafebee4ce1390",
"X-Forwarded-For":"37.179.128.97, 130.176.90.132",
"X-Forwarded-Port":"443",
"X-Forwarded-Proto":"https"
},
"multiValueHeaders":{
"Accept":[
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
],
"Accept-Encoding":[
"gzip, deflate, br"
],
"Accept-Language":[
"en-gb"
],
"CloudFront-Forwarded-Proto":[
"https"
],
"CloudFront-Is-Desktop-Viewer":[
"true"
],
"CloudFront-Is-Mobile-Viewer":[
"false"
],
"CloudFront-Is-SmartTV-Viewer":[
"false"
],
"CloudFront-Is-Tablet-Viewer":[
"false"
],
"CloudFront-Viewer-Country":[
"IT"
],
"Host":[
"tth4frgaw0.execute-api.us-east-1.amazonaws.com"
],
"User-Agent":[
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15"
],
"Via":[
"2.0 3a2b7bab76093d39e8da0874d82ee34d.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id":[
"jYCwSHUHZ4FzIhvuv6ZiNmhAoROo607LWSFL7xJIZnGTvhZtlRMwjQ=="
],
"X-Amzn-Trace-Id":[
"Root=1-5f2e9b26-3d35d2de2ebafebee4ce1390"
],
"X-Forwarded-For":[
"37.179.128.97, 130.176.90.132"
],
"X-Forwarded-Port":[
"443"
],
"X-Forwarded-Proto":[
"https"
]
},
"queryStringParameters":null,
"multiValueQueryStringParameters":null,
"pathParameters":null,
"stageVariables":null,
"requestContext":{
"resourceId":"qimgur",
"resourcePath":"/products",
"httpMethod":"GET",
"extendedRequestId":"Q80t-FPLoAMF7OQ=",
"requestTime":"08/Aug/2020:12:31:34 +0000",
"path":"/dev/products",
"accountId":"339065908707",
"protocol":"HTTP/1.1",
"stage":"dev",
"domainPrefix":"tth4frgaw0",
"requestTimeEpoch":1596889894141,
"requestId":"6217a5c2-ff35-42bd-866c-f2b1ceefe6a6",
"identity":{
"cognitoIdentityPoolId":null,
"accountId":null,
"cognitoIdentityId":null,
"caller":null,
"sourceIp":"37.179.128.97",
"principalOrgId":null,
"accessKey":null,
"cognitoAuthenticationType":null,
"cognitoAuthenticationProvider":null,
"userArn":null,
"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15",
"user":null
},
"domainName":"tth4frgaw0.execute-api.us-east-1.amazonaws.com",
"apiId":"tth4frgaw0"
},
"body":null,
"isBase64Encoded":false
}
"""
func testRequestDecodingOtherRequest() {
let data = APIGatewayTests.otherEventBody.data(using: .utf8)!
var req: APIGateway.Request?
XCTAssertNoThrow(req = try JSONDecoder().decode(APIGateway.Request.self, from: data))
}
@fabianfett Hi,
I am working on API Services with API Gateway(Http Api), Lambda and DynamoDB.
I have created a Custom Authoriser in swift using swift lambda runtime
I am using APIGateway.V2.Request as payload but I wasn't able to decode it as isBase64Encoded
property is nil from Api Gateway payload but In APIGateway.V2.Request object it is must so I am getting error Swift.DecodingError.keyNotFound
:
2021-10-15T11:29:30+0000 warning Lambda : lifecycleIteration=1 lambda handler returned an error: requestDecoding(Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "isBase64Encoded", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"isBase64Encoded\", intValue: nil) (\"isBase64Encoded\").", underlyingError: nil)))
for example my authoriser code is:
import AWSLambdaRuntime
import AWSLambdaEvents
Lambda.run { (context, request: APIGateway.V2.Request, callback: @escaping (Result<APIGateway.V2.Response, Error>) -> Void) in
let responseBody = """
{
"isAuthorized": true,
"context": {
"exampleKey": "exampleValue"
}
}
"""
callback(.success(APIGateway.V2.Response(statusCode: .ok, body:responseBody)))
}
Currently I am using my own copy of ApiRequest with optional isBase64Encoded
. Is there a better approach to fix it?
I am using Http api authorise with simple response, I have looked into aws docs here
https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html
and found that in payload version 2.0 they are not sending isBase64Encoded
as well
Authorizer-Payload-2.0.txt
I'll prepare a PR to fix it , if you agree to amend it.
@vikas4goyal I think we should have special request and response types for the Lambda authorizers. I'd be happy to review such a pr. However please raise this pr against this repo, since all new event development is done there:
https://github.com/swift-server/swift-aws-lambda-events
If you have any questions please reach out!
Closing this.
@Andrea-Scuderi please reopen an issue on the Lambda event project if still an issue for you (or even better, send a PR :-) )
There is no harm to set headers
and multiValueHeaders
as optional in the Swift struct to accommodate for different payload. There is no AWS doc that mention if the fields are mandatory or not in the request (it does for the response only)
@vikas4goyal There is now a type for Lambda authorizers in the Lambda event project
https://github.com/swift-server/swift-aws-lambda-events/issues
Hi, I made a small demo using Serverless framework with API Gateway, Lambda and DynamoDB.
Originally I tried to use
APIGateway.Request
as payload but I wasn't able to decode it as some of the properties are nil.https://github.com/swift-server/swift-aws-lambda-runtime/blob/master/Sources/AWSLambdaEvents/APIGateway.swift
I'll prepare a PR to fix it, if you agree to amend it.
My example: https://github.com/swift-sprinter/aws-serverless-swift-api-template