osls
Version:
Open-source alternative to Serverless Framework
291 lines (265 loc) • 8.88 kB
JavaScript
'use strict';
/* eslint-disable global-require */
const BbPromise = require('bluebird');
const memoize = require('memoizee');
const validate = require('./lib/validate');
const compileRestApi = require('./lib/rest-api');
const compileRequestValidators = require('./lib/request-validator');
const compileApiKeys = require('./lib/api-keys');
const compileUsagePlan = require('./lib/usage-plan');
const compileUsagePlanKeys = require('./lib/usage-plan-keys');
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: { $ref: '#/definitions/awsCfInstruction' },
},
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: {
anyOf: [{ type: 'string' }, { $ref: '#/definitions/awsCfInstruction' }],
},
},
type: {
anyOf: ['token', 'cognito_user_pools', 'request', 'aws_iam', 'custom'].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' },
},
},
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'] },
schemas: {
type: 'object',
additionalProperties: { anyOf: [{ type: 'object' }, { type: 'string' }] },
},
template: {
type: 'object',
additionalProperties: { type: 'string' },
},
uri: { $ref: '#/definitions/awsCfInstruction' },
},
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: { $ref: '#/definitions/awsCfInstruction' },
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,
timeoutInMillis: { type: 'integer', minimum: 50 },
},
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.createRequestValidator = memoize(this.createRequestValidator.bind(this));
this.hooks = {
'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/get-service-state').getServiceState;
const state = getServiceState.call(this);
const updateStage = require('./lib/hack/update-stage').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/disassociate-usage-plan').disassociateUsagePlan;
return BbPromise.bind(this).then(globalValidate).then(disassociateUsagePlan);
},
};
}
}
module.exports = AwsCompileApigEvents;