Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nodecaf

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodecaf - npm Package Compare versions

Comparing version 0.7.0 to 0.7.1

15

CHANGELOG.md

@@ -8,2 +8,16 @@ # Nodecaf Changelog

## [v0.7.1] - 2019-07-08
### Added
- a function to apply config files on top of each other
### Fixed
- type filtering to not require content-type from requests without body
### Changed
- parser to ignore request body when no content-length is present
### Removed
- the settings argument that used to be passed to user init function
## [v0.7.0] - 2019-07-07

@@ -144,1 +158,2 @@

[v0.7.0]: https://gitlab.com/GCSBOSS/nodecaf/-/tags/v0.7.0
[v0.7.1]: https://gitlab.com/GCSBOSS/nodecaf/-/tags/v0.7.1s

43

lib/app-server.js

@@ -9,3 +9,4 @@ const fs = require('fs');

const { parseTypes } = require('./parse-types');
const setupLogger = require('./logger');
const { setupLogger, logRequest } = require('./logger');
const loadConf = require('./conf-loader');
const errors = require('./errors');

@@ -20,2 +21,8 @@ const HTTP_VERBS = ['get', 'post', 'patch', 'put', 'head'];

function noAPI(req, res){
res.statusCode = 404;
res.write('This Nodecaf server has no API setup.');
res.end();
}
/* o\

@@ -28,18 +35,9 @@ Application Server to be instanced by users. Contain the basic REST

constructor(settings){
this.settings = settings || {};
this.afterStop = this.beforeStart = this.onRouteError = noop;
this.exposed = {};
this.name = this.settings.name || 'express';
this.version = this.settings.version || '0.0.0';
this.express = express();
this.express.app = this;
this.server = null;
this.accepts = false;
this.express = noAPI;
this.setup(settings);
// Setup logger.
setupLogger.bind(this)(this.settings.log);
// Prepare Express middleware.
this.express.use(compression());
// Create adapted versions of all Express routing methods.

@@ -54,2 +52,17 @@ this.routerFuncs = HTTP_VERBS.reduce( (o, method) =>

setup(objectOrPath, type){
this.settings = this.settings || {};
if(typeof objectOrPath == 'string')
objectOrPath = loadConf(objectOrPath, type);
this.settings = { ...this.settings, ...objectOrPath };
this.name = this.settings.name || 'express';
this.version = this.settings.version || '0.0.0';
// Setup logger.
setupLogger.bind(this)(this.settings.log);
}
/* o\

@@ -70,2 +83,8 @@ Define a whitelist of accepted request body mime-types for all routes

api(callback){
// Prepare Express and middleware.
this.express = express();
this.express.app = this;
this.express.use(compression());
this.express.use(logRequest.bind(this));
callback.bind(this)(this.routerFuncs);

@@ -72,0 +91,0 @@ this.express.use(routeNotFoundHandler);

@@ -37,3 +37,4 @@ const path = require('path');

let settings = loadConf(input.confType, input.confPath || false);
if(input.confPath)
var settings = loadConf(input.confPath, input.confType);

@@ -40,0 +41,0 @@ let doc = new APIDoc(settings);

@@ -5,3 +5,2 @@

const YAML = require('yaml');
const path = require('path');

@@ -13,8 +12,7 @@ const loaders = {

module.exports = function(type, conf){
conf = conf || process.env.CONF_FILE || false;
module.exports = function loadConf(conf, type){
type = type || 'toml';
// Bale if not conf file is informed.
if(!conf)
return {};
if(typeof loaders[type] !== 'function')
throw new Error('Conf type not supported: ' + type);

@@ -25,8 +23,5 @@ // Attempt loading said conf file.

}
// DOES NOT throw exception if conf file is not found.
catch(e){
console.log('Could not read conf file at: ' + path.resolve(conf));
return {};
throw new Error('Couldn\'t open conf file: ' + conf);
}
}
const bunyan = require('bunyan');
module.exports = function setupLogger(conf){
conf = conf || {};
let level = conf.level || bunyan.INFO;
module.exports = {
// Prepare logger instance.
this.log = bunyan.createLogger({
name: this.name,
serializers: bunyan.stdSerializers
});
setupLogger(conf){
conf = conf || {};
let level = conf.level || bunyan.INFO;
// Desable logging if no output method is defined.
if(!conf.file && !conf.stream){
this.log.level(bunyan.FATAL + 1);
return;
}
// Prepare logger instance.
this.log = bunyan.createLogger({
name: this.name,
serializers: bunyan.stdSerializers
});
// Remove default stdout stream.
this.log.streams.shift();
// Desable logging if no output method is defined.
if(!conf.file && !conf.stream){
this.log.level(bunyan.FATAL + 1);
return;
}
// if 'stream' is set.
if(conf.stream)
this.log.addStream({ stream: conf.stream, level: level });
// Remove default stdout stream.
this.log.streams.shift();
// If 'file' is set.
else
this.log.addStream({ path: conf.file, level: level });
// if 'stream' is set.
if(conf.stream)
this.log.addStream({ stream: conf.stream, level: level });
// Setup logging for every request.
this.express.use((req, res, next) => {
// If 'file' is set.
else
this.log.addStream({ path: conf.file, level: level });
},
logRequest(req, res, next){
this.log.debug({ req: req }, 'REQUEST');
next();
});
}
}

@@ -18,3 +18,6 @@ const os = require('os');

// this => app
if(!custom && !this.accepts)
let filter = custom || this.accepts || false;
if(!filter || !req.headers['content-length'])
return next();

@@ -26,4 +29,3 @@

let arr = custom || this.accepts;
if(!arr.includes(ct))
if(!filter.includes(ct))
return next(errors.BadRequest('Unsupported content type \'' + ct + '\''));

@@ -35,2 +37,5 @@

async function parse(req, res, next){
req.body = '';
if(!req.headers['content-length'])
return next();

@@ -41,3 +46,3 @@ try{

catch(e){
ct = { type: 'plain/text', parameters: { charset: 'utf8' } };
ct = { type: 'text/plain', parameters: { charset: 'utf8' } };
}

@@ -50,3 +55,3 @@

length: req.headers['content-length'],
encoding: ct.parameters.charset
encoding: ct.parameters.charset || 'utf8'
});

@@ -53,0 +58,0 @@ next();

const assert = require('assert');
const AppServer = require('./app-server');
const loadConf = require('./conf-loader');

@@ -29,8 +28,9 @@ /* istanbul ignore next */

// Load conf and inputs it on the app class.
let settings = loadConf(confType || 'toml', confPath || false);
let app = init(settings);
let app = init();
assert(app instanceof AppServer);
if(confPath)
app.setup(confPath, confType);
// Handle signals.
let debug = settings.debug || false;
let debug = app.settings.debug || false;
process.on('SIGINT', term.bind(null, app, debug));

@@ -37,0 +37,0 @@ process.on('SIGTERM', term.bind(null, app, debug));

{
"name": "nodecaf",
"version": "0.7.0",
"version": "0.7.1",
"description": "Nodecaf is an Express framework for developing REST APIs in a quick and convenient manner.",

@@ -53,4 +53,5 @@ "main": "lib/main.js",

"swagger-parser": "^7.0.1",
"tempper": "^0.1.0",
"wtfnode": "^0.8.0"
}
}

@@ -44,4 +44,4 @@ # [Nodecaf](https://gitlab.com/GCSBOSS/nodecaf)

module.exports = function init(conf){
let app = new AppServer(conf);
module.exports = function init(){
let app = new AppServer();

@@ -160,12 +160,12 @@ // Expose things to all routes putting them in the 'shared' object.

The data in the config file can be accessed in `lib/main.js` through the first
parameter of the exported `init` function:
The config data can be passed as an object to the app constructor in `lib/main.js`:
```js
module.exports = function init(conf){
console.log(conf);
module.exports = function init(){
let conf = { key: 'value' };
let app = new AppServer(conf);
}
```
You can also use the config data through [it's handler arg](#handler-args) in
You can use the config data through [it's handler arg](#handler-args) in
all route handlers as follows:

@@ -175,6 +175,24 @@

post('/foo', function({ conf }){
console.log(conf.myField);
console.log(conf.key); //=> 'value'
});
```
#### Layered Configs
You can also use the `app.setup` to add a given configuration
file or object on top of the current one as follows:
```js
app.setup('/path/to/settings.toml');
app.setup('/path/to/settings.yaml', 'yaml');
app.setup({ key: 'value' });
app.setup({ key: 'new-value', foo: 'bar' });
```
Layering is useful, for example, to keep a **default** settings file in your server
source code to be overwritten by your user's.
### Logging

@@ -181,0 +199,0 @@

//const wtf = require('wtfnode');
const assert = require('assert');
const Tempper = require('tempper');
const fs = require('fs');
const os = require('os');
const assertPathExists = p => fs.existsSync(p);
const assertPathExists = p => assert(fs.existsSync(p));
describe('CLI: nodecaf', () => {
var resDir, projDir;
var tmp;
before(function(){
projDir = process.cwd();
process.chdir('./test');
resDir = process.cwd() + '/res/';
tmp = new Tempper();
});
after(function(){
process.chdir(projDir);
tmp.clear();
process.chdir('..');
});

@@ -24,13 +23,9 @@

const init = require('../lib/cli/init');
let tdir;
beforeEach(function(){
let suffix = Math.random() * 1e3;
tdir = os.tmpdir + '/' + String(new Date()).replace(/\D/g, '') + suffix + '/';
fs.mkdirSync(tdir);
process.chdir(tdir);
afterEach(function(){
tmp.refresh();
});
it('Should fail when unsupported conf type is sent', () => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
tmp.addFile('res/test-package.json', './package.json');
assert.throws( () =>

@@ -45,7 +40,7 @@ init({ confPath: 'foo', confType: 'baz' }), /type not supported/g );

it('Should fail when \'lib\' or \'bin\' directories already exist', () => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
fs.mkdirSync('./bin');
tmp.addFile('res/test-package.json', './package.json');
tmp.mkdir('bin');
assert.throws( () => init({}), /already exists/g);
fs.rmdirSync('./bin');
fs.mkdirSync('./lib');
tmp.mkdir('lib');
assert.throws( () => init({}), /already exists/g);

@@ -55,3 +50,3 @@ });

it('Should generate basic structure files', () => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
tmp.addFile('res/test-package.json', './package.json');
init({});

@@ -61,3 +56,3 @@ assertPathExists('./bin/my-proj.js');

assertPathExists('./lib/api.js');
let pkgInfo = require(tdir + 'package.json');
let pkgInfo = require(tmp.dir + '/package.json');
assert.equal(pkgInfo.bin['my-proj'], 'bin/my-proj.js');

@@ -67,8 +62,8 @@ });

it('Should target specified directory', () => {
fs.mkdirSync('./foo');
fs.copyFileSync(resDir + 'nmless-package.json', './foo/package.json');
tmp.mkdir('foo');
tmp.addFile('res/nmless-package.json', './foo/package.json');
const cli = require('cli');
cli.setArgv(['thing', '-p', './foo']);
init();
let pkgInfo = require(tdir + 'foo/package.json');
let pkgInfo = require(tmp.dir + '/foo/package.json');
assert.equal(pkgInfo.bin['my-app'], 'bin/my-app.js');

@@ -78,5 +73,5 @@ });

it('Should use specified project name', () => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
tmp.addFile('res/test-package.json', './package.json');
init({ name: 'proj-foo' });
let pkgInfo = require(tdir + 'package.json');
let pkgInfo = require(tmp.dir + '/package.json');
assert.equal(pkgInfo.bin['proj-foo'], 'bin/proj-foo.js');

@@ -86,3 +81,3 @@ });

it('Should generate conf file if specified', () => {
fs.copyFileSync(resDir + 'nmless-package.json', './package.json');
tmp.addFile('res/nmless-package.json', './package.json');
init({ confPath: './conf.toml' });

@@ -93,3 +88,3 @@ assertPathExists('./conf.toml');

it('Should generate create conf file dir if it doesn\'t exist', () => {
fs.copyFileSync(resDir + 'nmless-package.json', './package.json');
tmp.addFile('res/nmless-package.json', './package.json');
init({ confPath: './my/conf.toml' });

@@ -103,9 +98,5 @@ assertPathExists('./my/conf.toml');

const SwaggerParser = require('swagger-parser');
let tdir;
beforeEach(function(){
let suffix = Math.random() * 1e3;
tdir = os.tmpdir + '/' + String(new Date()).replace(/\D/g, '') + suffix + '/';
fs.mkdirSync(tdir);
process.chdir(tdir);
afterEach(function(){
tmp.refresh();
});

@@ -118,3 +109,3 @@

it('Should fail when no API file is found', () => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
tmp.addFile('res/test-package.json', './package.json');
const cli = require('cli');

@@ -127,6 +118,7 @@ cli.setArgv(['thing']);

it('Should output a well formed JSON API doc to default file', done => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
fs.copyFileSync(resDir + 'api.js', './api.js');
tmp.addFile('res/test-package.json', './package.json');
tmp.addFile('res/api.js', './api.js');
tmp.addFile('res/conf.toml', './conf.toml');
openapi({ apiPath: './api.js' });
openapi({ apiPath: './api.js', confPath: './conf.toml' });

@@ -137,4 +129,4 @@ SwaggerParser.validate('./output.json', done);

it('Should output a well formed YAML API doc to given file', done => {
fs.copyFileSync(resDir + 'test-package.json', './package.json');
fs.copyFileSync(resDir + 'api.js', './api.js');
tmp.addFile('res/test-package.json', './package.json');
tmp.addFile('res/api.js', './api.js');

@@ -141,0 +133,0 @@ openapi({ apiPath: './api.js', outFile: './outfile.yml' });

@@ -10,14 +10,16 @@ //const wtf = require('wtfnode');

it('Should NOT fail if no conf file is specified', () => {
let obj = loadConf('toml');
assert.deepEqual(obj, {});
it('Should fail if no conf file is specified', () => {
assert.throws( () => loadConf() );
});
it('Should NOT fail if conf file is not found', () => {
let obj = loadConf('toml', './bla');
assert.deepEqual(obj, {});
it('Should fail if conf file is not found', () => {
assert.throws( () => loadConf('./bla') );
});
it('Should fail if given conf type is not supported', () => {
assert.throws( () => loadConf('./test/res/conf.xml', 'xml'), /type not supported/ );
});
it('Should properly load a TOML file and generate an object', () => {
let obj = loadConf('toml', './test/res/conf.toml');
let obj = loadConf('./test/res/conf.toml');
assert.strictEqual(obj.key, 'value');

@@ -27,3 +29,3 @@ });

it('Should properly load an YAML file and generate an object', () => {
let obj = loadConf('yaml', './test/res/conf.yaml');
let obj = loadConf('./test/res/conf.yaml', 'yaml');
assert.strictEqual(obj.key, 'value');

@@ -67,7 +69,2 @@ });

it('Should create the Express server', () => {
let app = new AppServer();
assert.strictEqual(typeof app.express.use, 'function');
});
});

@@ -243,3 +240,3 @@

LOCAL_HOST + 'foo',
{ '--no-auto': true },
{ '--no-auto': true, 'Content-Length': 13 },
'{"foo":"bar"}'

@@ -268,4 +265,36 @@ );

it('Should accept requests without body payload', async () => {
let app = new AppServer();
app.api(function({ post }){
this.accept([ 'urlencoded', 'text/html' ]);
post('/foo', ({ res }) => res.end());
});
await app.start();
let { status } = await post(
LOCAL_HOST + 'foo',
{ '--no-auto': true }
);
assert.strictEqual(status, 200);
await app.stop();
});
});
describe('::setup', () => {
it('Should apply settings on top of existing one', () => {
let app = new AppServer({ key: 'value' });
app.setup({ key: 'value2', key2: 'value' });
assert.strictEqual(app.settings.key, 'value2');
assert.strictEqual(app.settings.key2, 'value');
});
it('Should load form file when path is sent', () => {
let app = new AppServer({ key: 'valueOld' });
app.setup('test/res/conf.toml');
assert.strictEqual(app.settings.key, 'value');
});
});
});

@@ -364,3 +393,3 @@

LOCAL_HOST + 'foobar',
{ '--no-auto': true },
{ '--no-auto': true, 'Content-Length': 13 },
JSON.stringify({foo: 'bar'})

@@ -465,2 +494,18 @@ );

it('Should accept requests without a body payload', async () => {
let app = new AppServer();
app.api(function({ post }){
let acc = accept('text/html');
post('/foo', acc, ({ res }) => res.end());
});
await app.start();
let { status } = await post(
LOCAL_HOST + 'foo',
{ '--no-auto': true },
'{"foo":"bar"}'
);
assert.strictEqual(status, 200);
await app.stop();
});
});

@@ -486,4 +531,3 @@

let app
await run({ init(settings){
assert(typeof settings == 'object');
await run({ init(){
app = new AppServer();

@@ -500,2 +544,16 @@ app.api(function({ get }){

it('Should inject the given conf file', async () => {
let app
await run({ init(){
app = new AppServer();
app.api(function({ get }){
get('/bar', ({ res, conf }) => res.end(conf.key));
});
return app;
}, confPath: 'test/res/conf.toml' });
let { body } = await get(LOCAL_HOST + 'bar');
assert.strictEqual(body, 'value');
await app.stop();
});
});

@@ -502,0 +560,0 @@

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc