Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lambda-event-sources): "api" event source #1742

Merged
merged 1 commit into from
Feb 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-apigateway/lib/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class Method extends cdk.Construct {

this.resource = props.resource;
this.restApi = props.resource.resourceApi;
this.httpMethod = props.httpMethod;
this.httpMethod = props.httpMethod.toUpperCase();

validateHttpMethod(this.httpMethod);

Expand All @@ -87,7 +87,7 @@ export class Method extends cdk.Construct {
const methodProps: CfnMethodProps = {
resourceId: props.resource.resourceId,
restApiId: this.restApi.restApiId,
httpMethod: props.httpMethod,
httpMethod: this.httpMethod,
operationName: options.operationName || defaultMethodOptions.operationName,
apiKeyRequired: options.apiKeyRequired || defaultMethodOptions.apiKeyRequired,
authorizationType: options.authorizationType || defaultMethodOptions.authorizationType || AuthorizationType.None,
Expand Down
114 changes: 96 additions & 18 deletions packages/@aws-cdk/aws-apigateway/lib/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { Method, MethodOptions } from './method';
import { RestApi } from './restapi';

export interface IRestApiResource extends cdk.IConstruct {
/**
* The parent of this resource or undefined for the root resource.
*/
readonly parentResource?: IRestApiResource;

/**
* The rest API that this resource is part of.
*
Expand Down Expand Up @@ -37,6 +42,17 @@ export interface IRestApiResource extends cdk.IConstruct {
*/
readonly defaultMethodOptions?: MethodOptions;

/**
* Gets or create all resources leading up to the specified path.
*
* - Path may only start with "/" if this method is called on the root resource.
* - All resources are created using default options.
*
* @param path The relative path
* @returns a new or existing resource.
*/
resourceForPath(path: string): Resource;

/**
* Defines a new child resource where this resource is the parent.
* @param pathPart The path part for the child resource
Expand All @@ -45,6 +61,14 @@ export interface IRestApiResource extends cdk.IConstruct {
*/
addResource(pathPart: string, options?: ResourceOptions): Resource;

/**
* Retrieves a child resource by path part.
*
* @param pathPart The path part of the child resource
* @returns the child resource or undefined if not found
*/
getResource(pathPart: string): IRestApiResource | undefined;

/**
* Adds a greedy proxy resource ("{proxy+}") and an ANY method to this route.
* @param options Default integration and method options.
Expand Down Expand Up @@ -89,7 +113,71 @@ export interface ResourceProps extends ResourceOptions {
pathPart: string;
}

export class Resource extends cdk.Construct implements IRestApiResource {
export abstract class ResourceBase extends cdk.Construct implements IRestApiResource {
public abstract readonly parentResource?: IRestApiResource;
public abstract readonly resourceApi: RestApi;
public abstract readonly resourceId: string;
public abstract readonly resourcePath: string;
public abstract readonly defaultIntegration?: Integration;
public abstract readonly defaultMethodOptions?: MethodOptions;

private readonly children: { [pathPart: string]: Resource } = { };

constructor(scope: cdk.Construct, id: string) {
super(scope, id);
}

public addResource(pathPart: string, options?: ResourceOptions): Resource {
return new Resource(this, pathPart, { parent: this, pathPart, ...options });
}

public addMethod(httpMethod: string, integration?: Integration, options?: MethodOptions): Method {
return new Method(this, httpMethod, { resource: this, httpMethod, integration, options });
}

public addProxy(options?: ResourceOptions): ProxyResource {
return new ProxyResource(this, '{proxy+}', { parent: this, ...options });
}

public getResource(pathPart: string): IRestApiResource | undefined {
return this.children[pathPart];
}

public trackChild(pathPart: string, resource: Resource) {
this.children[pathPart] = resource;
}

public resourceForPath(path: string): Resource {
if (!path) {
return this;
}

if (path.startsWith('/')) {
if (this.resourcePath !== '/') {
throw new Error(`Path may start with "/" only for the resource, but we are at: ${this.resourcePath}`);
}

// trim trailing "/"
return this.resourceForPath(path.substr(1));
}

const parts = path.split('/');
const next = parts.shift();
if (!next || next === '') {
throw new Error(`resourceForPath cannot be called with an empty path`);
}

let resource = this.getResource(next);
if (!resource) {
resource = this.addResource(next);
}

return resource.resourceForPath(parts.join('/'));
}
}

export class Resource extends ResourceBase {
public readonly parentResource?: IRestApiResource;
public readonly resourceApi: RestApi;
public readonly resourceId: string;
public readonly resourcePath: string;
Expand All @@ -101,6 +189,12 @@ export class Resource extends cdk.Construct implements IRestApiResource {

validateResourcePathPart(props.pathPart);

this.parentResource = props.parent;

if (props.parent instanceof ResourceBase) {
props.parent.trackChild(props.pathPart, this);
}

const resourceProps: CfnResourceProps = {
restApiId: props.parent.resourceApi.restApiId,
parentId: props.parent.resourceId,
Expand Down Expand Up @@ -130,18 +224,6 @@ export class Resource extends cdk.Construct implements IRestApiResource {
...props.defaultMethodOptions
};
}

public addResource(pathPart: string, options?: ResourceOptions): Resource {
return new Resource(this, pathPart, { parent: this, pathPart, ...options });
}

public addMethod(httpMethod: string, integration?: Integration, options?: MethodOptions): Method {
return new Method(this, httpMethod, { resource: this, httpMethod, integration, options });
}

public addProxy(options?: ResourceOptions): ProxyResource {
return new ProxyResource(this, '{proxy+}', { parent: this, ...options });
}
}

export interface ProxyResourceProps extends ResourceOptions {
Expand Down Expand Up @@ -171,8 +253,6 @@ export class ProxyResource extends Resource {
*/
public readonly anyMethod?: Method;

private readonly parentResource: IRestApiResource;

constructor(scope: cdk.Construct, id: string, props: ProxyResourceProps) {
super(scope, id, {
parent: props.parent,
Expand All @@ -181,8 +261,6 @@ export class ProxyResource extends Resource {
defaultMethodOptions: props.defaultMethodOptions,
});

this.parentResource = props.parent;

const anyMethod = props.anyMethod !== undefined ? props.anyMethod : true;
if (anyMethod) {
this.anyMethod = this.addMethod('ANY');
Expand All @@ -192,7 +270,7 @@ export class ProxyResource extends Resource {
public addMethod(httpMethod: string, integration?: Integration, options?: MethodOptions): Method {
// In case this proxy is mounted under the root, also add this method to
// the root so that empty paths are proxied as well.
if (this.parentResource.resourcePath === '/') {
if (this.parentResource && this.parentResource.resourcePath === '/') {
this.parentResource.addMethod(httpMethod);
}
return super.addMethod(httpMethod, integration, options);
Expand Down
42 changes: 22 additions & 20 deletions packages/@aws-cdk/aws-apigateway/lib/restapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CfnAccount, CfnRestApi } from './apigateway.generated';
import { Deployment } from './deployment';
import { Integration } from './integration';
import { Method, MethodOptions } from './method';
import { IRestApiResource, ProxyResource, Resource, ResourceOptions } from './resource';
import { IRestApiResource, ResourceBase, ResourceOptions } from './resource';
import { Stage, StageOptions } from './stage';

export interface RestApiImportProps {
Expand Down Expand Up @@ -221,25 +221,7 @@ export class RestApi extends cdk.Construct implements IRestApi {
this.configureCloudWatchRole(resource);
}

// configure the "root" resource
this.root = {
get dependencyRoots() { return [this]; },
node: this.node,
addResource: (pathPart: string, options?: ResourceOptions) => {
return new Resource(this, pathPart, { parent: this.root, pathPart, ...options });
},
addMethod: (httpMethod: string, integration?: Integration, options?: MethodOptions) => {
return new Method(this, httpMethod, { resource: this.root, httpMethod, integration, options });
},
addProxy: (options?: ResourceOptions) => {
return new ProxyResource(this, '{proxy+}', { parent: this.root, ...options });
},
defaultIntegration: props.defaultIntegration,
defaultMethodOptions: props.defaultMethodOptions,
resourceApi: this,
resourceId: resource.restApiRootResourceId,
resourcePath: '/'
};
this.root = new RootResource(this, props, resource.restApiRootResourceId);
}

/**
Expand Down Expand Up @@ -406,3 +388,23 @@ class ImportedRestApi extends cdk.Construct implements IRestApi {
return this.props;
}
}

class RootResource extends ResourceBase {
public readonly parentResource?: IRestApiResource;
public readonly resourceApi: RestApi;
public readonly resourceId: string;
public readonly resourcePath: string;
public readonly defaultIntegration?: Integration | undefined;
public readonly defaultMethodOptions?: MethodOptions | undefined;

constructor(api: RestApi, props: RestApiProps, resourceId: string) {
super(api, 'Default');

this.parentResource = undefined;
this.defaultIntegration = props.defaultIntegration;
this.defaultMethodOptions = props.defaultMethodOptions;
this.resourceApi = api;
this.resourceId = resourceId;
this.resourcePath = '/';
}
}
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-apigateway/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { format as formatUrl } from 'url';
const ALLOWED_METHODS = [ 'ANY', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT' ];

export function validateHttpMethod(method: string, messagePrefix: string = '') {
if (!ALLOWED_METHODS.includes(method.toUpperCase())) {
if (!ALLOWED_METHODS.includes(method)) {
throw new Error(`${messagePrefix}Invalid HTTP method "${method}". Allowed methods: ${ALLOWED_METHODS.join(',')}`);
}
}
Expand Down
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-apigateway/test/test.method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,5 +348,22 @@ export = {
}
}));
test.done();
},

'method is always set as uppercase'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const api = new apigateway.RestApi(stack, 'api');

// WHEN
api.root.addMethod('get');
api.root.addMethod('PoSt');
api.root.addMethod('PUT');

// THEN
expect(stack).to(haveResource('AWS::ApiGateway::Method', { HttpMethod: "POST" }));
expect(stack).to(haveResource('AWS::ApiGateway::Method', { HttpMethod: "GET" }));
expect(stack).to(haveResource('AWS::ApiGateway::Method', { HttpMethod: "PUT" }));
test.done();
}
};
Loading