mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
Merge branch 'master' into feature/BET-1118
This commit is contained in:
commit
eba004c2e6
@ -25,7 +25,8 @@ serverless deploy
|
||||
- `--verbose` or `-v` Shows all stack events during deployment, and display any Stack Output.
|
||||
- `--function` or `-f` Invoke `deploy function` (see above). Convenience shortcut - cannot be used with `--package`.
|
||||
- `--conceal` Hides secrets from the output (e.g. API Gateway key values).
|
||||
- `--aws-s3-accelerate` Enables S3 Transfer Acceleration making uploading artifacts much faster. You can read more about it [here](http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html). **Note: When using Transfer Acceleration, additional data transfer charges may apply**
|
||||
- `--aws-s3-accelerate` Enables S3 Transfer Acceleration making uploading artifacts much faster. You can read more about it [here](http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html). It requires additional `s3:PutAccelerateConfiguration` permissions. **Note: When using Transfer Acceleration, additional data transfer charges may apply.**
|
||||
- `--no-aws-s3-accelerate` Explicitly disables S3 Transfer Acceleration). It also requires additional `s3:PutAccelerateConfiguration` permissions.
|
||||
|
||||
## Artifacts
|
||||
|
||||
|
||||
@ -633,7 +633,39 @@ See the [api gateway documentation](https://docs.aws.amazon.com/apigateway/lates
|
||||
**Notes:**
|
||||
|
||||
- A missing/empty request Content-Type is considered to be the API Gateway default (`application/json`)
|
||||
- API Gateway docs refer to "WHEN_NO_TEMPLATE" (singular), but this will fail during creation as the actual value should be "WHEN_NO_TEMPLATES" (plural)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [[Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)](#read-this-on-the-main-serverless-docs-sitehttpswwwserverlesscomframeworkdocsprovidersawseventsapigateway)
|
||||
- [API Gateway](#api-gateway)
|
||||
- [Lambda Proxy Integration](#lambda-proxy-integration)
|
||||
- [Simple HTTP Endpoint](#simple-http-endpoint)
|
||||
- [Example "LAMBDA-PROXY" event (default)](#example-lambda-proxy-event-default)
|
||||
- [HTTP Endpoint with Extended Options](#http-endpoint-with-extended-options)
|
||||
- [Enabling CORS](#enabling-cors)
|
||||
- [HTTP Endpoints with `AWS_IAM` Authorizers](#http-endpoints-with-awsiam-authorizers)
|
||||
- [HTTP Endpoints with Custom Authorizers](#http-endpoints-with-custom-authorizers)
|
||||
- [Catching Exceptions In Your Lambda Function](#catching-exceptions-in-your-lambda-function)
|
||||
- [Setting API keys for your Rest API](#setting-api-keys-for-your-rest-api)
|
||||
- [Request Parameters](#request-parameters)
|
||||
- [Lambda Integration](#lambda-integration)
|
||||
- [Example "LAMBDA" event (before customization)](#example-lambda-event-before-customization)
|
||||
- [Request templates](#request-templates)
|
||||
- [Default Request Templates](#default-request-templates)
|
||||
- [Custom Request Templates](#custom-request-templates)
|
||||
- [Pass Through Behavior](#pass-through-behavior)
|
||||
- [Responses](#responses)
|
||||
- [Custom Response Headers](#custom-response-headers)
|
||||
- [Custom Response Templates](#custom-response-templates)
|
||||
- [Status codes](#status-codes)
|
||||
- [Available Status Codes](#available-status-codes)
|
||||
- [Using Status Codes](#using-status-codes)
|
||||
- [Custom Status Codes](#custom-status-codes)
|
||||
- [Setting an HTTP Proxy on API Gateway](#setting-an-http-proxy-on-api-gateway)
|
||||
- [Share API Gateway and API Resources](#share-api-gateway-and-api-resources)
|
||||
|
||||
### Responses
|
||||
|
||||
@ -828,3 +860,116 @@ endpoint of your proxy, and the URI you want to set a proxy to.
|
||||
|
||||
Now that you have these two CloudFormation templates defined in your `serverless.yml` file, you can simply run
|
||||
`serverless deploy` and that will deploy these custom resources for you along with your service and set up a proxy on your Rest API.
|
||||
|
||||
## Share API Gateway and API Resources
|
||||
|
||||
As you application grows, you will have idea to break it out into multiple services. However, each serverless project generates new API Gateway by default. If you want to share same API Gateway for muliple projects, you 'll need to reference REST API ID and Root Resource ID into serverless.yml files
|
||||
|
||||
```yml
|
||||
service: service-name
|
||||
provider:
|
||||
name: aws
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx # REST API resource ID. Default is generated by the framework
|
||||
restApiRootResourceId: xxxxxxxxxx # Root resource, represent as / path
|
||||
|
||||
functions:
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
In case the application has many chilren and grandchildren paths, you also want to break them out into smaller services.
|
||||
|
||||
```yml
|
||||
service: service-a
|
||||
provider:
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx
|
||||
restApiRootResourceId: xxxxxxxxxx
|
||||
|
||||
functions:
|
||||
create:
|
||||
handler: posts.create
|
||||
events:
|
||||
- http:
|
||||
method: post
|
||||
path: /posts
|
||||
```
|
||||
|
||||
```yml
|
||||
service: service-b
|
||||
provider:
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx
|
||||
restApiRootResourceId: xxxxxxxxxx
|
||||
|
||||
functions:
|
||||
create:
|
||||
handler: posts.createComment
|
||||
events:
|
||||
- http:
|
||||
method: post
|
||||
path: /posts/{id}/comments
|
||||
```
|
||||
|
||||
They reference the same parent path `/posts`. Cloudformation will throw error if we try to generate existed one. To avoid that, we must reference source ID of `/posts`.
|
||||
|
||||
```yml
|
||||
service: service-a
|
||||
provider:
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx
|
||||
restApiRootResourceId: xxxxxxxxxx
|
||||
restApiResources:
|
||||
/posts: xxxxxxxxxx
|
||||
|
||||
functions:
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
```yml
|
||||
service: service-b
|
||||
provider:
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx
|
||||
restApiRootResourceId: xxxxxxxxxx
|
||||
restApiResources:
|
||||
/posts: xxxxxxxxxx
|
||||
|
||||
functions:
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
You can define more than one path resource. Otherwise, serverless will generate paths from root resource. `restApiRootResourceId` can be optional if there isn't path that need to be generated from the root
|
||||
|
||||
```yml
|
||||
service: service-a
|
||||
provider:
|
||||
apiGateway:
|
||||
restApiId: xxxxxxxxxx
|
||||
# restApiRootResourceId: xxxxxxxxxx # Optional
|
||||
restApiResources:
|
||||
/posts: xxxxxxxxxx
|
||||
/categories: xxxxxxxxx
|
||||
|
||||
|
||||
functions:
|
||||
listPosts:
|
||||
handler: posts.list
|
||||
events:
|
||||
- http:
|
||||
method: get
|
||||
path: /posts
|
||||
|
||||
listCategories:
|
||||
handler: categories.list
|
||||
events:
|
||||
- http:
|
||||
method: get
|
||||
path: /categories
|
||||
|
||||
```
|
||||
|
||||
For best practice and CI, CD friendly, we should define Cloudformation resources from early service, then use Cross-Stack References for another ones.
|
||||
|
||||
@ -45,6 +45,13 @@ provider:
|
||||
- myFirstKey
|
||||
- ${opt:stage}-myFirstKey
|
||||
- ${env:MY_API_KEY} # you can hide it in a serverless variable
|
||||
apiGateway: # Optional API Gateway global config
|
||||
restApiId: xxxxxxxxxx # REST API resource ID. Default is generated by the framework
|
||||
restApiRootResourceId: xxxxxxxxxx # Root resource ID, represent as / path
|
||||
restApiResources: # List of existing resources that were created in the REST API. This is required or the stack will be conflicted
|
||||
'/users': xxxxxxxxxx
|
||||
'/users/create': xxxxxxxxxx
|
||||
|
||||
usagePlan: # Optional usage plan configuration
|
||||
quota:
|
||||
limit: 5000
|
||||
@ -95,6 +102,7 @@ package: # Optional deployment packaging configuration
|
||||
- .travis.yml
|
||||
excludeDevDependencies: false # Config if Serverless should automatically exclude dev dependencies in the deployment package. Defaults to true
|
||||
|
||||
|
||||
functions:
|
||||
usersCreate: # A Function
|
||||
handler: users.create # The file and module for this specific function.
|
||||
|
||||
@ -27,7 +27,7 @@ module.exports = {
|
||||
Enabled: true,
|
||||
Name: apiKey,
|
||||
StageKeys: [{
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
StageName: this.provider.getStage(),
|
||||
}],
|
||||
},
|
||||
|
||||
@ -12,7 +12,7 @@ module.exports = {
|
||||
AuthorizerResultTtlInSeconds: authorizer.resultTtlInSeconds,
|
||||
IdentitySource: authorizer.identitySource,
|
||||
Name: authorizer.name,
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
};
|
||||
|
||||
if (typeof authorizer.identityValidationExpression === 'string') {
|
||||
|
||||
@ -52,7 +52,7 @@ module.exports = {
|
||||
IntegrationResponses: this.generateCorsIntegrationResponses(preflightHeaders),
|
||||
},
|
||||
ResourceId: resourceRef,
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -34,19 +34,28 @@ describe('#compileCors()', () => {
|
||||
};
|
||||
awsCompileApigEvents = new AwsCompileApigEvents(serverless, options);
|
||||
awsCompileApigEvents.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi';
|
||||
awsCompileApigEvents.apiGatewayResourceLogicalIds = {
|
||||
'users/create': 'ApiGatewayResourceUsersCreate',
|
||||
'users/list': 'ApiGatewayResourceUsersList',
|
||||
'users/update': 'ApiGatewayResourceUsersUpdate',
|
||||
'users/delete': 'ApiGatewayResourceUsersDelete',
|
||||
'users/any': 'ApiGatewayResourceUsersAny',
|
||||
};
|
||||
awsCompileApigEvents.apiGatewayResourceNames = {
|
||||
'users/create': 'UsersCreate',
|
||||
'users/list': 'UsersList',
|
||||
'users/update': 'UsersUpdate',
|
||||
'users/delete': 'UsersDelete',
|
||||
'users/any': 'UsersAny',
|
||||
awsCompileApigEvents.apiGatewayResources = {
|
||||
'users/create': {
|
||||
name: 'UsersCreate',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersCreate',
|
||||
},
|
||||
|
||||
'users/list': {
|
||||
name: 'UsersList',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersList',
|
||||
},
|
||||
'users/update': {
|
||||
name: 'UsersUpdate',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersUpdate',
|
||||
},
|
||||
'users/delete': {
|
||||
name: 'UsersDelete',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersDelete',
|
||||
},
|
||||
'users/any': {
|
||||
name: 'UsersAny',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersAny',
|
||||
},
|
||||
};
|
||||
awsCompileApigEvents.validated = {};
|
||||
});
|
||||
|
||||
@ -12,7 +12,7 @@ module.exports = {
|
||||
[this.apiGatewayDeploymentLogicalId]: {
|
||||
Type: 'AWS::ApiGateway::Deployment',
|
||||
Properties: {
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
StageName: this.provider.getStage(),
|
||||
},
|
||||
DependsOn: this.apiGatewayMethodLogicalIds,
|
||||
@ -27,7 +27,7 @@ module.exports = {
|
||||
'Fn::Join': ['',
|
||||
[
|
||||
'https://',
|
||||
{ Ref: this.apiGatewayRestApiLogicalId },
|
||||
this.provider.getApiGatewayRestApiId(),
|
||||
`.execute-api.${
|
||||
this.provider.getRegion()
|
||||
}.amazonaws.com/${
|
||||
|
||||
@ -20,7 +20,7 @@ module.exports = {
|
||||
HttpMethod: event.http.method.toUpperCase(),
|
||||
RequestParameters: requestParameters,
|
||||
ResourceId: resourceId,
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -35,17 +35,24 @@ describe('#compileMethods()', () => {
|
||||
awsCompileApigEvents = new AwsCompileApigEvents(serverless, options);
|
||||
awsCompileApigEvents.validated = {};
|
||||
awsCompileApigEvents.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi';
|
||||
awsCompileApigEvents.apiGatewayResourceLogicalIds = {
|
||||
'users/create': 'ApiGatewayResourceUsersCreate',
|
||||
'users/list': 'ApiGatewayResourceUsersList',
|
||||
'users/update': 'ApiGatewayResourceUsersUpdate',
|
||||
'users/delete': 'ApiGatewayResourceUsersDelete',
|
||||
};
|
||||
awsCompileApigEvents.apiGatewayResourceNames = {
|
||||
'users/create': 'UsersCreate',
|
||||
'users/list': 'UsersList',
|
||||
'users/update': 'UsersUpdate',
|
||||
'users/delete': 'UsersDelete',
|
||||
awsCompileApigEvents.apiGatewayResources = {
|
||||
'users/create': {
|
||||
name: 'UsersCreate',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersCreate',
|
||||
},
|
||||
|
||||
'users/list': {
|
||||
name: 'UsersList',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersList',
|
||||
},
|
||||
'users/update': {
|
||||
name: 'UsersUpdate',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersUpdate',
|
||||
},
|
||||
'users/delete': {
|
||||
name: 'UsersDelete',
|
||||
resourceLogicalId: 'ApiGatewayResourceUsersDelete',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
':',
|
||||
{ Ref: 'AWS::AccountId' },
|
||||
':',
|
||||
{ Ref: this.apiGatewayRestApiLogicalId },
|
||||
this.provider.getApiGatewayRestApiId(),
|
||||
'/*/*',
|
||||
],
|
||||
] },
|
||||
|
||||
@ -6,67 +6,248 @@ const _ = require('lodash');
|
||||
module.exports = {
|
||||
|
||||
compileResources() {
|
||||
const resourcePaths = this.getResourcePaths();
|
||||
|
||||
this.apiGatewayResourceNames = {};
|
||||
this.apiGatewayResourceLogicalIds = {};
|
||||
this.apiGatewayResources = this.getResourcePaths();
|
||||
|
||||
// ['users', 'users/create', 'users/create/something']
|
||||
resourcePaths.forEach(path => {
|
||||
const pathArray = path.split('/');
|
||||
const resourceName = this.provider.naming.normalizePath(path);
|
||||
const resourceLogicalId = this.provider.naming.getResourceLogicalId(path);
|
||||
const pathPart = pathArray.pop();
|
||||
const parentPath = pathArray.join('/');
|
||||
const parentRef = this.getResourceId(parentPath);
|
||||
_.keys(this.apiGatewayResources).forEach((path) => {
|
||||
const resource = this.apiGatewayResources[path];
|
||||
if (resource.resourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.apiGatewayResourceNames[path] = resourceName;
|
||||
this.apiGatewayResourceLogicalIds[path] = resourceLogicalId;
|
||||
resource.resourceLogicalId = this.provider.naming.getResourceLogicalId(path);
|
||||
resource.resourceId = { Ref: resource.resourceLogicalId };
|
||||
|
||||
const parentRef = resource.parent
|
||||
? resource.parent.resourceId : this.getResourceId();
|
||||
|
||||
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
|
||||
[resourceLogicalId]: {
|
||||
[resource.resourceLogicalId]: {
|
||||
Type: 'AWS::ApiGateway::Resource',
|
||||
Properties: {
|
||||
ParentId: parentRef,
|
||||
PathPart: pathPart,
|
||||
RestApiId: { Ref: this.apiGatewayRestApiLogicalId },
|
||||
PathPart: resource.pathPart,
|
||||
RestApiId: this.provider.getApiGatewayRestApiId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return BbPromise.resolve();
|
||||
},
|
||||
|
||||
getResourcePaths() {
|
||||
const paths = _.reduce(this.validated.events, (resourcePaths, event) => {
|
||||
let path = event.http.path;
|
||||
combineResourceTrees(trees) {
|
||||
const self = this;
|
||||
|
||||
while (path !== '') {
|
||||
if (resourcePaths.indexOf(path) === -1) {
|
||||
resourcePaths.push(path);
|
||||
function getNodePaths(result, node) {
|
||||
const r = result;
|
||||
r[node.path] = node;
|
||||
if (!node.name) {
|
||||
r[node.path].name = self.provider.naming.normalizePath(node.path);
|
||||
}
|
||||
|
||||
node.children.forEach((child) => getNodePaths(result, child));
|
||||
}
|
||||
|
||||
return _.reduce(trees, (result, tree) => {
|
||||
getNodePaths(result, tree);
|
||||
return result;
|
||||
}, {});
|
||||
},
|
||||
|
||||
getResourcePaths() {
|
||||
const trees = [];
|
||||
const predefinedResourceNodes = [];
|
||||
const methodNodes = [];
|
||||
const predefinedResources = this.provider.getApiGatewayPredefinedResources();
|
||||
|
||||
|
||||
function cutBranch(node) {
|
||||
if (!node.parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const n = node;
|
||||
if (node.parent.children.length <= 1) {
|
||||
n.parent.children = [];
|
||||
} else {
|
||||
n.parent.children = node.parent.children.filter((c) => c.path !== n.path);
|
||||
n.parent.isCut = true;
|
||||
}
|
||||
n.parent = null;
|
||||
}
|
||||
|
||||
// organize all resource paths into N-ary tree
|
||||
function applyResource(resource, isMethod) {
|
||||
let root;
|
||||
let parent;
|
||||
let currentPath;
|
||||
const path = resource.path.replace(/^\//, '').replace(/\/$/, '');
|
||||
const pathParts = path.split('/');
|
||||
|
||||
function applyNodeResource(node, parts, index) {
|
||||
const n = node;
|
||||
if (index === parts.length - 1) {
|
||||
n.name = resource.name;
|
||||
if (resource.resourceId) {
|
||||
n.resourceId = resource.resourceId;
|
||||
if (_.every(predefinedResourceNodes, (iter) => iter.path !== n.path)) {
|
||||
predefinedResourceNodes.push(node);
|
||||
}
|
||||
}
|
||||
if (isMethod && !node.hasMethod) {
|
||||
n.hasMethod = true;
|
||||
if (_.every(methodNodes, (iter) => iter.path !== n.path)) {
|
||||
methodNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const splittedPath = path.split('/');
|
||||
splittedPath.pop();
|
||||
path = splittedPath.join('/');
|
||||
parent = node;
|
||||
}
|
||||
return resourcePaths;
|
||||
}, []);
|
||||
// (stable) sort so that parents get processed before children
|
||||
return _.sortBy(paths, path => path.split('/').length);
|
||||
|
||||
pathParts.forEach((pathPart, index) => {
|
||||
currentPath = currentPath ? `${currentPath}/${pathPart}` : pathPart;
|
||||
root = root || _.find(trees, (node) => node.path === currentPath);
|
||||
parent = parent || root;
|
||||
|
||||
let node;
|
||||
if (parent) {
|
||||
if (parent.path === currentPath) {
|
||||
applyNodeResource(parent, pathParts, index);
|
||||
return;
|
||||
} else if (parent.children.length > 0) {
|
||||
node = _.find(parent.children, (n) => n.path === currentPath);
|
||||
if (node) {
|
||||
applyNodeResource(node, pathParts, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = {
|
||||
path: currentPath,
|
||||
pathPart,
|
||||
parent,
|
||||
|
||||
level: index,
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
root = node;
|
||||
trees.push(root);
|
||||
}
|
||||
|
||||
applyNodeResource(node, pathParts, index);
|
||||
});
|
||||
}
|
||||
|
||||
predefinedResources.forEach(applyResource);
|
||||
this.validated.events.forEach((event) => {
|
||||
if (event.http.path) {
|
||||
applyResource(event.http, true);
|
||||
}
|
||||
});
|
||||
|
||||
// if predefinedResources array is empty, return all paths
|
||||
if (predefinedResourceNodes.length === 0) {
|
||||
return this.combineResourceTrees(trees);
|
||||
}
|
||||
|
||||
// if all methods have resource ID already, no need to validate resource trees
|
||||
if (_.every(this.validated.events, (event) =>
|
||||
_.some(predefinedResourceNodes, (node) =>
|
||||
node.path === event.http.path))) {
|
||||
return _.reduce(predefinedResources, (resourceMap, resource) => {
|
||||
const r = resourceMap;
|
||||
r[resource.path] = resource;
|
||||
|
||||
if (!resource.name) {
|
||||
r[resource.path].name = this.provider.naming.normalizePath(resource.path);
|
||||
}
|
||||
return r;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// cut resource branches from trees
|
||||
const sortedResourceNodes = _.sortBy(predefinedResourceNodes,
|
||||
node => node.level);
|
||||
const validatedTrees = [];
|
||||
|
||||
for (let i = sortedResourceNodes.length - 1; i >= 0; i--) {
|
||||
const node = sortedResourceNodes[i];
|
||||
let parent = node;
|
||||
|
||||
while (parent && parent.parent) {
|
||||
if (parent.parent.hasMethod && !parent.parent.resourceId) {
|
||||
throw new Error(`Resource ID for path ${parent.parent.path} is required`);
|
||||
}
|
||||
|
||||
if (parent.parent.resourceId || parent.parent.children.length > 1) {
|
||||
cutBranch(parent);
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// get branches that begin from root resource
|
||||
methodNodes.forEach((node) => {
|
||||
let iter = node;
|
||||
while (iter) {
|
||||
if (iter.resourceId) {
|
||||
cutBranch(iter);
|
||||
if (_.every(validatedTrees, (t) => t.path !== node.path)) {
|
||||
validatedTrees.push(iter);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (iter.isCut || (!iter.parent && iter.level > 0)) {
|
||||
throw new Error(`Resource ID for path ${iter.path} is required`);
|
||||
}
|
||||
|
||||
if (!iter.parent) {
|
||||
validatedTrees.push(iter);
|
||||
break;
|
||||
}
|
||||
|
||||
iter = iter.parent;
|
||||
}
|
||||
});
|
||||
|
||||
return this.combineResourceTrees(validatedTrees);
|
||||
},
|
||||
|
||||
getResourceId(path) {
|
||||
if (path === '') {
|
||||
return { 'Fn::GetAtt': [this.apiGatewayRestApiLogicalId, 'RootResourceId'] };
|
||||
if (!path) {
|
||||
return this.provider.getApiGatewayRestApiRootResourceId();
|
||||
}
|
||||
return { Ref: this.apiGatewayResourceLogicalIds[path] };
|
||||
|
||||
if (!this.apiGatewayResources || !this.apiGatewayResources[path]) {
|
||||
throw new Error(`Can not find API Gateway resource from path ${path}`);
|
||||
}
|
||||
|
||||
if (!this.apiGatewayResources[path].resourceId
|
||||
&& this.apiGatewayResources[path].resourceLogicalId) {
|
||||
this.apiGatewayResources[path].resourceId =
|
||||
{ Ref: this.apiGatewayResources[path].resourceLogicalId };
|
||||
}
|
||||
return this.apiGatewayResources[path].resourceId;
|
||||
},
|
||||
|
||||
getResourceName(path) {
|
||||
if (path === '') {
|
||||
if (path === '' || !this.apiGatewayResources) {
|
||||
return '';
|
||||
}
|
||||
return this.apiGatewayResourceNames[path];
|
||||
|
||||
return this.apiGatewayResources[path].name;
|
||||
},
|
||||
};
|
||||
|
||||
@ -70,15 +70,15 @@ describe('#compileResources()', () => {
|
||||
},
|
||||
},
|
||||
];
|
||||
expect(awsCompileApigEvents.getResourcePaths()).to.deep.equal([
|
||||
expect(Object.keys(awsCompileApigEvents.getResourcePaths())).to.deep.equal([
|
||||
'foo',
|
||||
'bar',
|
||||
'foo/bar',
|
||||
'bar',
|
||||
'bar/-',
|
||||
'bar/foo',
|
||||
'bar/{id}',
|
||||
'bar/{foo_id}',
|
||||
'bar/{id}/foobar',
|
||||
'bar/{foo_id}',
|
||||
'bar/{foo_id}/foobar',
|
||||
]);
|
||||
});
|
||||
@ -160,12 +160,16 @@ describe('#compileResources()', () => {
|
||||
},
|
||||
];
|
||||
return awsCompileApigEvents.compileResources().then(() => {
|
||||
expect(awsCompileApigEvents.apiGatewayResourceLogicalIds).to.deep.equal({
|
||||
const expectedResourceLogicalIds = {
|
||||
baz: 'ApiGatewayResourceBaz',
|
||||
'baz/foo': 'ApiGatewayResourceBazFoo',
|
||||
foo: 'ApiGatewayResourceFoo',
|
||||
'foo/{foo_id}': 'ApiGatewayResourceFooFooidVar',
|
||||
'foo/{foo_id}/bar': 'ApiGatewayResourceFooFooidVarBar',
|
||||
};
|
||||
Object.keys(expectedResourceLogicalIds).forEach((path) => {
|
||||
expect(awsCompileApigEvents.apiGatewayResources[path].resourceLogicalId)
|
||||
.equal(expectedResourceLogicalIds[path]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -186,10 +190,14 @@ describe('#compileResources()', () => {
|
||||
},
|
||||
];
|
||||
return awsCompileApigEvents.compileResources().then(() => {
|
||||
expect(awsCompileApigEvents.apiGatewayResourceLogicalIds).to.deep.equal({
|
||||
const expectedResourceLogicalIds = {
|
||||
foo: 'ApiGatewayResourceFoo',
|
||||
'foo/bar': 'ApiGatewayResourceFooBar',
|
||||
'foo/{bar}': 'ApiGatewayResourceFooBarVar',
|
||||
};
|
||||
Object.keys(expectedResourceLogicalIds).forEach((path) => {
|
||||
expect(awsCompileApigEvents.apiGatewayResources[path].resourceLogicalId)
|
||||
.equal(expectedResourceLogicalIds[path]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -242,4 +250,239 @@ describe('#compileResources()', () => {
|
||||
.Resources).to.deep.equal({});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create child resources only if there are predefined parent resources', () => {
|
||||
awsCompileApigEvents.serverless.service.provider.apiGateway = {
|
||||
restApiId: '6fyzt1pfpk',
|
||||
restApiRootResourceId: 'z5d4qh4oqi',
|
||||
restApiResources: {
|
||||
'/foo': 'axcybf2i39',
|
||||
'/users': 'zxcvbnmasd',
|
||||
'/users/friends': 'fcasdoojp1',
|
||||
'/groups': 'iuoyiusduo',
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
http: {
|
||||
path: 'foo/bar',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'foo/bar',
|
||||
method: 'POST',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'foo/bar',
|
||||
method: 'DELETE',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'bar/-',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'bar/foo',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'bar/{id}/foobar',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'bar/{id}',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends/comments',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/me/posts',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'groups/categories',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
];
|
||||
return awsCompileApigEvents.compileResources().then(() => {
|
||||
try {
|
||||
awsCompileApigEvents.getResourceId('users/{userId}');
|
||||
throw new Error('Expected API Gateway resource not found error, got success');
|
||||
} catch (e) {
|
||||
expect(e.message).to.equal('Can not find API Gateway resource from path users/{userId}');
|
||||
}
|
||||
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceFoo).to.equal(undefined);
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceBar.Properties.RestApiId)
|
||||
.to.equal('6fyzt1pfpk');
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceBar.Properties.ParentId)
|
||||
.to.equal('z5d4qh4oqi');
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceFooBar.Properties.ParentId)
|
||||
.to.equal('axcybf2i39');
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceBarIdVar.Properties.ParentId.Ref)
|
||||
.to.equal('ApiGatewayResourceBar');
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceUsersMePosts).not.equal(undefined);
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceUsersFriendsComments.Properties.ParentId)
|
||||
.to.equal('fcasdoojp1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create any child resources if all resources exists', () => {
|
||||
awsCompileApigEvents.serverless.service.provider.apiGateway = {
|
||||
restApiId: '6fyzt1pfpk',
|
||||
restApiRootResourceId: 'z5d4qh4oqi',
|
||||
restApiResources: {
|
||||
foo: 'axcybf2i39',
|
||||
users: 'zxcvbnmasd',
|
||||
'users/friends': 'fcasdoojp1',
|
||||
'users/is/this/a/long/path': 'sadvgpoujk',
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
http: {
|
||||
path: 'foo',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/is/this/a/long/path',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return awsCompileApigEvents.compileResources().then(() => {
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceFoo).to.equal(undefined);
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceUsers).to.equal(undefined);
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceUsersFriends).to.equal(undefined);
|
||||
expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayResourceUsersIsThis).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error if parent of existing resources is required', () => {
|
||||
awsCompileApigEvents.serverless.service.provider.apiGateway = {
|
||||
restApiId: '6fyzt1pfpk',
|
||||
restApiRootResourceId: 'z5d4qh4oqi',
|
||||
restApiResources: {
|
||||
'users/friends': 'fcasdoojp1',
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
http: {
|
||||
path: 'users',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends/{friendId}',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => awsCompileApigEvents.compileResources())
|
||||
.to.throw(Error, 'Resource ID for path users is required');
|
||||
});
|
||||
|
||||
it('should named all method paths if all resources are predefined', () => {
|
||||
awsCompileApigEvents.serverless.service.provider.apiGateway = {
|
||||
restApiId: '6fyzt1pfpk',
|
||||
restApiRootResourceId: 'z5d4qh4oqi',
|
||||
restApiResources: {
|
||||
'users/friends': 'fcasdoojp1',
|
||||
'users/friends/{id}': 'fcasdoojp1',
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends',
|
||||
method: 'POST',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends',
|
||||
method: 'DELETE',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends/{id}',
|
||||
method: 'GET',
|
||||
},
|
||||
},
|
||||
{
|
||||
http: {
|
||||
path: 'users/friends/{id}',
|
||||
method: 'POST',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return awsCompileApigEvents.compileResources().then(() => {
|
||||
expect(Object.keys(awsCompileApigEvents.serverless
|
||||
.service.provider.compiledCloudFormationTemplate
|
||||
.Resources).every((k) => ['ApiGatewayMethodundefinedGet',
|
||||
'ApiGatewayMethodundefinedPost'].indexOf(k) === -1))
|
||||
.to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,6 +5,11 @@ const BbPromise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
compileRestApi() {
|
||||
if (this.serverless.service.provider.apiGateway &&
|
||||
this.serverless.service.provider.apiGateway.restApiId) {
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
this.apiGatewayRestApiLogicalId = this.provider.naming.getRestApiLogicalId();
|
||||
|
||||
let endpointType = 'EDGE';
|
||||
|
||||
@ -60,6 +60,22 @@ describe('#compileRestApi()', () => {
|
||||
})
|
||||
);
|
||||
|
||||
it('should ignore REST API resource creation if there is predefined restApi config',
|
||||
() => {
|
||||
awsCompileApigEvents.serverless.service.provider.apiGateway = {
|
||||
restApiId: '6fyzt1pfpk',
|
||||
restApiRootResourceId: 'z5d4qh4oqi',
|
||||
};
|
||||
return awsCompileApigEvents
|
||||
.compileRestApi().then(() => {
|
||||
expect(
|
||||
awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources
|
||||
).to.deep.equal({});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('throw error if endpointType property is not a string', () => {
|
||||
awsCompileApigEvents.serverless.service.provider.endpointType = ['EDGE'];
|
||||
expect(() => awsCompileApigEvents.compileRestApi()).to.throw(Error);
|
||||
|
||||
@ -25,6 +25,14 @@ module.exports = {
|
||||
|
||||
const bucketName = this.serverless.service.provider.deploymentBucket;
|
||||
const isS3TransferAccelerationEnabled = this.provider.isS3TransferAccelerationEnabled();
|
||||
const isS3TransferAccelerationDisabled = this.provider.isS3TransferAccelerationDisabled();
|
||||
|
||||
if (isS3TransferAccelerationEnabled && isS3TransferAccelerationDisabled) {
|
||||
const errorMessage = [
|
||||
'You cannot enable and disable S3 Transfer Acceleration at the same time',
|
||||
].join('');
|
||||
return BbPromise.reject(new this.serverless.classes.Error(errorMessage));
|
||||
}
|
||||
|
||||
if (bucketName) {
|
||||
return BbPromise.bind(this)
|
||||
@ -45,17 +53,25 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
this.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ServerlessDeploymentBucket.Properties = {
|
||||
AccelerateConfiguration: {
|
||||
AccelerationStatus:
|
||||
isS3TransferAccelerationEnabled ? 'Enabled' : 'Suspended',
|
||||
},
|
||||
};
|
||||
|
||||
if (isS3TransferAccelerationEnabled) {
|
||||
// enable acceleration via CloudFormation
|
||||
this.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ServerlessDeploymentBucket.Properties = {
|
||||
AccelerateConfiguration: {
|
||||
AccelerationStatus: 'Enabled',
|
||||
},
|
||||
};
|
||||
// keep track of acceleration status via CloudFormation Output
|
||||
this.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Outputs.ServerlessDeploymentBucketAccelerated = { Value: true };
|
||||
} else if (isS3TransferAccelerationDisabled) {
|
||||
// explicitly disable acceleration via CloudFormation
|
||||
this.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ServerlessDeploymentBucket.Properties = {
|
||||
AccelerateConfiguration: {
|
||||
AccelerationStatus: 'Suspended',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const coreTemplateFileName = this.provider.naming.getCoreTemplateFileName();
|
||||
|
||||
@ -61,14 +61,45 @@ describe('#generateCoreTemplate()', () => {
|
||||
|
||||
return expect(awsPlugin.generateCoreTemplate()).to.be.fulfilled
|
||||
.then(() => {
|
||||
const template = awsPlugin.serverless.service.provider.compiledCloudFormationTemplate;
|
||||
expect(
|
||||
awsPlugin.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Outputs.ServerlessDeploymentBucketName.Value
|
||||
template.Outputs.ServerlessDeploymentBucketName.Value
|
||||
).to.equal(bucketName);
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(
|
||||
awsPlugin.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ServerlessDeploymentBucket
|
||||
template.Resources.ServerlessDeploymentBucket
|
||||
).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('should use a custom bucket if specified, even with S3 transfer acceleration', () => {
|
||||
const bucketName = 'com.serverless.deploys';
|
||||
|
||||
awsPlugin.serverless.service.provider.deploymentBucket = bucketName;
|
||||
awsPlugin.provider.options['aws-s3-accelerate'] = true;
|
||||
|
||||
const coreCloudFormationTemplate = awsPlugin.serverless.utils.readFileSync(
|
||||
path.join(
|
||||
__dirname,
|
||||
'core-cloudformation-template.json'
|
||||
)
|
||||
);
|
||||
awsPlugin.serverless.service.provider
|
||||
.compiledCloudFormationTemplate = coreCloudFormationTemplate;
|
||||
|
||||
return expect(awsPlugin.generateCoreTemplate()).to.be.fulfilled
|
||||
.then(() => {
|
||||
const template = awsPlugin.serverless.service.provider.compiledCloudFormationTemplate;
|
||||
expect(
|
||||
template.Outputs.ServerlessDeploymentBucketName.Value
|
||||
).to.equal(bucketName);
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(
|
||||
template.Resources.ServerlessDeploymentBucket
|
||||
).to.not.exist;
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(
|
||||
template.Outputs.ServerlessDeploymentBucketAccelerated
|
||||
).to.not.exist;
|
||||
});
|
||||
});
|
||||
@ -80,11 +111,6 @@ describe('#generateCoreTemplate()', () => {
|
||||
.Resources.ServerlessDeploymentBucket
|
||||
).to.be.deep.equal({
|
||||
Type: 'AWS::S3::Bucket',
|
||||
Properties: {
|
||||
AccelerateConfiguration: {
|
||||
AccelerationStatus: 'Suspended',
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
@ -114,4 +140,36 @@ describe('#generateCoreTemplate()', () => {
|
||||
expect(template.Outputs.ServerlessDeploymentBucketAccelerated.Value).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should explicitly disable S3 Transfer Acceleration, if requested', () => {
|
||||
sinon.stub(awsPlugin.provider, 'request').resolves();
|
||||
sinon.stub(serverless.utils, 'writeFileSync').resolves();
|
||||
serverless.config.servicePath = './';
|
||||
awsPlugin.provider.options['no-aws-s3-accelerate'] = true;
|
||||
|
||||
return awsPlugin.generateCoreTemplate()
|
||||
.then(() => {
|
||||
const template = serverless.service.provider.coreCloudFormationTemplate;
|
||||
expect(template.Resources.ServerlessDeploymentBucket).to.be.deep.equal({
|
||||
Type: 'AWS::S3::Bucket',
|
||||
Properties: {
|
||||
AccelerateConfiguration: {
|
||||
AccelerationStatus: 'Suspended',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should explode if transfer acceleration is both enabled and disabled', () => {
|
||||
sinon.stub(awsPlugin.provider, 'request').resolves();
|
||||
sinon.stub(serverless.utils, 'writeFileSync').resolves();
|
||||
serverless.config.servicePath = './';
|
||||
awsPlugin.provider.options['aws-s3-accelerate'] = true;
|
||||
awsPlugin.provider.options['no-aws-s3-accelerate'] = true;
|
||||
|
||||
return expect(
|
||||
awsPlugin.generateCoreTemplate()
|
||||
).to.be.rejectedWith(serverless.classes.Error, /at the same time/);
|
||||
});
|
||||
});
|
||||
|
||||
@ -317,6 +317,10 @@ class AwsProvider {
|
||||
return !!this.options['aws-s3-accelerate'];
|
||||
}
|
||||
|
||||
isS3TransferAccelerationDisabled() {
|
||||
return !!this.options['no-aws-s3-accelerate'];
|
||||
}
|
||||
|
||||
disableTransferAccelerationForCurrentDeploy() {
|
||||
delete this.options['aws-s3-accelerate'];
|
||||
}
|
||||
@ -363,6 +367,53 @@ class AwsProvider {
|
||||
return this.request('STS', 'getCallerIdentity', {})
|
||||
.then((result) => result.Account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API Gateway Rest API ID from serverless config
|
||||
*/
|
||||
getApiGatewayRestApiId() {
|
||||
if (this.serverless.service.provider.apiGateway
|
||||
&& this.serverless.service.provider.apiGateway.restApiId) {
|
||||
return this.serverless.service.provider.apiGateway.restApiId;
|
||||
}
|
||||
|
||||
return { Ref: this.naming.getRestApiLogicalId() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Rest API Root Resource ID from serverless config
|
||||
*/
|
||||
getApiGatewayRestApiRootResourceId() {
|
||||
if (this.serverless.service.provider.apiGateway
|
||||
&& this.serverless.service.provider.apiGateway.restApiRootResourceId) {
|
||||
return this.serverless.service.provider.apiGateway.restApiRootResourceId;
|
||||
}
|
||||
return { 'Fn::GetAtt': [this.naming.getRestApiLogicalId(), 'RootResourceId'] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Rest API Predefined Resources from serverless config
|
||||
*/
|
||||
getApiGatewayPredefinedResources() {
|
||||
if (!this.serverless.service.provider.apiGateway
|
||||
|| !this.serverless.service.provider.apiGateway.restApiResources) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(this.serverless.service.provider.apiGateway.restApiResources)) {
|
||||
return this.serverless.service.provider.apiGateway.restApiResources;
|
||||
}
|
||||
|
||||
if (typeof this.serverless.service.provider.apiGateway.restApiResources !== 'object') {
|
||||
throw new Error('REST API resource must be an array of object');
|
||||
}
|
||||
|
||||
return Object.keys(this.serverless.service.provider.apiGateway.restApiResources)
|
||||
.map((key) => ({
|
||||
path: key,
|
||||
resourceId: this.serverless.service.provider.apiGateway.restApiResources[key],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AwsProvider;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user