mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
342 lines
11 KiB
JavaScript
342 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
/* eslint-disable global-require */
|
|
|
|
const BbPromise = require('bluebird');
|
|
const _ = require('lodash');
|
|
|
|
const validate = require('./lib/validate');
|
|
const compileRestApi = require('./lib/restApi');
|
|
const compileRequestValidators = require('./lib/requestValidator');
|
|
const compileApiKeys = require('./lib/apiKeys');
|
|
const compileUsagePlan = require('./lib/usagePlan');
|
|
const compileUsagePlanKeys = require('./lib/usagePlanKeys');
|
|
const compileResources = require('./lib/resources');
|
|
const compileCors = require('./lib/cors');
|
|
const compileMethods = require('./lib/method/index');
|
|
const compileAuthorizers = require('./lib/authorizers');
|
|
const compileDeployment = require('./lib/deployment');
|
|
const compilePermissions = require('./lib/permissions');
|
|
const compileStage = require('./lib/stage');
|
|
const getMethodAuthorization = require('./lib/method/authorization');
|
|
const getMethodIntegration = require('./lib/method/integration');
|
|
const getMethodResponses = require('./lib/method/responses');
|
|
|
|
function caseInsensitive(str) {
|
|
return { type: 'string', regexp: new RegExp(`^${str}$`, 'i').toString() };
|
|
}
|
|
|
|
const contentHandlingSchema = { enum: ['CONVERT_TO_BINARY', 'CONVERT_TO_TEXT'] };
|
|
|
|
const allowedMethods = ['GET', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'HEAD', 'DELETE', 'ANY'];
|
|
const methodPattern = new RegExp(`^(?:\\*|${allowedMethods.join('|')})$`, 'i');
|
|
const methodPathPattern = new RegExp(`^(?:\\*|(${allowedMethods.join('|')}) (\\/\\S*))$`, 'i');
|
|
|
|
const requestParametersSchema = {
|
|
type: 'object',
|
|
additionalProperties: {
|
|
anyOf: [
|
|
{ type: 'boolean' },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
required: { type: 'boolean' },
|
|
mappedValue: { type: 'string' },
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const authorizerSchema = {
|
|
anyOf: [
|
|
{ type: 'string' },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
arn: { $ref: '#/definitions/awsArn' },
|
|
authorizerId: { $ref: '#/definitions/awsCfInstruction' },
|
|
claims: { type: 'array', items: { type: 'string' } },
|
|
identitySource: { type: 'string' },
|
|
identityValidationExpression: { type: 'string' },
|
|
managedExternally: { type: 'boolean' },
|
|
name: { type: 'string' },
|
|
resultTtlInSeconds: { type: 'integer', minimum: 0, maximum: 3600 },
|
|
scopes: { type: 'array', items: { type: 'string' } },
|
|
type: {
|
|
anyOf: ['token', 'cognito_user_pools', 'request', 'aws_iam'].map(caseInsensitive),
|
|
},
|
|
},
|
|
required: [],
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
};
|
|
|
|
const corsSchema = {
|
|
anyOf: [
|
|
{ type: 'boolean' },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
allowCredentials: { type: 'boolean' },
|
|
cacheControl: { type: 'string' },
|
|
headers: { type: 'array', items: { type: 'string' } },
|
|
maxAge: { type: 'integer', minimum: 1 },
|
|
methods: { type: 'array', items: { enum: allowedMethods } },
|
|
origin: { type: 'string' },
|
|
origins: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
},
|
|
},
|
|
oneOf: [{ required: ['origin'] }, { required: ['origins'] }],
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
};
|
|
|
|
const requestSchema = {
|
|
type: 'object',
|
|
properties: {
|
|
contentHandling: contentHandlingSchema,
|
|
method: { type: 'string', regexp: methodPattern.toString() },
|
|
parameters: {
|
|
type: 'object',
|
|
properties: {
|
|
querystrings: requestParametersSchema,
|
|
headers: requestParametersSchema,
|
|
paths: requestParametersSchema,
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
passThrough: { enum: ['NEVER', 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES'] },
|
|
schema: {
|
|
type: 'object',
|
|
additionalProperties: { anyOf: [{ type: 'object' }, { type: 'string' }] },
|
|
},
|
|
schemas: {
|
|
type: 'object',
|
|
additionalProperties: { anyOf: [{ type: 'object' }, { type: 'string' }] },
|
|
},
|
|
template: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'string' },
|
|
},
|
|
uri: { type: 'string' },
|
|
},
|
|
additionalProperties: false,
|
|
};
|
|
|
|
const responseSchema = {
|
|
type: 'object',
|
|
properties: {
|
|
contentHandling: contentHandlingSchema,
|
|
headers: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'string' },
|
|
},
|
|
template: { type: 'string' },
|
|
statusCodes: {
|
|
type: 'object',
|
|
propertyNames: {
|
|
type: 'string',
|
|
pattern: '^\\d{3}$',
|
|
},
|
|
additionalProperties: {
|
|
type: 'object',
|
|
properties: {
|
|
headers: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'string' },
|
|
},
|
|
pattern: { type: 'string' },
|
|
template: {
|
|
anyOf: [
|
|
{ type: 'string' },
|
|
{
|
|
type: 'object',
|
|
additionalProperties: { type: 'string' },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
};
|
|
|
|
class AwsCompileApigEvents {
|
|
constructor(serverless, options) {
|
|
this.serverless = serverless;
|
|
this.options = options;
|
|
this.provider = this.serverless.getProvider('aws');
|
|
|
|
this.serverless.configSchemaHandler.defineFunctionEvent('aws', 'http', {
|
|
anyOf: [
|
|
{ type: 'string', regexp: methodPathPattern.toString() },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
async: { type: 'boolean' },
|
|
authorizer: authorizerSchema,
|
|
connectionId: { type: 'string' },
|
|
connectionType: { anyOf: ['vpc-link', 'VPC_LINK'].map(caseInsensitive) },
|
|
cors: corsSchema,
|
|
integration: {
|
|
anyOf: [
|
|
'LAMBDA_PROXY',
|
|
'LAMBDA-PROXY',
|
|
'LAMBDA',
|
|
'AWS',
|
|
'AWS_PROXY',
|
|
'AWS-PROXY',
|
|
'HTTP',
|
|
'HTTP_PROXY',
|
|
'HTTP-PROXY',
|
|
'MOCK',
|
|
].map(caseInsensitive),
|
|
},
|
|
method: { type: 'string', regexp: methodPattern.toString() },
|
|
operationId: { type: 'string' },
|
|
path: { type: 'string', regexp: /^(?:\*|\/?\S*)$/.toString() },
|
|
private: { type: 'boolean' },
|
|
request: requestSchema,
|
|
response: responseSchema,
|
|
},
|
|
required: ['path', 'method'],
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
// used for the generated method logical ids (GET, PATCH, PUT, DELETE, OPTIONS, ...)
|
|
this.apiGatewayMethodLogicalIds = [];
|
|
|
|
Object.assign(
|
|
this,
|
|
validate,
|
|
compileRestApi,
|
|
compileApiKeys,
|
|
compileUsagePlan,
|
|
compileUsagePlanKeys,
|
|
compileResources,
|
|
compileCors,
|
|
compileMethods,
|
|
compileRequestValidators,
|
|
compileAuthorizers,
|
|
compileDeployment,
|
|
compilePermissions,
|
|
compileStage,
|
|
getMethodAuthorization,
|
|
getMethodIntegration,
|
|
getMethodResponses
|
|
);
|
|
|
|
this.hooks = {
|
|
'initialize': () => {
|
|
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"'
|
|
);
|
|
}
|
|
|
|
if (
|
|
this.serverless.service.provider.name === 'aws' &&
|
|
Object.values(this.serverless.service.functions).some(({ events }) =>
|
|
events.some(({ http }) => _.get(http, 'request.schema'))
|
|
)
|
|
) {
|
|
this.serverless._logDeprecation(
|
|
'AWS_API_GATEWAY_SCHEMAS',
|
|
'Starting with next major version, "http.request.schema" property will be replaced by "http.request.schemas".'
|
|
);
|
|
}
|
|
|
|
if (
|
|
this.serverless.service.provider.name === 'aws' &&
|
|
this.serverless.service.provider.apiGateway &&
|
|
this.serverless.service.provider.apiGateway.restApiId &&
|
|
this.serverless.service.provider.tracing &&
|
|
this.serverless.service.provider.tracing.apiGateway != null
|
|
) {
|
|
this.serverless._logDeprecation(
|
|
'AWS_API_GATEWAY_NON_APPLICABLE_SETTINGS',
|
|
'When external API Gateway resource is imported via ' +
|
|
'`provider.apiGateway.restApiId`, property ' +
|
|
'"provider.tracing.apiGateway" will be ignored.'
|
|
);
|
|
}
|
|
if (
|
|
this.serverless.service.provider.name === 'aws' &&
|
|
this.serverless.service.provider.apiGateway &&
|
|
this.serverless.service.provider.apiGateway.restApiId &&
|
|
this.serverless.service.provider.logs &&
|
|
this.serverless.service.provider.logs.restApi != null
|
|
) {
|
|
this.serverless._logDeprecation(
|
|
'AWS_API_GATEWAY_NON_APPLICABLE_SETTINGS',
|
|
'When external API Gateway resource is imported via ' +
|
|
'`provider.apiGateway.restApiId`, property ' +
|
|
'"provider.logs.restApi" will be ignored.'
|
|
);
|
|
}
|
|
},
|
|
'package:compileEvents': async () => {
|
|
this.validated = this.validate();
|
|
|
|
if (this.validated.events.length === 0) {
|
|
return BbPromise.resolve();
|
|
}
|
|
|
|
return BbPromise.bind(this)
|
|
.then(this.compileRestApi)
|
|
.then(this.compileResources)
|
|
.then(this.compileCors)
|
|
.then(this.compileMethods)
|
|
.then(this.compileRequestValidators)
|
|
.then(this.compileAuthorizers)
|
|
.then(this.compileDeployment)
|
|
.then(this.compileApiKeys)
|
|
.then(this.compileUsagePlan)
|
|
.then(this.compileUsagePlanKeys)
|
|
.then(this.compilePermissions)
|
|
.then(this.compileStage);
|
|
},
|
|
|
|
// TODO should be removed once AWS fixes the CloudFormation problems using a separate Stage
|
|
'after:deploy:deploy': async () => {
|
|
const getServiceState = require('../../../../lib/getServiceState').getServiceState;
|
|
|
|
const state = getServiceState.call(this);
|
|
const updateStage = require('./lib/hack/updateStage').updateStage;
|
|
|
|
this.state = state;
|
|
return updateStage.call(this);
|
|
},
|
|
|
|
// TODO should be removed once AWS fixes the removal via CloudFormation
|
|
'before:remove:remove': async () => {
|
|
const globalValidate = require('../../../../lib/validate').validate;
|
|
const disassociateUsagePlan = require('./lib/hack/disassociateUsagePlan')
|
|
.disassociateUsagePlan;
|
|
|
|
return BbPromise.bind(this).then(globalValidate).then(disassociateUsagePlan);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = AwsCompileApigEvents;
|