feat(AWS API Gateway): Move api-specific keys to provider.apiGateway

This commit is contained in:
Frédéric Barthelet 2021-01-04 10:04:57 +01:00 committed by GitHub
parent 715ba602d7
commit eacae9a64d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 311 additions and 246 deletions

View File

@ -6,6 +6,16 @@ layout: Doc
# Serverless Framework Deprecations
<a name="AWS_API_GATEWAY_SPECIFIC_KEYS"><div>&nbsp;</div></a>
## API Gateway specific configuration
Please use `provider.apiGateway.apiKeys` instead of `provider.apiKeys`.
Please use `provider.apiGateway.resourcePolicy` instead of `provider.resourcePolicy`.
Please use `provider.apiGateway.usagePlan` instead of `provider.usagePlan`.
Starting with v3.0.0, API Gateway-specific configuration keys `apiKeys`, `resourcePolicy` and `usagePlan` will be relocated from `provider` to `provider.apiGateway`.
<a name="PARAMETERIZED_ARGUMENT"><div>&nbsp;</div></a>
## Parameterized `org`, `app`, `service`, `stage`, and `region` usage

View File

@ -608,12 +608,7 @@ In case an exception is thrown in your lambda function AWS will send an error me
### Setting API keys for your Rest API
You can specify a list of API keys to be used by your service Rest API by adding an `apiKeys` array property to the
`provider` object in `serverless.yml`. You'll also need to explicitly specify which endpoints are `private` and require
one of the api keys to be included in the request by adding a `private` boolean property to the `http` event object you
want to set as private. API Keys are created globally, so if you want to deploy your service to different stages make sure
your API key contains a stage variable as defined below. When using API keys, you can optionally define usage plan quota
and throttle, using `usagePlan` object.
You can specify a list of API keys to be used by your service Rest API by adding an `apiKeys` array property to the `provider.apiGateway` object in `serverless.yml`. You'll also need to explicitly specify which endpoints are `private` and require one of the api keys to be included in the request by adding a `private` boolean property to the `http` event object you want to set as private. API Keys are created globally, so if you want to deploy your service to different stages make sure your API key contains a stage variable as defined below. When using API keys, you can optionally define usage plan quota and throttle, using `usagePlan` object.
When setting the value, you need to be aware that changing value will require replacement and CloudFormation doesn't allow
two API keys with the same name. It means that you need to change the name also when changing the value. If you don't care
@ -625,23 +620,24 @@ Here's an example configuration for setting API keys for your service Rest API:
service: my-service
provider:
name: aws
apiKeys:
- myFirstKey
- ${opt:stage}-myFirstKey
- ${env:MY_API_KEY} # you can hide it in a serverless variable
- name: myThirdKey
value: myThirdKeyValue
- value: myFourthKeyValue # let cloudformation name the key (recommended when setting api key value)
description: Api key description # Optional
customerId: A string that will be set as the customerID for the key # Optional
usagePlan:
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
apiGateway:
apiKeys:
- myFirstKey
- ${opt:stage}-myFirstKey
- ${env:MY_API_KEY} # you can hide it in a serverless variable
- name: myThirdKey
value: myThirdKeyValue
- value: myFourthKeyValue # let cloudformation name the key (recommended when setting api key value)
description: Api key description # Optional
customerId: A string that will be set as the customerID for the key # Optional
usagePlan:
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
functions:
hello:
events:
@ -661,30 +657,31 @@ You can also setup multiple usage plans for your API. In this case you need to m
service: my-service
provider:
name: aws
apiKeys:
- free:
- myFreeKey
- ${opt:stage}-myFreeKey
- paid:
- myPaidKey
- ${opt:stage}-myPaidKey
usagePlan:
- free:
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
- paid:
quota:
limit: 50000
offset: 1
period: MONTH
throttle:
burstLimit: 2000
rateLimit: 1000
apiGateway:
apiKeys:
- free:
- myFreeKey
- ${opt:stage}-myFreeKey
- paid:
- myPaidKey
- ${opt:stage}-myPaidKey
usagePlan:
- free:
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
- paid:
quota:
limit: 50000
offset: 1
period: MONTH
throttle:
burstLimit: 2000
rateLimit: 1000
functions:
hello:
events:

View File

@ -87,13 +87,6 @@ provider:
environment: # Service wide environment variables
serviceEnvVar: 123456789
endpointType: regional # Optional endpoint configuration for API Gateway REST API. Default is Edge.
apiKeys: # List of API keys to be used by your service API Gateway REST API
- myFirstKey
value: myFirstKeyValue
description: myFirstKeyDescription
customerId: myFirstKeyCustomerId
- ${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
@ -102,12 +95,37 @@ provider:
'/users/create': xxxxxxxxxx
websocketApiId: # Websocket API resource ID. Default is generated by the framework
apiKeySourceType: HEADER # Source of API key for usage plan. HEADER or AUTHORIZER.
apiKeys: # List of API keys to be used by your service API Gateway REST API
- myFirstKey
value: myFirstKeyValue
description: myFirstKeyDescription
customerId: myFirstKeyCustomerId
- ${opt:stage}-myFirstKey
- ${env:MY_API_KEY} # you can hide it in a serverless variable
minimumCompressionSize: 1024 # Compress response when larger than specified size in bytes (must be between 0 and 10485760)
description: Some Description # Optional description for the API Gateway stage deployment
binaryMediaTypes: # Optional binary media types the API might return
- '*/*'
metrics: false # Optional detailed Cloud Watch Metrics
shouldStartNameWithService: false # Use `${service}-${stage}` naming for API Gateway. Will be `true` by default in next major version.
resourcePolicy:
- Effect: Allow
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
Condition:
IpAddress:
aws:SourceIp:
- '123.123.123.123'
usagePlan: # Optional usage plan configuration
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
alb:
targetGroupPrefix: xxxxxxxxxx # Optional prefix to prepend when generating names for target groups
authorizers:
@ -152,14 +170,6 @@ provider:
audience:
- xxxx
- xxxx
usagePlan: # Optional usage plan configuration
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
stackTags: # Optional CF stack tags
key: value
iamManagedPolicies: # Optional IAM Managed Policies, which allows to include the policies into IAM Role
@ -200,23 +210,13 @@ provider:
stackParameters:
- ParameterKey: 'Keyname'
ParameterValue: 'Value'
resourcePolicy:
- Effect: Allow
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
Condition:
IpAddress:
aws:SourceIp:
- '123.123.123.123'
rollbackConfiguration:
MonitoringTimeInMinutes: 20
RollbackTriggers:
- Arn: arn:aws:cloudwatch:us-east-1:000000000000:alarm:health
Type: AWS::CloudWatch::Alarm
- Arn: arn:aws:cloudwatch:us-east-1:000000000000:alarm:latency
Type: AWS::CloudWatch::Alarm
rollbackConfiguration:
MonitoringTimeInMinutes: 20
RollbackTriggers:
- Arn: arn:aws:cloudwatch:us-east-1:000000000000:alarm:health
Type: AWS::CloudWatch::Alarm
- Arn: arn:aws:cloudwatch:us-east-1:000000000000:alarm:latency
Type: AWS::CloudWatch::Alarm
tags: # Optional service wide function tags
foo: bar
baz: qux

View File

@ -9,7 +9,9 @@ module.exports = {
info.apiKeys = [];
// check if the user has set api keys
const apiKeyDefinitions = this.serverless.service.provider.apiKeys;
const apiKeyDefinitions =
_.get(this.serverless.service.provider.apiGateway, 'apiKeys') ||
this.serverless.service.provider.apiKeys;
const apiKeyNames = [];
if (Array.isArray(apiKeyDefinitions) && apiKeyDefinitions.length) {
apiKeyDefinitions.forEach(definition => {

View File

@ -249,6 +249,19 @@ class AwsCompileApigEvents {
'to adapt to the new behavior now.'
);
}
if (
this.serverless.service.provider.name === 'aws' &&
(this.serverless.service.provider.apiKeys ||
this.serverless.service.provider.resourcePolicy ||
this.serverless.service.provider.usagePlan)
) {
this.serverless._logDeprecation(
'AWS_API_GATEWAY_SPECIFIC_KEYS',
'Starting with next major version, API Gateway-specific configuration keys ' +
'"apiKeys", "resourcePolicy" and "usagePlan" will be relocated from "provider" ' +
'to "provider.apiGateway"'
);
}
},
'package:compileEvents': () => {
this.validated = this.validate();

View File

@ -31,18 +31,22 @@ function createApiKeyResource(that, apiKey) {
module.exports = {
compileApiKeys() {
if (this.serverless.service.provider.apiKeys) {
const apiKeys =
_.get(this.serverless.service.provider.apiGateway, 'apiKeys') ||
this.serverless.service.provider.apiKeys;
if (apiKeys) {
const resources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources;
let keyNumber = 0;
this.serverless.service.provider.apiKeys.forEach(apiKeyDefinition => {
apiKeys.forEach(apiKeyDefinition => {
// if multiple API key types are used
const name = Object.keys(apiKeyDefinition)[0];
const usagePlan =
_.get(this.serverless.service.provider.apiGateway, 'usagePlan') ||
this.serverless.service.provider.usagePlan;
if (
_.isObject(apiKeyDefinition) &&
Array.isArray(this.serverless.service.provider.usagePlan) &&
_.flatten(
this.serverless.service.provider.usagePlan.map(item => Object.keys(item))
).includes(name)
Array.isArray(usagePlan) &&
_.flatten(usagePlan.map(item => Object.keys(item))).includes(name)
) {
keyNumber = 0;
apiKeyDefinition[name].forEach(key => {

View File

@ -5,7 +5,9 @@ const _ = require('lodash');
module.exports = {
disassociateUsagePlan() {
const apiKeys = this.serverless.service.provider.apiKeys;
const apiKeys =
_.get(this.serverless.service.provider.apiGateway, 'apiKeys') ||
this.serverless.service.provider.apiKeys;
if (apiKeys && apiKeys.length) {
this.serverless.cli.log('Removing usage plan association...');

View File

@ -54,13 +54,13 @@ module.exports = {
},
});
if (
this.serverless.service.provider.resourcePolicy &&
Object.keys(this.serverless.service.provider.resourcePolicy).length
) {
const resourcePolicy =
_.get(this.serverless.service.provider.apiGateway, 'resourcePolicy') ||
this.serverless.service.provider.resourcePolicy;
if (resourcePolicy && Object.keys(resourcePolicy).length) {
const policy = {
Version: '2012-10-17',
Statement: this.serverless.service.provider.resourcePolicy,
Statement: resourcePolicy,
};
_.merge(
this.serverless.service.provider.compiledCloudFormationTemplate.Resources[

View File

@ -21,6 +21,9 @@ function createUsagePlanResource(that, name) {
},
};
const template = _.cloneDeep(resourceTemplate);
const usagePlan =
_.get(that.serverless.service.provider.apiGateway, 'usagePlan') ||
that.serverless.service.provider.usagePlan;
// this is done for backward compatibility
if (name === 'default') {
// create old legacy resources
@ -31,39 +34,36 @@ function createUsagePlanResource(that, name) {
that.serverless.service.service
} ${that.provider.getStage()} stage`;
// assign quota
if (_.get(that.serverless.service.provider, 'usagePlan.quota')) {
if (_.get(usagePlan, 'quota')) {
_.merge(template, {
Properties: {
Quota: _.merge(
{ Limit: that.serverless.service.provider.usagePlan.quota.limit },
{ Offset: that.serverless.service.provider.usagePlan.quota.offset },
{ Period: that.serverless.service.provider.usagePlan.quota.period }
{ Limit: usagePlan.quota.limit },
{ Offset: usagePlan.quota.offset },
{ Period: usagePlan.quota.period }
),
},
});
}
// assign throttle
if (_.get(that.serverless.service.provider, 'usagePlan.throttle')) {
if (_.get(usagePlan, 'throttle')) {
_.merge(template, {
Properties: {
Throttle: _.merge(
{ BurstLimit: that.serverless.service.provider.usagePlan.throttle.burstLimit },
{ RateLimit: that.serverless.service.provider.usagePlan.throttle.rateLimit }
{ BurstLimit: usagePlan.throttle.burstLimit },
{ RateLimit: usagePlan.throttle.rateLimit }
),
},
});
}
} else {
// assign quota
const quotaProperties = that.serverless.service.provider.usagePlan.reduce(
(accum, planObject) => {
if (planObject[name] && planObject[name].quota) {
return planObject[name].quota;
}
return accum;
},
{}
);
const quotaProperties = usagePlan.reduce((accum, planObject) => {
if (planObject[name] && planObject[name].quota) {
return planObject[name].quota;
}
return accum;
}, {});
if (Object.keys(quotaProperties).length) {
_.merge(template, {
Properties: {
@ -76,15 +76,12 @@ function createUsagePlanResource(that, name) {
});
}
// assign throttle
const throttleProperties = that.serverless.service.provider.usagePlan.reduce(
(accum, planObject) => {
if (planObject[name] && planObject[name].throttle) {
return planObject[name].throttle;
}
return accum;
},
{}
);
const throttleProperties = usagePlan.reduce((accum, planObject) => {
if (planObject[name] && planObject[name].throttle) {
return planObject[name].throttle;
}
return accum;
}, {});
if (Object.keys(quotaProperties).length) {
_.merge(template, {
Properties: {
@ -101,12 +98,18 @@ function createUsagePlanResource(that, name) {
module.exports = {
compileUsagePlan() {
if (this.serverless.service.provider.usagePlan || this.serverless.service.provider.apiKeys) {
const apiKeys =
_.get(this.serverless.service.provider.apiGateway, 'apiKeys') ||
this.serverless.service.provider.apiKeys;
const usagePlan =
_.get(this.serverless.service.provider.apiGateway, 'usagePlan') ||
this.serverless.service.provider.usagePlan;
if (usagePlan || apiKeys) {
const resources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources;
this.apiGatewayUsagePlanNames = [];
if (Array.isArray(this.serverless.service.provider.usagePlan)) {
this.serverless.service.provider.usagePlan.forEach(planObject => {
if (Array.isArray(usagePlan)) {
usagePlan.forEach(planObject => {
const usagePlanName = Object.keys(planObject)[0];
const logicalId = this.provider.naming.getUsagePlanLogicalId(usagePlanName);
const resourceTemplate = createUsagePlanResource(this, usagePlanName);

View File

@ -24,11 +24,14 @@ function createUsagePlanKeyResource(that, usagePlanLogicalId, keyNumber, keyName
module.exports = {
compileUsagePlanKeys() {
if (this.serverless.service.provider.apiKeys) {
const apiKeys =
_.get(this.serverless.service.provider.apiGateway, 'apiKeys') ||
this.serverless.service.provider.apiKeys;
if (apiKeys) {
const resources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources;
let keyNumber = 0;
this.serverless.service.provider.apiKeys.forEach(apiKeyDefinition => {
apiKeys.forEach(apiKeyDefinition => {
// if multiple API key types are used
const apiKey = Object.entries(apiKeyDefinition)[0];
const name = apiKey[0];

View File

@ -248,6 +248,33 @@ class AwsProvider {
type: 'string',
pattern: '^execute-api:/',
},
awsApiGatewayApiKeys: {
type: 'array',
items: {
anyOf: [
{ type: 'string' },
{
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' },
description: { type: 'string' },
customerId: { type: 'string' },
},
anyOf: [{ required: ['name'] }, { required: ['value'] }],
additionalProperties: false,
},
{
type: 'object',
maxProperties: 1,
additionalProperties: {
type: 'array',
items: { type: 'string' },
},
},
],
},
},
awsArn: {
anyOf: [
{ $ref: '#/definitions/awsArnString' },
@ -515,6 +542,7 @@ class AwsProvider {
apiGateway: {
type: 'object',
properties: {
apiKeys: { $ref: '#/definitions/awsApiGatewayApiKeys' },
apiKeySourceType: {
anyOf: ['HEADER', 'AUTHORIZER'].map(caseInsensitive),
},
@ -525,6 +553,7 @@ class AwsProvider {
description: { type: 'string' },
metrics: { type: 'boolean' },
minimumCompressionSize: { type: 'integer', minimum: 0, maximum: 10485760 },
resourcePolicy: { $ref: '#/definitions/awsResourcePolicyStatements' },
restApiId: { $ref: '#/definitions/awsCfInstruction' },
restApiResources: {
anyOf: [
@ -545,37 +574,24 @@ class AwsProvider {
},
restApiRootResourceId: { $ref: '#/definitions/awsCfInstruction' },
shouldStartNameWithService: { const: true },
usagePlan: {
anyOf: [
apiGatewayUsagePlan,
{
type: 'array',
items: {
type: 'object',
additionalProperties: apiGatewayUsagePlan,
maxProperties: 1,
},
},
],
},
websocketApiId: { $ref: '#/definitions/awsCfInstruction' },
},
additionalProperties: false,
},
apiKeys: {
type: 'array',
items: {
anyOf: [
{ type: 'string' },
{
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' },
description: { type: 'string' },
customerId: { type: 'string' },
},
anyOf: [{ required: ['name'] }, { required: ['value'] }],
additionalProperties: false,
},
{
type: 'object',
maxProperties: 1,
additionalProperties: {
type: 'array',
items: { type: 'string' },
},
},
],
},
},
apiKeys: { $ref: '#/definitions/awsApiGatewayApiKeys' },
apiName: { type: 'string' },
alb: {
type: 'object',

View File

@ -7,9 +7,10 @@ provider:
name: aws
runtime: nodejs12.x
versionFunctions: false
apiKeys:
- name: ${self:service.name}-api-key-1
value: ${self:service.name}-api-key-1
apiGateway:
apiKeys:
- name: ${self:service.name}-api-key-1
value: ${self:service.name}-api-key-1
functions:
# core functions

View File

@ -29,7 +29,9 @@ describe('#getApiKeyValues()', () => {
it('should add API Key values to this.gatheredData if API key names are available', () => {
// set the API Keys for the service
awsInfo.serverless.service.provider.apiKeys = ['foo', 'bar'];
awsInfo.serverless.service.provider.apiGateway = {
apiKeys: ['foo', 'bar'],
};
awsInfo.gatheredData = {
info: {},
@ -87,7 +89,7 @@ describe('#getApiKeyValues()', () => {
it('should resolve if AWS does not return API key values', () => {
// set the API Keys for the service
awsInfo.serverless.service.provider.apiKeys = ['foo', 'bar'];
awsInfo.serverless.service.provider.apiGateway = { apiKeys: ['foo', 'bar'] };
awsInfo.gatheredData = {
info: {},
@ -112,7 +114,7 @@ describe('#getApiKeyValues()', () => {
});
it('should resolve if API key names are not available', () => {
awsInfo.serverless.service.provider.apiKeys = null;
awsInfo.serverless.service.provider.apiGateway = {};
awsInfo.gatheredData = {
info: {},

View File

@ -27,21 +27,22 @@ describe('#compileApiKeys()', () => {
});
it('should support api key notation', () => {
awsCompileApigEvents.serverless.service.provider.apiKeys = [
'1234567890',
{ name: '2345678901' },
{ value: 'valueForKeyWithoutName', description: 'Api key description' },
{ name: '3456789012', value: 'valueForKey3456789012' },
{
name: '9876543211',
value: 'valueForKey9876543211',
customerId: 'customerid98765',
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: [
'1234567890',
{ name: '2345678901' },
{ value: 'valueForKeyWithoutName', description: 'Api key description' },
{ name: '3456789012', value: 'valueForKey3456789012' },
{
name: '9876543211',
value: 'valueForKey9876543211',
customerId: 'customerid98765',
},
],
// Added purely to test https://github.com/serverless/serverless/issues/7844 regression
usagePlan: {
quota: { limit: 5000 },
},
];
// Added purely to test https://github.com/serverless/serverless/issues/7844 regression
awsCompileApigEvents.serverless.service.provider.usagePlan = {
quota: { limit: 5000 },
};
return awsCompileApigEvents.compileApiKeys().then(() => {
@ -138,21 +139,23 @@ describe('#compileApiKeys()', () => {
describe('when using usage plan notation', () => {
it('should support usage plan notation', () => {
awsCompileApigEvents.serverless.service.provider.usagePlan = [{ free: [] }, { paid: [] }];
awsCompileApigEvents.serverless.service.provider.apiKeys = [
{
free: [
'1234567890',
{ name: '2345678901' },
{
value: 'valueForKeyWithoutName',
description: 'Api key description',
},
{ name: '3456789012', value: 'valueForKey3456789012' },
],
},
{ paid: ['0987654321', 'jihgfedcba'] },
];
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: [
{
free: [
'1234567890',
{ name: '2345678901' },
{
value: 'valueForKeyWithoutName',
description: 'Api key description',
},
{ name: '3456789012', value: 'valueForKey3456789012' },
],
},
{ paid: ['0987654321', 'jihgfedcba'] },
],
usagePlan: [{ free: [] }, { paid: [] }],
};
return awsCompileApigEvents.compileApiKeys().then(() => {
const expectedApiKeys = {
@ -175,7 +178,7 @@ describe('#compileApiKeys()', () => {
{ name: 'jihgfedcba', value: undefined, description: undefined },
],
};
awsCompileApigEvents.serverless.service.provider.apiKeys.forEach(plan => {
awsCompileApigEvents.serverless.service.provider.apiGateway.apiKeys.forEach(plan => {
const planName = Object.keys(plan)[0]; // free || paid
const apiKeys = expectedApiKeys[planName];
apiKeys.forEach((apiKey, index) => {

View File

@ -63,7 +63,7 @@ describe('#disassociateUsagePlan()', () => {
});
it('should remove association from the usage plan', () => {
disassociateUsagePlan.serverless.service.provider.apiKeys = ['apiKey1'];
disassociateUsagePlan.serverless.service.provider.apiGateway = { apiKeys: ['apiKey1'] };
return disassociateUsagePlan.disassociateUsagePlan().then(() => {
expect(providerRequestStub.callCount).to.be.equal(3);
@ -95,7 +95,7 @@ describe('#disassociateUsagePlan()', () => {
});
it('should resolve if no api keys are given', () => {
disassociateUsagePlan.serverless.service.provider.apiKeys = [];
disassociateUsagePlan.serverless.service.provider.apiGateway = { apiKeys: [] };
return disassociateUsagePlan.disassociateUsagePlan().then(() => {
expect(providerRequestStub.callCount).to.be.equal(0);

View File

@ -55,19 +55,21 @@ describe('#compileRestApi()', () => {
}));
it('should create a REST API resource with resource policy', () => {
awsCompileApigEvents.serverless.service.provider.resourcePolicy = [
{
Effect: 'Allow',
Principal: '*',
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*'],
Condition: {
IpAddress: {
'aws:SourceIp': ['123.123.123.123'],
awsCompileApigEvents.serverless.service.provider.apiGateway = {
resourcePolicy: [
{
Effect: 'Allow',
Principal: '*',
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*'],
Condition: {
IpAddress: {
'aws:SourceIp': ['123.123.123.123'],
},
},
},
},
];
],
};
return awsCompileApigEvents.compileRestApi().then(() => {
const resources =
awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources;

View File

@ -28,7 +28,7 @@ describe('#compileUsagePlan()', () => {
});
it('should compile default usage plan resource', () => {
serverless.service.provider.apiKeys = ['1234567890'];
serverless.service.provider.apiGateway = { apiKeys: ['1234567890'] };
return awsCompileApigEvents.compileUsagePlan().then(() => {
expect(
awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources[
@ -66,15 +66,17 @@ describe('#compileUsagePlan()', () => {
});
it('should support custom usage plan resource via single object notation', () => {
serverless.service.provider.usagePlan = {
quota: {
limit: 500,
offset: 10,
period: 'MONTH',
},
throttle: {
burstLimit: 200,
rateLimit: 100,
serverless.service.provider.apiGateway = {
usagePlan: {
quota: {
limit: 500,
offset: 10,
period: 'MONTH',
},
throttle: {
burstLimit: 200,
rateLimit: 100,
},
},
};
@ -139,34 +141,36 @@ describe('#compileUsagePlan()', () => {
const logicalIdFree = awsCompileApigEvents.provider.naming.getUsagePlanLogicalId(freePlanName);
const logicalIdPaid = awsCompileApigEvents.provider.naming.getUsagePlanLogicalId(paidPlanName);
serverless.service.provider.usagePlan = [
{
[freePlanName]: {
quota: {
limit: 1000,
offset: 100,
period: 'MONTH',
},
throttle: {
burstLimit: 1,
rateLimit: 1,
serverless.service.provider.apiGateway = {
usagePlan: [
{
[freePlanName]: {
quota: {
limit: 1000,
offset: 100,
period: 'MONTH',
},
throttle: {
burstLimit: 1,
rateLimit: 1,
},
},
},
},
{
[paidPlanName]: {
quota: {
limit: 1000000,
offset: 200,
period: 'MONTH',
},
throttle: {
burstLimit: 1000,
rateLimit: 1000,
{
[paidPlanName]: {
quota: {
limit: 1000000,
offset: 200,
period: 'MONTH',
},
throttle: {
burstLimit: 1000,
rateLimit: 1000,
},
},
},
},
];
],
};
return awsCompileApigEvents.compileUsagePlan().then(() => {
// resources for the "free" plan
@ -275,8 +279,8 @@ describe('#compileUsagePlan()', () => {
});
it('should compile custom usage plan resource with restApiId provided', () => {
serverless.service.provider.apiKeys = ['1234567890'];
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: ['1234567890'],
restApiId: 'xxxxx',
};
@ -329,7 +333,7 @@ describe('UsagePlan', () => {
};
it('Should have values for throttle', () => {
serverlessConfigurationExtension.provider.usagePlan = { throttle };
serverlessConfigurationExtension.provider.apiGateway = { usagePlan: { throttle } };
return runServerless({
fixture: 'apiGateway',
configExt: serverlessConfigurationExtension,
@ -345,7 +349,7 @@ describe('UsagePlan', () => {
});
it('Should have values for quota', () => {
serverlessConfigurationExtension.provider.usagePlan = { quota };
serverlessConfigurationExtension.provider.apiGateway = { usagePlan: { quota } };
return runServerless({
fixture: 'apiGateway',
configExt: serverlessConfigurationExtension,
@ -360,7 +364,7 @@ describe('UsagePlan', () => {
});
it('Should have values for quota and throttle', () => {
serverlessConfigurationExtension.provider.usagePlan = { throttle, quota };
serverlessConfigurationExtension.provider.apiGateway = { usagePlan: { throttle, quota } };
return runServerless({
fixture: 'apiGateway',
configExt: serverlessConfigurationExtension,

View File

@ -30,10 +30,9 @@ describe('#compileUsagePlanKeys()', () => {
it('should support api key notation', () => {
const defaultUsagePlanLogicalId = awsCompileApigEvents.provider.naming.getUsagePlanLogicalId();
awsCompileApigEvents.apiGatewayUsagePlanNames = ['default'];
awsCompileApigEvents.serverless.service.provider.apiKeys = [
'1234567890',
{ name: 'abcdefghij', value: 'abcdefghijvalue' },
];
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: ['1234567890', { name: 'abcdefghij', value: 'abcdefghijvalue' }],
};
return awsCompileApigEvents.compileUsagePlanKeys().then(() => {
// key 1
@ -91,13 +90,15 @@ describe('#compileUsagePlanKeys()', () => {
paid: awsCompileApigEvents.provider.naming.getUsagePlanLogicalId(paidUsagePlanName),
};
awsCompileApigEvents.apiGatewayUsagePlanNames = [freeUsagePlanName, paidUsagePlanName];
awsCompileApigEvents.serverless.service.provider.apiKeys = [
{ free: ['1234567890', { name: 'abcdefghij', value: 'abcdefghijvalue' }] },
{ paid: ['0987654321', 'jihgfedcba'] },
];
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: [
{ free: ['1234567890', { name: 'abcdefghij', value: 'abcdefghijvalue' }] },
{ paid: ['0987654321', 'jihgfedcba'] },
],
};
return awsCompileApigEvents.compileUsagePlanKeys().then(() => {
awsCompileApigEvents.serverless.service.provider.apiKeys.forEach(plan => {
awsCompileApigEvents.serverless.service.provider.apiGateway.apiKeys.forEach(plan => {
const planName = Object.keys(plan)[0]; // free || paid
const apiKeys = plan[planName];
apiKeys.forEach((apiKey, index) => {
@ -132,7 +133,9 @@ describe('#compileUsagePlanKeys()', () => {
it('should throw if api key name does not match a usage plan', () => {
awsCompileApigEvents.apiGatewayUsagePlanNames = ['default'];
awsCompileApigEvents.serverless.service.provider.apiKeys = [{ free: ['1234567890'] }];
awsCompileApigEvents.serverless.service.provider.apiGateway = {
apiKeys: [{ free: ['1234567890'] }],
};
expect(() => awsCompileApigEvents.compileUsagePlanKeys()).to.throw(
/has no usage plan defined/
);