options => {
const configs = getConfigurations(options);
const app = createServer(options);
return registerServices(app, options, configs);
}...
## HTTPS
If you want to run dyson over https:// you have to provide a self-signed (or authority-signed) certificate into the `options.https
` the same way it's required for NodeJS HTTPS to work:
``` javascript
const fs = require('fs');
dyson.bootstrap({
configDir: `${__dirname}/dummy`,
port: 3001,
https: {
key: fs.readFileSync(`${__dirname}'/certs/sample.key`),
crt: fs.readFileSync(`${__dirname}/certs/sample.crt`)
}
});
...options => {
const app = express();
let server;
if(options.https) {
server = https.createServer(options.https, app).listen(options.port);
} else {
server = app.listen(options.port);
}
app.set('dyson_server', server);
return app;
}...
const options = {
configDir: path.join(__dirname, 'services'),
port: 8765
};
const configs = dyson.getConfigurations(options),
appBefore = dyson.createServer(options),
appAfter = dyson.registerServices(appBefore, options, configs);
console.log(`Dyson listening at port ${options.port}`);
```
Dyson configuration can also be installed into any Express server:
...options => {
const rawConfigs = loader.load(options.configDir);
return defaults.assignToAll(rawConfigs);
}...
path = require('path');
const options = {
configDir: path.join(__dirname, 'services'),
port: 8765
};
const configs = dyson.getConfigurations(options),
appBefore = dyson.createServer(options),
appAfter = dyson.registerServices(appBefore, options, configs);
console.log(`Dyson listening at port ${options.port}`);
```
Dyson configuration can also be installed into any Express server:
...(app, options, configs) => {
app.set('dyson_options', options);
util.setQuiet(options.quiet);
app.use(cors({ origin: true, credentials: true }));
app.use(rawBody());
app.use(cookieParser());
app.use(favicon(`${__dirname}/favicon.ico`));
const bodyParserOptions = options.bodyParserJsonLimit ? { limit: options.bodyParserJsonLimit } : {};
app.use(bodyParser.json(bodyParserOptions));
const bodyParserUrlOptions = { extended: true };
bodyParserUrlOptions.limit = options.bodyParserUrlencodedLimit ? options.bodyParserUrlencodedLimit : null;
app.use(bodyParser.urlencoded(bodyParserUrlOptions));
Object.keys(configs).forEach(method => {
configs[method].forEach(config => {
if(options.proxy === true && config.proxy !== false) {
// Allows each config file to be bypassed without removing it
util.log('Proxying', method.toUpperCase(), 'service at', config.path);
} else {
const middlewares = [
setConfig(config),
requireParameter,
config.status,
config.callback,
delay(config.delay),
config.render
];
util.log('Registering', method.toUpperCase(), 'service at', config.path);
app[method].apply(app, [config.path].concat(middlewares));
if(method !== 'options') {
app.options(config.path, cors({ origin: true, credentials: true }));
}
}
});
});
if(options.proxy) {
app.all('*', delay(options.proxyDelay), proxy.middleware);
} else {
!util.isTest() && app.all('*', (req, res) => {
console.error(`404 NOT FOUND: ${req.url}`);
res.writeHead(404);
res.end();
});
}
return app;
}...
const options = {
configDir: path.join(__dirname, 'services'),
port: 8765
};
const configs = dyson.getConfigurations(options),
appBefore = dyson.createServer(options),
appAfter = dyson.registerServices(appBefore, options, configs);
console.log(`Dyson listening at port ${options.port}`);
```
Dyson configuration can also be installed into any Express server:
``` javascript
...options => {
if(options.length) {
const [ dir, port ] = options;
const opts = _.defaults(readOptions(dir), {
port: port || 3000,
configDir: dir,
proxy: false,
multiRequest: ',',
quiet: false
});
opts.configDir = path.resolve(opts.configDir);
fs.stat(opts.configDir, (error, stats) => {
if(!error && stats.isDirectory()) {
dyson.bootstrap(opts);
console.info(`Dyson listening at port: ${opts.port}`);
} else {
console.error(`Directory does not exist: ${opts.configDir}`);
}
});
} else {
showHelp();
}
}...
#!/usr/bin/env node
const cli = require('../lib/cli');
cli.execute(process.argv.slice(2));
...(configs, method) => {
configs = _.isArray(configs) ? configs : [configs];
return _.compact(configs.map(config => {
if(!config || !config.path) {
return;
}
// Assign method specific defaults
config = _.defaults(config, defaults[method]);
// Bind each method to the config itself
return _.bindAll(config);
}));
}...
it('should apply defaults (and not overwrite existing values)', () => {
const config = {
path: '/test',
template: {}
};
defaults.assign(config, 'get');
config.should.have.property('cache').and.be.type('boolean');
config.should.have.property('size');
config.should.have.property('collection').and.be.type('boolean');
config.should.have.property('callback').and.be.type('function');
config.should.have.property('path').and.equal(config.path);
...rawConfigs => {
const configs = {};
methods.forEach(method => { configs[method] = assign(rawConfigs[method], method);
});
return configs;
}...
const app = createServer(options);
return registerServices(app, options, configs);
};
const getConfigurations = options => {
const rawConfigs = loader.load(options.configDir);
return defaults.assignToAll(rawConfigs);
};
const createServer = options => {
const app = express();
let server;
if(options.https) {
...callback = function (req, res, next) {
res.body = req.body;
next();
}...
this._counter++;
},
_counter: 0
};
defaults.assign(config, 'get');
config.callback().render();
config._counter.should.equal(2);
});
});
});
...configDir => {
const rawConfigs = requireDir(module, path.resolve(configDir)),
configs = {};
methods.forEach(method => {
configs[method] = _.flattenDeep(findRecursive(rawConfigs, method));
});
return configs;
}...
const bootstrap = options => {
const configs = getConfigurations(options);
const app = createServer(options);
return registerServices(app, options, configs);
};
const getConfigurations = options => {
const rawConfigs = loader.load(options.configDir);
return defaults.assignToAll(rawConfigs);
};
const createServer = options => {
const app = express();
let server;
...(req, path) => {
const options = req.app.get('dyson_options'),
host = req.headers.host.split(':'),
hostname = host[0],
port = host[1],
delimiter = options.multiRequest,
promises = [],
range = isMultiRequest(path, options);
range.split(delimiter).forEach((id, index, list) => {
const d = when.defer(),
url = path.replace(list, id);
let data = '';
promises.push(d.promise);
http.get({ hostname, port, path: url }, res => {
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
d.resolve(JSON.parse(data));
});
}).on('error', error => {
/* eslint-disable no-console */
console.error(error.message);
});
});
return promises;
}...
}).then(data => {
res.body = cache[path] = data;
util.log('Resolving response for', req.method, path);
next();
});
} else {
when.all(multiRequest.doMultiRequest(req, path)).then(data => {
res.body = cache[path] = data;
util.log('Resolving response for:', req.method, path, '(multiRequest)');
next();
});
}
} else {
...(path, options) => {
const delimiter = options.multiRequest;
if(!delimiter) {
return false;
}
return path.split('/').find(fragment => fragment.includes(delimiter));
}...
path = req.url,
exposeRequest = service.exposeRequest || options.exposeRequest && service.exposeRequest !== false;
const templateArgs = exposeRequest ? [req] : [req.params, req.query, req.body, req.cookies, req.headers],
containerArgs = exposeRequest ? [req] : [req.params, req.query];
if(!(service.cache && cache[path])) {
if(!multiRequest.isMultiRequest(path, options)) {
const isCollection = _.isFunction(service.collection) ? service.collection.apply(service, templateArgs) : service.collection,
template = _.isFunction(service.template) ? service.template.apply(service, templateArgs) : service.template;
let promise;
if(!isCollection) {
promise = setValues(template, templateArgs);
} else {
...(req, res) => {
const options = req.app.get('dyson_options'),
proxyHost = options.proxyHost,
proxyPort = options.proxyPort,
proxyURI = `${proxyHost}${proxyPort ? `:${proxyPort}` : ''}${req.url}`;
let readStream;
util.log('Proxying', req.url, 'to', proxyURI);
if(req._body) {
readStream = new Stream.Readable();
readStream._read = function() {
this.push(req.rawBody);
this.push(null);
};
} else {
readStream = req;
}
readStream.pipe(request({
method: req.method,
url: proxyURI,
headers: _.omit(req.headers, ['host'])
}, error => {
if(error) {
console.error(`500 INTERNAL SERVER ERROR: ${proxyURI}`);
console.error(error);
res.writeHead(500);
res.end();
}
})).pipe(res);
}n/a
(req, res, next) => {
const service = res.locals.config,
options = res.app.get('dyson_options'),
path = req.url,
exposeRequest = service.exposeRequest || options.exposeRequest && service.exposeRequest !== false;
const templateArgs = exposeRequest ? [req] : [req.params, req.query, req.body, req.cookies, req.headers],
containerArgs = exposeRequest ? [req] : [req.params, req.query];
if(!(service.cache && cache[path])) {
if(!multiRequest.isMultiRequest(path, options)) {
const isCollection = _.isFunction(service.collection) ? service.collection.apply(service, templateArgs) : service.collection
,
template = _.isFunction(service.template) ? service.template.apply(service, templateArgs) : service.template;
let promise;
if(!isCollection) {
promise = setValues(template, templateArgs);
} else {
const size = _.isFunction(service.size) ? service.size.apply(service, templateArgs) : service.size;
promise = when.map(_.times(parseInt(size, 10)), () => setValues(template, templateArgs));
}
promise.then(data => {
return !service.container ? data : setValues(_.result(service, 'container'), containerArgs.concat([data]), service);
}).then(data => {
res.body = cache[path] = data;
util.log('Resolving response for', req.method, path);
next();
});
} else {
when.all(multiRequest.doMultiRequest(req, path)).then(data => {
res.body = cache[path] = data;
util.log('Resolving response for:', req.method, path, '(multiRequest)');
next();
});
}
} else {
util.log('Resolving response for', req.method, path, '(cached)');
res.body = cache[path];
next();
}
}...
}
};
});
it('should expose request to template', () => {
configDefaults.generate(req, res, next);
sinon.assert.calledWithExactly(res.locals.config.template, req);
});
it('should expose request to container', () => {
...(req, res) => {
res.send(res.body);
}...
this._counter++;
},
_counter: 0
};
defaults.assign(config, 'get');
config.callback().render();
config._counter.should.equal(2);
});
});
});
...(template = null, params, scope) => {
return when.promise(resolve => {
if(!template) return resolve(null);
if(typeof template === 'string') return resolve(template);
const promises = [],
obj = _.isArray(template) ? [] : Object.create(template);
_.forEach(template, (value, key) => {
if(template.hasOwnProperty(key)) {
obj[key] = _.isFunction(value) ? value.apply(scope || obj, params) : _.isPlainObject(value) ? setValues(value, params, obj
) : value;
if(when.isPromiseLike(obj[key])) {
promises.push(obj[key]);
obj[key].then(function(key, value) {
obj[key] = value;
}.bind(obj, key));
}
}
});
when.all(promises).then(() => resolve(obj));
});
}...
describe('dyson.response', () => {
describe('.setValues', () => {
it('should return a promise', () => {
const actual = configDefaults.setValues({});
actual.should.have.property('then');
actual.then.should.be.a.Function;
});
it('should render data based on template', done => {
...() => {
return process.env.NODE_ENV === 'test';
}...
if(options.proxy) {
app.all('*', delay(options.proxyDelay), proxy.middleware);
} else {
!util.isTest() && app.all('*', (req, res) => {
console.error(`404 NOT FOUND: ${req.url}`);
res.writeHead(404);
res.end();
});
}
return app;
...log = function () {
if(!isTest() && !quiet) {
console.log.apply(console, arguments);
}
}...
port: 8765
};
const configs = dyson.getConfigurations(options),
appBefore = dyson.createServer(options),
appAfter = dyson.registerServices(appBefore, options, configs);
console.log(`Dyson listening at port ${options.port}`);
```
Dyson configuration can also be installed into any Express server:
``` javascript
const express = require('express'),
dyson = require('./lib/dyson'),
...isQuiet => {
quiet = typeof isQuiet === 'boolean' ? isQuiet : true;
}...
next();
};
// Register middleware to Express as service for each config (as in: `app.get(config.path, config.callback);`)
const registerServices = (app, options, configs) => {
app.set('dyson_options', options);
util.setQuiet(options.quiet);
app.use(cors({ origin: true, credentials: true }));
app.use(rawBody());
app.use(cookieParser());
app.use(favicon(`${__dirname}/favicon.ico`));
const bodyParserOptions = options.bodyParserJsonLimit ? { limit: options.bodyParserJsonLimit } : {};
...