Skip to content

The plugin separates x-amazon-apigateway extension syntax from your openapi3 files

License

Notifications You must be signed in to change notification settings

yndlingsfar/serverless-openapi-integration-helper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


Feedback is appreciated! If you have an idea for how this plugin/library can be improved (or even just a complaint/criticism) then please open an issue.

Serverless Plugin: AWS Api Gateway integration helper

  1. Overview
  2. Installation & Setup
  3. Plugin configuration
  4. Usage
  5. Command
  6. CORS Generator
  7. AUTO-MOCK Generator
  8. VALIDATION Generator
  9. PROXY Manager
  10. Configuration Reference
  11. Known Issues
    1. Stage Deployment
    2. Variable Resolving
  12. Example
  13. Approach to a functional test of schema validation

Overview

The plugin provides the functionality to merge OpenApiSpecification files (formerly known as swagger) with one or multiple YML files containing the the x-amazon-apigateway extensions. There are several use-cases to keep both information separated, e.g. it is needed to deploy different api gateway integrations depending on a stage environment.

When dealing with functional tests you do not want to test the production environment, but only a mocking response.

The plugin supports YML based OpenApi3 specification files only

Features

  • Serverless 3.x Support
  • deploy stage dependent x-amazon-apigateway integrations
  • separate infrastructure (aws) from openapi specification
  • use mock integrations for functional testing
  • auto-generating CORS methods, headers and api gateway mocking response
  • hook into package & deploy lifeCycle and generate combined openApi files on the fly during deployment
  • auto-inject generated openApi file into the Body property of specified API Gateway
  • generate mocking responses without specifying x-amazon-apigateway-integration objects
  • generate request-validation blocks
  • generate all required x-amazon-apigateway-integration objects automatically
  • full proxy generation support with [NEW]: feature: PROXY Manager

See the examples folder for a full working example

Installation & Setup

Run npm install in your Serverless project.

$ npm install --save-dev serverless-openapi-integration-helper

Add the plugin to your serverless.yml file

plugins:
  - serverless-openapi-integration-helper

Plugin configuration

You can configure the plugin under the key openApiIntegration. See See Configuration Reference for a list of available options

The value for openApiIntegration must be either a single object or an array of objects. For an array of objects, the apiResourceName property must be specified in each object to determine which Aws::ApiGateway::RestApi resource to insert the merged API specification into, while for a single object it's optional and will default to the first found Aws::ApiGateway::RestApi resource.

The mapping array must be used to configure where the files containing the x-amazon-apigateway-integration blocks are located.

openApiIntegration:
    package: true #New feature! Hook into the package & deploy process
    inputFile: schema.yml
    mapping:
       - stage: [dev, prod] #multiple stages
         path: integrations
       - stage: test #single stage
         path: mocks

In the above example all YML files inside the integrations directory will be processed and merged with the schema.yml file when deploying the dev stage

serverless deploy --stage=dev

To use a different x-amazon-apigateway to perform functional tests (with mocking responses e.g) the directory mock is processed and merged with the schema.yml file when deploying the test stage

serverless deploy --stage=test

Multiple APIs

To handle multiple APIs, supply an array as the value of openApiIntegrations instead of a single object, and specify the target Aws::ApiGateway::RestApi resource for each item using the apiResourceName property, and make sure to specify unique output files by setting outputDirectory and/or outputFile uniquely:

openApiIntegration:
  - package: true
    inputFile: schema-1.yml
    apiResourceName: Api1
    outputFile: api-1.yml
    mapping:
      - stage: [dev, prod]
        path: integrations/api-1
  - package: true
    inputFile: schema-2.yml
    apiResourceName: Api2
    outputFile: api-2.yml
    mapping:
       - stage: [dev, prod]
         path: integrations/api-2

...

resources:
  Resources:
    Api1:
      Type: AWS::ApiGateway::RestApi
      Properties:
        Body: ~ # auto-generated by plugin from schema-1

        ...

    Api2:
       Type: AWS::ApiGateway::RestApi
       Properties:
          Body: ~ # auto-generated by plugin from schema-2

          ...

Usage

You can setup a fully working API GATEWAY with any openApi 3.0 specification file First create the input file containing the OpenApiSpecification

# ./schema.yml
openapi: 3.0.0
info:
  description: User Registration
  version: 1.0.0
  title: UserRegistration
paths:
  /api/v1/user:
    post:
      summary: adds a user
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Customer'
      responses:
        '201':
          description: user created
components:
  schemas:
    Customer:
      type: object
      required:
        - email_address
        - password
      properties:
        email_address:
          type: string
          example: [email protected]
        password:
          type: string
          format: password
          example: someStrongPassword#

The plugin will generate the x-amazon-apigateway integrations objects for all methods that do not have an integration.

#generate a file containing a gateway mock integration in the directory /mocks
serverless integration create --output mocks --type mock --stage=test

#generate a file containing the production integration in the directory integrations/
serverless integration create --output integrations --type http --stage=prod

Supported types are

  • http_proxy
  • http
  • aws
  • aws_proxy
  • mock

The plugin now generates a merged file during deployment that is automatically injected in your serverless resources

#Create OpenApi File containing mocking responses (usable in functional tests) and deploy to ApiGateway
serverless deploy --stage==test
#Create OpenApi File containing the production integration and deploy to ApiGateway
serverless deploy --stage=prod

The generated output is automatically injected in the resources.Resources.YOUR_API_GATEWAY.Properties.Body property

resources:
  Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        ApiKeySourceType: HEADER
        Body: ~ #autogenerated by plugin
        Description: "Some Description"
        FailOnWarnings: false
        Name: ${opt:stage, self:provider.stage}-some-name
        EndpointConfiguration:
          Types:
            - REGIONAL
    ApiGatewayDeployment:
      Type: AWS::ApiGateway::Deployment
      Properties:
        RestApiId:
          Ref: ApiGatewayRestApi
        StageName: ${opt:stage, self:provider.stage}

Commands

Manual merge

The generate command can be used independently with

serverless integration merge --stage=dev

Of course then the API Gateway Body property has to be specified manually

resources:
  Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        ApiKeySourceType: HEADER
        Body: ${file(openapi-integration/api.yml)}

CORS generator

The plugin can generate full CORS support out of the box.

openApiIntegration:
  cors: true
  ...

If enabled, the plugin generates all required OPTIONS methods as well as the required header informations and adds a mocking response to API Gateway. You can customize the CORS templates by placing your own files inside a directory openapi-integration (in your project root). The following files can be overwritten:

Filename Description
headers.yml All headers required for CORS support
integration.yml Contains the x-amazon-apigateway-integration block
path.yml OpenApi specification for the OPTIONS method
response-parameters.yml The response Parameters of the x-amazon-apigateway-integration responses

See the EXAMPLES directory for detailed instructions.

Auto Mock Generator

If enabled, the plugin generates mocking responses for all methods that do not have an x-amazon-apigateway-integration block defined. It takes the first 2xx response defined in the openApi specification and generates a simple mocking response on the fly

openApiIntegration:
  autoMock: true
  ...

When using the autoMock feature, you do not need to specify inputPath mappings, since all endpoints are mocked automatically

openApiIntegration:
    package: true
    inputFile: schema.yml
    mapping: ~

VALIDATION generator

The plugin supports full request validation out of the box

openApiIntegration:
  validation: true
  ...

If enabled, the plugin generates the x-amazon-apigateway-request-validators blocks and adds a basic request validation to all methods. You can customize the VALIDATION template by placing your own files inside a directory openapi-integration (in your project root). The following files can be overwritten:

Filename Description
request-validator.yml The x-amazon-apigateway-request-validators block

See the EXAMPLES directory for detailed instructions.

Proxy Manager

The proxymanager feature automates the complete generation of an HTTP proxy integration. You only have to define the target URL and all necessary AWS integration blocks are generated on-the-fly during deployment.

openApiIntegration:
   cors: true
   validation: true
   mapping:
      - stage: [dev, prod]
        proxyManager:
           type: http_proxy
           baseUrl: https://www.example.com
           pattern: "(?<=api\/v1)\/.+"
  ...

With this setting, no separate integration files need to be created

A combination of your own and auto-generated files is still possible without any problems

Proxy Manager configuration

type

at the moment only http_proxy supported

baseUrl

The base url is required to map the path variable from the openapi specification to the URI from the aws integration.

Example:

#original openapi specification
paths:
   /api/v1/user:
    post:
      ... 

will be translated to

#generated openapi specification output
paths:
   /api/v1/user:
      post:
         ...
         x-amazon-apigateway-integration:
            type: http_proxy
            passthroughBehavior: when_no_match
            httpMethod: POST
            uri: https://www.example.com/api/v1/user

pattern

The pattern can be used to adapt the mapping of the base url using regexp, to remove a prefix, or a version string

Example:

baseUrl: https://www.example.com
pattern: "(?<=api\/v1)\/.+"

will translate the route /api/v1/user to https://www.example.com/user

Configuration Reference

configure the plugin under the key openApiIntegration

openApiIntegration:
  inputFile: schema.yml #required
  package: true #optional, defaults to false 
  inputDirectory: ./ #optional, defaults to ./
  cors: true #optional, defaults to false
  autoMock: true #optional, defaults to false
  validation: true #optional, defaults to false
  mapping: #optional, can be completely blank if autoMock option is enabled
    - stage: [dev, prod] #multiple stages
      path: integrations
      proxyManager: #optional
         type: http_proxy
         baseUrl: https://example.com
         pattern: "(?<=v1)\/.+"
    - stage: test #single stage
      path: mocks/customer.yml
  outputFile: api.yml #optional, defaults to api.yml
  outputDirectory: openapi-integration #optional, defaults to ./openapi-integration

Known Issues

Stage deployment

When using serverless framework only to deploy your aws resources without having any lambda functions or triggers, the AWS Gateway deploymemt does not behave as expected. Any deployment to an existing stage will be ignored, since CloudFormation does not redeploy a stage if the DeploymentIdentifier has not changed.

The plugin serverless-random-gateway-deployment-id solves this problem by adding a random id to the deployment-name and all references to it on every deploy

See the examples folder for a full working example

Variable Resolving

Serverless variables inside the openapi integration files are not resolved correctly when using the package & deploy hooks. This problem can be solved by using the api gateway STAGE VARIABLES.

See the examples folder for a full working example

Example

service:
  name: user-registration

provider:
  name: aws
  stage: dev
  region: eu-central-1

plugins:
  - serverless-openapi-integration-helper
  
openApiIntegration:
  inputFile: schema.yml
  package: true
  mapping:
    - path: integrations
      stage: [dev, prod]
    - path: mocks/customer.yml
      stage: test

functions:

resources:
  Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        ApiKeySourceType: HEADER
        Body: ~
        Description: "Some Description"
        FailOnWarnings: false
        Name: ${opt:stage, self:provider.stage}-some-name
        EndpointConfiguration:
          Types:
            - REGIONAL
    ApiGatewayDeployment:
      Type: AWS::ApiGateway::Deployment
      Properties:
        RestApiId:
          Ref: ApiGatewayRestApi
        StageName: ${opt:stage, self:provider.stage}
serverless deploy --stage=test
serverless deploy --stage=prod

Approach to a functional test of schema validation

The plugin works well in combination with the serverless-plugin-test-helper to automate tests against the deployed api gateway

Install The plugin test helper package

npm install --save-dev serverless-plugin-test-helper

add the plugin as a plugin dependency in your serverless configuration file and configure the plugin according to the Readme

#./serveless.yml
plugins:
  - serverless-plugin-test-helper
  - serverless-openapi-integration-helper

[...]

resources:
   Outputs:
      GatewayUrl: # This is the key that will be used in the generated outputs file
         Description: This is a helper for functional tests
         Value: !Join
            - ''
            - - 'https://'
              - !Ref ApiGatewayRestApi
              - '.execute-api.'
              - ${opt:region, self:provider.region}
              - '.amazonaws.com/'
              - ${opt:stage, self:provider.stage}

   Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        ApiKeySourceType: HEADER
        Body: ~
        Description: User Registration (${opt:stage, self:provider.stage})
        FailOnWarnings: false
        Name: ${opt:stage, self:provider.stage}-gateway
        EndpointConfiguration:
          Types:
            - REGIONAL
    ApiGatewayDeployment:
      Type: AWS::ApiGateway::Deployment
      Properties:
        RestApiId:
          Ref: ApiGatewayRestApi
        StageName: ${opt:stage, self:provider.stage}

Testing the schema validation

Add a functional test (e.g. with jest)

//tests/registration.js
import {getOutput} from 'serverless-plugin-test-helper';
import axios from 'axios';

axios.defaults.adapter = require('axios/lib/adapters/http'); //Todo

const URL = getOutput('GatewayUrl');
test('request validation on registration', async () => {
    expect.assertions(1);
    const {status} = await axios.post(URL + '/api/v1/user',
        {
            "email_address": "[email protected]",
            "password": "someStrongPassword#"
        },
        {
            headers: {
                'Content-Type': 'application/json',
            }
        });
    expect(status).toEqual(201);
});

test('request validation on registration (invalid request)', async () => {
    expect.assertions(1);
    try {
        await axios.post(URL + '/api/v1/user',
            {
                "email": "[email protected]"
            },
            {
                headers: {
                    'Content-Type': 'application/json',
                }
            });
    } catch (e) {
        expect(e.response).toMatchObject({
            statusText: 'Bad Request',
            status: 400
        });
    }
});

Then perform the functional test

serverless deploy --stage=test
npm test
serverless remove --stage=test

The command will

  • merge the openapi specification file with the MOCK integration configured before
  • deploy to API Gateway in an isolated TEST infrastructure environment (your other environments will not be affected since we are deploying to a separated gateway)
  • perform the test and verify that schema validation is correct
  • remove all TEST resources if test succeeded

See the examples folder for a full working example