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 cb263bc5083cc..6f41fd9240e74 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,11 +35,15 @@ export interface MethodOptions { */ apiKeyRequired?: boolean; + /** + * The responses that can be sent to the client who calls the method. + */ + methodResponses?: MethodResponse[] + // TODO: // - RequestValidatorId // - RequestModels // - RequestParameters - // - MethodResponses requestParameters?: { [param: string]: boolean }; } @@ -93,7 +98,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 +194,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..0307ec610486a --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/lib/methodresponse.ts @@ -0,0 +1,25 @@ +import { IModel } from './model'; + +export interface MethodResponse { + + /** + * The method response's status code, which you map to an IntegrationResponse. + */ + 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. + */ + 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. + */ + 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..61bdc43877494 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/lib/model.ts @@ -0,0 +1,13 @@ +export interface IModel { + readonly modelId: string; +} + +export class EmptyModel implements IModel { + public readonly modelId = 'Empty'; +} + +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 645d1f9f9f398..3a45f3d73cfe0 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) { @@ -301,6 +301,63 @@ export = { // THEN test.throws(() => api.root.addMethod('GET', integration), /cannot set 'vpcLink' where 'connectionType' is INTERNET/); + 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(); } };