Socket
Socket
Sign inDemoInstall

yukon

Package Overview
Dependencies
21
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.4 to 0.0.5

54

api.js
var fs = require('fs');
var path = require('path');
var sa = require('superagent');
var _ = require('lodash');

@@ -8,35 +10,38 @@ module.exports = function(app, config) {

return {
callApi: callApi,
getData: getData,
};
// route call to get stub or use live API
function callApi(req, res, next, callArgs) {
debug("callApi called, callArgs = " + callArgs);
function getData(callArgs, req, res, next) {
debug("getData called");
if (callArgs.stubPath)
readStub(req, res, next, callArgs);
if (callArgs.useStub)
readStub(callArgs, req, res, next);
else
getData(req, res, next, callArgs);
callApi(callArgs, req, res, next);
}
// call live API - return data as res.locals[namespace] (namespace = data1, data2, data3 etc. for component level API calls)
function getData(req, res, next, callArgs) {
debug('getData called, namespace = ' + callArgs.namespace);
function callApi(args, req, res, next) {
// if path ends with '/', assume it gets an id from the express request :id matcher
callArgs.apiPath = callArgs.apiPath.match(/\/$/) ? callArgs.apiPath + req.params.id : callArgs.apiPath;
callArgs.apiVerb = callArgs.apiVerb || 'get';
callArgs.apiParams = callArgs.apiParams || {};
callArgs.apiBodyType = callArgs.apiBodyType || 'json';
callArgs.paramMethod = (callArgs.apiVerb === 'get') ? 'query' : 'send';
debug('callApi called, namespace = ' + args.namespace);
var callArgs = _.assign(_.cloneDeep(config.apiDefaults), args);
callArgs.paramMethod = (callArgs.verb === 'get') ? 'query' : 'send';
// MAGIC ALERT: if path ends with '/', assume it gets an id from the express request :id matcher
callArgs.path = callArgs.path.match(/\/$/) ? callArgs.path + req.params.id : callArgs.path;
console.log(callArgs);
config.beforeApiCall(callArgs, req, res);
var call = sa
[callArgs.apiVerb](callArgs.apiPath)
[callArgs.paramMethod](callArgs.apiParams)
.type(callArgs.apiBodyType)
[callArgs.verb](callArgs.path)
[callArgs.paramMethod](callArgs.params)
.type(callArgs.bodyType)
.timeout(callArgs.timeout);
callArgs.customHeaders.forEach(function(header) {
debug('adding custom header: '+header.name+'='+header.value);
call.set(header.name, header.value);

@@ -61,9 +66,16 @@ });

// return stub data as res.locals[namespace] (namespace = data1, data2, data3 etc. for component level API calls)
function readStub(req, res, next, callArgs) {
debug('loooking for stub - ' + callArgs.stubPath);
// return stub data as res.locals[namespace] same as API
function readStub(callArgs, req, res, next) {
// MAGIC ALERT: framework assumes the stub name = nodule name if no stubPath is supplied
var stubName = callArgs.stubPath || req.nodule.name;
var stub = (stubName.indexOf('/') > -1)
? path.join(process.cwd(), stubName)
: path.join(req.nodule.path, stubName+'.stub.json');
debug('loooking for stub - ' + stub);
var data = {};
try {
data = fs.readFileSync(callArgs.stubPath);
data = fs.readFileSync(stub);
debug('stub found!, namespace='+callArgs.namespace);

@@ -70,0 +82,0 @@ }

@@ -1,125 +0,50 @@

// calls all APIs in parallel (inlcuding those added by the app-level appDoApi middleware)
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var debug;
// calls all APIs in parallel (inlcuding those added by the app-level appDoApi middleware)
module.exports = function(app, config) {
debug = config.debug('yukon->doApi');
var api = require('./api.js')(app, config);
// called as route comes in, before it goes to API
return function(req, res, next) {
debug("called");
console.log(req.nodule.apiCalls);
if (req.nodule.apiCalls.length > 0) {
// skip if nodule doesn't have any API calls
if (req.nodule.skipApi) {
next();
return;
}
var dataIdx = 1;
req.nodule.apiCalls.forEach(function(apiCall, index) {
if (!apiCall.namespace) apiCall.namespace = "data" + dataIdx++;
});
var apiCalls = req.nodule.apiCalls || [], apiArgs = req.nodule.apiArgs || [], nodule = req.nodule;
processStubs(app, req);
var apiParamsIsArray = false;
if (typeof nodule.apiPath === "string")
nodule.apiPath = [nodule.apiPath];
// need to identify when apiPath is an array and params are effectively an array (although still technically an object) of objects
else if (nodule.apiPath && !_.isEmpty(nodule.apiParams) && _.keys(nodule.apiParams)[0].match(/\d+/))
apiParamsIsArray = true;
_.each(nodule.apiPath, function(apiPath, index) {
apiCalls.unshift(req.nodule.callApi); // add apiCalls to front of apiCalls array in case app-level apiCalls have no apiArgs
apiArgs.unshift({
apiPath : apiPath,
apiHost : getValue(nodule.apiHost, index),
apiParams : getValue(nodule.apiParams, index, apiParamsIsArray),
apiVerb : getValue(nodule.apiVerb, index),
apiBodyType : getValue(nodule.apiBodyType, index),
handleError : getValue(nodule.handleError, index),
timeout : getValue(nodule.timeout, index),
stubPath : getValue(nodule.stubPath, index),
namespace : "data" + (1*index+1) });
});
if (apiCalls.length > 0) {
parallel(apiCalls, req, res, next, apiArgs);
parallel(req.nodule.apiCalls, req, res, next);
}
else
else {
next();
}
};
};
// return correct value for property if it is an array, or return the same value for every API if the property is not an array
function getValue(property, index, forceArray) {
if (property && (property instanceof Array || forceArray))
return property[index];
else
return property;
}
// invokes N number of api calls, then invokes the express next() when all have returned or one returns an error
function parallel(apiCalls, req, res, next) {
debug('parallel started!! # calls:' + apiCalls.length);
var results = 0;
var errorReceived = null;
function processStubs(app, req) {
if (req.nodule.forceStub) {
apiCalls.forEach(function(apiCall){
api.getData(apiCall, req, res, function(err){
if (err && !errorReceived) {
errorReceived = err;
next(err); // if any error - send full request into error flow, exit out of parallel calls
}
var nodule = req.nodule;
// set one stub to array to share stub finding logic with multiple stub scenario
if (typeof nodule.stubName === "string") {
nodule.stubName = [nodule.stubName];
}
// if there is no stub we need to create an array of the same length as apiPath - kind of awkward but it's just stub mode
else if (!nodule.stubName) {
// if apiPath is a string then look for one stub, if it's an array look for that many stubs, if there is no apiPath don't look for stubs.
var arrayLength = (typeof nodule.apiPath === 'string') ? 1 : ((nodule.apiPath) ? nodule.apiPath.length : 0);
nodule.stubName = new Array(arrayLength);
// initialize array with strings
for (var i=0; i<arrayLength; i++)
nodule.stubName[i] = "";
}
// loop over all stubs
nodule.stubPath = [];
var stubPath, sharedPath;
_.each(nodule.stubName, function(stubName, index) {
stubName = (stubName) ? stubName : nodule.name; // "guess" the nodule name if no stubName is supplied
stubPath = path.join(nodule.path, stubName+'.stub.json');
if (nodule.sharedStubPath)
sharedPath = path.join(process.cwd(), nodule.sharedStubPath, stubName+'.stub.json'); // TODO - make config setting
// look for stob in nodule folder, then shared folder, if no stub is found use API
if (fs.existsSync(stubPath))
nodule.stubPath.push(stubPath);
else if (sharedPath && fs.existsSync(sharedPath))
nodule.stubPath.push(sharedPath);
results++;
if (!errorReceived && results === apiCalls.length) {
debug('parallel done!!');
next(); // if all calls return successfully call the express next()
}
});
});
debug('nodule name = ' + nodule.name + ', stubPath = ' + nodule.stubPath);
}
}
// grabs N number of middleware methods, calls next() when all have returned
// optional args array matches each element in the apiMiddlewares array
function parallel(apiMiddlewares, req, res, next, args) {
debug('parallel started!! # calls:' + apiMiddlewares.length);
var results = 0;
var errorReceived = null;
if (!args) args = [];
apiMiddlewares.forEach(function(middleware, index){
middleware(req, res, function(err){
// if error send full request into error flow (errors are logged at API level)
if (err && !errorReceived) {
errorReceived = err;
next(err);
}
results++;
if (!errorReceived && results === apiMiddlewares.length) {
debug('parallel done!!');
next();
}
}, args[index]);
});
}
};

@@ -10,7 +10,5 @@ var _ = require('lodash');

var api = require('./api.js')(app, yukonConfig);
yukonConfig.noduleDefaults.middlewares = [
config.appPreApi || passThrough,
require('./preApi')(app, yukonConfig, api), // preprocessing logic before APIs are called
require('./preApi')(app, yukonConfig), // preprocessing logic before APIs are called

@@ -36,17 +34,11 @@ config.appDoApi || passThrough,

var defaultConfig = {
// called at the start of every api call if defined
// called at the start of every api or stub call
beforeApiCall: null,
// called after every api call if defined
// called after every api call
afterApiCall: null,
// called after every stub call if defined
// called after every stub call
afterStubCall: null,
// directory to start in for templateNames with a directory in them
templateRoot: 'nodules',
// alternate place to look for stubs than the nodule dir
sharedStubPath: null,
// default debug function

@@ -60,20 +52,7 @@ yukonCustomDebug: function(identifier) {

noduleDefaults: {
// array of middleware functions to be executed on each request, set in yukon init method
middlewares: null,
// use to skip API call(s) altogether
skipApi: false,
// array of optional functions to be called with the apis specified in the nodule (IE - global or semi-global calls like getProfile, getGlobalNav)
// these can be conditional per nodule/request if set in the appDoApi middleware
apiCalls: [],
// arguments (if needed) to go with the apiCalls above
apiAgs: [],
// Properties inherited from nodule.js (see nodule conf (TODO:link here) as these may get out of date):
// middlewares (REQUIRED) - array of (or function which returns array of) middleware functions which will be called in order for each nodule on each express request
// route (REQUIRED) - needs to be defined in each nodule, and be unique
// routeVerb - (default:get)
// routeIndex - (default:0)
// middlewares - array of middleware functions to be executed on each request, defined in yukon module init

@@ -83,60 +62,59 @@ // NOTE: the params below call be mutated in the preProcessor using this.myParam notation

// app looks for [nodule name].[ templateExt ] if not specified and request is not JSON
// MAGIC ALERT: if template name is null, the framework looks for [nodule name].templateExt
// first in the nodule folder, then in the shared template folder
templateName: null,
// app looks for templates with the same filename + this extension
// the framework looks for templates with the template name + this extension
templateExt: '.jade',
// can be array or string - path to API server, can be used to over-ride default
apiHost: null,
// 'html', 'json' only current values - use this to force any nodule to behave like a json or html call regardless of naming conventions or directory conventions
contentType: null,
// path to API - note: if path ends with a / framework automatically appends req.params.id
// Notes on multiple APIs per component:
// 1. Set the apiPath property to an array instead of a string.
// 2. The app places each API call in res.locals.data1, res.locals.data2, etc.
// 3. If any of the APIs needs params, make sure you put them in the correct slot. IE - apiParams[1] = {indludecta:'true'};
// 4. If an array of stubNames is created - those will be used in place of the corresponding APIs when in stub mode.
// 6. As before, to use the same stub for each call just leave stubName blank (looks for standard name) or specify a custom string.
// 7. As always you can set any of the properties above dynamically inside your preProcessor using the this.[propertyName] nomenclature.
apiPath: null,
// use to manipulate query params or other business logic before api call(s)
preProcessor: function(req, res) { },
// params to send to API server
// If apiVerb is 'post', this can be a deep json object (apiBodyType=json) or a shallow object of name value pairs (apiBodyType=form)
// If using multiple apiPaths, make sure you put the apiParams in the correct slot. IE - apiParams[1] = {indludecta:'true'};
apiParams: {},
// use to process data returned from the API before calling template or sending back to client as JSON
postProcessor: function(req, res) { },
// NOTE: one important property you usually need to set in the postProcessor is res.renderData
// this is the data sent to the jade template or back to the client as JSON
// MAGIC ALERT: if you don't specify res.renderData the framework sets res.renderData = res.locals.data1
// valid values: get, post, put, del - or array of these if different values are needed for different calls (uses 'del' since delete is a reserved word)
apiVerb: 'get',
// set this.error to an Error() instance to call next(error) inside the preProcessor or postProcessor
error: null,
// valid values: json, form - or array of these if different values are needed for different calls (use 'form' for a standard post submit with name/value pairs - everything else is json body)
apiBodyType: 'json',
// array of apiCalls to call in parallel
// NOTE: global or semi-global calls like getProfile, getGlobalNav, etc. can be added to this array in the appDoApi middleware
apiCalls: [],
},
// set to > 0 (milliseconds) force stub calls to simualate a longer api call - works for API or Stubs
// WARNING: don't forget to turn this off in production mode!
apiSleep: 0,
/// API CALL PROPERTIES ////////////////////////////////////////////////////////////////
/// NOTE: there can be multiple api calls per nodule, all called in parallel
apiDefaults: {
// path to server, can be used to over-ride default
host: null,
// (numeric) - max API return time in ms, needs to be array if more than one API is called, set other values to null to use default - IE - [null,20000,null]
timeout: null,
// MAGIC ALERT: if api path ends with a slash(/), the framework automatically tries to append req.params.id from the express :id wildcard
// as this is a very common REST paradigm
path: null,
// if not specified, app looks for [nodule name].stub.json - looks for stubName first in nodule folder, then in app/shared/stubs
// Note: can be array to use stubs for multiple api calls
stubName: null,
// params to send to API server
// if verb is 'post', this can be a deep json object (apiBodyType=json) or a shallow object of name value pairs (apiBodyType=form)
params: {},
// set true to force api to use stub (IE - if API isn't ready yet)
forceStub: false,
// valid values: get, post, put, del (express uses 'del' since delete is a reserved word)
verb: 'get',
// 'html', 'json' only current values - use this to force any nodule to behave like a json or html call regardless of naming conventions or directory conventions
contentType: null,
// valid values: json, form (use 'form' for a standard post submit with name/value pairs - everything wants json body)
bodyType: 'json',
// set this to an Error() instance to "throw" an error from your nodule - see channel.js for example
error: null,
// (numeric) - max API return time in ms
timeout: null,
// use to manipulate query params or other business logic before api call(s)
preProcessor: function(req, res) { },
// set true to force api to use stub (IE - if API isn't ready yet)
useStub: false,
// use to process data returned from the API before calling template or sending back to client as JSON
postProcessor: function(req, res) { },
// NOTE: one important property you can set in this function is res.renderData - this is the data sent to the jade template or back to the client as JSON
// if you don't specify res.renderData the app uses res.locals.data1
}
// can contain path or just name if in same folder
// MAGIC ALERT: if not specified, app looks for [nodule name].stub.json in nodule folder
stubPath: null,
},
};
{
"name": "yukon",
"version": "0.0.4",
"version": "0.0.5",
"description": "Self-discovering component-based API-driven framework based on express",

@@ -30,5 +30,5 @@ "main": "index.js",

"gitHead": "d30252453e13ddf4b9d52282dd4e56879e4fa4bf",
"_id": "yukon@0.0.2",
"_shasum": "cac4972c51bf63dbb7377a51ded1bb32036f997e",
"_from": "yukon@^0.0.2"
"_id": "yukon@0.0.4",
"_shasum": "98e0eb40b7bddfb0e7f80dd6da0853ccc2e2a9ab",
"_from": "yukon@^0.0.4"
}

@@ -28,3 +28,3 @@ var path = require('path');

res.templatePath = (templateName.indexOf('/') > -1)
? path.join(process.cwd(), config.templateRoot, templateName)
? path.join(process.cwd(), templateName)
: path.join(nodule.path, templateName);

@@ -31,0 +31,0 @@ }

// wraps nodule.preProcessor, called after app-level appPreApi middleware
module.exports = function(app, config, api) {
module.exports = function(app, config) {
return function(req, res, next) {

@@ -8,7 +8,4 @@ config.debug('yukon-preApi')('called');

// TDOO - research if this is somehow a bad idea
req.nodule.callApi = api.callApi; // set ref to api caller for app to use
next();
};
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc