AWS CDK如何根据OpenApi规范创建由Lambda支持的API网关? [英] AWS CDK how to create an API Gateway backed by Lambda from OpenApi spec?
问题描述
我想使用AWS CDK定义API网关和APIG将代理的lambda.
OpenAPI规范支持Swagger规范的 Q1.这似乎是个鸡与蛋的问题,以上是实现此目的的唯一方法吗? 我尝试使用 SpecRestApi CDK构造.该文档指出: 在此方法中创建的所有方法默认使用的集成
除非指定了集成,否则API. 这似乎应该能够使用CDK规范中定义的lambda定义默认集成,因此所有方法都可以使用此集成,而无需事先知道lambda的uri. 因此,我尝试了这一点: 但是,当我尝试部署它时,出现错误: 这似乎是一个错误,所以我在这里在这里提交了./p>
Q2.如何使用CDK定义API网关和Lambda,并通过OpenAPI规范将两者连接在一起? 我使用 https://github.com/spullara/mustache.java 进行解析我的OpenAPI规范文件,并替换其中引用API网关的调用ARN(其本身引用Lambda ARN)的模板值. 请注意, 我的OpenAPI规范如下(已删除标题和模型部分): 还请注意,当我授予API网关权限来像这样调用lambda时: 我仍然收到500错误,并且在日志中可以看到对Lambda函数的权限无效"错误消息.如果我向Lambda添加权限,如下所示: 然后,在权限生效之前,我目前需要重新部署API.我还在努力避免这种情况. 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 Example: Q1. This seems like a chicken-and-egg problem, is the above the only way to do this? I tried to use the 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: The OpenAPI spec defined in However, when I try to deploy this, I get an error: 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? 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). Note that My OpenAPI spec looks like this (header and model sections removed): Also note that when I granted API Gateway permissions to invoke the lambda like this: 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: then I currently need to redeploy the API before the permissions take effect. I'm still working through how to avoid this. 这篇关于AWS CDK如何根据OpenApi规范创建由Lambda支持的API网关?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!x-amazon-apigateway-integration
自定义扩展(详细
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();
openapi.yaml
中定义的OpenAPI规范不包含x-amazon-apigateway-integration
节;它只有一个在标准OpenApi 3规范中定义的GET方法.No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)
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();
props
是引用Stack
道具的变量,而myLambda
是引用SingletonFunction
的变量.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"
myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build());
myLambda.addPermission("PermitAPIGInvocation", Permission.builder()
.action("lambda:InvokeFunction")
.principal(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build())
.sourceArn(openapiRestApi.arnForExecuteApi())
.build());
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. /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"
defaultIntegration
property of the SpecRestApi CDK construct. The documentation states:
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();
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.No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)
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();
props
is a variable that refers to the Stack
props and myLambda
is a reference to a SingletonFunction
.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"
myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build());
myLambda.addPermission("PermitAPIGInvocation", Permission.builder()
.action("lambda:InvokeFunction")
.principal(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build())
.sourceArn(openapiRestApi.arnForExecuteApi())
.build());