diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d45b4..eb88bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # create-rest-api changelog +## [4.2.1](https://github.com/ivanoff/create-rest-api/tree/4.2.1) (2017-08-25) +[Full Changelog](https://github.com/ivanoff/create-rest-api/compare/4.1.3...4.2.1) + +**What Was Done:** + +- link to second model can be defined before initialization of second model + + +## [4.1.3](https://github.com/ivanoff/create-rest-api/tree/4.1.3) (2017-08-22) +[Full Changelog](https://github.com/ivanoff/create-rest-api/compare/3.0.1...4.1.3) + +**What Was Done:** + +- add authentication and token verification + + ## [3.0.1](https://github.com/ivanoff/create-rest-api/tree/2.2.1) (2017-06-24) [Full Changelog](https://github.com/ivanoff/create-rest-api/compare/2.2.1...3.0.1) diff --git a/README.md b/README.md index ccd3aaa..43292bf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ # Create REST API -### v.4.1.3 +### v.4.2.1 ### Create your REST API from scarch @@ -70,12 +70,12 @@ api.model('comments'); // /comments are free to anyone api.needToken(); // after that etheryfing will be checked for token ( X-Access-Token in headers, use /login to get one ) -api.model('stars'); // for login store use /my/{login}/stars, for group store - /our/{group}/stars, others - /stars - api.model('movies', { // for login store use /my/{login}/movies, for group store - /our/{group}/movies, others - /movies stars: { link: 'stars' }, }); +api.model('stars'); // for login store use /my/{login}/stars, for group store - /our/{group}/stars, others - /stars + api.start(); ``` diff --git a/lib/server.js b/lib/server.js index f388799..2b4fcdb 100644 --- a/lib/server.js +++ b/lib/server.js @@ -62,6 +62,7 @@ module.exports = function () { }); var _this = this; + app._relationsStored = []; // For api controller app.use(function (req, res, next) { @@ -74,6 +75,24 @@ module.exports = function () { app._start = function (HOST, PORT, dbUrl) { + for( let relation of app._relationsStored ) { + let name = relation.shift(); + let model = relation.shift(); + for( let field of Object.keys(model) ) { + if (model[field].hasOwnProperty('link')) { + let needTokenOld = app._needToken; + + app._needToken = app.protected[model[field].link]; + addRelations(name, field, model[field].link, '_id', model[field].type); + + app._needToken = app.protected[name]; + addRelations(model[field].link, '_id', name, field, null, model[field].type); + + app._needToken = needTokenOld; + } + }; + } + // Last route - Cannot GET path app.use(function (req, res, next) { if (res.headersSent) return next(); @@ -116,37 +135,7 @@ module.exports = function () { log.debug('%s model registered', name); - function addRelations(name, field, l, id, type1, type2) { - if (!app.controllers[l]) { - log.error('%s.%s => %s.%s relation failed: model %s not found', name, field, l, id, l); - return; - } - - log.debug('%s.%s => %s.%s relation added', name, field, l, id); - - var extendedName = name + '/:' + name + 'Id/' + l; - if (app._needToken) app.protected[extendedName] = app._needToken; - app.routes[extendedName] = new DefaultRoutes(extendedName, app.controllers[l], app); - - app.relations[extendedName] = { - name: name + 'Id', - table1: name, - field1: field, - table2: l, - field2: id, - type1: type1, - type2: type2, - }; - }; - - if (model) { - Object.keys(model).forEach(function (field) { - if (model[field].hasOwnProperty('link')) { - addRelations(name, field, model[field].link, '_id', model[field].type); - addRelations(model[field].link, '_id', name, field, null, model[field].type); - } - }); - } + if (model) app._relationsStored.push([name, model]); }; app.registerModel = function (name, model) { @@ -184,6 +173,28 @@ module.exports = function () { }); }; + function addRelations(name, field, l, id, type1, type2) { + if (!app.controllers[l]) { + log.error('%s.%s => %s.%s relation failed: model %s not found', name, field, l, id, l); + return; + } + log.debug('%s.%s => %s.%s relation added', name, field, l, id); + + var extendedName = name + '/:' + name + 'Id/' + l; + if (app._needToken) app.protected[extendedName] = app._needToken; + app.routes[extendedName] = new DefaultRoutes(extendedName, app.controllers[l], app); + + app.relations[extendedName] = { + name: name + 'Id', + table1: name, + field1: field, + table2: l, + field2: id, + type1: type1, + type2: type2, + }; + }; + return app; }; diff --git a/package.json b/package.json index f66623a..58cd49f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-rest-api", - "version": "4.1.3", + "version": "4.2.1", "description": "Create REST API", "main": "index.js", "scripts": { diff --git a/test/07.server.js b/test/07.server.js index eaf1f45..b039ed8 100644 --- a/test/07.server.js +++ b/test/07.server.js @@ -41,6 +41,11 @@ app.model('movies', { }); describe('App', function () { + + before(() => { + app._start(null, 8891, dbUrl); + }); + describe('/categories', function () { var id; var idM; diff --git a/test/12.token.js b/test/12.token.js index 3d7870c..f2b7a9a 100644 --- a/test/12.token.js +++ b/test/12.token.js @@ -33,6 +33,11 @@ appT.use(function (req, res, next) { require('../routes/login')(appT); +appT.model('no_link', { + broken: {link: 'no_link_url'} +}); + + appT.model('messages'); appT.needToken({ login: 'users.login', password: 'users.password' }); appT.model('ingredients'); @@ -43,6 +48,10 @@ appT.model('recipe', { describe('Token', function () { var token; + before(() => { + appT._start(null, 8892, dbUrl); + }); + describe('/messages', function () { var id; it('add one', function (done) { @@ -288,6 +297,7 @@ describe('Token', function () { describe('/our/admin/ingredients', function () { var idI; var id; + it('add one', function (done) { chai.request(appT) .post('/our/admin/ingredients') diff --git a/test/15.relations.js b/test/15.relations.js index 6becef3..617e003 100644 --- a/test/15.relations.js +++ b/test/15.relations.js @@ -37,6 +37,10 @@ app.model('news', { describe('News/Types relation', function () { + before(() => { + app._start(null, 8893, dbUrl); + }); + describe('/types and /news', function () { var typeId; var typeId2; diff --git a/test/23.url.secret.js b/test/23.url.secret.js index fa49b60..e8c8a6e 100644 --- a/test/23.url.secret.js +++ b/test/23.url.secret.js @@ -29,13 +29,17 @@ app.use(function (req, res, next) { next(); }); +app.model('shop', { + cars: {link: 'cars'} +}); + app.model('cars'); -app.models.cars.get = (params, next) => { next('manual error'); }; + +app.model('bus'); app.model('trains'); app.models.trains.get = (params, next) => { unknownFunction(); }; - -app.model('bus'); +app.models.trains.post = (params, next) => { next('manual error'); }; app.needToken(); @@ -50,6 +54,48 @@ app.model('secured_no_token'); app._start(null, 8881, dbUrl); +describe('Add related data', function () { + var carId; + var shopId; + it('add car', function (done) { + chai.request('http://127.0.0.1:8881') + .post('/cars') + .send({ model: 'Ford' }) + .end(function (err, res) { + expect(res).to.have.status(201); + expect(res.body).to.have.property('model').eql('Ford'); + expect(res.body).to.have.property('_id'); + carId = res.body._id; + done(); + }); + }); + + it('add shop', function (done) { + chai.request('http://127.0.0.1:8881') + .post('/shop') + .send({ name: 'Ford shop', cars: [carId] }) + .end(function (err, res) { + expect(res).to.have.status(201); + expect(res.body).to.have.property('name').eql('Ford shop'); + expect(res.body).to.have.property('_id'); + shopId = res.body._id; + done(); + }); + }); + + it('get all cars by shopId', function (done) { + chai.request('http://127.0.0.1:8881') + .get('/shop/' + shopId + '/cars') + .end(function (err, res) { + expect(res).to.have.status(200); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.have.property('model').eql('Ford'); + expect(res.body[0]).to.have.property('_id').eql(carId); + done(); + }); + }); +}); + describe('Using Secret', function () { var token; var tokenExpired = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1OTlkY2I3NjM2NTlmODY2Y'