AWS CDK 如何从 OpenApi 规范创建由 Lambda 支持的 API 网关? [英] AWS CDK how to create an API Gateway backed by Lambda from OpenApi spec?

查看:22
本文介绍了AWS CDK 如何从 OpenApi 规范创建由 Lambda 支持的 API 网关?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 AWS CDK 定义 API 网关和 APIG 将代理到的 lambda.

OpenAPI 规范支持对 Swagger 规范的 x-amazon-apigateway-integration 自定义扩展(详细 here),需要 lambda 的调用 URL.如果 lambda 定义在与 API 相同的堆栈中,我看不到如何在 OpenAPI 规范中提供它.我能想到的最好的方法是定义一个包含 lambda 的堆栈,然后从中获取输出并运行 sed 以在 OpenAPI 规范中进行查找和替换以插入 uri,然后使用修改后的 OpenAPI 规范创建第二个堆栈.

例子:

/项目:邮政:x-amazon-apigateway 集成: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"http方法:POST"类型:aws_proxy"

第一季度.这似乎是一个先有鸡还是先有蛋的问题,以上是唯一的方法吗?

我尝试使用 defaultIntegration SpecRestApi CDK 结构.文档指出:

<块引用>

一个集成,用作在此创建的所有方法的默认值API,除非指定了集成.

这似乎应该能够使用 CDK 规范中定义的 lambda 定义默认集成,因此所有方法都使用此集成,而无需提前知道 lambda 的 uri.

因此我尝试了这个:

SingletonFunction myLambda = ...SpecRestApi openapiRestApi = SpecRestApi.Builder.create(this, "MyApi").restApiName("MyApi").apiDefinition(ApiDefinition.fromAsset("openapi.yaml")).defaultIntegration(LambdaIntegration.Builder.create(myLambda).proxy(假).建造()).部署(真).建造();

openapi.yaml 中定义的 OpenAPI 规范不包含 x-amazon-apigateway-integration 节;它只是在标准 OpenApi 3 规范中定义了一个 GET 方法.

但是,当我尝试部署它时,我收到一个错误:

没有为方法定义集成(服务:AmazonApiGateway;状态代码:400;错误代码:BadRequestException;请求 ID:56113150-1460-4ed2-93b9-a12618864582)

这似乎是一个错误,所以我在这里提交了一个.p>

第二季度.如何使用 CDK 定义 API 网关和 Lambda 并通过 OpenAPI 规范将两者连接在一起?

解决方案

看起来我所追求的东西被 这个 CDK 问题.与此同时,我在这里 并想出了一个解决方法.

我用 https://github.com/spullara/mustache.java 来解析我的 OpenAPI 规范文件并替换其中引用 API 网关的调用 ARN(本身引用 Lambda ARN)的模板值.

Map变量=新的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 = new StringWriter();MustacheFactory mf = new DefaultMustacheFactory();对象 openapiSpecAsObject;尝试(阅读器阅读器=新文件阅读器(新文件(myapi.yaml"))){小胡子 mustache = mf.compile(reader, "OAS");mustache.execute(作家,范围);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)).部署(真).建造();

注意 props 是一个引用 Stack 属性的变量,而 myLambda 是一个 SingletonFunction.

我的 OpenAPI 规范如下所示(已删除标题和模型部分):

路径:/项目:得到:摘要:列出所有项目.回复:200":描述:好的内容:应用程序/json:架构:$ref: '#/components/schemas/ItemList'x-amazon-apigateway 集成:uri:{{restapi-lambda}}"passthroughBehavior:when_no_match"http方法:POST"类型:aws_proxy"

另请注意,当我授予 API Gateway 权限以像这样调用 lambda 时:

myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com").建造());

我仍然收到 500 错误,并且在日志中我可以看到Lambda 函数的权限无效"错误消息.如果我向 Lambda 添加权限,如下所示:

myLambda.addPermission("PermitAPIGInvocation", Permission.builder().action("lambda:InvokeFunction").principal(ServicePrincipal.Builder.create(apigateway.amazonaws.com").建造()).sourceArn(openapiRestApi.arnForExecuteApi()).建造());

那么我目前需要在权限生效之前重新部署 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 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?

解决方案

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.

这篇关于AWS CDK 如何从 OpenApi 规范创建由 Lambda 支持的 API 网关?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆