I make a lambda function as RestApi with below cloudformation template:
Description: MY APP
Transform: AWS::Serverless-2016-10-31
Resources:
ConfigLambdaPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref postingCredentialsFunction
Principal: apigateway.amazonaws.com
postingCredentialsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src.postingCredentials
Runtime: nodejs16.x
Environment:
Variables:
NODE_ENV: lc
Timeout: 100
Events:
Api:
Type: Api
Properties:
Path: /credentials
Method: POST
The problem is, it work on aws cloud environment but not on localstack:
samlocal build
samlocal deploy --guided
I got error when I try to post /credential by using AWS toolkit on VS Code
Logs:
Execution log for request 7d304a00-c150-49b6-a79d-c868630802d3
Mon Nov 21 06:18:57 UTC 2022 : Starting execution for request: 7d304a00-c150-49b6-a79d-c868630802d3
Mon Nov 21 06:18:57 UTC 2022 : HTTP Method: POST, Resource Path: /credentials
Mon Nov 21 06:18:57 UTC 2022 : Method request path: {}
Mon Nov 21 06:18:57 UTC 2022 : Method request query string: {}
Mon Nov 21 06:18:57 UTC 2022 : Method request headers: {}
Mon Nov 21 06:18:57 UTC 2022 : Method request body before transformations: {
"username": "as",
"password": "aa"
}
Mon Nov 21 06:18:57 UTC 2022 : Endpoint request URI: https://lambda.ap-southeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:769850762534:function:ilo-postingCredentialsFunction-Zglh1bCwlG2V/invocations
Mon Nov 21 06:18:57 UTC 2022 : Endpoint request headers: {X-Amz-Date=20221121T061857Z, x-amzn-apigateway-api-id=zs6s8fq3sl, Accept=application/json, User-Agent=AmazonAPIGateway_zs6s8fq3sl, Host=lambda.ap-southeast-1.amazonaws.com, X-Amz-Content-Sha256=38fe2c870e6f9e0e6d740e99ea07d43897a026e116b1b00abb97d50f7ca6b711, X-Amzn-Trace-Id=Root=1-637b1851-355c273bbb88ef34acbf473e, x-amzn-lambda-integration-tag=7d304a00-c150-49b6-a79d-c868630802d3, Authorization=**************************************************************************************************************************************************************************************************************************************************************************************************************************************************b5cd77, X-Amz-Source-Arn=arn:aws:execute-api:ap-southeast-1:769850762534:zs6s8fq3sl/test-invoke-stage/POST/credentials, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEH4aDmFwLXNvdXRoZWFzdC0xIkYwRAIgT1Qcm51tu2MVKGbEm0lkZsRbWAqCYkBRvHVyeJA1NWYCIBpzxYUHknHlWYQWQyV1FepImS [TRUNCATED]
Mon Nov 21 06:18:57 UTC 2022 : Endpoint request body after transformations: {"resource":"/credentials","path":"/credentials","httpMethod":"POST","headers":null,"multiValueHeaders":null,"queryStringParameters":null,"multiValueQueryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"fhgft9","resourcePath":"/credentials","httpMethod":"POST","extendedRequestId":"b8C8uFIISQ0Fuaw=","requestTime":"21/Nov/2022:06:18:57 +0000","path":"/credentials","accountId":"769850762534","protocol":"HTTP/1.1","stage":"test-invoke-stage","domainPrefix":"testPrefix","requestTimeEpoch":1669011537358,"requestId":"7d304a00-c150-49b6-a79d-c868630802d3","identity":{"cognitoIdentityPoolId":null,"cognitoIdentityId":null,"apiKey":"test-invoke-api-key","principalOrgId":null,"cognitoAuthenticationType":null,"userArn":"arn:aws:iam::769850762534:user/loc.dang","apiKeyId":"test-invoke-api-key-id","userAgent":"aws-sdk-nodejs/2.1211.0 darwin/v16.14.2 AWS-Toolkit-For-VSCode/1.53.0 Visual-Studio-Code/1.73.1 ClientId/68f27f1c-6738-4b2 [TRUNCATED]
Mon Nov 21 06:18:57 UTC 2022 : Sending request to https://lambda.ap-southeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:769850762534:function:ilo-postingCredentialsFunction-Zglh1bCwlG2V/invocations
Mon Nov 21 06:18:57 UTC 2022 : Execution failed due to configuration error: Invalid permissions on Lambda function
Mon Nov 21 06:18:57 UTC 2022 : Method completed with status: 500
Request returned status: 500:
{"message": "Internal server error"}
I have configured lambda permission but it still doesn't work. Am I wrong somewhere?
Related
I have this CloudFormation method definition:
DownloadMethodGet:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ApiKeyRequired: true
ResourceId: !Ref OfflineExtractorJobIdDownloadApiResource
RestApiId: !Ref RestApi
AuthorizationType: CUSTOM
AuthorizerId: !Ref RestApiAuthorizer
RequestParameters:
method.request.path.jobid: true
method.request.header.Accept: false
method.request.header.Content-Type: false
method.request.header.Range: false
Integration:
Type: AWS
CacheKeyParameters:
- 'method.request.path.jobid'
RequestParameters:
integration.request.path.jobid: 'method.request.path.jobid'
integration.request.header.Accept: 'method.request.header.Accept'
integration.request.header.Content-Type: 'method.request.header.Content-Type'
integration.request.header.Range: 'method.request.header.Range'
Uri: !Sub "${DownloadS3BucketArn}/export-{jobid}.db"
Credentials: !Ref DownloadS3RoleName
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
ContentHandling: CONVERT_TO_BINARY
ResponseParameters:
method.response.header.Content-Type: "integration.response.header.Content-Type"
method.response.header.Content-Range: "integration.response.header.Content-Range"
ResponseTemplates:
application/json: ''
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: "Empty"
ResponseParameters:
method.response.header.Content-Type: true
method.response.header.Content-Range: false
When testing through ApiGateway UI, I'm getting
Sun Dec 11 06:24:35 UTC 2022 : Endpoint request body after transformations:
Sun Dec 11 06:24:35 UTC 2022 : Sending request to https://s3-external-1.amazonaws.com/the-bucket/export-the-nosql-file.db
Sun Dec 11 06:24:35 UTC 2022 : Received response. Status: 200, Integration latency: 279 ms
Sun Dec 11 06:24:35 UTC 2022 : Endpoint response headers: {x-amz-id-2=BDwDmSffhPUoXGUB/VxQxZ5XVGufOcxa5an7gmwK9O2zXJROFOGHh/H0BRYPAabt6pVPR9EMubg=, x-amz-request-id=JNQ0Q84HENXZJTPH, Date=Sun, 11 Dec 2022 06:24:36 GMT, Last-Modified=Wed, 13 Jul 2022 08:57:15 GMT, ETag="6f7f45c10069472887d4306164fc84cd", Accept-Ranges=bytes, Content-Type=application/octet-stream, Server=AmazonS3, Content-Length=7102464}
Sun Dec 11 06:24:36 UTC 2022 : Endpoint response body before transformations: SQLite format 3# (�Y [TRUNCATED]
Sun Dec 11 06:24:36 UTC 2022 : Execution failed due to configuration error: Unable to base64 decode the body.
Sun Dec 11 06:24:36 UTC 2022 : Method completed with status: 500
Seems that the binary data have been received from S3 correctly with application/octet-stream ContentType but not correctly pushed through api gateway.
This configuration works fine with image/jpg and zip objects, but not with NoSQL db
When testing from Postman or Curl, Im getting 200 with no content
Can anyone help me with th configuration?
Thanks!
EDIT:
Once I change
ContentHandling: CONVERT_TO_BINARY to CONVERT_TO_TEXT, ApiGateway test UI seems working:
Execution log for request xxx
Sun Dec 11 09:47:02 UTC 2022 : Starting execution for request: xxx
Sun Dec 11 09:47:02 UTC 2022 : HTTP Method: GET, Resource Path: /offline-extractor/jobs2/the-file/download
Sun Dec 11 09:47:02 UTC 2022 : Method request path: {jobid=0014aa18-d3f7-4508-a5df-63fda8573dfc}
Sun Dec 11 09:47:02 UTC 2022 : Method request query string: {}
Sun Dec 11 09:47:02 UTC 2022 : Method request headers: {}
Sun Dec 11 09:47:02 UTC 2022 : Method request body before transformations:
Sun Dec 11 09:47:02 UTC 2022 : Endpoint request URI: https://s3-external-1.amazonaws.com/acuttera-offline-extractor-prod/export-the-file.db
Sun Dec 11 09:47:02 UTC 2022 : Endpoint request headers: {Authorization=*****************************************************************************************************************************************************************************************************************************************************************************13e589, X-Amz-Date=20221211T094702Z, x-amzn-apigateway-api-id=xxx, Accept=application/json, User-Agent=AmazonAPIGateway_hoypjdvhx7, X-Amz-Security-Token=IQ [TRUNCATED]
Sun Dec 11 09:47:02 UTC 2022 : Endpoint request body after transformations:
Sun Dec 11 09:47:02 UTC 2022 : Sending request to https://s3-external-1.amazonaws.com/the-bucket/export-the-file.db
Sun Dec 11 09:47:02 UTC 2022 : Received response. Status: 200, Integration latency: 67 ms
Sun Dec 11 09:47:02 UTC 2022 : Endpoint response headers: {x-amz-id-2=xxx x-amz-request-id=xxx, Date=Sun, 11 Dec 2022 09:47:03 GMT, Last-Modified=Sun, 10 Jul 2022 11:43:24 GMT, ETag="xxx", Accept-Ranges=bytes, Content-Type=application/octet-stream, Server=AmazonS3, Content-Length=638976}
Sun Dec 11 09:47:02 UTC 2022 : Endpoint response body before transformations: SQLite format 3# ��U [TRUNCATED]
Sun Dec 11 09:47:02 UTC 2022 : Method response body after transformations: SQLite format 3# ��U [TRUNCATED]
Sun Dec 11 09:47:02 UTC 2022 : Method response headers: {X-Amzn-Trace-Id=Root=1-xxx, Content-Type=application/octet-stream}
Sun Dec 11 09:47:02 UTC 2022 : Successfully completed execution
Sun Dec 11 09:47:02 UTC 2022 : Method completed with status: 200
But when requesting from CURL, still getting 200 with empty body. On the contrary of the log I see in ApiGateway, CURL receives Content-Type:application/json no matter what Accept header I send to the server
To be honest, I was relying on auto-deploy but in fact, auto-deploy did not deploy the stack at all :-(
When deploying the API manually, it started working like a charm
I'm trying to resolve an ssm parameter throught AWS Apigateway, but i can't find any example.
The cloud formation part where I define the method integration is the follow:
Integration:
Type: AWS
Uri: !Sub "arn:aws:apigateway:us-west-2:ssm:action/GetParameter"
IntegrationHttpMethod: POST
PassthroughBehavior: NEVER
RequestTemplates:
application/json: '
{
"Name": "/dev/configs/xoxo"
}
'
When I test it in the AWS console this are the logs
Mon Aug 10 18:14:10 UTC 2020 : Method request body before transformations:
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request URI: https://ssm.us-west-2.amazonaws.com/?Action=GetParameter
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request headers: {Authorization=**********************************************************************************************************************************************************************************************************************************************************************dbf24f, X-Amz-Date=20200810T181410Z, x-amzn-apigateway-api-id=mrvijesndc, Accept=application/json, User-Agent=AmazonAPIGateway_mrvijesndc, X-Amz-Security-Token=...[TRUNCATED]
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request body after transformations: { "Name": "/dev/configs/xoxo"}
Mon Aug 10 18:14:10 UTC 2020 : Sending request to https://ssm.us-west-2.amazonaws.com/?Action=GetParameter
Mon Aug 10 18:14:10 UTC 2020 : Received response. Status: 400, Integration latency: 43 ms
Mon Aug 10 18:14:10 UTC 2020 : Endpoint response headers: {Server=Server, Date=Mon, 10 Aug 2020 18:14:10 GMT, Content-Type=application/json, Content-Length=220, Connection=keep-alive, x-amzn-RequestId=c2e56a30-d846-4cd6-b4af-1df95267a3fd}
Mon Aug 10 18:14:10 UTC 2020 : Endpoint response body before transformations: {"Error":{"Code":"ValidationError","Message":"1 validation error detected: Value null at 'name' failed to satisfy constraint: Member must not be null.","Type":"Sender"},"RequestId":"c2e56a30-d846-4cd6-b4af-1df95267a3fd"}
I suppose it is not making the call correctly, could anyone help me ?
Seems like the SSM API endpoint, when called in this manner, expects to find its parameters in the query string, not in the request body. So the following ought to work: (not tested as such, because I use Terraform instead of CloudFormation)
Integration:
Type: AWS
Uri: !Sub "arn:aws:apigateway:us-west-2:ssm:action/GetParameter"
IntegrationHttpMethod: POST
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestParameters:
integration.request.querystring.Name: "'/dev/configs/xoxo'"
Well, after a long research, google hide this post for me along 8 hours, finally, I found it:
https://forums.aws.amazon.com/thread.jspa?threadID=288118
I hope be helpfully for someone like me in the future
Execution failed due to configuration error: Illegal character in path at index 46: http://petstore-demo-endpoint.execute-api.com/{proxy}?type=fish
Logs :
Execution log for request 965a6cd1-1f68-4b33-8bab-3741e40b0028
Fri Mar 20 08:19:39 UTC 2020 : Starting execution for request: 965a6cd1-1f68-4b33-8bab-3741e40b0028
Fri Mar 20 08:19:39 UTC 2020 : HTTP Method: GET, Resource Path: /test/petstore/pets
Fri Mar 20 08:19:39 UTC 2020 : Method request path: {proxy=petstore/pets}
Fri Mar 20 08:19:39 UTC 2020 : Method request query string: {type=fish}
Fri Mar 20 08:19:39 UTC 2020 : Method request headers: {}
Fri Mar 20 08:19:39 UTC 2020 : Method request body before transformations:
Fri Mar 20 08:19:39 UTC 2020 : Endpoint request URI: http://petstore-demo-endpoint.execute-api.com/{proxy}?type=fish
Fri Mar 20 08:19:39 UTC 2020 : Endpoint request headers: {x-amzn-apigateway-api-id=ssg89ys6y6, User-Agent=AmazonAPIGateway_ssg89ys6y6}
Fri Mar 20 08:19:39 UTC 2020 : Endpoint request body after transformations:
Fri Mar 20 08:19:39 UTC 2020 : Sending request to http://petstore-demo-endpoint.execute-api.com/{proxy}?type=fish
Fri Mar 20 08:19:39 UTC 2020 : Execution failed due to configuration error: Illegal character in path at index 46: http://petstore-demo-endpoint.execute-api.com/{proxy}?type=fish
Fri Mar 20 08:19:39 UTC 2020 : Method completed with status: 500
Adding this here for others who face the issue
https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-path-character-error/
I'm using API Gateway's AWS Service integration type to add logs to the Cloudwatch Logs service using the PutLogEvents action, as described here: https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
I've successfully setup a similar type of method to add items into a DynamoDB table using the PutItem action and that worked just fine, so I'm not sure what I'm missing with this one.
I've double checked for typos and problems with my template. Have successfully used PutLogEvents using the CLI tools, so the setup seems all OK.
Here's some screenshots of my setup from the AWS dash:
Here's the mapping template I'm using:
{
"logGroupName": "FromAPI",
"logStreamName": "$input.path('$.streamName')",
"logEvents": [
{
"timestamp": $input.path('$.ts'),
"message": "$input.path('$.message')"
}
]
}
The response I get back has a 200 status code, but the following response body:
{
"Output": {
"__type": "com.amazon.coral.service#UnknownOperationException",
"message": null
},
"Version": "1.0"
}
Here's a redacted (xxx) execution log:
Execution log for request xxx
Fri Apr 19 02:28:58 UTC 2019 : Starting execution for request: xxx
Fri Apr 19 02:28:58 UTC 2019 : HTTP Method: POST, Resource Path: /log
Fri Apr 19 02:28:58 UTC 2019 : Method request path: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request query string: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request headers: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request body before transformations: {
"streamName": "12345",
"ts": 1555641510000,
"message": "help!"
}
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request URI: https://logs.xxx.amazonaws.com/?Action=PutLogEvents
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request headers: {Authorization=xxx, X-Amz-Date=20190419T022858Z, x-amzn-apigateway-api-id=xxx, Accept=application/json, User-Agent=AmazonAPIGateway_xxx, X-Amz-Security-Token=xxx [TRUNCATED]
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request body after transformations: {
"logGroupName": "FromAPI",
"logStreamName": "12345",
"logEvents": [
{
"timestamp": 1555641510000,
"message": "help!"
}
]
}
Fri Apr 19 02:28:58 UTC 2019 : Sending request to https://logs.xxx.amazonaws.com/?Action=PutLogEvents
Fri Apr 19 02:28:58 UTC 2019 : Received response. Status: 200, Integration latency: 38 ms
Fri Apr 19 02:28:58 UTC 2019 : Endpoint response headers: {x-amzn-RequestId=xxx, Content-Type=application/json, Content-Length=105, Date=Fri, 19 Apr 2019 02:28:58 GMT}
Fri Apr 19 02:28:58 UTC 2019 : Endpoint response body before transformations: {"Output":{"__type":"com.amazon.coral.service#UnknownOperationException","message":null},"Version":"1.0"}
Fri Apr 19 02:28:58 UTC 2019 : Method response body after transformations: {"Output":{"__type":"com.amazon.coral.service#UnknownOperationException","message":null},"Version":"1.0"}
Fri Apr 19 02:28:58 UTC 2019 : Method response headers: {X-Amzn-Trace-Id=Root=xxx, Content-Type=application/json}
Fri Apr 19 02:28:58 UTC 2019 : Successfully completed execution
Fri Apr 19 02:28:58 UTC 2019 : Method completed with status: 200
Nothing gets logged into my Cloudwatch Logs stream - the API Gateway integration request response body contains an UnknownOperationException error.
My best guess is that this request is not mapping to the PutLogEvents action for some reason. Strange that the status code is 200 in this situation.
I'm guessing it's just some typo - or an additional header that I need to send through? Any ideas?
It should work if you add the following lines at the top of your Mapping Template:
#set($context.requestOverride.header['X-Amz-Target'] = "Logs_20140328.PutLogEvents")
#set($context.requestOverride.header['Content-Type'] = "application/x-amz-json-1.1")
This is very tricky and not well documented. You can find those headers in the sample request for PutLogEvents.
As per here, I believe that setting a header of X-Amz-Invocation-Type: Event should set my Lambda invocation to be asynchronous.
However, by putting a import time;time.sleep(5000) at the beginning of my Lambda function, and sending requests to my API Gateway, I observe that:
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id <resource-id> \
--http-method POST | jq -r '.requestParameters'
{
"integration.request.header.X-Amz-Invocation-Type": "'Event'"
}
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id <resource-id> \
--http-method POST | jq -r '.uri'
arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/LATEST/<path> -d '{}'
{"message": "Endpoint request timed out"}
[here I removed the sleep from my lambda function and made it return immediately]
$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/LATEST/<path> -d '{}'
{"body": "Request OK", "headers": {"Content-Type": "application/json"}, "statusCode": 200}
Assuming that the documentation is accurate, my best guess is that I've misconfigured the Integration somehow - perhaps the "'Event'" is incorrect and it should be "Event"? I'm pretty sure the single-quotes are required, however, to demarcate a static value (as opposed to value parsed from the request). In particular, I suspect that my responseParameters are not right:
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id dzv1zj \
--http-method POST | jq -r '.integrationResponses'
{
"200": {
"responseTemplates": {
"application/json": null
},
"statusCode": "200"
}
}
Should null there be some VTL that staticly returns a 200 OK?
As for alternatives: I see that invoke-async is deprecated. I'd really rather not go to the overhead of going API Gateway -> SNS -> Lambda.
EDIT: Here are the logs from calling the API via the "Test" option on the console:
Execution log for request test-request
Wed Mar 07 17:24:57 UTC 2018 : Starting execution for request: test-invoke-request
Wed Mar 07 17:24:57 UTC 2018 : HTTP Method: POST, Resource Path: /<path>
Wed Mar 07 17:24:57 UTC 2018 : Method request path: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request query string: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request headers: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request body before transformations: {"abc":"def"}
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request headers: {X-Amz-Date=20180307T172457Z, x-amzn-apigateway-api-id=<api-id>, Accept=application/json, User-Agent=AmazonAPIGateway_<api-id>, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=2c3fbda5f48b04e39d3a87f89e5bd00b48b6e5e3c4a093de65de0a87b8cc8b3b, X-Amzn-Trace-Id=Root=1-5aa02069-8670eb5d98dbc4ade9df03d8, x-amzn-lambda-integration-tag=test-request, Authorization=**********************************************************************************************************************************************************************************************************************************************************************************************************************************************bfe3de, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:<account-id>:<api-id>/null/POST/<path>, X-Amz-Invocation-Type=Event, X-Amz-Security-Token=[REDACTED] [TRUNCATED]
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request body after transformations: {"abc":"def"}
Wed Mar 07 17:24:57 UTC 2018 : Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
Wed Mar 07 17:24:57 UTC 2018 : Received response. Integration latency: 43 ms
Wed Mar 07 17:24:57 UTC 2018 : Endpoint response body before transformations:
Wed Mar 07 17:24:57 UTC 2018 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, Connection=keep-alive, x-amzn-RequestId=75501cbf-222c-11e8-a1fc-2b19f19a9429, Content-Length=0, Date=Wed, 07 Mar 2018 17:24:57 GMT, X-Amzn-Trace-Id=root=1-5aa02069-8670eb5d98dbc4ade9df03d8;sampled=0}
Wed Mar 07 17:24:57 UTC 2018 : Method response body after transformations:
Wed Mar 07 17:24:57 UTC 2018 : Method response headers: {X-Amzn-Trace-Id=sampled=0;root=1-5aa02069-8670eb5d98dbc4ade9df03d8, Content-Type=application/json}
Wed Mar 07 17:24:57 UTC 2018 : Successfully completed execution
Wed Mar 07 17:24:57 UTC 2018 : Method completed with status: 200
Now I feel stupid - I needed to deploy my API. That seems like the API Gateway equivalent of "Have you tried turning it off and turning it on again?" :)
I found scubbo's answer but it didn't enlighten me until much later, so I post the same, but with a screenshot for any future lost soul