web-dev-qa-db-fra.com

AWS CDK Comment créer une passerelle API Sachilie par Lambda à partir de Openapi Spec?

Je veux utiliser AWS CDK pour définir une passerelle API et une Lambda que l'APIG sera proxy.

La spécification OpenAPI prend en charge un x-Amazon-apigateway-integration Extension personnalisée à la spécification Swagger (détaillée ici ), pour laquelle une URL d'invocation de la Lambda est requise. Si la Lambda est définie dans la même pile que l'API, je ne vois pas comment fournir ceci dans la spécification OpenAPI. Le mieux que je puisse penser serait de définir une pile avec la Lambda dans, puis d'obtenir la sortie à partir de cet ouvrage et d'exécuter sed pour effectuer une recherche de recherche et de remplacement dans la spécification OpenAPI pour insérer l'URI, puis créer une seconde pile avec cette spécification OpenAPI modifiée.

Exemple:

  /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. Cela semble être un problème de poulet et d'œuf, est ce qui est ci-dessus le seul moyen de faire cela?

J'ai essayé d'utiliser la propriété defaultIntegration propriété de la construction SPECRESPII CDK. La documentation indique:

Une intégration à utiliser par défaut pour toutes les méthodes créées dans cette API, à moins qu'une intégration soit spécifiée.

Cela semble que cela semble être en mesure de définir une intégration par défaut à l'aide d'un Lambda défini dans la spécification CDK et que toutes les méthodes utilisent cette intégration, sans avoir à connaître l'URI de la Lambda à l'avance.

Ainsi j'ai essayé ceci:

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();

La spécification OpenAPI définie dans openapi.yaml n'inclut pas un x-Amazon-apigateway-integration Stanza; Il suffit d'une seule méthode GET définie dans une spécification Standard OpenAPI 3.

Cependant, lorsque j'essaie de déployer cela, je reçois une erreur:

No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)

Cela semble être un bug, donc j'ai déposé un ici .

Q2. Comment définir une passerelle API et Lambda en utilisant CDK et filez-vous les deux ensemble via une spécification OpenAPI?

6
John

J'ai proposé une solution qui est un peu plus simple que les autres réponses ici, car elles ne nécessitent pas de variables de scène ni de déploiements multiples.

Tout d'abord, définissez le uri du x-Amazon-apigateway-integration à une variable comme ${API_LAMBDA_ARN} et utiliser le même type et httpMethod comme dans cet exemple:

[...]
  "paths": {
    "/pets": {
      "get": {
        "summary": "List all pets",
        "responses": {
          [...]
        },
        "x-Amazon-apigateway-integration": {
          "uri": "${API_LAMBDA_ARN}",
          "type": "AWS_PROXY",
          "httpMethod": "POST",
        }
      }
    }
  },
[...]

Ensuite, vous pouvez utiliser cette construction (ou une implémentation dossée équivalente) pour remplacer la variable pendant la période de construction et créer une API HTTP de passerelle API basée sur le document OpenAPI:

from aws_cdk import (
    core,
    aws_iam as iam,
    aws_lambda as _lambda,
    aws_apigatewayv2 as apigateway
)


class OpenApiLambdaStack(core.Stack):
    def __init__(
        self, scope: core.Construct, construct_id: str, **kwargs
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # function that handles api request(s)
        api_lambda = _lambda.Function([...])

        # read openapi document
        with open("openapi.json", "r") as json_file:
            content = json_file.read()
        # replace the variable by the lambda functions arn
        content = content.replace("${API_LAMBDA_ARN}", api_lambda.function_arn)
        openapi = json.loads(content)

        # create apigateway
        http_api = apigateway.HttpApi(self, "OpenApiLambdaGateway")
        # use escape hatches to import OpenAPI Document
        # see: https://docs.aws.Amazon.com/cdk/latest/guide/cfn_layer.html
        http_api_cfn: apigateway.CfnApi = http_api.node.default_child
        http_api_cfn.add_property_override("Body", openapi)
        http_api_cfn.add_property_deletion_override("Name")
        http_api_cfn.add_property_deletion_override("ProtocolType")
        # let it fail on warnings to be sure everything went right
        http_api_cfn.add_property_override("FailOnWarnings", True)

        # construct arn of createad api gateway (to grant permission)
        http_api_arn = (
            f"arn:{self.partition}:execute-api:"
            f"{http_api.env.region}:{http_api.env.account}:"
            f"{http_api.http_api_id}/*/*/*"
        )

        # grant apigateway permission to invoke api lambda function
        api_lambda.add_permission(
            f"Invoke By {http_api.node.id} Permission",
            principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=http_api_arn,
        )
        
        # output api gateway url
        core.CfnOutput(self, "HttpApiUrl", value=http_api.url)

Les utilisateurs de Python pourraient également être intéressés par la construction OpenAPposer Construction que j'ai publiée pour rendre ce processus encore plus simple. Il prend en charge JSON et YAML.

0
suud