ServerlessComponent: continue work on function loading

This commit is contained in:
Austen Collins 2016-02-03 13:49:05 -08:00
parent 110eee79ab
commit f0de3a5c93
33 changed files with 214 additions and 120 deletions

View File

@ -68,12 +68,13 @@ class ServerlessComponent {
load() {
let _this = this,
skip = ['node_modules', 'lib'],
reserved = ['node_modules', 'lib', '.DS_Store'],
componentJson,
componentContents;
// Helper to instantiate functions
let loadFn = function(fnName, fnKey, cPath) {
let loadFn = function(fnName, cPath) {
console.log("Loading Function...", fnName, cPath)
let func = new _this._S.classes.Function(_this._S, {
component: _this._config.component,
cPath: cPath ? cPath : null,
@ -82,7 +83,7 @@ class ServerlessComponent {
return func.load()
.then(function(instance) {
componentJson.functions[fnKey]= instance;
componentJson.functions[cPath ? cPath + '/' + fnName : fnName]= instance;
return componentJson.functions[fnKey];
});
};
@ -103,37 +104,32 @@ class ServerlessComponent {
componentContents = fs.readdirSync(_this._config.fullPath);
return componentContents;
})
.each(function(f, i) {
.each(function(sf, i) {
// Skip node_modules and lib
if (skip.indexOf(f.trim() == -1) return;
let fPath1 = path.join(_this._config.fullPath, sf);
// Skip reserved names and files
if (reserved.indexOf(sf.trim()) !== -1 || !fs.lstatSync(fPath1).isDirectory()) return;
// If s-function.json doesn't exist, look in 2 subfolders
if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], 's-function.json'))) {
let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], 's-function.json'));
return loadFn(fnJson.name, fnJson.name, null);
if (SUtils.fileExistsSync(path.join(fPath1, 's-function.json'))) {
let fnJson = SUtils.readAndParseJsonSync(path.join(fPath1, 's-function.json'));
return loadFn(fnJson.name, null);
} else {
// Loop through 1st level subfolders looking for more functions
return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i])))
.each(function(sf1, i) {
.each(function(sf2, i) {
let fPath2 = path.join(fPath1, sf2);
// Skip reserved names and files
if (reserved.indexOf(sf2.trim()) !== -1 || !fs.lstatSync(fPath2).isDirectory()) return;
// If s-function.json doesn't exist, look in 2 subfolders
if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, 's-function.json'))) {
let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, 's-function.json'));
return loadFn(fnJson.name, fnJson.name, sf1 + '/' + fnJson.name);
} else {
// Loop through 2nd level subfolders looking for more functions
return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i], sf1)))
.each(function(sf2, i) {
// If s-function.json doesn't exist, look in 2 subfolders
if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, sf2, 's-function.json'))) {
let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, sf2, 's-function.json'));
return loadFn(fnJson.name, fnJson.name, sf1 + '/' + sf2 + '/' + fnJson.name);
}
});
if (SUtils.fileExistsSync(path.join(fPath2, 's-function.json'))) {
let fnJson = SUtils.readAndParseJsonSync(path.join(fPath2, 's-function.json'));
return loadFn(fnJson.name, fnJson.name, sf2 + '/' + fnJson.name);
}
});
}
@ -257,7 +253,7 @@ class ServerlessComponent {
})
.then(function() {
// Save all nested modules
// Save all nested functions
if (options && options.deep) {
return BbPromise.try(function () {
return Object.keys(_this.functions);

View File

@ -26,7 +26,7 @@ class ServerlessFunction {
// Validate required attributes
if (!config.component || !config.function) throw new SError('Missing required config.component or config.function');
console.log("ServerlessFunction Constructor", config);
let _this = this;
_this._S = Serverless;
_this._config = {};
@ -310,7 +310,7 @@ class ServerlessFunction {
writeDeferred.push(
fs.mkdirSync(_this._config.fullPath),
SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}')
)
);
if (_this.getRuntime() === 'nodejs') {
writeDeferred.push(
SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js')))

View File

@ -69,7 +69,7 @@ class ServerlessState {
/**
* Set Asset
* - Add or replace an asset to the state
* - Accepts a class instance of: Project, Component, Module, Function, Endpoint
* - Accepts a class instance of: Project, Component, Function, Endpoint
*/
setAsset(data) {
@ -90,7 +90,7 @@ class ServerlessState {
}
if (!added) func.endpoints.push(data);
} else {
return new SError('State.setAsset() failed because you did not submit an instance of a Project, Component, Module, Function or Endpoint class.');
return new SError('State.setAsset() failed because you did not submit an instance of a Project, Component, Function or Endpoint class.');
}
}
@ -220,8 +220,8 @@ class ServerlessState {
/**
* Get Functions
* - Returns an array of this state's function instances
* - Options: paths, component, module, function
* - options.paths is an array of Serverless paths like this: ['component/moduleOne/functionOne', 'component/moduleOne/functionOne']
* - Options: paths, component, function
* - options.paths is an array of Serverless paths like this: ['component/functionOne', 'component/subfolder1/subfolder2/functionTwo']
*/
getFunctions(options) {
@ -233,13 +233,9 @@ class ServerlessState {
// Get all
for (let i = 0; i < Object.keys(_this.project.components).length; i++) {
let component = _this.project.components[Object.keys(_this.project.components)[i]];
if (!component.modules) continue;
for (let j = 0; j < Object.keys(component.modules).length; j++) {
let module = component.modules[Object.keys(component.modules)[j]];
if (!module.functions) continue;
for (let k = 0; k < Object.keys(module.functions).length; k++) {
allFunctions.push(module.functions[Object.keys(module.functions)[k]]);
}
console.log("here", component.functions)
for (let j = 0; j < Object.keys(component.functions).length; j++) {
allFunctions.push(component.functions[Object.keys(component.functions)[j]]);
}
}
@ -251,7 +247,7 @@ class ServerlessState {
for (let i = 0; i < allFunctions.length; i++) {
let func = allFunctions[i];
if (options.component && options.module && options.function) {
if (options.component && options.cPath && options.function) {
if (func._config.component == options.component && func._config.module == options.module && func.name == options.function) {
foundFunctions.push(options.returnPaths ? func._config.sPath : func);
}

View File

@ -70,23 +70,24 @@ exports.buildSPath = function(data) {
* Parse sPath
*/
exports.parseSPath = function(path) {
let pathArray = path.split('/');
if (pathArray.length < 1) {
return { component: pathArray[0] }
exports.parseSPath = function(sPath) {
console.log("parseSPath", sPath)
let pArray = sPath.split('/');
if (pArray.length < 1) {
return { component: pArray[0] }
} else {
let parsed = {
component: pathArray[0],
function: pathArray[pathArray.length - 1].split('@')[0],
urlPath: pathArray[pathArray.length - 1].split('@')[1].split('~')[0],
urlMethod: pathArray[pathArray.length - 1].split('@')[1].split('~')[1],
event: pathArray[pathArray.length - 1].split('#')[1]
component: pArray[0],
function: pArray[1] ? pArray[pArray.length - 1].split('@')[0] : null,
urlPath: pArray[1] ? pArray[pArray.length - 1].split('@')[1] ? pArray[pArray.length - 1].split('@')[1].split('~')[0] : null : null,
urlMethod: pArray[1] ? pArray[pArray.length - 1].split('@')[1] ? pArray[pArray.length - 1].split('@')[1].split('~')[1] : null : null,
event: pArray[1] ? pArray[pArray.length - 1].split('#')[1] : null
};
pathArray.shift();
pathArray.pop();
pArray.shift();
pArray.pop();
// Check for any cPath
if (pathArray.length) {
parsed.cPath = pathArray.join('/');
if (pArray.length) {
parsed.cPath = pArray.join('/');
}
return parsed;
}

View File

@ -10,29 +10,26 @@ describe('All Tests', function() {
});
after(function() {});
require('./tests/classes/ServerlessEndpointTest');
require('./tests/classes/ServerlessFunctionTest');
require('./tests/classes/ServerlessModuleTest');
require('./tests/classes/ServerlessComponentTest');
require('./tests/classes/ServerlessProjectTest');
require('./tests/classes/ServerlessStateTest');
require('./tests/actions/TestPluginCustom');
require('./tests/actions/TestDefaultActionHook');
require('./tests/actions/StageCreate');
require('./tests/actions/RegionCreate');
require('./tests/actions/ComponentCreate');
require('./tests/actions/ModuleCreate');
require('./tests/actions/FunctionCreate');
require('./tests/actions/EnvList');
require('./tests/actions/EnvGet');
require('./tests/actions/EnvSetUnset');
require('./tests/actions/ResourcesDeploy');
require('./tests/actions/FunctionRun');
require('./tests/actions/FunctionDeploy');
require('./tests/actions/EndpointDeploy');
require('./tests/actions/ProjectInit');
require('./tests/actions/ProjectInstall');
require('./tests/actions/ProjectLifeCycle.js');
require('./tests/actions/FunctionInvoke.js');
require('./tests/actions/FunctionLogs.js');
//require('./tests/classes/ServerlessProjectTest');
//require('./tests/classes/ServerlessComponentTest');
//require('./tests/classes/ServerlessFunctionTest');
//require('./tests/classes/ServerlessEndpointTest');
//require('./tests/actions/TestPluginCustom');
//require('./tests/actions/TestDefaultActionHook');
//require('./tests/actions/StageCreate');
//require('./tests/actions/RegionCreate');
//require('./tests/actions/ComponentCreate');
//require('./tests/actions/ModuleCreate');
//require('./tests/actions/FunctionCreate');
//require('./tests/actions/EnvList');
//require('./tests/actions/EnvGet');
//require('./tests/actions/EnvSetUnset');
//require('./tests/actions/ResourcesDeploy');
//require('./tests/actions/FunctionRun');
//require('./tests/actions/FunctionDeploy');
//require('./tests/actions/EndpointDeploy');
//require('./tests/actions/ProjectInit');
//require('./tests/actions/ProjectInstall');
//require('./tests/actions/ProjectLifeCycle.js');
});

View File

@ -0,0 +1,34 @@
{
"name" : "function0",
"custom": {
"envVars": [],
"excludePatterns": []
},
"handler": "module1/function0/handler.handler",
"timeout": 6,
"memorySize": 1024,
"events": [],
"endpoints": [
{
"path": "module1/function0",
"method": "GET",
"authorizationType": "${endpointVariable}",
"apiKeyRequired": false,
"requestParameters": "$${endpointTemplate}",
"requestTemplates": "$${apiRequestTemplate}",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {},
"responseModels": {},
"responseTemplates": {
"application/json": ""
}
},
"400": {
"statusCode": "400"
}
}
}
]
}

View File

@ -0,0 +1,12 @@
'use strict';
// Load ENV
var ServerlessHelpers = require('serverless-helpers-js');
ServerlessHelpers.loadEnv();
// Lambda Handler
module.exports.handler = function(event, context) {
return context.done(null, {
message: '"functionOne" lambda function has run successfully'
});
};

View File

@ -0,0 +1,7 @@
{
"apiRequestTemplate": {
"application/json": {
"pathParams" : "$input.path('$.id1')"
}
}
}

View File

@ -0,0 +1,3 @@
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJ1X2FkN2Y5MGMwLTNiZWItMTFlNS05MDVjLWUzNzViNDljNDhkMiIsImlhdCI6MTQzODgzMTk3NSwiZXhwIjoxNDM5NDM2Nzc1LCJpc3MiOiJKQVdTIn0.FlISdHxUrFdrDkovrq_RRwxHqlzaSL3GShr27xlvcB0"
}

View File

@ -0,0 +1,3 @@
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJ1X2FkN2Y5MGMwLTNiZWItMTFlNS05MDVjLWUzNzViNDljNDhkMiIsImlhdCI6MTQzODgzMTk3NSwiZXhwIjoxNDM5NDM2Nzc1LCJpc3MiOiJKQVdTIn0.FlISdHxUrFdrDkovrq_RRwxHqlzaSL3GShr27xlvcB0"
}

View File

@ -0,0 +1,10 @@
'use strict';
// Load ENV
var ServerlessHelpers = require('serverless-helpers-js');
ServerlessHelpers.loadEnv();
// Lambda Handler
module.exports.handler = function(event, context) {
return context.done(null, { message: '"functionThree" lambda function has run successfully' });
};

View File

@ -0,0 +1,59 @@
{
"name": "function4",
"custom": {
"envVars": [],
"excludePatterns": []
},
"handler": "module1/function4/handler.handler",
"timeout": 6,
"memorySize": 1024,
"events": [],
"endpoints": [
{
"path": "module1/function4",
"method": "GET",
"authorizationType": "none",
"apiKeyRequired": false,
"requestParameters": {},
"requestTemplates": {
"application/json": "{\"access_token\":\"$input.params('access_token')\",\"body\":\"$input.json('$')\"}"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {},
"responseModels": {},
"responseTemplates": {
"application/json": ""
}
},
"400": {
"statusCode": "400"
}
}
},
{
"path": "module1/function4",
"method": "POST",
"authorizationType": "none",
"apiKeyRequired": false,
"requestTemplates": {
"application/json": "{\"access_token\":\"$input.params('access_token')\",\"body\":\"$input.json('$')\"}"
},
"requestParameters": {},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {},
"responseModels": {},
"responseTemplates": {
"application/json": ""
}
},
"400": {
"statusCode": "400"
}
}
}
]
}

View File

@ -78,7 +78,7 @@ describe('Test Action: Endpoint Deploy', function() {
stage: config.stage,
region: config.region,
paths: [
'nodejscomponent/module1/function1@module1/function1~GET'
'nodejscomponent/group1/function1@group1/function1~GET'
]
};

View File

@ -57,7 +57,7 @@ describe('Test action: Function Create', function() {
let evt = {
options: {
component: 'nodejscomponent',
module: 'module1',
module: 'group1',
function: 'new'
}
};
@ -65,7 +65,7 @@ describe('Test action: Function Create', function() {
serverless.actions.functionCreate(evt)
.then(function(evt) {
validateEvent(evt);
let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'nodejscomponent', 'module1', 'function1', 's-function.json'));
let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'nodejscomponent', 'group1', 'function1', 's-function.json'));
assert.equal(true, typeof functionJson.name != 'undefined');
assert.equal(true, functionJson.endpoints.length);
done();

View File

@ -108,7 +108,7 @@ describe('Test Action: Function Deploy', function() {
stage: config.stage,
region: config.region,
paths: [
'nodejscomponent/module1/function1'
'nodejscomponent/group1/function1'
]
};
@ -132,7 +132,7 @@ describe('Test Action: Function Deploy', function() {
// stage: config.stage,
// region: config.region,
// paths: [
// 'nodejscomponent/module1/function1'
// 'nodejscomponent/group1/function1'
// ]
// };
//
@ -167,7 +167,7 @@ describe('Test Action: Function Deploy', function() {
// stage: config.stage,
// region: config.region,
// paths: [
// 'nodejscomponent/module1/function1'
// 'nodejscomponent/group1/function1'
// ]
// };
//

View File

@ -58,7 +58,7 @@ describe('Test Action: Function Run', function() {
this.timeout(0);
let options = {
path: 'nodejscomponent/module1/function1'
path: 'nodejscomponent/group1/function1'
};
serverless.actions.functionRun(options)

View File

@ -35,9 +35,9 @@ describe('Test Serverless Endpoint Class', function() {
// Instantiate Class
instance = new serverless.classes.Endpoint(serverless, {
component: 'nodejscomponent',
module: 'module1',
module: 'group1',
function: 'function1',
endpointPath: 'module1/function1',
endpointPath: 'group1/function1',
endpointMethod: 'GET'
});
@ -95,7 +95,7 @@ describe('Test Serverless Endpoint Class', function() {
it('Create new and save', function(done) {
let endpoint = new serverless.classes.Endpoint(serverless, {
component: 'nodejscomponent',
module: 'module1',
module: 'group1',
function: 'function1',
endpointPath: 'test',
endpointMethod: 'GET'

View File

@ -35,7 +35,7 @@ describe('Test Serverless Function Class', function() {
// Instantiate Class
instance = new serverless.classes.Function(serverless, {
component: 'nodejscomponent',
module: 'module1',
module: 'group1',
function: 'function1'
});
@ -94,7 +94,7 @@ describe('Test Serverless Function Class', function() {
it('Create new and save', function(done) {
let func = new serverless.classes.Function(serverless, {
component: 'nodejscomponent',
module: 'module1',
module: 'group1',
function: 'function4'
});

View File

@ -36,7 +36,7 @@ describe('Test Serverless Module Class', function() {
// Instantiate Class
instance = new serverless.classes.Module(serverless, {
component: 'nodejscomponent',
module: 'module1'
module: 'group1'
});
done();
@ -94,7 +94,7 @@ describe('Test Serverless Module Class', function() {
it('Create new and save', function(done) {
let module = new serverless.classes.Module(serverless, {
component: 'nodejscomponent',
module: 'module1'
module: 'group1'
});
module.save()

View File

@ -142,30 +142,6 @@ describe('Test Serverless State Class', function() {
done();
});
it('Get modules w/o paths', function(done) {
let modules = instance.getModules();
assert.equal(true, modules[0].name === 'module1');
done();
});
it('Get modules w paths', function(done) {
let modules = instance.getModules({ paths: ['nodejscomponent/module1'] });
assert.equal(true, modules[0].name === 'module1');
done();
});
it('Get modules by component and module', function(done) {
let modules = instance.getModules({ component: 'nodejscomponent', module: 'module1' });
assert.equal(true, modules[0].name === 'module1');
done();
});
it('Get modules by component', function(done) {
let modules = instance.getModules({ component: 'nodejscomponent' });
assert.equal(true, modules.length === 1);
done();
});
it('Get functions w/o paths', function(done) {
let functions = instance.getFunctions();
assert.equal(true, functions.length === 3);
@ -173,13 +149,13 @@ describe('Test Serverless State Class', function() {
});
it('Get functions w paths', function(done) {
let functions = instance.getFunctions({ paths: ['nodejscomponent/module1/function1'] });
let functions = instance.getFunctions({ paths: ['nodejscomponent/group1/function1'] });
assert.equal(true, functions.length === 1);
done();
});
it('Get functions by component, module and function', function(done) {
let functions = instance.getFunctions({ component: 'nodejscomponent', module: 'module1', function: 'function1' });
let functions = instance.getFunctions({ component: 'nodejscomponent', module: 'group1', function: 'function1' });
assert.equal(true, functions.length === 1);
done();
});
@ -191,19 +167,19 @@ describe('Test Serverless State Class', function() {
});
it('Get endpoints w paths', function(done) {
let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/module1/function1@module1/function1~GET'] });
let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/group1/function1@group1/function1~GET'] });
assert.equal(true, endpoints.length === 1);
done();
});
it('Get endpoints by component, module, function, path and method', function(done) {
let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'module1', function: 'function3', endpointPath: 'module1/function3', endpointMethod: 'POST' });
let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function3', endpointPath: 'group1/function3', endpointMethod: 'POST' });
assert.equal(true, endpoints.length === 1);
done();
});
it('Get endpoints by component, module and function', function(done) {
let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'module1', function: 'function1' });
let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function1' });
assert.equal(true, endpoints.length === 1);
done();
});