问题
I want to use AWS CDK to define an API Gateway and a lambda that the APIG will proxy to.
The OpenAPI spec supports a x-amazon-apigateway-integration
custom extension to the Swagger spec (detailed here), for which an invocation URL of the lambda is required. If the lambda is defined in the same stack as the API, I don't see how to provide this in the OpenAPI spec. The best I can think of would be to define one stack with the lambda in, then get the output from this and run sed
to do a find-and-replace in the OpenAPI spec to insert the uri, then create a second stack with this modified OpenAPI spec.
Example:
/items:
post:
x-amazon-apigateway-integration:
uri: "arn:aws:apigateway:eu-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-2:123456789012:function:MyStack-SingletonLambda4677ac3018fa48679f6-B1OYQ50UIVWJ/invocations"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
Q1. This seems like a chicken-and-egg problem, is the above the only way to do this?
I tried to use the defaultIntegration property of the SpecRestApi CDK construct. The documentation states:
An integration to use as a default for all methods created within this API unless an integration is specified.
This seems like a should be able to define a default integration using a lambda defined in the CDK spec and therefore have all methods use this integration, without needing to know the uri of the lambda in advance.
Thus I tried this:
SingletonFunction myLambda = ...
SpecRestApi openapiRestApi = SpecRestApi.Builder.create(this, "MyApi")
.restApiName("MyApi")
.apiDefinition(ApiDefinition.fromAsset("openapi.yaml"))
.defaultIntegration(LambdaIntegration.Builder.create(myLambda)
.proxy(false)
.build())
.deploy(true)
.build();
The OpenAPI spec defined in openapi.yaml
does not include a x-amazon-apigateway-integration
stanza; it just has a single GET method defined within a standard OpenApi 3 specification.
However, when I try to deploy this, I get an error:
No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)
This seems like a bug, so I filed one here.
Q2. How do I define an API Gateway and Lambda using CDK and wire the two together via an OpenAPI spec?
回答1:
It looks like what I'm after is tracked by this CDK issue. In the meantime, I was guided by the comment on that issue here and came up with a workaround.
I used https://github.com/spullara/mustache.java to parse my OpenAPI spec file and replace template values in it that referenced the invocation ARN of the API gateway (that itself references the Lambda ARN).
Map<String, Object> variables = new HashMap<>();
variables.put("restapi-lambda", String.format("arn:aws:apigateway:%s:lambda:path/2015-03-31/functions/%s/invocations", props.getEnv().getRegion(), myLambda.getFunctionArn()));
Writer writer = new StringWriter();
MustacheFactory mf = new DefaultMustacheFactory();
Object openapiSpecAsObject;
try (Reader reader = new FileReader(new File("myapi.yaml"))) {
Mustache mustache = mf.compile(reader, "OAS");
mustache.execute(writer, scopes);
writer.flush();
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
openapiSpecAsObject = yamlMapper.readValue(writer.toString(), Object.class);
}
SpecRestApi openapiRestApi = SpecRestApi.Builder.create(this, "MyRestApi")
.restApiName("MyRestApi")
.apiDefinition(ApiDefinition.fromInline(openapiSpecAsObject))
.deploy(true)
.build();
Note that props
is a variable that refers to the Stack
props and myLambda
is a reference to a SingletonFunction
.
My OpenAPI spec looks like this (header and model sections removed):
paths:
/items:
get:
summary: List all items.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ItemList'
x-amazon-apigateway-integration:
uri: "{{restapi-lambda}}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
Also note that when I granted API Gateway permissions to invoke the lambda like this:
myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build());
I still get a 500 error and in the logs I can see an "Invalid permissions on Lambda function" error message. If I add permissions to the Lambda, like this:
myLambda.addPermission("PermitAPIGInvocation", Permission.builder()
.action("lambda:InvokeFunction")
.principal(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build())
.sourceArn(openapiRestApi.arnForExecuteApi())
.build());
then I currently need to redeploy the API before the permissions take effect. I'm still working through how to avoid this.
来源:https://stackoverflow.com/questions/62179893/aws-cdk-how-to-create-an-api-gateway-backed-by-lambda-from-openapi-spec