Skip to content

Commit

Permalink
refactor(apigateway): API cleanups (#2903)
Browse files Browse the repository at this point in the history
* Fixed formatting
* Add `vcpLink.addTargets` and made `targets` optional, with a validation error.

BREAKING CHANGE: `MethodOptions.authorizerId` is now called `authorizer` and accepts an `IAuthorizer` which is a placeholder interface for the authorizer resource.
* **apigateway:** `restapi.executeApiArn` renamed to `arnForExecuteApi`.
* **apigateway:** `restapi.latestDeployment` and `deploymentStage` are now read-only.
  • Loading branch information
Elad Ben-Israel committed Jun 18, 2019
1 parent 3ae7511 commit 53e1191
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 48 deletions.
9 changes: 9 additions & 0 deletions packages/@aws-cdk/aws-apigateway/lib/authorizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Represents an API Gateway authorizer.
*/
export interface IAuthorizer {
/**
* The authorizer ID.
*/
readonly authorizerId: string;
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apigateway/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './usage-plan';
export * from './vpc-link';
export * from './methodresponse';
export * from './model';
export * from './authorizer';

// AWS::ApiGateway CloudFormation Resources:
export * from './apigateway.generated';
Expand Down
13 changes: 6 additions & 7 deletions packages/@aws-cdk/aws-apigateway/lib/method.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Construct, Resource, Stack } from '@aws-cdk/cdk';
import { CfnMethod, CfnMethodProps } from './apigateway.generated';
import { IAuthorizer } from './authorizer';
import { ConnectionType, Integration } from './integration';
import { MockIntegration } from './integrations/mock';
import { MethodResponse } from './methodresponse';
Expand All @@ -23,11 +24,8 @@ export interface MethodOptions {
/**
* If `authorizationType` is `Custom`, this specifies the ID of the method
* authorizer resource.
*
* NOTE: in the future this will be replaced with an `IAuthorizer`
* construct.
*/
readonly authorizerId?: string;
readonly authorizer?: IAuthorizer;

/**
* Indicates whether the method requires clients to submit a valid API key.
Expand Down Expand Up @@ -108,6 +106,7 @@ export class Method extends Resource {
const options = props.options || {};

const defaultMethodOptions = props.resource.defaultMethodOptions || {};
const authorizer = options.authorizer || defaultMethodOptions.authorizer;

const methodProps: CfnMethodProps = {
resourceId: props.resource.resourceId,
Expand All @@ -116,7 +115,7 @@ export class Method extends Resource {
operationName: options.operationName || defaultMethodOptions.operationName,
apiKeyRequired: options.apiKeyRequired || defaultMethodOptions.apiKeyRequired,
authorizationType: options.authorizationType || defaultMethodOptions.authorizationType || AuthorizationType.None,
authorizerId: options.authorizerId || defaultMethodOptions.authorizerId,
authorizerId: authorizer && authorizer.authorizerId,
requestParameters: options.requestParameters,
integration: this.renderIntegration(props.integration),
methodResponses: this.renderMethodResponses(options.methodResponses),
Expand Down Expand Up @@ -153,15 +152,15 @@ export class Method extends Resource {
}

const stage = this.restApi.deploymentStage.stageName.toString();
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, stage);
return this.restApi.arnForExecuteApi(this.httpMethod, this.resource.path, stage);
}

/**
* Returns an execute-api ARN for this method's "test-invoke-stage" stage.
* This stage is used by the AWS Console UI when testing the method.
*/
public get testMethodArn(): string {
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, 'test-invoke-stage');
return this.restApi.arnForExecuteApi(this.httpMethod, this.resource.path, 'test-invoke-stage');
}

private renderIntegration(integration?: Integration): CfnMethod.IntegrationProperty {
Expand Down
37 changes: 20 additions & 17 deletions packages/@aws-cdk/aws-apigateway/lib/restapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,30 +178,24 @@ export class RestApi extends Resource implements IRestApi {
public readonly restApiRootResourceId: string;

/**
* API Gateway deployment that represents the latest changes of the API.
* This resource will be automatically updated every time the REST API model changes.
* This will be undefined if `deploy` is false.
* Represents the root resource ("/") of this API. Use it to define the API model:
*
* api.root.addMethod('ANY', redirectToHomePage); // "ANY /"
* api.root.addResource('friends').addMethod('GET', getFriendsHandler); // "GET /friends"
*
*/
public latestDeployment?: Deployment;
public readonly root: IResource;

/**
* API Gateway stage that points to the latest deployment (if defined).
*
* If `deploy` is disabled, you will need to explicitly assign this value in order to
* set up integrations.
*/
public deploymentStage?: Stage;

/**
* Represents the root resource ("/") of this API. Use it to define the API model:
*
* api.root.addMethod('ANY', redirectToHomePage); // "ANY /"
* api.root.addResource('friends').addMethod('GET', getFriendsHandler); // "GET /friends"
*
*/
public readonly root: IResource;
public deploymentStage: Stage;

private readonly methods = new Array<Method>();
private _latestDeployment: Deployment | undefined;

constructor(scope: Construct, id: string, props: RestApiProps = { }) {
super(scope, id);
Expand Down Expand Up @@ -231,6 +225,15 @@ export class RestApi extends Resource implements IRestApi {
this.root = new RootResource(this, props, resource.attrRootResourceId);
}

/**
* API Gateway deployment that represents the latest changes of the API.
* This resource will be automatically updated every time the REST API model changes.
* This will be undefined if `deploy` is false.
*/
public get latestDeployment() {
return this._latestDeployment;
}

/**
* The deployed root URL of this REST API.
*/
Expand Down Expand Up @@ -275,7 +278,7 @@ export class RestApi extends Resource implements IRestApi {
* @param path The resource path. Must start with '/' (default `*`)
* @param stage The stage (default `*`)
*/
public executeApiArn(method: string = '*', path: string = '/*', stage: string = '*') {
public arnForExecuteApi(method: string = '*', path: string = '/*', stage: string = '*') {
if (!path.startsWith('/')) {
throw new Error(`"path" must begin with a "/": '${path}'`);
}
Expand Down Expand Up @@ -317,7 +320,7 @@ export class RestApi extends Resource implements IRestApi {
const deploy = props.deploy === undefined ? true : props.deploy;
if (deploy) {

this.latestDeployment = new Deployment(this, 'Deployment', {
this._latestDeployment = new Deployment(this, 'Deployment', {
description: 'Automatically created by the RestApi construct',
api: this,
retainDeployments: props.retainDeployments
Expand All @@ -328,7 +331,7 @@ export class RestApi extends Resource implements IRestApi {
const stageName = (props.deployOptions && props.deployOptions.stageName) || 'prod';

this.deploymentStage = new Stage(this, `DeploymentStage.${stageName}`, {
deployment: this.latestDeployment,
deployment: this._latestDeployment,
...props.deployOptions
});

Expand Down
22 changes: 11 additions & 11 deletions packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ export interface ThrottlingPerMethod {
* The method for which you specify the throttling settings.
* @default none
*/
readonly method: Method,
readonly method: Method;

/**
* Specifies the overall request rate (average requests per second) and burst capacity.
* @default none
*/
readonly throttle: ThrottleSettings
readonly throttle: ThrottleSettings;
}

/**
Expand All @@ -90,57 +90,57 @@ export interface UsagePlanPerApiStage {
/**
* @default none
*/
readonly api?: IRestApi,
readonly api?: IRestApi;

/**
*
* [disable-awslint:ref-via-interface]
* @default none
*/
readonly stage?: Stage,
readonly stage?: Stage;

/**
* @default none
*/
readonly throttle?: ThrottlingPerMethod[]
readonly throttle?: ThrottlingPerMethod[];
}

export interface UsagePlanProps {
/**
* API Stages to be associated which the usage plan.
* @default none
*/
readonly apiStages?: UsagePlanPerApiStage[],
readonly apiStages?: UsagePlanPerApiStage[];

/**
* Represents usage plan purpose.
* @default none
*/
readonly description?: string,
readonly description?: string;

/**
* Number of requests clients can make in a given time period.
* @default none
*/
readonly quota?: QuotaSettings
readonly quota?: QuotaSettings;

/**
* Overall throttle settings for the API.
* @default none
*/
readonly throttle?: ThrottleSettings,
readonly throttle?: ThrottleSettings;

/**
* Name for this usage plan.
* @default none
*/
readonly name?: string
readonly name?: string;

/**
* ApiKey to be associated with the usage plan.
* @default none
*/
readonly apiKey?: IApiKey
readonly apiKey?: IApiKey;
}

export class UsagePlan extends Resource {
Expand Down
37 changes: 30 additions & 7 deletions packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import { Construct, Resource } from '@aws-cdk/cdk';
import { Construct, Lazy, Resource } from '@aws-cdk/cdk';
import { CfnVpcLink } from './apigateway.generated';

/**
Expand All @@ -8,9 +8,9 @@ import { CfnVpcLink } from './apigateway.generated';
export interface VpcLinkProps {
/**
* The name used to label and identify the VPC link.
* @default automatically generated name
* @default - automatically generated name
*/
readonly name?: string;
readonly vpcLinkName?: string;

/**
* The description of the VPC link.
Expand All @@ -21,8 +21,10 @@ export interface VpcLinkProps {
/**
* The network load balancers of the VPC targeted by the VPC link.
* The network load balancers must be owned by the same AWS account of the API owner.
*
* @default - no targets. Use `addTargets` to add targets
*/
readonly targets: elbv2.INetworkLoadBalancer[];
readonly targets?: elbv2.INetworkLoadBalancer[];
}

/**
Expand All @@ -36,15 +38,36 @@ export class VpcLink extends Resource {
*/
public readonly vpcLinkId: string;

constructor(scope: Construct, id: string, props: VpcLinkProps) {
private readonly targets = new Array<elbv2.INetworkLoadBalancer>();

constructor(scope: Construct, id: string, props: VpcLinkProps = {}) {
super(scope, id);

const cfnResource = new CfnVpcLink(this, 'Resource', {
name: props.name || this.node.uniqueId,
name: props.vpcLinkName || this.node.uniqueId,
description: props.description,
targetArns: props.targets.map(nlb => nlb.loadBalancerArn)
targetArns: Lazy.listValue({ produce: () => this.renderTargets() })
});

this.vpcLinkId = cfnResource.refAsString;

if (props.targets) {
this.addTargets(...props.targets);
}
}

public addTargets(...targets: elbv2.INetworkLoadBalancer[]) {
this.targets.push(...targets);
}

protected validate(): string[] {
if (this.targets.length === 0) {
return [ `No targets added to vpc link` ];
}
return [];
}

private renderTargets() {
return this.targets.map(nlb => nlb.loadBalancerArn);
}
}
8 changes: 4 additions & 4 deletions packages/@aws-cdk/aws-apigateway/test/test.restapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export = {
api.root.addMethod('GET');

// WHEN
const arn = api.executeApiArn('method', '/path', 'stage');
const arn = api.arnForExecuteApi('method', '/path', 'stage');

// THEN
test.deepEqual(stack.resolve(arn), { 'Fn::Join':
Expand All @@ -426,7 +426,7 @@ export = {
api.root.addMethod('GET');

// THEN
test.throws(() => api.executeApiArn('method', 'hey-path', 'stage'), /"path" must begin with a "\/": 'hey-path'/);
test.throws(() => api.arnForExecuteApi('method', 'hey-path', 'stage'), /"path" must begin with a "\/": 'hey-path'/);
test.done();
},

Expand Down Expand Up @@ -534,7 +534,7 @@ export = {
const api = new apigateway.RestApi(stack, 'myapi', {
defaultIntegration: rootInteg,
defaultMethodOptions: {
authorizerId: 'AUTHID',
authorizer: { authorizerId: 'AUTHID' },
authorizationType: apigateway.AuthorizationType.IAM,
}
});
Expand All @@ -553,7 +553,7 @@ export = {
const child2 = api.root.addResource('child2', {
defaultIntegration: new apigateway.MockIntegration(),
defaultMethodOptions: {
authorizerId: 'AUTHID2',
authorizer: { authorizerId: 'AUTHID2' },
}
});

Expand Down
Loading

0 comments on commit 53e1191

Please sign in to comment.