Skip to content

Commit

Permalink
100% coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanoff committed Aug 23, 2017
1 parent 84f09f0 commit 23adb6a
Show file tree
Hide file tree
Showing 25 changed files with 698 additions and 363 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# Create REST API

### v.4.1.1
### v.4.1.3

### Create your REST API from scarch

Expand Down Expand Up @@ -83,12 +83,15 @@ api.start();
; get new token
curl -X POST -H "Content-Type: application/json" -d '{"login":"admin","password":"admin"}' 127.0.0.1:8877/login
; > {"token":"{TOKEN}","login":"admin","group":"admin","name":"Administrator"}
; renew token
curl -X PATCH -H "X-Access-Token: {TOKEN}" 127.0.0.1:8877/login

; add movie owner by login:admin
curl -X POST -H "X-Access-Token: {TOKEN}" -H "Content-Type: application/json" -d '{"name":"Movie 1 admin login"}' 127.0.0.1:8877/my/admin/movies

; get login:admin owner movies
curl -H "X-Access-Token: {TOKEN}" 127.0.0.1:8877/my/admin/movies

; renew token
curl -X PATCH -H "X-Access-Token: {TOKEN}" 127.0.0.1:8877/login
```

## Usage Example with validation
Expand Down
98 changes: 49 additions & 49 deletions controllers/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var async = require('async');
exports = module.exports = function (name, model) {

return {
makeLinks: function(name, id, relations) {
makeLinks: function (name, id, relations) {
var res = {};
res['/' + name + '/' + id] = {
GET: 'self',
Expand All @@ -22,14 +22,15 @@ exports = module.exports = function (name, model) {
};

var relKeys = Object.keys(relations);
relKeys.forEach( function(key) {
if(relations[key].table1 === name)
res['/' + key.replace( ':' + relations[key].name, id)] = {
GET: 'get '+relations[key].table2,
POST: 'add '+relations[key].table2,
DELETE: 'delete '+relations[key].table2,
relKeys.forEach(function (key) {
if (relations[key].table1 === name)
res['/' + key.replace(':' + relations[key].name, id)] = {
GET: 'get ' + relations[key].table2,
POST: 'add ' + relations[key].table2,
DELETE: 'delete ' + relations[key].table2,
};
});

return res;
},

Expand All @@ -40,13 +41,14 @@ exports = module.exports = function (name, model) {
},

updateRelationsDoc: function (rel, doc, type, field) {
if(!rel || !doc || !rel[type] || !rel.data) return doc;
if(rel[type] === 'array') {
if(!doc[rel[field]]) doc[rel[field]] = [];
if (!rel || !doc || !rel[type] || !rel.data) return doc;
if (rel[type] === 'array') {
if (!doc[rel[field]]) doc[rel[field]] = [];
doc[rel[field]].push(rel.data);
} else {
doc[rel[field]] = rel.data;
}

return doc;
},

Expand Down Expand Up @@ -89,9 +91,9 @@ exports = module.exports = function (name, model) {
}
}

if(req.params.login) search.login = req.params.login;
if(req.params.group) search.group = req.params.group;
if(!req.params.group && !req.params.login){
if (req.params.login) search.login = req.params.login;
if (req.params.group) search.group = req.params.group;
if (!req.params.group && !req.params.login) {
search.login = undefined;
search.group = undefined;
}
Expand All @@ -108,16 +110,16 @@ exports = module.exports = function (name, model) {
start: start,
limit: limit,
rel: rel,
}
};

model.get(params, function (err, docs) {
if (err) return req._error.show(err);
if (!docs || !docs[0]) return req._error.NOT_FOUND(name, search);

if(!Object.keys(fields).length || fields._links) {
docs.forEach(function(doc) {
if (!Object.keys(fields).length || fields._links) {
docs.forEach(function (doc) {
doc._links = _this.makeLinks(name, doc._id, req._relations);
})
});
}

res.json(docs);
Expand All @@ -128,7 +130,7 @@ exports = module.exports = function (name, model) {
var _this = this;
var search = { _id: req.params._id };
req._log.debug(req._id, name, 'model.get(', search, ', {} , {} , 0 , 1 , null )');
model.get({search: search, limit: 1}, function (err, docs) {
model.get({ search: search, limit: 1 }, function (err, docs) {
if (err) return req._error.show(err);
if (!docs || !docs[0]) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);

Expand All @@ -144,33 +146,35 @@ exports = module.exports = function (name, model) {
var doc = req.body;

async.waterfall([
function(flow) {
function (flow) {
if (req._options.validation) {
v.validate(model.object, doc, flow);
} else {
flow();
}
},
function(flow) {

function (flow) {
var rel = _this.getRelationsData(req);
doc = _this.updateRelationsDoc(rel, doc, 'type2', 'field2');

if(req.params.login) doc.login = req.params.login;
if(req.params.group) doc.group = req.params.group;
if (req.params.login) doc.login = req.params.login;
if (req.params.group) doc.group = req.params.group;

model.add(doc, flow);
},
], function(err, result, rel) {
], function (err, result, rel) {
if (err) return req._error.DATA_VALIDATION_ERROR(err);

// Update related document current data
var rel = _this.getRelationsData(req);
if (rel && rel.type1) {
var id = rel.data;
var m = req.models[rel.table1];
m.get({search: {_id: id}, limit: 1}, function(err,data){
m.get({ search: { _id: id }, limit: 1 }, function (err, data) {
rel.data = doc._id;
data[0] = _this.updateRelationsDoc(rel, data[0], 'type1', 'field1');
m.update(id, {$set: data[0]}, function(){});
m.update(id, { $set: data[0] }, function () {});
});
}

Expand All @@ -189,10 +193,10 @@ exports = module.exports = function (name, model) {
if (err && req._options.validation) return req._error.DATA_VALIDATION_ERROR(err);

var search = { _id: req.params._id };
if(req.params.login) search.login = req.params.login;
if(req.params.group) search.group = req.params.group;
if (req.params.login) search.login = req.params.login;
if (req.params.group) search.group = req.params.group;

model.get({search: search, limit: 1}, function (err, oldDoc) {
model.get({ search: search, limit: 1 }, function (err, oldDoc) {
if (Array.isArray(oldDoc)) oldDoc = oldDoc[0];

if (err) return req._error.show(err);
Expand All @@ -203,8 +207,8 @@ exports = module.exports = function (name, model) {
model.update(req.params._id, toUpdate, function (err, doc) {
if (err) return req._error.show(err);

if(req.params.login) doc.login = req.params.login;
if(req.params.group) doc.group = req.params.group;
if (req.params.login) doc.login = req.params.login;
if (req.params.group) doc.group = req.params.group;

doc._links = _this.makeLinks(name, doc._id, req._relations);

Expand All @@ -222,44 +226,40 @@ exports = module.exports = function (name, model) {
},

delete: function (req, res, next) {
var rel = this.getRelationsData(req);
if(!rel || !rel.data) {
var search = req.params._id? { _id: req.params._id } : {};

if(req.params.login) search.login = req.params.login;
if(req.params.group) search.group = req.params.group;
var search = {};
if (req.params.login) search.login = req.params.login;
if (req.params.group) search.group = req.params.group;

var rel = this.getRelationsData(req);
if (!rel || !rel.data) {
if (req.params._id) search._id = req.params._id;
model.delete(search, function (err, doc) {
if (err) return req._error.show(err);
if (!doc || !doc.result.n) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);
res.json({ ok: 1, deleted: _.map(doc.ops, '_id'), updated: [] });
});
} else {
var m = req.models[rel.table2];
var search = {};
search[rel.field2] = { $in: [rel.data] };

if(req.params.login) search.login = req.params.login;
if(req.params.group) search.group = req.params.group;

m.get({search: search}, function (err, doc) {
m.get({ search: search }, function (err, doc) {
var deleted = [];
var updated = [];
doc.forEach( function(d) {
doc.forEach(function (d) {
var index = d[rel.field2].indexOf(rel.data);
if (index > -1) {
d[rel.field2].splice(index, 1);
}
if(!d[rel.field2][0]) {
d[rel.field2].splice(index, 1);

if (!d[rel.field2][0]) {
deleted.push(d._id);
m.delete({_id: d._id}, function(err,obj){})
m.delete({ _id: d._id }, function (err, obj) {});
} else {
updated.push(d._id);
m.update(d._id, {$set:{writers:d[rel.field2]}}, function(err,obj){})
m.update(d._id, { $set: { writers: d[rel.field2] } }, function (err, obj) {});
}
});

res.json({ ok: 1, deleted: deleted, updated: updated });
})
});
}
},

Expand Down
63 changes: 31 additions & 32 deletions controllers/login.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
"use strict"
'use strict'
var uuid = require('uuid');
var md5 = require('md5');
var jwt = require('jsonwebtoken');
//var token = require('../lib/token');
var LoginModel = require('../models/login');

exports.login = function(req, res, next) {
var secret = req._config && req._config.token? req._config.token.secret : undefined;
if(!secret) return req._error.NO_TOKEN_SECRET();
exports.login = function (req, res, next) {
var secret = req._config && req._config.token ? req._config.token.secret : undefined;
if (!secret) return req._error.NO_TOKEN_SECRET();

if (!req.body.login || !req.body.password) return req._error.USER_NOT_FOUND();
req.body.password = md5( req.body.password );
req.body.password = md5(req.body.password);

LoginModel.search(req, req.body, function(err, doc){
// if(err) return req._error.show(err);
if(!doc) return req._error.USER_NOT_FOUND();
LoginModel.search(req, req.body, function (err, doc) {
// if(err) return req._error.show(err);
if (!doc) return req._error.USER_NOT_FOUND();

var refreshToken = uuid.v4();

Expand All @@ -24,36 +23,36 @@ exports.login = function(req, res, next) {
login: doc.login,
group: doc.group,
name: doc.name,
}
};

var token = jwt.sign(data, secret, {expiresIn: req._config.token.expire || 60});
var token = jwt.sign(data, secret, { expiresIn: req._config.token.expire || 60 });

LoginModel.update( req, {_id: doc._id}, { _refreshToken: refreshToken } );
LoginModel.update(req, { _id: doc._id }, { _refreshToken: refreshToken });

res.json({
token: token,
login: doc.login,
group: doc.group,
name: doc.name,
_links:{
self:{href: '/token'},
}
})
_links: {
self: { href: '/token' },
},
});
});
};

exports.update = function(req, res, next) {
var secret = req._config && req._config.token? req._config.token.secret : undefined;
if(!secret) return req._error.NO_TOKEN_SECRET();
exports.update = function (req, res, next) {
var secret = req._config && req._config.token ? req._config.token.secret : undefined;
if (!secret) return req._error.NO_TOKEN_SECRET();

var token = req.headers['x-access-token'] || req.body.token || req.query.token;
if(!token) return req._error.NO_TOKEN();
if (!token) return req._error.NO_TOKEN();

var decoded = jwt.decode(token, secret);

LoginModel.search(req, {refreshToken: decoded._refreshToken}, function(err, doc){
// if(err) return req._error.show(err);
if(!doc) return req._error.USER_NOT_FOUND();
LoginModel.search(req, { refreshToken: decoded._refreshToken }, function (err, doc) {
// if(err) return req._error.show(err);
if (!doc) return req._error.USER_NOT_FOUND();

var refreshToken = uuid.v4();

Expand All @@ -63,27 +62,27 @@ exports.update = function(req, res, next) {
login: doc.login,
group: doc.group,
name: doc.name,
}
};

var token = jwt.sign(data, secret, {expiresIn: req._config.token.expire || 60});
var token = jwt.sign(data, secret, { expiresIn: req._config.token.expire || 60 });

LoginModel.update( req, { _id: doc._id }, { _refreshToken: refreshToken } );
LoginModel.update(req, { _id: doc._id }, { _refreshToken: refreshToken });

res.json({
token: token,
login: doc.login,
group: doc.group,
name: doc.name,
_links:{
self:{href: '/token'},
}
})
_links: {
self: { href: '/token' },
},
});
});
};

exports.info = function(req, res, next) {
exports.info = function (req, res, next) {
var token = req.headers['x-access-token'] || req.body.token || req.query.token;
if(!token) return req._error.NO_TOKEN();
if (!token) return req._error.NO_TOKEN();

var decoded = jwt.decode(token, req._config.token.secret);
res.json(decoded);
Expand Down
21 changes: 11 additions & 10 deletions example/index2.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ var config = require('./lib/config');

//var Api = require('create-rest-api');
var Api = require('../');

//var api = new Api(config, {validation: true});
const api = new Api({
listen : {
host : '127.0.0.1',
port : 8877
listen: {
host: '127.0.0.1',
port: 8877,
},
db : {
mongo : {
url : '127.0.0.1',
port : 27017,
name : 'test'
}
db: {
mongo: {
url: '127.0.0.1',
port: 27017,
name: 'test',
},
},
}, {validation: true});
}, { validation: true });

api.model('categories', {
name: { type: 'string', required: true },
Expand Down

0 comments on commit 23adb6a

Please sign in to comment.