UNPKG

@carlsberg/openapi-aws-extensions

Version:

A library that adds API Gateway extensions to OpenAPI specification files

159 lines (134 loc) 4.13 kB
import { ContentHandling, IntegrationType, PassthroughBehavior } from '@aws-cdk/aws-apigateway' import fs from 'fs' import {METHODS} from 'http' import { OpenApiBuilder, OpenAPIObject, OperationObject, PathItemObject } from 'openapi3-ts' import path from 'path' import * as yaml from 'yaml' export interface Options { lambdaFunctionName?: string outputFile?: string outputFormat: 'json' | 'yaml' cors?: {timeout: number} } function specFromJSON(str: string): OpenApiBuilder { return new OpenApiBuilder(JSON.parse(str)) } function specFromYaml(str: string): OpenApiBuilder { return new OpenApiBuilder(yaml.parse(str)) } function extensionForOperation( method: string, op: OperationObject, options: Options ): object { if (method === 'options') { if (!options.cors) { return {} } return { type: IntegrationType.MOCK, passthroughBehavior: PassthroughBehavior.WHEN_NO_MATCH, timeoutInMillis: options.cors.timeout, requestTemplates: {'application/json': '{"statusCode": 200}'}, responses: { default: { statusCode: 200, responseParameters: { 'method.response.header.Access-Control-Allow-Methods': "'GET,OPTIONS,POST'", 'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", 'method.response.header.Access-Control-Allow-Origin': "'*'" } } } } } return { type: IntegrationType.AWS_PROXY, httpMethod: 'POST', uri: `arn:\${AWS::Partition}:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/arn:\${AWS::Partition}:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${options.lambdaFunctionName}/invocations`, passthroughBehavior: PassthroughBehavior.WHEN_NO_MATCH, contentHandling: ContentHandling.CONVERT_TO_TEXT, responses: Object.keys(op.responses).reduce((responses, statusCode) => { // const response: ResponseObject = op.responses[statusCode] return {...responses, [statusCode]: {statusCode}} }, {}) } } function addExtensionsToPath( pathItem: PathItemObject, options: Options ): PathItemObject { for (const method of METHODS.map(m => m.toLowerCase())) { const operation: OperationObject = pathItem[method] if (!operation) { continue } const extension = extensionForOperation(method, operation, options) pathItem[method] = extension ? { ...operation, 'x-amazon-apigateway-integration': extension } : operation } return pathItem } export function addExtensions( input: string | OpenAPIObject, options: Options = {outputFormat: 'yaml'} ): string { if (!options.lambdaFunctionName) { throw new Error( `openapi-aws-extensions currently only supports Lambda integrations. The \`lambdaFunctionName\` option is required.` ) } let builder: OpenApiBuilder if (typeof input === 'string') { if (fs.existsSync(input) === false) { throw new Error(`No specification file at path: ${input}`) } const ext = path.extname(input).replace('.', '') const fileContents = fs.readFileSync(input, 'utf-8') switch (ext) { case 'json': builder = specFromJSON(fileContents) break case 'yml': case 'yaml': builder = specFromYaml(fileContents) break default: throw new Error(`Unsupported specification file extension: "${ext}"`) } if (options.outputFormat !== ext) { options.outputFormat = ext === 'yml' ? 'yaml' : ext } } else { builder = new OpenApiBuilder(input) } for (const pathName of Object.keys(builder.rootDoc.paths)) { builder.rootDoc.paths[pathName] = addExtensionsToPath( builder.rootDoc.paths[pathName], options ) } const result = options.outputFormat === 'json' ? builder.getSpecAsJson() : builder.getSpecAsYaml() if (options.outputFile) { fs.writeFileSync(options.outputFile, result, {encoding: 'utf-8'}) } return result }