mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
feat(AWS API Gateway): Move api-specific keys to provider.apiGateway
This commit is contained in:
parent
715ba602d7
commit
eacae9a64d
@ -6,6 +6,16 @@ layout: Doc
|
||||
|
||||
# Serverless Framework Deprecations
|
||||
|
||||
<a name="AWS_API_GATEWAY_SPECIFIC_KEYS"><div> </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> </div></a>
|
||||
|
||||
## Parameterized `org`, `app`, `service`, `stage`, and `region` usage
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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...');
|
||||
|
||||
@ -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[
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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: {},
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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/
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user