mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
EndpointDeploy: continue work on authroizers WIP
This commit is contained in:
parent
9c6793e22c
commit
fee7589a67
@ -171,17 +171,17 @@ class Project extends SerializerFileSystem {
|
||||
return _.flatten( _.map( this.getAllFunctions(), f => f.getAllEndpoints() ) );
|
||||
}
|
||||
|
||||
getEndpoint( endpointPath, endpointMethod ){
|
||||
getEndpoint(endpointName){
|
||||
return _.find( _.values( this.getAllEndpoints() ), e =>
|
||||
e.path === endpointPath && e.method === endpointMethod
|
||||
e.getName() === endpointName
|
||||
)
|
||||
}
|
||||
|
||||
getEndpointsByName(names) {
|
||||
getEndpointsByNames(names) {
|
||||
let _this = this;
|
||||
let endpoints = [];
|
||||
names.forEach(function(name) {
|
||||
let endpoint = _this.getEndpoint(name.split('~')[0], name.split('~')[1]);
|
||||
let endpoint = _this.getEndpoint(name);
|
||||
if (!endpoint) throw new SError(`Endpoint "${name}" doesn't exist in your project`);
|
||||
endpoints.push(endpoint);
|
||||
});
|
||||
|
||||
@ -151,7 +151,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
_this.apiResources = null;
|
||||
|
||||
// Get populated endpoint
|
||||
_this.endpoint = _this.project.getEndpoint( _this.evt.options.endpointPath, _this.evt.options.endpointMethod );
|
||||
_this.endpoint = _this.project.getEndpoint(_this.evt.options.name);
|
||||
|
||||
if (!_this.endpoint) BbPromise.reject(new SError(`Endpoint could not be found: ${_this.evt.options.endpointPath}#${_this.evt.options.endpointMethod}`));
|
||||
|
||||
|
||||
@ -115,11 +115,11 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
.then(function() {
|
||||
|
||||
// Display Successfully Deployed Endpoints, if any
|
||||
if (_this.deployed) {
|
||||
if (_this.evt.data.deployed) {
|
||||
SCli.log('Successfully deployed endpoints in "' + _this.evt.options.stage + '" to the following regions:');
|
||||
for (let i = 0; i < Object.keys(_this.deployed).length; i++) {
|
||||
let region = _this.deployed[Object.keys(_this.deployed)[i]];
|
||||
SCli.log(Object.keys(_this.deployed)[i] + ' ------------------------');
|
||||
for (let i = 0; i < Object.keys(_this.evt.data.deployed).length; i++) {
|
||||
let region = _this.evt.data.deployed[Object.keys(_this.evt.data.deployed)[i]];
|
||||
SCli.log(Object.keys(_this.evt.data.deployed)[i] + ' ------------------------');
|
||||
for (let j = 0; j < region.length; j++) {
|
||||
SCli.log(' ' + region[j].endpointMethod + ' - ' + region[j].endpointPath + ' - ' + region[j].endpointUrl);
|
||||
}
|
||||
@ -127,11 +127,11 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
}
|
||||
|
||||
// Display Failed Deployed Endpoints, if any
|
||||
if(_this.failed) {
|
||||
if(_this.evt.data.failed) {
|
||||
SCli.log('Failed to deploy endpoints in "' + _this.evt.options.stage + '" to the following regions:');
|
||||
for (let i = 0; i < Object.keys(_this.failed).length; i++) {
|
||||
let region = _this.failed[Object.keys(_this.failed)[i]];
|
||||
SCli.log(Object.keys(_this.failed)[i] + ' ------------------------');
|
||||
for (let i = 0; i < Object.keys(_this.evt.data.failed).length; i++) {
|
||||
let region = _this.evt.data.failed[Object.keys(_this.evt.data.failed)[i]];
|
||||
SCli.log(Object.keys(_this.evt.data.failed)[i] + ' ------------------------');
|
||||
for (let j = 0; j < region.length; j++) {
|
||||
SCli.log(' ' + region[j].endpointMethod + ' - ' + region[j].endpointPath + ': ' + region[j].message );
|
||||
// Show Error Stacktrace if in debug mode
|
||||
@ -146,8 +146,8 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
* Return EVT
|
||||
*/
|
||||
|
||||
_this.evt.data.deployed = _this.deployed;
|
||||
_this.evt.data.failed = _this.failed;
|
||||
_this.evt.data.deployed = _this.evt.data.deployed;
|
||||
_this.evt.data.failed = _this.evt.data.failed;
|
||||
return _this.evt;
|
||||
|
||||
});
|
||||
@ -170,7 +170,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
|
||||
// Prepare endpoints
|
||||
if (_this.evt.options.names.length) {
|
||||
_this.endpoints = _this.project.getEndpointsByName(_this.evt.options.names);
|
||||
_this.endpoints = _this.project.getEndpointsByNames(_this.evt.options.names);
|
||||
}
|
||||
|
||||
// If CLI and no endpoint names targeted, deploy from CWD
|
||||
@ -211,6 +211,8 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
|
||||
_processDeployment() {
|
||||
|
||||
let _this = this;
|
||||
|
||||
// Create new event object
|
||||
let newEvt = {
|
||||
options: {
|
||||
@ -222,7 +224,11 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
}
|
||||
};
|
||||
|
||||
return this.S.actions.endpointDeployApiGateway(newEvt);
|
||||
return this.S.actions.endpointDeployApiGateway(newEvt)
|
||||
.then(function(evt) {
|
||||
_this.evt.data.deployed = evt.data.deployed;
|
||||
_this.evt.data.failed = evt.data.failed;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,386 +14,388 @@
|
||||
*/
|
||||
|
||||
module.exports = function(SPlugin, serverlessPath) {
|
||||
const path = require('path'),
|
||||
SError = require(path.join(serverlessPath, 'Error')),
|
||||
SCli = require('../utils/cli'),
|
||||
BbPromise = require('bluebird'),
|
||||
async = require('async'),
|
||||
fs = require('fs');
|
||||
let SUtils;
|
||||
|
||||
// Promisify fs module
|
||||
BbPromise.promisifyAll(fs);
|
||||
|
||||
class EndpointDeployApiGateway extends SPlugin {
|
||||
|
||||
constructor(S, config) {
|
||||
super(S, config);
|
||||
SUtils = S.utils;
|
||||
}
|
||||
|
||||
static getName() {
|
||||
return 'serverless.core.' + EndpointDeployApiGateway.name;
|
||||
}
|
||||
|
||||
registerActions() {
|
||||
this.S.addAction(this.endpointDeployApiGateway.bind(this), {
|
||||
handler: 'endpointDeployApiGateway',
|
||||
description: 'Deploys a REST API to API Gateway in one or multiple regions'
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler
|
||||
*/
|
||||
|
||||
endpointDeployApiGateway(evt) {
|
||||
|
||||
let _this = this;
|
||||
_this.evt = evt;
|
||||
_this.project = _this.S.getProject();
|
||||
_this.provider = _this.S.getProvider();
|
||||
_this.awsAccountNumber = _this.project.getRegion(_this.evt.options.stage, _this.evt.options.region).getVariables().iamRoleArnLambda.replace('arn:aws:iam::', '').split(':')[0];
|
||||
_this.regions = _this.evt.options.region ? [_this.evt.options.region] : _this.project.getAllRegionNames(_this.evt.options.stage);
|
||||
_this.spinner = SCli.spinner();
|
||||
_this.endpoints = _this.project.getEndpointsByName(_this.evt.options.names);
|
||||
|
||||
return _this._validateAndPrepare()
|
||||
.bind(_this)
|
||||
.then(_this._processDeployment)
|
||||
.then(_this._createDeployment)
|
||||
.then(function() {
|
||||
|
||||
/**
|
||||
* Return EVT
|
||||
*/
|
||||
|
||||
_this.evt.data.deploymentId = _this.deployment.id;
|
||||
return _this.evt;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate And Prepare
|
||||
*/
|
||||
|
||||
_validateAndPrepare() {
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Deployment
|
||||
*/
|
||||
|
||||
_processDeployment() {
|
||||
|
||||
let _this = this;
|
||||
|
||||
// Status
|
||||
console.log('');
|
||||
SCli.log('Deploying endpoints in "' + _this.evt.options.stage + '" to the following regions: ' + _this.regions.join(', '));
|
||||
_this.spinner.start();
|
||||
|
||||
return BbPromise.try(function() {
|
||||
return _this.regions;
|
||||
})
|
||||
.bind(_this)
|
||||
.each(function(region) {
|
||||
|
||||
// Process each Region
|
||||
return _this._processRegionDeployment(region);
|
||||
})
|
||||
.then(function() {
|
||||
|
||||
// Stop Spinner
|
||||
_this.spinner.stop(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Deployment In Region
|
||||
*/
|
||||
|
||||
_processRegionDeployment(region) {
|
||||
|
||||
let _this = this;
|
||||
|
||||
// Get or Create REST API for Region by name
|
||||
let restApi;
|
||||
if (_this.project.getRegion(_this.evt.options.stage, region).getVariables()['apiGatewayApi']) {
|
||||
restApi = _this.project.getRegion(_this.evt.options.stage, region).getVariables()['apiGatewayApi'];
|
||||
} else {
|
||||
restApi = _this.project.name;
|
||||
}
|
||||
|
||||
return _this._findOrCreateRestApi(
|
||||
restApi,
|
||||
_this.evt.options.stage,
|
||||
region)
|
||||
.then(function(restApiData) {
|
||||
|
||||
_this.restApiData = restApiData;
|
||||
|
||||
let regionInstance = _this.project.getRegion(_this.evt.options.stage, region);
|
||||
regionInstance.addVariables({
|
||||
apiGatewayApi: restApiData.name
|
||||
});
|
||||
|
||||
return regionInstance.save();
|
||||
})
|
||||
.bind(_this)
|
||||
.then(_this._createAuthorizers)
|
||||
.then(function() {
|
||||
return new BbPromise(function (resolve, reject) {
|
||||
|
||||
// A function can have multiple endpoints. Process all endpoints for this Function
|
||||
async.eachSeries(_this.endpoints, function (endpoint, eCb) {
|
||||
|
||||
return BbPromise.try(function () {
|
||||
|
||||
// Find authorizerId, if specified
|
||||
let authorizerId;
|
||||
if (endpoint.authorizerFunction) {
|
||||
if (_this.authorizers[endpoint.authorizerFunction]) {
|
||||
authorizerId = _this.authorizers[endpoint.authorizerFunction];
|
||||
} else {
|
||||
throw new SError(`Endpoint ${endpoint.getName()} has an 'authorizerFunction' specified that does not exist in this project.`);
|
||||
}
|
||||
}
|
||||
if (endpoint.authorizerId) {
|
||||
authorizerId = endpoint.authorizerId;
|
||||
}
|
||||
|
||||
// Create new event object
|
||||
let newEvt = {
|
||||
options: {
|
||||
stage: _this.evt.options.stage,
|
||||
region: region,
|
||||
name: endpoint.getName(),
|
||||
authorizerId: authorizerId ? authorizerId : null,
|
||||
aliasEndpoint: _this.evt.options.aliasEndpoint
|
||||
}
|
||||
};
|
||||
|
||||
return _this.S.actions.endpointBuildApiGateway(newEvt);
|
||||
})
|
||||
.then(function (result) {
|
||||
|
||||
// Stash deployed endpoints
|
||||
if (!_this.deployed) _this.deployed = {};
|
||||
if (!_this.deployed[region]) _this.deployed[region] = [];
|
||||
_this.deployed[region].push({
|
||||
endpointPath: endpoint.path,
|
||||
endpointMethod: endpoint.method,
|
||||
endpointUrl: result.data.url
|
||||
});
|
||||
|
||||
})
|
||||
.catch(function (e) {
|
||||
|
||||
// Stash Failed Endpoint
|
||||
if (!_this.failed) _this.failed = {};
|
||||
if (!_this.failed[region]) _this.failed[region] = [];
|
||||
_this.failed[region].push({
|
||||
endpointPath: endpoint ? endpoint.path : 'unknown',
|
||||
endpointMethod: endpoint ? endpoint.method : 'unknown',
|
||||
message: e.message,
|
||||
stack: e.stack
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
|
||||
// If no endpoints were successfully deployed, skip
|
||||
if (!_this.deployed) return eCb();
|
||||
|
||||
// Deploy API Gateway Deployment in region
|
||||
return _this._createDeployment()
|
||||
.then(function() {
|
||||
return eCb();
|
||||
});
|
||||
});
|
||||
}, function () {
|
||||
return resolve();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Or Create REST API
|
||||
*/
|
||||
|
||||
_findOrCreateRestApi(restApiName, stage, region) {
|
||||
let _this = this;
|
||||
|
||||
return _this.provider.getApiByName(restApiName, stage, region)
|
||||
.then(function(restApi) {
|
||||
|
||||
// Return, if found
|
||||
if (restApi) return restApi;
|
||||
|
||||
// Otherwise, create new REST API
|
||||
let params = {
|
||||
name: restApiName, /* required */
|
||||
description: 'A REST API for a Serverless project in region: ' + region
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'createRestApi', params, stage, region)
|
||||
.then(function (response) {
|
||||
|
||||
SUtils.sDebug(
|
||||
'"'
|
||||
+ stage
|
||||
+ ' - '
|
||||
+ region
|
||||
+ '": created a new REST API on AWS API Gateway with name: '
|
||||
+ response.name);
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Authorizers
|
||||
*/
|
||||
|
||||
_createAuthorizers() {
|
||||
|
||||
let _this = this;
|
||||
let functions = _this.project.getAllFunctions();
|
||||
_this.authorizers = {};
|
||||
|
||||
return BbPromise.try(function() {
|
||||
|
||||
// Delete pre-existing on authorizers deployed on API Gateway
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id,
|
||||
limit: 100
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'getAuthorizers', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(a) {
|
||||
return a.items;
|
||||
});
|
||||
})
|
||||
.each(function(a) {
|
||||
|
||||
// Otherwise, delete pre-existing on authorizers deployed on API Gateway
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id,
|
||||
authorizerId: a.id
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'deleteAuthorizer', params, _this.evt.options.stage, _this.evt.options.region);
|
||||
})
|
||||
.then(function() {
|
||||
return functions;
|
||||
})
|
||||
.each(function(fn) {
|
||||
|
||||
// If no authorizer data, skip
|
||||
if (!fn.authorizer || !Object.keys(fn.authorizer).length) return;
|
||||
|
||||
let f = fn.toObjectPopulated({ stage: _this.evt.options.stage, region: _this.evt.options.region });
|
||||
|
||||
// Create new authorizer on API Gateway
|
||||
|
||||
// Fetch Lambda
|
||||
let params = {
|
||||
FunctionName: fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region }),
|
||||
Qualifier: _this.evt.options.stage
|
||||
};
|
||||
|
||||
return _this.provider.request('Lambda', 'getFunction', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(l) {
|
||||
// Validate required authorizer params
|
||||
if (!f.authorizer.identitySource) throw new SError(`Authorizer is missing identitySource property in function ${f.name}`);
|
||||
|
||||
// Create Authorizer params. Set defaults.
|
||||
let authorizer = f.authorizer;
|
||||
authorizer.restApiId = _this.restApiData.id;
|
||||
authorizer.name = authorizer.name || fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region });
|
||||
authorizer.type = authorizer.type || 'TOKEN';
|
||||
|
||||
let alias = '${stageVariables.functionAlias}';
|
||||
|
||||
// Construct authorizer URI
|
||||
authorizer.authorizerUri = 'arn:aws:apigateway:'
|
||||
+ _this.evt.options.region
|
||||
+ ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
|
||||
+ _this.evt.options.region
|
||||
+ ':'
|
||||
+ _this.awsAccountNumber
|
||||
+ ':function:'
|
||||
+ fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region })
|
||||
+ ':'
|
||||
+ alias
|
||||
+ '/invocations';
|
||||
|
||||
return _this.provider.request('APIGateway', 'createAuthorizer', authorizer, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(a) {
|
||||
_this.authorizers[fn.name] = a.id;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Deployment
|
||||
*/
|
||||
|
||||
_createDeployment() {
|
||||
|
||||
let _this = this;
|
||||
|
||||
return new BbPromise( function( resolve, reject ){
|
||||
|
||||
let doDeploy = function() {
|
||||
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id, /* required */
|
||||
stageName: _this.evt.options.stage, /* required */
|
||||
//cacheClusterEnabled: false, TODO: Implement
|
||||
//cacheClusterSize: '0.5 | 1.6 | 6.1 | 13.5 | 28.4 | 58.2 | 118 | 237', TODO: Implement
|
||||
description: _this.evt.options.description || 'Serverless deployment',
|
||||
stageDescription: _this.evt.options.stage,
|
||||
variables: {
|
||||
functionAlias: _this.evt.options.stage
|
||||
}
|
||||
};
|
||||
|
||||
_this.provider.request('APIGateway', 'createDeployment', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(response) {
|
||||
|
||||
_this.deployment = response;
|
||||
|
||||
SUtils.sDebug(
|
||||
'"'
|
||||
+ _this.evt.options.stage
|
||||
+ ' - '
|
||||
+ _this.evt.options.region
|
||||
+ ' - REST API: '
|
||||
+ 'created API Gateway deployment: '
|
||||
+ response.id);
|
||||
|
||||
return resolve();
|
||||
})
|
||||
.catch(function(error) {
|
||||
if( error.statusCode == 429 ) {
|
||||
SUtils.sDebug("'Too many requests' received, sleeping 5 seconds");
|
||||
setTimeout( doDeploy, 5000 );
|
||||
} else
|
||||
reject( new SError(error.message) );
|
||||
});
|
||||
};
|
||||
|
||||
return doDeploy();
|
||||
});
|
||||
}
|
||||
const path = require('path'),
|
||||
SError = require(path.join(serverlessPath, 'Error')),
|
||||
SCli = require('../utils/cli'),
|
||||
BbPromise = require('bluebird'),
|
||||
async = require('async'),
|
||||
fs = require('fs');
|
||||
let SUtils;
|
||||
|
||||
// Promisify fs module
|
||||
BbPromise.promisifyAll(fs);
|
||||
|
||||
class EndpointDeployApiGateway extends SPlugin {
|
||||
|
||||
constructor(S, config) {
|
||||
super(S, config);
|
||||
SUtils = S.utils;
|
||||
}
|
||||
|
||||
return( EndpointDeployApiGateway );
|
||||
static getName() {
|
||||
return 'serverless.core.' + EndpointDeployApiGateway.name;
|
||||
}
|
||||
|
||||
registerActions() {
|
||||
this.S.addAction(this.endpointDeployApiGateway.bind(this), {
|
||||
handler: 'endpointDeployApiGateway',
|
||||
description: 'Deploys a REST API to API Gateway in one or multiple regions'
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler
|
||||
*/
|
||||
|
||||
endpointDeployApiGateway(evt) {
|
||||
|
||||
let _this = this;
|
||||
_this.evt = evt;
|
||||
_this.project = _this.S.getProject();
|
||||
_this.provider = _this.S.getProvider();
|
||||
_this.awsAccountNumber = _this.project.getRegion(_this.evt.options.stage, _this.evt.options.region).getVariables().iamRoleArnLambda.replace('arn:aws:iam::', '').split(':')[0];
|
||||
_this.regions = _this.evt.options.region ? [_this.evt.options.region] : _this.project.getAllRegionNames(_this.evt.options.stage);
|
||||
_this.spinner = SCli.spinner();
|
||||
_this.endpoints = _this.project.getEndpointsByNames(_this.evt.options.names);
|
||||
|
||||
return _this._validateAndPrepare()
|
||||
.bind(_this)
|
||||
.then(_this._processDeployment)
|
||||
.then(function() {
|
||||
|
||||
/**
|
||||
* Return EVT
|
||||
*/
|
||||
|
||||
return _this.evt;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate And Prepare
|
||||
*/
|
||||
|
||||
_validateAndPrepare() {
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Deployment
|
||||
*/
|
||||
|
||||
_processDeployment() {
|
||||
|
||||
let _this = this;
|
||||
|
||||
// Status
|
||||
console.log('');
|
||||
SCli.log('Deploying endpoints in "' + _this.evt.options.stage + '" to the following regions: ' + _this.regions.join(', '));
|
||||
_this.spinner.start();
|
||||
|
||||
return BbPromise.try(function() {
|
||||
return _this.regions;
|
||||
})
|
||||
.bind(_this)
|
||||
.each(function(region) {
|
||||
|
||||
// Process each Region
|
||||
return _this._processRegionDeployment(region);
|
||||
})
|
||||
.then(function() {
|
||||
|
||||
// Stop Spinner
|
||||
_this.spinner.stop(true);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Deployment In Region
|
||||
*/
|
||||
|
||||
_processRegionDeployment(region) {
|
||||
|
||||
let _this = this;
|
||||
|
||||
// Get or Create REST API for Region by name
|
||||
let restApi;
|
||||
if (_this.project.getRegion(_this.evt.options.stage, region).getVariables()['apiGatewayApi']) {
|
||||
restApi = _this.project.getRegion(_this.evt.options.stage, region).getVariables()['apiGatewayApi'];
|
||||
} else {
|
||||
restApi = _this.project.name;
|
||||
}
|
||||
|
||||
return _this._findOrCreateRestApi(
|
||||
restApi,
|
||||
_this.evt.options.stage,
|
||||
region)
|
||||
.then(function(restApiData) {
|
||||
|
||||
_this.restApiData = restApiData;
|
||||
|
||||
let regionInstance = _this.project.getRegion(_this.evt.options.stage, region);
|
||||
regionInstance.addVariables({
|
||||
apiGatewayApi: restApiData.name
|
||||
});
|
||||
|
||||
return regionInstance.save();
|
||||
})
|
||||
.bind(_this)
|
||||
.then(_this._createAuthorizers)
|
||||
.then(function() {
|
||||
|
||||
// Build REST API Endpoints
|
||||
|
||||
return new BbPromise(function (resolve, reject) {
|
||||
|
||||
// A function can have multiple endpoints. Process all endpoints for this Function
|
||||
async.eachSeries(_this.endpoints, function (endpoint, eCb) {
|
||||
|
||||
return BbPromise.try(function () {
|
||||
|
||||
// Find authorizerId, if specified
|
||||
let authorizerId;
|
||||
if (endpoint.authorizerFunction) {
|
||||
if (_this.authorizers[endpoint.authorizerFunction]) {
|
||||
authorizerId = _this.authorizers[endpoint.authorizerFunction];
|
||||
} else {
|
||||
throw new SError(`Endpoint ${endpoint.getName()} has an 'authorizerFunction' specified that does not exist in this project.`);
|
||||
}
|
||||
}
|
||||
if (endpoint.authorizerId) {
|
||||
authorizerId = endpoint.authorizerId;
|
||||
}
|
||||
|
||||
// Create new event object
|
||||
let newEvt = {
|
||||
options: {
|
||||
stage: _this.evt.options.stage,
|
||||
region: region,
|
||||
name: endpoint.getName(),
|
||||
authorizerId: authorizerId ? authorizerId : null,
|
||||
aliasEndpoint: _this.evt.options.aliasEndpoint
|
||||
}
|
||||
};
|
||||
|
||||
return _this.S.actions.endpointBuildApiGateway(newEvt);
|
||||
})
|
||||
.then(function (result) {
|
||||
|
||||
// Stash deployed endpoints
|
||||
if (!_this.evt.data.deployed) _this.evt.data.deployed = {};
|
||||
if (!_this.evt.data.deployed[region]) _this.evt.data.deployed[region] = [];
|
||||
_this.evt.data.deployed[region].push({
|
||||
endpointPath: endpoint.path,
|
||||
endpointMethod: endpoint.method,
|
||||
endpointUrl: result.data.url
|
||||
});
|
||||
|
||||
return eCb();
|
||||
})
|
||||
.catch(function (e) {
|
||||
|
||||
// Stash Failed Endpoint
|
||||
if (!_this.evt.data.failed) _this.evt.data.failed = {};
|
||||
if (!_this.evt.data.failed[region]) _this.evt.data.failed[region] = [];
|
||||
_this.evt.data.failed[region].push({
|
||||
endpointPath: endpoint ? endpoint.path : 'unknown',
|
||||
endpointMethod: endpoint ? endpoint.method : 'unknown',
|
||||
message: e.message,
|
||||
stack: e.stack
|
||||
});
|
||||
|
||||
return eCb();
|
||||
});
|
||||
|
||||
}, function () {
|
||||
return resolve();
|
||||
});
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
|
||||
// If no endpoints were successfully deployed, skip
|
||||
if (!_this.evt.data.deployed || !_this.evt.data.deployed[region]) return;
|
||||
|
||||
// Deploy API Gateway Deployment in region
|
||||
return _this._createDeployment();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Or Create REST API
|
||||
*/
|
||||
|
||||
_findOrCreateRestApi(restApiName, stage, region) {
|
||||
let _this = this;
|
||||
|
||||
return _this.provider.getApiByName(restApiName, stage, region)
|
||||
.then(function(restApi) {
|
||||
|
||||
// Return, if found
|
||||
if (restApi) return restApi;
|
||||
|
||||
// Otherwise, create new REST API
|
||||
let params = {
|
||||
name: restApiName, /* required */
|
||||
description: 'A REST API for a Serverless project in region: ' + region
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'createRestApi', params, stage, region)
|
||||
.then(function (response) {
|
||||
|
||||
SUtils.sDebug(
|
||||
'"'
|
||||
+ stage
|
||||
+ ' - '
|
||||
+ region
|
||||
+ '": created a new REST API on AWS API Gateway with name: '
|
||||
+ response.name);
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Authorizers
|
||||
*/
|
||||
|
||||
_createAuthorizers() {
|
||||
|
||||
let _this = this;
|
||||
let functions = _this.project.getAllFunctions();
|
||||
_this.authorizers = {};
|
||||
|
||||
return BbPromise.try(function() {
|
||||
|
||||
// Delete pre-existing on authorizers deployed on API Gateway
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id,
|
||||
limit: 100
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'getAuthorizers', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(a) {
|
||||
return a.items;
|
||||
});
|
||||
})
|
||||
.each(function(a) {
|
||||
|
||||
// Otherwise, delete pre-existing on authorizers deployed on API Gateway
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id,
|
||||
authorizerId: a.id
|
||||
};
|
||||
|
||||
return _this.provider.request('APIGateway', 'deleteAuthorizer', params, _this.evt.options.stage, _this.evt.options.region);
|
||||
})
|
||||
.then(function() {
|
||||
return functions;
|
||||
})
|
||||
.each(function(fn) {
|
||||
|
||||
// If no authorizer data, skip
|
||||
if (!fn.authorizer || !Object.keys(fn.authorizer).length) return;
|
||||
|
||||
let f = fn.toObjectPopulated({ stage: _this.evt.options.stage, region: _this.evt.options.region });
|
||||
|
||||
// Create new authorizer on API Gateway
|
||||
|
||||
// Fetch Lambda
|
||||
let params = {
|
||||
FunctionName: fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region }),
|
||||
Qualifier: _this.evt.options.stage
|
||||
};
|
||||
|
||||
return _this.provider.request('Lambda', 'getFunction', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(l) {
|
||||
// Validate required authorizer params
|
||||
if (!f.authorizer.identitySource) throw new SError(`Authorizer is missing identitySource property in function ${f.name}`);
|
||||
|
||||
// Create Authorizer params. Set defaults.
|
||||
let authorizer = f.authorizer;
|
||||
authorizer.restApiId = _this.restApiData.id;
|
||||
authorizer.name = authorizer.name || fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region });
|
||||
authorizer.type = authorizer.type || 'TOKEN';
|
||||
|
||||
let alias = '${stageVariables.functionAlias}';
|
||||
|
||||
// Construct authorizer URI
|
||||
authorizer.authorizerUri = 'arn:aws:apigateway:'
|
||||
+ _this.evt.options.region
|
||||
+ ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
|
||||
+ _this.evt.options.region
|
||||
+ ':'
|
||||
+ _this.awsAccountNumber
|
||||
+ ':function:'
|
||||
+ fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region })
|
||||
+ ':'
|
||||
+ alias
|
||||
+ '/invocations';
|
||||
|
||||
return _this.provider.request('APIGateway', 'createAuthorizer', authorizer, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(a) {
|
||||
_this.authorizers[fn.name] = a.id;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Deployment
|
||||
*/
|
||||
|
||||
_createDeployment() {
|
||||
|
||||
let _this = this;
|
||||
|
||||
return new BbPromise( function( resolve, reject ){
|
||||
|
||||
let doDeploy = function() {
|
||||
|
||||
let params = {
|
||||
restApiId: _this.restApiData.id, /* required */
|
||||
stageName: _this.evt.options.stage, /* required */
|
||||
//cacheClusterEnabled: false, TODO: Implement
|
||||
//cacheClusterSize: '0.5 | 1.6 | 6.1 | 13.5 | 28.4 | 58.2 | 118 | 237', TODO: Implement
|
||||
description: _this.evt.options.description || 'Serverless deployment',
|
||||
stageDescription: _this.evt.options.stage,
|
||||
variables: {
|
||||
functionAlias: _this.evt.options.stage
|
||||
}
|
||||
};
|
||||
|
||||
_this.provider.request('APIGateway', 'createDeployment', params, _this.evt.options.stage, _this.evt.options.region)
|
||||
.then(function(response) {
|
||||
|
||||
_this.deployment = response;
|
||||
|
||||
SUtils.sDebug(
|
||||
'"'
|
||||
+ _this.evt.options.stage
|
||||
+ ' - '
|
||||
+ _this.evt.options.region
|
||||
+ ' - REST API: '
|
||||
+ 'created API Gateway deployment: '
|
||||
+ response.id);
|
||||
|
||||
return resolve();
|
||||
})
|
||||
.catch(function(error) {
|
||||
if( error.statusCode == 429 ) {
|
||||
SUtils.sDebug("'Too many requests' received, sleeping 5 seconds");
|
||||
setTimeout( doDeploy, 5000 );
|
||||
} else
|
||||
reject( new SError(error.message) );
|
||||
});
|
||||
};
|
||||
|
||||
return doDeploy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return( EndpointDeployApiGateway );
|
||||
};
|
||||
@ -157,7 +157,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
this.regions = this.evt.options.region ? [this.evt.options.region] : this.project.getAllRegionNames(this.evt.options.stage);
|
||||
|
||||
this.endpoints = _.map(this.evt.options.names, (name) => {
|
||||
const endpoint = this.project.getEndpoint(name.split('#')[0], name.split('#')[1]);
|
||||
const endpoint = this.project.getEndpoint(name);
|
||||
if (!endpoint) throw new SError(`Endpoint "${name}" doesn't exist in your project`);
|
||||
return endpoint;
|
||||
});
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
module.exports = function(ServerlessPlugin) { // Always pass in the ServerlessPlugin Class
|
||||
|
||||
const path = require('path'),
|
||||
const path = require('path'),
|
||||
fs = require('fs'),
|
||||
BbPromise = require('bluebird'); // Serverless uses Bluebird Promises and we recommend you do to because they provide more than your average Promise :)
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ describe('All Tests', function() {
|
||||
before(function() {});
|
||||
after(function() {});
|
||||
|
||||
// require('./tests/classes/Project');
|
||||
// require('./tests/classes/Project');
|
||||
// require('./tests/classes/Function');
|
||||
// require('./tests/classes/Endpoint');
|
||||
// require('./tests/classes/Stage');
|
||||
@ -23,7 +23,7 @@ describe('All Tests', function() {
|
||||
// require('./tests/actions/ResourcesDeploy');
|
||||
// require('./tests/actions/FunctionRun');
|
||||
// require('./tests/actions/FunctionLogs');
|
||||
// require('./tests/actions/FunctionDeploy');
|
||||
require('./tests/actions/FunctionDeploy');
|
||||
require('./tests/actions/EndpointDeploy');
|
||||
// require('./tests/actions/EventDeploy');
|
||||
// require('./tests/actions/ProjectInit');
|
||||
|
||||
@ -44,6 +44,7 @@
|
||||
"path": "group1/function1",
|
||||
"method": "GET",
|
||||
"authorizationType": "${endpointVariable}",
|
||||
"authorizerFunction": "function4",
|
||||
"apiKeyRequired": false,
|
||||
"requestParameters": "$${endpointTemplate}",
|
||||
"requestTemplates": "$${apiRequestTemplate}",
|
||||
|
||||
@ -9,17 +9,17 @@
|
||||
"handler": "group2/function4/handler.handler",
|
||||
"timeout": 6,
|
||||
"memorySize": 1024,
|
||||
"runtime": "nodejs",
|
||||
"authorizer": {
|
||||
"type": "TOKEN",
|
||||
"identitySource": "method.request.header.Authorization"
|
||||
},
|
||||
"runtime": "nodejs",
|
||||
"events": [],
|
||||
"endpoints": [
|
||||
{
|
||||
"path": "group2/function4",
|
||||
"method": "GET",
|
||||
"authorizationType": "none",
|
||||
"authorizerFunction": "function4",
|
||||
"apiKeyRequired": false,
|
||||
"requestParameters": {},
|
||||
"requestTemplates": {
|
||||
|
||||
@ -97,29 +97,29 @@ describe('Test Action: Function Deploy', function() {
|
||||
* Tests
|
||||
*/
|
||||
|
||||
// describe('Function Deploy: Specify One Path', function() {
|
||||
// it('should deploy functions', function(done) {
|
||||
//
|
||||
// this.timeout(0);
|
||||
//
|
||||
// let options = {
|
||||
// stage: config.stage,
|
||||
// region: config.region,
|
||||
// names: [
|
||||
// 'function1'
|
||||
// ]
|
||||
// };
|
||||
//
|
||||
// serverless.actions.functionDeploy(options)
|
||||
// .then(function(evt) {
|
||||
// validateEvent(evt);
|
||||
// done();
|
||||
// })
|
||||
// .catch(e => {
|
||||
// done(e);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
describe('Function Deploy: Specify One Path', function() {
|
||||
it('should deploy functions', function(done) {
|
||||
|
||||
this.timeout(0);
|
||||
|
||||
let options = {
|
||||
stage: config.stage,
|
||||
region: config.region,
|
||||
names: [
|
||||
'function1'
|
||||
]
|
||||
};
|
||||
|
||||
serverless.actions.functionDeploy(options)
|
||||
.then(function(evt) {
|
||||
validateEvent(evt);
|
||||
done();
|
||||
})
|
||||
.catch(e => {
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Function Deploy: Nested W/ Custom Name & Limited Parent Dir', function() {
|
||||
it('should deploy functions', function(done) {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('Test Serverless Endpoint Class', function() {
|
||||
return serverless.init()
|
||||
.then(function() {
|
||||
|
||||
instance = serverless.getProject().getEndpoint('group1/function1', 'GET');
|
||||
instance = serverless.getProject().getEndpoint('group1/function1~GET');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
@ -92,7 +92,7 @@ describe('Test Serverless Project Class', function() {
|
||||
});
|
||||
|
||||
it('Get endpoints by path and method', function() {
|
||||
let endpoint = instance.getEndpoint('group1/function1', 'GET');
|
||||
let endpoint = instance.getEndpoint('group1/function1~GET');
|
||||
assert.isDefined(endpoint);
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user