Issue Running Google Lighthouse in Docker Container - amazon-web-services

I'm attempting to run lighthouse in a Docker container for eventual deployment to AWS Lambda (using its new docker image lambda deployments). However I'm getting an error that I can't seem to understand.
The following is my dockerfile
FROM amazon/aws-lambda-nodejs:12
ENV AWS_LAMBDA_FUNCTION_MEMORY_SIZE=10240
RUN curl https://intoli.com/install-google-chrome.sh | bash
copy package.json .
RUN npm install
COPY app.js .
CMD ["app.handler"]
The following is my app.js
const chromeLauncher = require("chrome-launcher");
const lighthouse = require("lighthouse");
exports.handler = async (event) => {
const chrome = await chromeLauncher.launch({
logLevel: "info",
chromeFlags: [
"--headless",
"--no-sandbox",
"--disable-gpu",
"--disable-dev-shm-usage",
"--single-process",
],
});
const results = await lighthouse("https://example.com", {
port: chrome.port,
disableStorageReset: true,
onlyCategories: ["performance"],
logLevel: "info",
});
return {
statusCode: 200,
results,
};
};
and the following is the log output. It seems to be able to connect to lighthouse but then is hanging on the first command it sends/
START RequestId: c9d7a07b-a5e2-4d03-8bf5-d0b5d248e3e7 Version: $LATEST
Sun, 24 Jan 2021 16:00:56 GMT ChromeLauncher Waiting for browser.
Sun, 24 Jan 2021 16:00:56 GMT ChromeLauncher Waiting for browser...
Sun, 24 Jan 2021 16:00:57 GMT ChromeLauncher Waiting for browser.....
Sun, 24 Jan 2021 16:00:57 GMT ChromeLauncher Waiting for browser.....✓
Sun, 24 Jan 2021 16:00:58 GMT status Connecting to browser
Sun, 24 Jan 2021 16:00:58 GMT status Resetting state with about:blank
Sun, 24 Jan 2021 16:01:28 GMT status Disconnecting from browser...
2021-01-24T16:01:28.172Z c9d7a07b-a5e2-4d03-8bf5-d0b5d248e3e7 ERROR Invoke Error {"errorType":"LHError","errorMessage":"PROTOCOL_TIMEOUT","code":"PROTOCOL_TIMEOUT","name":"LHError","friendlyMessage":"Waiting for DevTools protocol response has exceeded the allotted time. (Method: Network.enable)","lhrRuntimeError":true,"protocolMethod":"Network.enable","stack":["LHError: PROTOCOL_TIMEOUT"," at Timeout._onTimeout (/var/task/node_modules/lighthouse/lighthouse-core/gather/driver.js:409:21)"," at listOnTimeout (internal/timers.js:554:17)"," at processTimers (internal/timers.js:497:7)"]}
END RequestId: c9d7a07b-a5e2-4d03-8bf5-d0b5d248e3e7
REPORT RequestId: c9d7a07b-a5e2-4d03-8bf5-d0b5d248e3e7 Init Duration: 3.53 ms Duration: 33422.31 ms Billed Duration: 33500 ms Memory Size: 2010240 MB Max Memory Used: 2010240 MB

To run on AWS Function Lambda you should use
const chromium = require("chrome-aws-lambda");
const chrome = await chromium.puppeteer.launch({
args: [
...chromium.args,
"--disable-dev-shm-usage",
"--remote-debugging-port=9222",
],
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
const options = {
logLevel: "info",
output: "html",
onlyCategories: ["performance"],
preset: "mobile",
port: 9222,
};
const runnerResult = await lighthouse(`${body.url}`, options);
But, I'm facing errors in the performance category, there is no way to make it works, All the others categories work fine.

Related

aws api gateway return "internal server error" if response body is not stringify

I have the following handler code:
export default function handler(lambda) {
return function (event, context) {
return Promise.resolve()
// Run the Lambda
.then(() => lambda(event, context))
// On success
.then((responseBody) => [200, responseBody])
// On failure
.catch((e) => {
return [500, { error: e.message }];
})
// Return HTTP response
.then(([statusCode, body]) => ({
statusCode,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
},
body: JSON.stringify(body),
}))
// On failure
.catch((e) => {
return [500, { error: e.message }];
});
};
}
whenever I created a new Lambda function, I import this code and write the code base on it, like this:
import handler from "../libs/handler-lib";
export const main = handler(async (event, context) => {
"my code"
}
which works like a charm, but the problem is, I need to access the response body in aws stepfunctions to determine which function to execute next, so I change
body: JSON.stringify(body), to body: body,
Then every function works only in the lambda function console but fails if the lambda is triggered by the aws api gateway.
I think I am missing something obvious here but I cant tell.
Thanks in advance.
here is the full api gateway log:
Execution log for request 8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12
Sun Nov 15 16:00:51 UTC 2020 : Starting execution for request: 8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12
Sun Nov 15 16:00:51 UTC 2020 : HTTP Method: GET, Resource Path: /latest-patch-json-data
Sun Nov 15 16:00:51 UTC 2020 : Method request path: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request query string: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request headers: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request body before transformations:
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request URI: https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxxxx:function:tactics-api-dev-getLatestPatchJsonData/invocations
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request headers: {x-amzn-lambda-integration-tag=8677ed2f-5e9e-4913-xxxxxxxx, Authorization=*****************************************************************************************************************************************************************************************************************************************************************************************************************************7603ee, X-Amz-Date=20201115T160051Z, x-amzn-apigateway-api-id=f8brp6pbai, X-Amz-Source-Arn=arn:aws:execute-api:ap-northeast-1:296545255814:f8brp6pbai/test-invoke-stage/GET/latest-patch-json-data, Accept=application/json, User-Agent=AmazonAPIGateway_f8brp6pbai, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEIf//////////wEaDmFwLW5vcnRoZWFzdC0xIkgwRgIhALgraV2CJa+NzHo/sSqH4MgbwVveHpse1s9DNkN5B/5JAiEAnghcO0tmUqhMhB+PxZ/lWbApusJKxJSM1zoIAtqFHycqxwMI8P//////////ARABGgw5NjkyMzY4NTQ2MjYiDI4NEh+wCOuUEqvN/iqbA+UIa11YzWxKIHWZ5hBsK7z/zSPD7Jz9S/3SH3L1M/IpjgbFC3g/y7B+tDj1lZ5nuzXbqlpg092hCt5JR1otT7w45xu0yQdZwcVPT9LV [TRUNCATED]
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request body after transformations: {"resource":"/latest-patch-json-data","path":"/latest-patch-json-data","httpMethod":"GET","headers":null,"multiValueHeaders":null,"queryStringParameters":null,"multiValueQueryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"b31s1i","resourcePath":"/latest-patch-json-data","httpMethod":"GET","extendedRequestId":"WDmMCE8mtjMFqQg=","requestTime":"15/Nov/2020:16:00:51 +0000","path":"/latest-patch-json-data","accountId":"296545255814","protocol":"HTTP/1.1","stage":"test-invoke-stage","domainPrefix":"testPrefix","requestTimeEpoch":1605456051327,"requestId":"8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12","identity":{"cognitoIdentityPoolId":null,"cognitoIdentityId":null,"apiKey":"test-invoke-api-key","principalOrgId":null,"cognitoAuthenticationType":null,"userArn":"arn:aws:iam::296545255814:root","apiKeyId":"test-invoke-api-key-id","userAgent":"aws-internal/3 aws-sdk-java/1.11.864 Linux/4.9.217-0.3.ac.206.84.332.metal1.x86_64 OpenJD [TRUNCATED]
Sun Nov 15 16:00:51 UTC 2020 : Sending request to https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:tactics-api-dev-getLatestPatchJsonData/invocations
Sun Nov 15 16:00:52 UTC 2020 : Received response. Status: 200, Integration latency: 805 ms
Sun Nov 15 16:00:52 UTC 2020 : Endpoint response headers: {Date=Sun, 15 Nov 2020 16:00:52 GMT, Content-Type=application/json, Content-Length=284, Connection=keep-alive, x-amzn-RequestId=753115e8-0264-4517-9a2a-f67f045b7011, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5fb150b3-b7a44f36147dc1a10b321a85;sampled=0}
Sun Nov 15 16:00:52 UTC 2020 : Endpoint response body before transformations: {"statusCode":200,"headers":{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":true},"body":{"latestPatchVersion":"10.23","message":"PDH is up-to-date, PDH record: cc01f980-273a-11eb-b79f-676006f2be2a lastCheck time: 2020-11-15T16:00:51.996Z","type":"skip update"}}
Sun Nov 15 16:00:52 UTC 2020 : Execution failed due to configuration error: Malformed Lambda proxy response
Sun Nov 15 16:00:52 UTC 2020 : Method completed with status: 502
For lambda proxy, the lambda return object has to be of this shape :
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
That means, the body needs to be a string, not an object.

How to pass the query string parameters in the AWS API Gateway?

Following is my Lambda handler which is expecting the users data from the queryStringParameters:-
export const lambdaHandler = async (event, context) => {
try {
const numberOfUsersRequested= (event && event.queryStringParameters.users) ? event.queryStringParameters.users : 10;
const users = await generateUsers(numberOfUsersRequested).then(data => data.users);
I'm using AWS SAM to develop my Lambda and I can test it very well using the event.json as an input event to this Lambda locally. Here is a chunk of event.json where I'm passing the queryStringParamters users like this:-
{
"body": "{\"message\": \"mock data\"}",
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": false,
"queryStringParameters": {
"users": 200
},
Now, may I know how to pass the same QueryStringParameters from the AWS API Gateway console. Currently, I'm getting this 500 error on the AWS console for the API Gateway:-
{
"message": "Internal server error"
}
Mon Sep 28 01:24:15 UTC 2020 : Endpoint response headers: {Date=Mon, 28 Sep 2020 01:24:15 GMT, Content-Type=application/json, Content-Length=2, Connection=keep-alive, x-amzn-RequestId=0e1f110c-e80c-4ff1-870a-5cafd04167db, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5f713b3d-4762f9b07ee8c1d7c6623574;sampled=0}
Mon Sep 28 01:24:15 UTC 2020 : Endpoint response body before transformations: {}
Mon Sep 28 01:24:15 UTC 2020 : Execution failed due to configuration error: Output mapping refers to an invalid method response: 200
Mon Sep 28 01:24:15 UTC 2020 : Method completed with status: 500
I have performed the following steps to mitigate the issue but not working. It looks like something is missing :-
1)Added the url query string parameters as users in the Method Request (refer screenshot)
In the Integration Request -> Mapping Templates, added the mapping as application/json:-
{
"users": "$input.params('users')"
}
And finally passing the query string as users=6.
In your case, the event should just be:
{
"users": "6"
}
You can add the following to the beginning of your handler to confirm:
console.log(JSON.stringify(event, null, 2));
Therefore, to get users value you should just use event.users, not event.queryStringParameters.users.

AWS API Gateway Cannot GET / when function sleeped for long time

My current stack is AWS API Gateway --> AWS Lambda --> swagger-node + swagger-express-mw + aws-serverless-express.
So my Swagger API is hosted as one node.js Lambda Function and Invoked with aws_proxy from API Gateway. This works quite good. The only thing is that when the function sleeped for too long (cold start?) I get a Cannot GET / as Output from every URL I am calling first. From 2nd Request on, it runs very fast. Any ideas on that?
I don't think that it comes from API Gateway Integration Timeout as that are 30 seconds. The slowest invocation time of the function directly via lambda is around 2,5s and when it is called more often it is normally not more than 150ms. I also increased the Time of Lambda Timeout for that function to 10s so from there should also not come an error.
Logs from Test Request via API Gateway first Invocation
Response Body
Cannot GET /hello
Response Headers
{
"x-powered-by": "Express",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "18",
"date": "Sun, 19 Feb 2017 15:00:11 GMT",
"connection": "close",
"X-Amzn-Trace-Id": "<TRACE-ID>"
}
Logs
Execution log for request test-request
Sun Feb 19 15:00:07 UTC 2017 : Starting execution for request: test-invoke-request
Sun Feb 19 15:00:07 UTC 2017 : HTTP Method: GET, Resource Path: /hello
Sun Feb 19 15:00:07 UTC 2017 : Method request path: {}
Sun Feb 19 15:00:07 UTC 2017 : Method request query string: {}
Sun Feb 19 15:00:07 UTC 2017 : Method request headers: {}
Sun Feb 19 15:00:07 UTC 2017 : Method request body before transformations:
Sun Feb 19 15:00:07 UTC 2017 : Endpoint request URI: https://lambda.eu-central-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:eu-central-1:<ACCOUNT-ID>:function:api/invocations
Sun Feb 19 15:00:07 UTC 2017 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=**************************************************************************************************************************************************************************************************************************************************************************************************************************4b0637, X-Amz-Date=20170219T150007Z, x-amzn-apigateway-api-id=965h04axki, Accept=application/json, User-Agent=AmazonAPIGateway_965h04axki, X-Amz-Security-Token=<SECURITY-TOKEN>
Sun Feb 19 15:00:07 UTC 2017 : Endpoint request body after transformations: {"resource":"/hello","path":"/hello","httpMethod":"GET","headers":null,"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"accountId":"<ACCOUNT-ID>","resourceId":"ll6gw8","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"<ACCOUNT-ID>","cognitoIdentityId":null,"caller":"<ACCOUNT-ID>","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","accessKey":"<ACCESS-ID>","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::<ACCOUNT-ID>:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"<ACCOUNT-ID>"},"resourcePath":"/hello","httpMethod":"GET","apiId":"965h04axki"},"body":null,"isBase64Encoded":false}
Sun Feb 19 15:00:11 UTC 2017 : Endpoint response body before transformations: {"statusCode":404,"body":"Cannot GET /hello\n","headers":{"x-powered-by":"Express","x-content-type-options":"nosniff","content-type":"text/html; charset=utf-8","content-length":"18","date":"Sun, 19 Feb 2017 15:00:11 GMT","connection":"close"},"isBase64Encoded":false}
Sun Feb 19 15:00:11 UTC 2017 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=19f8554e-f6b4-11e6-8184-d3ccf0ccf643, Connection=keep-alive, Content-Length=267, Date=Sun, 19 Feb 2017 15:00:11 GMT, Content-Type=application/json}
Sun Feb 19 15:00:11 UTC 2017 : Method response body after transformations: Cannot GET /hello
Sun Feb 19 15:00:11 UTC 2017 : Method response headers: {x-powered-by=Express, x-content-type-options=nosniff, content-type=text/html; charset=utf-8, content-length=18, date=Sun, 19 Feb 2017 15:00:11 GMT, connection=close, X-Amzn-Trace-Id=Root=1-58a9b2f7-91fc7371e41d6ae9c2fbf64d}
Sun Feb 19 15:00:11 UTC 2017 : Successfully completed execution
Sun Feb 19 15:00:11 UTC 2017 : Method completed with status: 404
Logs from Test Request via API Gateway second Invocation
Response Body
"Hello, stranger!"
Response Headers
{
"x-powered-by": "Express",
"access-control-allow-origin": "*",
"content-type": "application/json; charset=utf-8",
"content-length": "18",
"etag": "W/\"12-E1p7iNXxJ4trMdmFBhlU9Q\"",
"date": "Mon, 13 Feb 2017 20:12:36 GMT",
"connection": "close",
"X-Amzn-Trace-Id": "<Trace-ID>"
}
Logs
Execution log for request test-request
Mon Feb 13 20:12:36 UTC 2017 : Starting execution for request: test-invoke-request
Mon Feb 13 20:12:36 UTC 2017 : HTTP Method: GET, Resource Path: /hello
Mon Feb 13 20:12:36 UTC 2017 : Method request path: {}
Mon Feb 13 20:12:36 UTC 2017 : Method request query string: {}
Mon Feb 13 20:12:36 UTC 2017 : Method request headers: {}
Mon Feb 13 20:12:36 UTC 2017 : Method request body before transformations:
Mon Feb 13 20:12:36 UTC 2017 : Endpoint request URI: https://lambda.eu-central-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:eu-central-1:<LAMBDA-FUNCTION-ID>:function:api/invocations
Mon Feb 13 20:12:36 UTC 2017 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=*******************************************************************************************************************************************************************************************************************************************************************************************************************************************3e1b18, X-Amz-Date=20170213T201236Z, x-amzn-apigateway-api-id=965h04axki, X-Amz-Source-Arn=arn:aws:execute-api:eu-central-1:<ACCOUNT-ID>:965h04axki/null/GET/hello, Accept=application/json, User-Agent=AmazonAPIGateway_965h04axki, X-Amz-Security-Token=<TOKEN>
Mon Feb 13 20:12:36 UTC 2017 : Endpoint request body after transformations: {"resource":"/hello","path":"/hello","httpMethod":"GET","headers":null,"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"accountId":"<ACCOUNT-ID>","resourceId":"ll6gw8","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"<ACCOUNT-ID>","cognitoIdentityId":null,"caller":"427402682812","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","accessKey":"<ACCESS-KEY>","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::<ACCOUNT-ID>:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"<ACCOUNT-ID>"},"resourcePath":"/hello","httpMethod":"GET","apiId":"965h04axki"},"body":null,"isBase64Encoded":false}
Mon Feb 13 20:12:36 UTC 2017 : Endpoint response body before transformations: {"statusCode":200,"body":"\"Hello, stranger!\"","headers":{"x-powered-by":"Express","access-control-allow-origin":"*","content-type":"application/json; charset=utf-8","content-length":"18","etag":"W/\"12-E1p7iNXxJ4trMdmFBhlU9Q\"","date":"Mon, 13 Feb 2017 20:12:36 GMT","connection":"close"},"isBase64Encoded":false}
Mon Feb 13 20:12:36 UTC 2017 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=c3354327-f228-11e6-8c1d-ed11cc413770, Connection=keep-alive, Content-Length=315, Date=Mon, 13 Feb 2017 20:12:36 GMT, Content-Type=application/json}
Mon Feb 13 20:12:36 UTC 2017 : Method response body after transformations: "Hello, stranger!"
Mon Feb 13 20:12:36 UTC 2017 : Method response headers: {x-powered-by=Express, access-control-allow-origin=*, content-type=application/json; charset=utf-8, content-length=18, etag=W/"12-E1p7iNXxJ4trMdmFBhlU9Q", date=Mon, 13 Feb 2017 20:12:36 GMT, connection=close, X-Amzn-Trace-Id=Root=1-58a21334-8ea6c4b5944eebb873bc7d2e}
Mon Feb 13 20:12:36 UTC 2017 : Successfully completed execution
Mon Feb 13 20:12:36 UTC 2017 : Method completed with status: 200
I think the response "Cannot GET /" is coming from your Lambda function itself. Can you check API Gateway CW logs (or Test Invoke feature in console) to see what's different in the integration request and response in the first call?
I didn't see any real documentation about it (just this Medium post) but I also experienced the fact that a Lambda can be frozen until the first invocation, or in the case it's not called for a long time.
A solution is to schedule a regular invocation to wake up your lambda, with Amazon CloudWatch Events
I know that is an old question, but if you use TypeORM (or more in general, if you wrap all your Express middlewares within a .then() callback of a Promise), and you use context.callbackWaitsForEmptyEventLoop = false in your lambda handler, maybe this could help you: https://github.com/typeorm/typeorm/issues/5894
Long story short: avoid to set that flag to false, if possible, otherwise avoid to wrap the Express middlewares within the .then() callback and, for instance, initialize your db connection in the first Express middleware.

HTTP request body not getting to AWS lambda function via AWS API Gateway

I have a very basic lambda function written in Scala deployed to AWS Lambda. The function works just fine when I test it via the AWS Lambda console.
Here's the function with some additional logging added for debug purposes.
package com.spacecorpshandbook.ostium.lambda.handler
import java.util
import com.google.gson.Gson
import temp.{ApiGatewayProxyResponse, Appointment, CancelResponse}
/**
* Amazon Lambda handler adapter for the Cancellation application
*/
class CancellationHandler {
def cancelAppointment(appointment: Appointment): ApiGatewayProxyResponse = {
System.out.println("++++ appointmentId is: " + appointment.getAppointmentId)
val apiGatewayProxyResponse = new ApiGatewayProxyResponse
val cancelResponse = new CancelResponse
cancelResponse.setMessage("Cancelled appointment with id " + appointment.getAppointmentId)
val gson: Gson = new Gson
apiGatewayProxyResponse.setBody(gson.toJson(cancelResponse))
apiGatewayProxyResponse.setStatusCode("200")
val headerValues = new util.HashMap[String, String]
headerValues put("Content-Type", "application/json")
apiGatewayProxyResponse.setHeaders(headerValues)
System.out.println("+++++ message before returning: " + apiGatewayProxyResponse.getBody)
apiGatewayProxyResponse
}
}
I was concerned that the POJO input/outputs being Scala beans might have been causing issues so I implemented Java versions temporarily just to rule that out.
The Integration Request on AWS API gateway is setup by default as Resources ANY with Lambda Proxy Integration enabled. Note that in this configuration when I test from the AWS API Gateway console the data is transformed and goes in but does't make it all the way to the lambda function
Execution log for request test-request
Fri Dec 09 11:14:40 UTC 2016 : Starting execution for request: test-invoke-request
Fri Dec 09 11:14:40 UTC 2016 : HTTP Method: PUT, Resource Path: /cancel-appointment
Fri Dec 09 11:14:40 UTC 2016 : Method request path: {}
Fri Dec 09 11:14:40 UTC 2016 : Method request query string: {}
Fri Dec 09 11:14:40 UTC 2016 : Method request headers: {Content-Type= application/json}
Fri Dec 09 11:14:40 UTC 2016 : Method request body before transformations: {
"applicationId": "asdfsfa"
}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************5c044d, X-Amz-Date=20161209T111440Z, x-amzn-apigateway-api-id=l5tcmj0vlk, Accept=application/json, User-Agent=AmazonAPIGateway_l5tcmj0vlk, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=857a062940a7fbb8134bad1c007e9975a10bd8323c39f6040e797a98e87ea1f6, X-Amzn-Trace-Id=Root=1-584a9220-9cd537954952cca7daee32bf, Content-Type=application/json}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint request body after transformations: {"resource":"/cancel-appointment","path":"/cancel-appointment","httpMethod":"PUT","headers":{"Content-Type":" application/json"},"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"accountId":"456204981758","resourceId":"xznq3u","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"456204981758","cognitoIdentityId":null,"caller":"456204981758","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","accessKey":"ASIAJ5D7KU524H7CTTTQ","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::456204981758:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"456204981758"},"resourcePath":"/cancel-appointment","httpMethod":"PUT","apiId":"l5tcmj0vlk"},"body":"{\n \"applicationId\": \"asdfsfa\"\n}","isBase64Encoded":false}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint response body before transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id null\"}"}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=adcadf25-be00-11e6-8855-75e96d772946, Connection=keep-alive, Content-Length=128, Date=Fri, 09 Dec 2016 11:14:39 GMT, Content-Type=application/json}
Fri Dec 09 11:14:40 UTC 2016 : Method response body after transformations: {"message":"Cancelled appointment with id null"}
Fri Dec 09 11:14:40 UTC 2016 : Method response headers: {Content-Type=application/json, X-Amzn-Trace-Id=Root=1-584a9220-9cd537954952cca7daee32bf}
Fri Dec 09 11:14:40 UTC 2016 : Successfully completed execution
Fri Dec 09 11:14:40 UTC 2016 : Method completed with status: 200
If I add a specific method, like POST and do not set it as a Lambda Proxy Integration I do indeed see the that the provide request body data makes it to the lambda function, is correctly de-serialized into my POJO and is returned
Execution log for request test-request
Fri Dec 09 11:22:02 UTC 2016 : Starting execution for request: test-invoke-request
Fri Dec 09 11:22:02 UTC 2016 : HTTP Method: POST, Resource Path: /cancel-appointment
Fri Dec 09 11:22:02 UTC 2016 : Method request path: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request query string: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request headers: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request body before transformations: {
"appointmentId" : "sfssdf"
}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************a8dc41, X-Amz-Date=20161209T112202Z, x-amzn-apigateway-api-id=l5tcmj0vlk, Accept=application/json, User-Agent=AmazonAPIGateway_l5tcmj0vlk, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=875dad4d4e05f8c12a7ca8aeaf69046d4153fc7f910e1eff1959cb011e8313a0, X-Amzn-Trace-Id=Root=1-584a93da-f841704d9feb371b31e41cb9, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint request body after transformations: {
"appointmentId" : "sfssdf"
}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint response body before transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id sfssdf\"}"}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=b4f5efce-be01-11e6-91c3-5b1e06f831e2, Connection=keep-alive, Content-Length=130, Date=Fri, 09 Dec 2016 11:22:02 GMT, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Method response body after transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id sfssdf\"}"}
Fri Dec 09 11:22:02 UTC 2016 : Method response headers: {X-Amzn-Trace-Id=Root=1-584a93da-f841704d9feb371b31e41cb9, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Successfully completed execution
Fri Dec 09 11:22:02 UTC 2016 : Method completed with status: 200
So everything looks great now, however when I actually do a real test against the AWS API URL from PostMan using HTTP method POST I get the response with null as the appointment Id and I can see in the CloudWatch logs that the appointmentId did not get set on the input Appointment object.
I feel like I'm missing something basic here. Any help would be greatly appreciated.
source code can be found here
Update
Resolved this problem by switching the lambda handler function to use Stream rather than attempting to serialize/deserialize JSON into POJOs. When using the API Gateway Lambda Proxy the input to handler is a complicated JSON structure that I didn't want to try and replicate as a Java/Scala class. It was easier to process the input as a stream, parse it into a JsonObject, and then convert the body of the message into my POJO using Gson or equivalent library. Sample handler below, you can also see a larger example here
class CancellationHandler {
def cancelAppointment(request: InputStream, response: OutputStream, context: Context): Unit = {
val logger = context.getLogger
val parser: JsonParser = new JsonParser
var inputObj: JsonObject = null
val gson: Gson = new Gson
try {
inputObj = parser.parse(IOUtils.toString(request, "UTF-8")).getAsJsonObject
} catch {
case e: IOException =>
logger.log("Error while reading request\n" + e.getMessage)
throw new RuntimeException(e.getMessage)
}
val body: String = inputObj.get("body").getAsString
val appointment: Appointment = gson.fromJson(body, classOf[Appointment])
val apiGatewayProxyResponse = new ApiGatewayProxyResponse
val cancelResponse = new CancelResponse
cancelResponse.setMessage("Cancelled appointment with id " + appointment.getAppointmentId)
apiGatewayProxyResponse.setBody(gson.toJson(cancelResponse))
apiGatewayProxyResponse.setStatusCode("200")
val headerValues = new util.HashMap[String, String]
headerValues put("Content-Type", "application/json")
apiGatewayProxyResponse.setHeaders(headerValues)
val output: String = gson.toJson(apiGatewayProxyResponse)
IOUtils.write(output, response, "UTF-8")
}
}
The input shape for the Lambda proxy will be different than the shape for the regular non-proxy Lambda integration. This is important for your use case of course because you're using Java/Scala where you have to explicitly structure the input POJO.
Here's what the proxy input will look like:
{
"resource": "\/pets",
"path": "\/pets",
"httpMethod": "POST",
"headers": null,
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
...
"stage": "test-invoke-stage",
"requestId": "test-invoke-request",
"identity": {
...
},
"resourcePath": "\/pets",
"httpMethod": "POST"
},
"body": "{\n \"foo\":\"bar\"\n}", <---- here's what you're looking for
"isBase64Encoded": false
}
Docs: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

Getting URL Parameter from API Gateway in Lambda

I am hoping that someone can help me get a URL Parameter passed from the AWS API Gateway in Lambda.
I am a little confused how to access this inside the Lambda code.
For the API Gateway I have followed these steps:
1) Went to Resources -> Integration Request
2) Clicked on the plus icon next to templates dropdown
3) Explicitly typed application/json in the content-type field
4) For input mapping Entered:
{ "apiToken": "$input.params('apiToken')" }
So for clarification, How do I access the parameter apiToken in my Lambda Function?
I have been looking at the walkthroughts and other docs for a while now and my brain is a little frazzled.
I appreciate all help and feedback :)
Update
So, the issue seemed to be that the AWS website AJAX save did not save what I entered in the mapping (I am on hotel wifi so could be that).
Now, When I test it I get:
Received event: { appToken: '123456' }
In the console log for lambda. However, When I go to the API Gateway, and deploy The response is:
"errorMessage": "Process exited before completing request"
Any Ideas?
P.S The function works in the Lambda Console however, when I test it using Postman [POST + Param] it does not work, with the error message supplied just above
Update 2
Here is my Entire Lambda Function
console.log('Loading event');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var http = require('http');
exports.handler = function(event, context) {
var tableName = "clients";
var appToken = event.params.appToken; //gr5f4sgnca25hki98
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: appToken } }
}, function (err, data)
{
if (err) {
context.done(err);
} else {
var url = data.Item.url.S + data.Item.api.S;
console.log(url);
console.log('Received event:', event.params.appToken);
http.get(url, function(res) {
// Continuously update stream with data
var body = '';
res.on('data', function(d) {
body += d;
});
res.on('end', function() {
var result = body.replace('\\', '');
context.succeed(JSON.parse(result));
});
res.on('error', function(e) {
context.fail("Got error: " + e.message);
});
});
}
});
};
In my test event I have:
{
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
When I run this in Lambda the respnse works, it connects ro an external JSON file and returns it.
However, In Postman I get the error:
"errorMessage": "Process exited before completing request"
Can anyone see why in the console it all works fine and when I try it out in deployment I still get the error?
When I open XCODE and test this url with Alamofire I get this error (if it helps)
Invalid Sequence around character 72
Update 3 (Test from API Console)
When tested from the API console, using:
{
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
in the Request Body. I get:
Request: /sendtoclient
Status: 200
Latency: 1839 ms
Response Body
{ "errorMessage": "Process exited before completing request" }
Response Headers
{"Content-Type":"application/json"}
Logs
Execution log for request test-request
Tue Mar 22 17:28:27 UTC 2016 : Starting execution for request: test-invoke-request
Tue Mar 22 17:28:27 UTC 2016 : HTTP Method: POST, Resource Path: /sendtoclient
Tue Mar 22 17:28:27 UTC 2016 : Method request path: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request query string: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request headers: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request body before transformations: {
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
Tue Mar 22 17:28:27 UTC 2016 : Endpoint request body after transformations: { "appToken": "" }
Tue Mar 22 17:28:29 UTC 2016 : Endpoint response body before transformations: {"errorMessage":"Process exited before completing request"}
Tue Mar 22 17:28:29 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=123456789, Connection=keep-alive, Content-Length=59, X-Amz-Function-Error=Unhandled, Date=Tue, 22 Mar 2016 17:28:29 GMT, Content-Type=application/json}
Tue Mar 22 17:28:29 UTC 2016 : Method response body after transformations: {"errorMessage":"Process exited before completing request"}
Tue Mar 22 17:28:29 UTC 2016 : Method response headers: {Content-Type=application/json}
Tue Mar 22 17:28:29 UTC 2016 : Successfully completed execution
Tue Mar 22 17:28:29 UTC 2016 : Method completed with status: 200
I'm not sure how to decipher this. Any help would be very much appreciated. I have however noticed this:
Endpoint request body after transformations: { "appToken": "" }
Which for some reason I feel may be significant
You should be using 'event.apiToken' in your function based upon the mapping you gave us. {apiToken} is in your resource name right?