diff --git a/packages/@aws-cdk/aws-apigateway/lib/integration.ts b/packages/@aws-cdk/aws-apigateway/lib/integration.ts index 6f305e2932ef..5b6a3040cb94 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integration.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integration.ts @@ -135,9 +135,33 @@ export interface IntegrationProps { } /** - * Result of binding an Integration to the Method + * Result of binding an Integration to a Method. */ export interface IntegrationConfig { + /** + * Integration options. + * @default - no integration options + */ + readonly options?: IntegrationOptions; + + /** + * Specifies an API method integration type. + */ + readonly type: IntegrationType; + + /** + * The Uniform Resource Identifier (URI) for the integration. + * @see https://docs.aws.amazon.com/apigateway/api-reference/resource/integration/#uri + * @default - no URI. Usually applies to MOCK integration + */ + readonly uri?: string; + + /** + * The integration's HTTP method type. + * @default - no integration method specified. + */ + readonly integrationHttpMethod?: string; + /** * This value is included in computing the Deployment's fingerprint. When the fingerprint * changes, a new deployment is triggered. @@ -155,23 +179,32 @@ export interface IntegrationConfig { * or implement on your own by specifying the set of props. */ export class Integration { - constructor(private readonly props: IntegrationProps) { } - - /** - * Allows `Method` to access the integration props. - * - * @internal - */ - public get _props() { - return this.props; + constructor(private readonly props: IntegrationProps) { + const options = this.props.options || { }; + if (options.credentialsPassthrough !== undefined && options.credentialsRole !== undefined) { + throw new Error('\'credentialsPassthrough\' and \'credentialsRole\' are mutually exclusive'); + } + + if (options.connectionType === ConnectionType.VPC_LINK && options.vpcLink === undefined) { + throw new Error('\'connectionType\' of VPC_LINK requires \'vpcLink\' prop to be set'); + } + + if (options.connectionType === ConnectionType.INTERNET && options.vpcLink !== undefined) { + throw new Error('cannot set \'vpcLink\' where \'connectionType\' is INTERNET'); + } } /** * Can be overridden by subclasses to allow the integration to interact with the method * being integrated, access the REST API object, method ARNs, etc. */ - public bind(_method: Method): IntegrationConfig | undefined { - return; + public bind(_method: Method): IntegrationConfig { + return { + options: this.props.options, + type: this.props.type, + uri: this.props.uri, + integrationHttpMethod: this.props.integrationHttpMethod, + }; } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts index 6300c3b77379..1f95b88233ab 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/aws.ts @@ -92,8 +92,9 @@ export class AwsIntegration extends Integration { }); } - public bind(method: Method): IntegrationConfig | undefined { + public bind(method: Method): IntegrationConfig { + const bindResult = super.bind(method); this.scope = method; - return; + return bindResult; } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts index 715c1d91237f..746423228678 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts @@ -52,8 +52,8 @@ export class LambdaIntegration extends AwsIntegration { this.enableTest = options.allowTestInvoke === undefined ? true : false; } - public bind(method: Method): IntegrationConfig | undefined { - super.bind(method); + public bind(method: Method): IntegrationConfig { + const bindResult = super.bind(method); const principal = new iam.ServicePrincipal('apigateway.amazonaws.com'); const desc = `${method.api.node.uniqueId}.${method.httpMethod}.${method.resource.path.replace(/\//g, '.')}`; @@ -79,6 +79,7 @@ export class LambdaIntegration extends AwsIntegration { deploymentToken = JSON.stringify({ functionName: cfnFunction.functionName }); } return { + ...bindResult, deploymentToken, }; } diff --git a/packages/@aws-cdk/aws-apigateway/lib/method.ts b/packages/@aws-cdk/aws-apigateway/lib/method.ts index b9a9143df650..6e3ed26a0c6d 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/method.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/method.ts @@ -1,7 +1,7 @@ import { Construct, Resource, Stack } from '@aws-cdk/core'; import { CfnMethod, CfnMethodProps } from './apigateway.generated'; import { Authorizer, IAuthorizer } from './authorizer'; -import { ConnectionType, Integration } from './integration'; +import { Integration, IntegrationConfig } from './integration'; import { MockIntegration } from './integrations/mock'; import { MethodResponse } from './methodresponse'; import { IModel } from './model'; @@ -204,7 +204,7 @@ export class Method extends Resource { authorizationType, authorizerId, requestParameters: options.requestParameters || defaultMethodOptions.requestParameters, - integration: this.renderIntegration(integration), + integration: this.renderIntegration(bindResult), methodResponses: this.renderMethodResponses(options.methodResponses), requestModels: this.renderRequestModels(options.requestModels), requestValidatorId: this.requestValidatorId(options), @@ -263,22 +263,9 @@ export class Method extends Resource { return this.api.arnForExecuteApi(this.httpMethod, pathForArn(this.resource.path), 'test-invoke-stage'); } - private renderIntegration(integration: Integration): CfnMethod.IntegrationProperty { - const options = integration._props.options || { }; - + private renderIntegration(bindResult: IntegrationConfig): CfnMethod.IntegrationProperty { + const options = bindResult.options ?? {}; let credentials; - if (options.credentialsPassthrough !== undefined && options.credentialsRole !== undefined) { - throw new Error('\'credentialsPassthrough\' and \'credentialsRole\' are mutually exclusive'); - } - - if (options.connectionType === ConnectionType.VPC_LINK && options.vpcLink === undefined) { - throw new Error('\'connectionType\' of VPC_LINK requires \'vpcLink\' prop to be set'); - } - - if (options.connectionType === ConnectionType.INTERNET && options.vpcLink !== undefined) { - throw new Error('cannot set \'vpcLink\' where \'connectionType\' is INTERNET'); - } - if (options.credentialsRole) { credentials = options.credentialsRole.roleArn; } else if (options.credentialsPassthrough) { @@ -288,12 +275,12 @@ export class Method extends Resource { } return { - type: integration._props.type, - uri: integration._props.uri, + type: bindResult.type, + uri: bindResult.uri, cacheKeyParameters: options.cacheKeyParameters, cacheNamespace: options.cacheNamespace, contentHandling: options.contentHandling, - integrationHttpMethod: integration._props.integrationHttpMethod, + integrationHttpMethod: bindResult.integrationHttpMethod, requestParameters: options.requestParameters, requestTemplates: options.requestTemplates, passthroughBehavior: options.passthroughBehavior, diff --git a/packages/@aws-cdk/aws-apigateway/test/test.integration.ts b/packages/@aws-cdk/aws-apigateway/test/test.integration.ts new file mode 100644 index 000000000000..40047b53df74 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/test.integration.ts @@ -0,0 +1,58 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import * as apigw from '../lib'; + +export = { + 'integration "credentialsRole" and "credentialsPassthrough" are mutually exclusive'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const role = new iam.Role(stack, 'MyRole', { assumedBy: new iam.ServicePrincipal('foo') }); + + // THEN + test.throws(() => new apigw.Integration({ + type: apigw.IntegrationType.AWS_PROXY, + options: { + credentialsPassthrough: true, + credentialsRole: role, + }, + }), /'credentialsPassthrough' and 'credentialsRole' are mutually exclusive/); + test.done(); + }, + + 'integration connectionType VpcLink requires vpcLink to be set'(test: Test) { + test.throws(() => new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + connectionType: apigw.ConnectionType.VPC_LINK, + }, + }), /'connectionType' of VPC_LINK requires 'vpcLink' prop to be set/); + test.done(); + }, + + 'connectionType of INTERNET and vpcLink are mutually exclusive'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const nlb = new elbv2.NetworkLoadBalancer(stack, 'NLB', { + vpc, + }); + const link = new apigw.VpcLink(stack, 'link', { + targets: [nlb], + }); + + // THEN + test.throws(() => new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + connectionType: apigw.ConnectionType.INTERNET, + vpcLink: link, + }, + }), /cannot set 'vpcLink' where 'connectionType' is INTERNET/); + test.done(); + }, +}; \ 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 ba5ab8c1c2c9..85111b463688 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.method.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.method.ts @@ -1,6 +1,4 @@ import { ABSENT, expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; @@ -327,72 +325,6 @@ export = { test.done(); }, - 'integration "credentialsRole" and "credentialsPassthrough" are mutually exclusive'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const api = new apigw.RestApi(stack, 'test-api', { deploy: false }); - const role = new iam.Role(stack, 'MyRole', { assumedBy: new iam.ServicePrincipal('foo') }); - - // WHEN - const integration = new apigw.Integration({ - type: apigw.IntegrationType.AWS_PROXY, - options: { - credentialsPassthrough: true, - credentialsRole: role, - }, - }); - - // THEN - test.throws(() => api.root.addMethod('GET', integration), /'credentialsPassthrough' and 'credentialsRole' are mutually exclusive/); - test.done(); - }, - - 'integration connectionType VpcLink requires vpcLink to be set'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const api = new apigw.RestApi(stack, 'test-api', { deploy: false }); - - // WHEN - const integration = new apigw.Integration({ - type: apigw.IntegrationType.HTTP_PROXY, - integrationHttpMethod: 'ANY', - options: { - connectionType: apigw.ConnectionType.VPC_LINK, - }, - }); - - // THEN - test.throws(() => api.root.addMethod('GET', integration), /'connectionType' of VPC_LINK requires 'vpcLink' prop to be set/); - test.done(); - }, - - 'connectionType of INTERNET and vpcLink are mutually exclusive'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const api = new apigw.RestApi(stack, 'test-api', { deploy: false }); - const vpc = new ec2.Vpc(stack, 'VPC'); - const nlb = new elbv2.NetworkLoadBalancer(stack, 'NLB', { - vpc, - }); - const link = new apigw.VpcLink(stack, 'link', { - targets: [nlb], - }); - - // WHEN - const integration = new apigw.Integration({ - type: apigw.IntegrationType.HTTP_PROXY, - integrationHttpMethod: 'ANY', - options: { - connectionType: apigw.ConnectionType.INTERNET, - vpcLink: link, - }, - }); - - // 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();