From 624bda37f8e543205186cc26789253936b98c9f0 Mon Sep 17 00:00:00 2001 From: John Shaskin Date: Tue, 5 Feb 2019 05:30:45 +0100 Subject: [PATCH] Add support for MethodResponse to aws-apigateway. Remove Dockerfile that was no longer needed Update python base image from 3.6 to 3.6.5 Make the dockerfile work Add MethodResponse support for aws-apigateway Remove Dockerfile that was no longer needed Update python base image from 3.6 to 3.6.5 Make the dockerfile work Add MethodResponse to API Gateway Method. Add some documentation to the MethodResponse properties. Update the test for MethodResponse with response models. Fix some formatting and finish adding code documentation for MethodResponse. Remove Dockerfile from this branch Fix bad merge to methodresponse test. Correct the MethodResponse response models documentation. Add IModel type to reference when configuring a MethodResponse Slight update to comments. Update API Gateway MethodResponse documentation. Author: John Shaskin Date: Mon Feb 05 5:30:45 2019 +0100 Add some documentation around the referencing default Empty/Error models. Edit documentation around the referencing default Empty/Error models. Edit documentation around the referencing default Empty/Error models. Fix 'trailing whitespace' in comments Add roadmap section with item for adding support for API gateway models --- packages/@aws-cdk/aws-apigateway/README.md | 4 ++ packages/@aws-cdk/aws-apigateway/lib/index.ts | 2 + .../@aws-cdk/aws-apigateway/lib/method.ts | 56 ++++++++++++++++-- .../aws-apigateway/lib/methodresponse.ts | 28 +++++++++ packages/@aws-cdk/aws-apigateway/lib/model.ts | 46 +++++++++++++++ .../aws-apigateway/test/test.method.ts | 59 ++++++++++++++++++- 6 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk/aws-apigateway/lib/methodresponse.ts create mode 100644 packages/@aws-cdk/aws-apigateway/lib/model.ts diff --git a/packages/@aws-cdk/aws-apigateway/README.md b/packages/@aws-cdk/aws-apigateway/README.md index 2de3f833ea457..8297d3062d564 100644 --- a/packages/@aws-cdk/aws-apigateway/README.md +++ b/packages/@aws-cdk/aws-apigateway/README.md @@ -213,6 +213,10 @@ to allow users revert the stage to an old deployment manually. See [awslabs/aws-cdk#723](https://github.com/awslabs/aws-cdk/issues/723) for a list of missing features. +### Roadmap + +- [ ] Support defining REST API Models [#1695](https://github.com/awslabs/aws-cdk/issues/1695) + ---- This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project. \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/lib/index.ts b/packages/@aws-cdk/aws-apigateway/lib/index.ts index 1ccd62b520805..b303ae6f4e243 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/index.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/index.ts @@ -7,6 +7,8 @@ export * from './stage'; export * from './integrations'; export * from './lambda-api'; export * from './vpc-link'; +export * from './methodresponse'; +export * from './model'; // AWS::ApiGateway CloudFormation Resources: export * from './apigateway.generated'; diff --git a/packages/@aws-cdk/aws-apigateway/lib/method.ts b/packages/@aws-cdk/aws-apigateway/lib/method.ts index 1e76106409f23..c4ead637ca3ec 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/method.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/method.ts @@ -2,6 +2,7 @@ import cdk = require('@aws-cdk/cdk'); import { CfnMethod, CfnMethodProps } from './apigateway.generated'; import { ConnectionType, Integration } from './integration'; import { MockIntegration } from './integrations/mock'; +import { MethodResponse } from './methodresponse'; import { IRestApiResource } from './resource'; import { RestApi } from './restapi'; import { validateHttpMethod } from './util'; @@ -34,12 +35,30 @@ export interface MethodOptions { */ apiKeyRequired?: boolean; + /** + * The responses that can be sent to the client who calls the method. + * @default None + * + * This property is not required, but if these are not supplied for a Lambda + * proxy integration, the Lambda function must return a value of the correct format, + * for the integration response to be correctly mapped to a response to the client. + * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-settings-method-response.html + */ + methodResponses?: MethodResponse[] + + /** + * The request parameters that API Gateway accepts. Specify request parameters + * as key-value pairs (string-to-Boolean mapping), with a source as the key and + * a Boolean as the value. The Boolean specifies whether a parameter is required. + * A source must match the format method.request.location.name, where the location + * is querystring, path, or header, and name is a valid, unique parameter name. + * @default None + */ + requestParameters?: { [param: string]: boolean }; + // TODO: // - RequestValidatorId // - RequestModels - // - RequestParameters - // - MethodResponses - requestParameters?: { [param: string]: boolean }; } export interface MethodProps { @@ -93,7 +112,8 @@ export class Method extends cdk.Construct { authorizationType: options.authorizationType || defaultMethodOptions.authorizationType || AuthorizationType.None, authorizerId: options.authorizerId || defaultMethodOptions.authorizerId, requestParameters: options.requestParameters, - integration: this.renderIntegration(props.integration) + integration: this.renderIntegration(props.integration), + methodResponses: this.renderMethodResponses(options.methodResponses), }; const resource = new CfnMethod(this, 'Resource', methodProps); @@ -188,6 +208,34 @@ export class Method extends cdk.Construct { credentials, }; } + + private renderMethodResponses(methodResponses: MethodResponse[] | undefined): CfnMethod.MethodResponseProperty[] | undefined { + if (!methodResponses) { + // Fall back to nothing + return undefined; + } + + return methodResponses.map(mr => { + let responseModels: {[contentType: string]: string} | undefined; + + if (mr.responseModels) { + responseModels = {}; + for (const contentType in mr.responseModels) { + if (mr.responseModels.hasOwnProperty(contentType)) { + responseModels[contentType] = mr.responseModels[contentType].modelId; + } + } + } + + const methodResponseProp = { + statusCode: mr.statusCode, + responseParameters: mr.responseParameters, + responseModels, + }; + + return methodResponseProp; + }); + } } export enum AuthorizationType { diff --git a/packages/@aws-cdk/aws-apigateway/lib/methodresponse.ts b/packages/@aws-cdk/aws-apigateway/lib/methodresponse.ts new file mode 100644 index 0000000000000..59032a06d860f --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/lib/methodresponse.ts @@ -0,0 +1,28 @@ +import { IModel } from './model'; + +export interface MethodResponse { + + /** + * The method response's status code, which you map to an IntegrationResponse. + * Required. + */ + statusCode: string; + + /** + * Response parameters that API Gateway sends to the client that called a method. + * Specify response parameters as key-value pairs (string-to-Boolean maps), with + * a destination as the key and a Boolean as the value. Specify the destination + * using the following pattern: method.response.header.name, where the name is a + * valid, unique header name. The Boolean specifies whether a parameter is required. + * @default None + */ + responseParameters?: { [destination: string]: boolean }; + + /** + * The resources used for the response's content type. Specify response models as + * key-value pairs (string-to-string maps), with a content type as the key and a Model + * resource name as the value. + * @default None + */ + responseModels?: { [contentType: string]: IModel }; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/lib/model.ts b/packages/@aws-cdk/aws-apigateway/lib/model.ts new file mode 100644 index 0000000000000..f38de58375302 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/lib/model.ts @@ -0,0 +1,46 @@ +export interface IModel { + readonly modelId: string; +} + +/** + * Represents a reference to a REST API's Empty model, which is available + * as part of the model collection by default. This can be used for mapping + * JSON responses from an integration to what is returned to a client, + * where strong typing is not required. In the absence of any defined + * model, the Empty model will be used to return the response payload + * unmapped. + * + * Definition + * { + * "$schema" : "http://json-schema.org/draft-04/schema#", + * "title" : "Empty Schema", + * "type" : "object" + * } + * + * @see https://docs.amazonaws.cn/en_us/apigateway/latest/developerguide/models-mappings.html#models-mappings-models + */ +export class EmptyModel implements IModel { + public readonly modelId = 'Empty'; +} + +/** + * Represents a reference to a REST API's Error model, which is available + * as part of the model collection by default. This can be used for mapping + * error JSON responses from an integration to a client, where a simple + * generic message field is sufficient to map and return an error payload. + * + * Definition + * { + * "$schema" : "http://json-schema.org/draft-04/schema#", + * "title" : "Error Schema", + * "type" : "object", + * "properties" : { + * "message" : { "type" : "string" } + * } + * } + */ +export class ErrorModel implements IModel { + public readonly modelId = 'Error'; +} + +// TODO: Implement Model, enabling management of custom models. \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/test.method.ts b/packages/@aws-cdk/aws-apigateway/test/test.method.ts index c1e0ee124b1d3..fb87eb75fa81b 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.method.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.method.ts @@ -5,7 +5,7 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import apigateway = require('../lib'); -import { ConnectionType } from '../lib'; +import { ConnectionType, EmptyModel, ErrorModel } from '../lib'; export = { 'default setup'(test: Test) { @@ -304,6 +304,63 @@ export = { test.done(); }, + 'methodResponse set one or more method responses via options'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const api = new apigateway.RestApi(stack, 'test-api', { deploy: false }); + + // WHEN + new apigateway.Method(stack, 'method-man', { + httpMethod: 'GET', + resource: api.root, + options: { + methodResponses: [{ + statusCode: '200' + }, { + statusCode: "400", + responseParameters: { + 'method.response.header.killerbees': false + } + }, { + statusCode: "500", + responseParameters: { + 'method.response.header.errthing': true + }, + responseModels: { + 'application/json': new EmptyModel(), + 'text/plain': new ErrorModel() + } + } + ] + } + }); + + // THEN + expect(stack).to(haveResource('AWS::ApiGateway::Method', { + HttpMethod: 'GET', + MethodResponses: [{ + StatusCode: "200" + }, { + StatusCode: "400", + ResponseParameters: { + 'method.response.header.killerbees': false + } + }, { + StatusCode: "500", + ResponseParameters: { + 'method.response.header.errthing': true + }, + ResponseModels: { + 'application/json': 'Empty', + 'text/plain': 'Error' + } + } + ] + })); + + test.done(); + }, + 'multiple integration responses can be used'(test: Test) { // @see https://github.com/awslabs/aws-cdk/issues/1608 // GIVEN const stack = new cdk.Stack();