0.3.1 • Published 2 years ago

openapi-enforcer-lambda v0.3.1

Weekly downloads
-
License
Apache-2.0
Repository
-
Last release
2 years ago

Contents

Installation

This library has a peer dependency on the openapi-enforcer package.

npm install openapi-enforcer-lambda openapi-enforcer

Documentation

Enforcer Lambda

enforcerLambda (openapi: string | unknown, options: Options = {}): { handler: (handler: Handler) => LambdaHandler, route: (controllers: RouteControllerMap) => LambdaHandler }

TLDR; Returns an enforcer object with a handler and a router. The handler gives you a req and a res object to use as you will, the router will manage the event and return a response.

This function takes your OpenAPI document and an optional set of options and returns a promise object with a handler and a router. The handler will use the event that triggered your lambda, your OpenAPI document, and your options to generate req and res objects to be used by your lambda to respond to API calls. The router will take those same objects and you will need to pass in a RouteControllerMap object. With those, the router will route all incoming API calls appropriately and send correctly formatted responses as specified in your OpenAPI document.

The RouteControllerMap object takes a controller and its corresponding operation. For example, if I have a controller persons and two corresponding operations, getPersonById and findPersonByName, outlined in my OpenAPI document, I would pass in the following:

persons: {
  getPersonById: async (req, res) => {
  	const {id} = req.path // get path parameter named "id"
    // ... talk to database
    res.status(200).send(foundPersonObject)
  },
  findPersonByName: async (req, res) => {
  	const {id} = req.path // get path parameter named "id"
    // ... talk to database
    res.status(200).send(foundPersonObject)
  }
}

Complete examples with the handler and the router are provided below.

Handler

handler (openapi: string | unknown, options: Options = {}, handler: Handler): LambdaHandler

This function provides the same functionality as the EnforcerLambda function, but it only returns a handler.

Router

route (openapi: string | unknown, options: Options = {}, controllers: RouteControllerMap): LambdaHandler

This function also provides the same functionality as the EnforcerLambda function, but it only returns a handler.

Options

PropertyDescriptionTypeDefault
allowOtherQueryParametersQuery parameters not specified in the OpenAPI document are permitted.boolean \ string[]false
enforcerOptionsOther options permitted by the OpenAPI enforcerRecord<string, any>null
handleBadRequestBad requests will be handled by the enforcer.booleantrue
handleBadResponseBad responses will be handled by the enforcer.booleantrue
handleNotFoundRequests that need to return a status 404 will be handled by the enforcer.booleantrue
handleMethodNotAllowedRequests that are not allowed will be handled by the enforcer.booleantrue
logErrorsLog errors to the console.booleantrue
xControllerHow controllers are indicated in the provided OpenAPI document.stringx-controller
xOperationHow operations are indicated in the provided OpenAPI document.stringx-operation

Examples

Simple Handler

Follow the handler pattern if you have only a few endpoints.

Note that the .handler function will not call your provided handler unless the request is valid. So your handler will not be called if there are invalid parameters, an invalid method, an invalid url, etc.

const EnforcerLambda = require('openapi-enforcer-lambda')
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = enforcer.handler((req, res) => {
    const { operation } = req
    if (operation.operationId === 'getPersonById') {
        const { id } = req.path // get path parameter named "id"
        // ... talk to database
        res.status(200).send(foundPersonObject)
    } else if (operation.operationId === 'findPersonByName') {
        const { name } = req.query // get query parameter named "name"
        // ... talk to database
        res.status(200).send(foundPersonsArray)
    }
})

Simple Router

Follow this router pattern if you have lots of endpoints.

Note that the .route function will not call your provided handler unless the request is valid. So your handler will not be called if there are invalid parameters, an invalid method, an invalid url, etc.

index.js

const EnforcerLambda = require('openapi-enforcer-lambda')
const persons = require('./routes/persons')
const dbConnection = require('./db')

const anotherDependency = {}
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = enforcer.route({
    // property "persons" will be used when the OpenAPI document has an
    // "x-controller" in a path is set to "persons"
    persons: persons(dbConnection, anotherDependency)
})

routes/persons.js

module.exports = async function (dbConn, anotherDependency) {
  return { 
    async getPersonById (req, res) {
      const { id } = req.path // get path parameter named "id"
      // ... talk to database
      res.status(200).send(foundPersonObject)
    },
    async findPersonByName (req, res) {
      const { id } = req.path // get path parameter named "id"
      // ... talk to database
      res.status(200).send(foundPersonObject)
    }
  }
}

Example with More Control

In this example we

  1. Manually create the Enforcer promise object and push that into the handler.
  2. We call the handler only when it is not a GET /health request.
const Enforcer = require('openapi-enforcer')
const EnforcerLambda = require('openapi-enforcer-lambda')

const openapiPromise = Enforcer('./openapi.yml')
  .catch(error => {
    console.error(error)
    process.exit(1)
  })
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = function (event) {
  if (event.httpMethod === 'GET' && event.path === '/health') {
    return {
      statusCode: 200,
      headers: {
        'content-type': 'text/plain'
      },
      body: 'OK'
    }
  } else {
    const handler = enforcer.handler(event)
    handler(openapiPromise, (req, res) => {
      const { operation } = req
      if (operation.operationId === 'getPersonById') {
        const { id } = req.path // get path parameter named "id"
        // ... talk to database
        res.status(200).send(foundPersonObject)
      } else if (operation.operationId === 'findPersonByName') {
        const { name } = req.query // get query parameter named "name"
        // ... talk to database
        res.status(200).send(foundPersonsArray)
      }
    })
  }
}

Testing a Handler Once

If you've created a lambda handler function then you can test it using the test function.

const EnforcerLambda = require('openapi-enforcer-lambda')
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = enforcer.handler((req, res) => {
    // your code here
})

const result = await EnforcerLambda.test(exports.handler, {
  method: 'GET',
  path: '/',
  headers: {},
  body: null
})

Testing a Handler More Than Once

If you've created a lambda handler function then you can test it using the test function.

const EnforcerLambda = require('openapi-enforcer-lambda')
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = enforcer.handler((req, res) => {
    // your code here
})

const test = EnforcerLambda.testSuite(exports.handler)

const result1 = await test({
  method: 'GET',
  path: '/',
  headers: {},
  body: null
})

const result2 = await test({
  method: 'GET',
  path: '/foo'
})

Simple HTTP Dev Server

This pattern is for development purposes only. Using this in production is not recommended.

This will start a server that you can connect to via HTTP and it will convert incoming HTTP requests into AWS Lambda events. The event will then be sent to the lambda handler. The response that is produced will be translated back into an HTTP response and sent back to the client.

This is ideal when you want to use Postman to test your Lambda, or if you need to use ngrok or a similar service to help with the development of your lambda.

index.js

Define your lambda handler.

const EnforcerLambda = require('openapi-enforcer-lambda')
const enforcer = EnforcerLambda('./openapi.yml')

exports.handler = enforcer.handler((req, res) => {
    // your request processing code here
})

server.js

Run this server.js file to start a server proxy that calls your lambda.

const { Server } = require('openapi-enforcer-lambda')
const { handler } = require('./index')

const server = new Server(3000, handler)
server.start()
  .then(() => {
    console.log('Server listening on port: ' + server.port)
  })
  .catch(console.error)
0.3.0

2 years ago

0.3.1

2 years ago

0.1.2

2 years ago

0.2.0

2 years ago

0.1.1

2 years ago

0.1.3

2 years ago

0.0.11

2 years ago

0.0.12

2 years ago

0.1.0

2 years ago

0.0.10

3 years ago

0.0.9

3 years ago

0.0.8

3 years ago

0.0.5

3 years ago

0.0.7

3 years ago

0.0.6

3 years ago

0.0.4

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago