From 78e179eb6b6024f6e0b33e70d5a10d164e07f51c Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 22 Jun 2016 11:59:11 -0400 Subject: [PATCH 1/6] feat(gen:endpoint): change PUT to do an upsert instead of an update --- templates/endpoint/basename.controller.js | 29 +++++++++++++++++++++++ templates/endpoint/index.js | 2 +- templates/endpoint/index.spec.js | 6 ++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/templates/endpoint/basename.controller.js b/templates/endpoint/basename.controller.js index 24c3946c3..aed5a0288 100644 --- a/templates/endpoint/basename.controller.js +++ b/templates/endpoint/basename.controller.js @@ -22,6 +22,18 @@ function respondWithResult(res, statusCode) { }; } +function saveUpsert(new) { + return function(entity) { + <%_ if(filters.mongooseModels) { -%> + var updated = _.assignIn(entity, updates); + return updated.save(); + <%_ } -%> + <%_ if(filters.sequelizeModels) { -%> + return entity.updateAttributes(updates); + <%_ } -%> + }; +} + function saveUpdates(updates) { return function(entity) { <%_ if(filters.mongooseModels) { -%> @@ -93,6 +105,23 @@ export function create(req, res) { .catch(handleError(res)); } +// Upserts the given <%= classedName %> in the DB at the specified ID +export function upsert(req, res) { + if(req.body._id) { + delete req.body._id; + } + <%_ if(filters.mongooseModels) { -%> + return <%= classedName %>.findOneAndUpdate(req.params.id, req.body, {upsert: true, setDefaultsOnInsert: true, runValidators: true}).exec()<% } %> + <%_ if(filters.sequelizeModels) { -%> + return <%= classedName %>.upsert(req.body, { + where: { + _id: req.params.id + } + })<% } %> + .then(respondWithResult(res)) + .catch(handleError(res)); +} + // Updates an existing <%= classedName %> in the DB export function update(req, res) { if(req.body._id) { diff --git a/templates/endpoint/index.js b/templates/endpoint/index.js index 26dc430dd..e23dac6f0 100644 --- a/templates/endpoint/index.js +++ b/templates/endpoint/index.js @@ -8,8 +8,8 @@ var router = express.Router(); router.get('/', controller.index);<% if (filters.models) { %> router.get('/:id', controller.show); router.post('/', controller.create); -router.put('/:id', controller.update); router.patch('/:id', controller.update); +router.put('/:id', controller.upsert); router.delete('/:id', controller.destroy);<% } %> module.exports = router; diff --git a/templates/endpoint/index.spec.js b/templates/endpoint/index.spec.js index adfc84b0f..2df1b12d3 100644 --- a/templates/endpoint/index.spec.js +++ b/templates/endpoint/index.spec.js @@ -6,7 +6,7 @@ var <%= cameledName %>CtrlStub = { index: '<%= cameledName %>Ctrl.index'<% if(filters.models) { %>, show: '<%= cameledName %>Ctrl.show', create: '<%= cameledName %>Ctrl.create', - update: '<%= cameledName %>Ctrl.update', + upsert: '<%= cameledName %>Ctrl.upsert', destroy: '<%= cameledName %>Ctrl.destroy'<% } %> }; @@ -58,9 +58,9 @@ describe('<%= classedName %> API Router:', function() { }); describe('PUT <%= route %>/:id', function() { - it('should route to <%= cameledName %>.controller.update', function() { + it('should route to <%= cameledName %>.controller.upsert', function() { <%= expect() %>routerStub.put - .withArgs('/:id', '<%= cameledName %>Ctrl.update') + .withArgs('/:id', '<%= cameledName %>Ctrl.upsert') <%= to() %>.have.been.calledOnce; }); }); From da1938019cefbfeab02640d2cf3075e978733dc5 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Thu, 30 Jun 2016 15:46:06 -0400 Subject: [PATCH 2/6] feat(gen:endpoint): change PATCH to do a patch add fast-json-patch dep --- templates/app/_package.json | 1 + templates/endpoint/basename.controller.js | 35 ++++++++--------------- templates/endpoint/index.js | 2 +- templates/endpoint/index.spec.js | 5 ++-- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/templates/app/_package.json b/templates/app/_package.json index 4645ff2bd..02746b66b 100644 --- a/templates/app/_package.json +++ b/templates/app/_package.json @@ -30,6 +30,7 @@ "errorhandler": "^1.4.2", "compression": "^1.5.2", "composable-middleware": "^0.3.0", + "fast-json-patch": "^1.0.0", "lodash": "^4.6.1", "lusca": "^1.3.0", "babel-runtime": "^6.6.1", diff --git a/templates/endpoint/basename.controller.js b/templates/endpoint/basename.controller.js index aed5a0288..c1a7b104d 100644 --- a/templates/endpoint/basename.controller.js +++ b/templates/endpoint/basename.controller.js @@ -3,13 +3,14 @@ * GET <%= route %> -> index<% if(filters.models) { %> * POST <%= route %> -> create * GET <%= route %>/:id -> show - * PUT <%= route %>/:id -> update + * PUT <%= route %>/:id -> upsert + * PATCH <%= route %>/:id -> patch * DELETE <%= route %>/:id -> destroy<% } %> */ 'use strict';<% if(filters.models) { %> -import _ from 'lodash';<% if(filters.mongooseModels) { %> +import jsonpatch from 'fast-json-patch';<% if(filters.mongooseModels) { %> import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequelizeModels) { %> import {<%= classedName %>} from '<%= relativeRequire(config.get('registerModelsFile')) %>';<% } %> @@ -22,27 +23,15 @@ function respondWithResult(res, statusCode) { }; } -function saveUpsert(new) { +function patchUpdates(patches) { return function(entity) { - <%_ if(filters.mongooseModels) { -%> - var updated = _.assignIn(entity, updates); - return updated.save(); - <%_ } -%> - <%_ if(filters.sequelizeModels) { -%> - return entity.updateAttributes(updates); - <%_ } -%> - }; -} + try { + jsonpatch.apply(entity, patches, /*validate*/ true); + } catch(err) { + return Promise.reject(err); + } -function saveUpdates(updates) { - return function(entity) { - <%_ if(filters.mongooseModels) { -%> - var updated = _.merge(entity, updates); - return updated.save(); - <%_ } -%> - <%_ if(filters.sequelizeModels) { -%> - return entity.updateAttributes(updates); - <%_ } -%> + return entity.save(); }; } @@ -123,7 +112,7 @@ export function upsert(req, res) { } // Updates an existing <%= classedName %> in the DB -export function update(req, res) { +export function patch(req, res) { if(req.body._id) { delete req.body._id; } @@ -134,7 +123,7 @@ export function update(req, res) { } })<% } %> .then(handleEntityNotFound(res)) - .then(saveUpdates(req.body)) + .then(patchUpdates(req.body)) .then(respondWithResult(res)) .catch(handleError(res)); } diff --git a/templates/endpoint/index.js b/templates/endpoint/index.js index e23dac6f0..8db1adc32 100644 --- a/templates/endpoint/index.js +++ b/templates/endpoint/index.js @@ -8,8 +8,8 @@ var router = express.Router(); router.get('/', controller.index);<% if (filters.models) { %> router.get('/:id', controller.show); router.post('/', controller.create); -router.patch('/:id', controller.update); router.put('/:id', controller.upsert); +router.patch('/:id', controller.patch); router.delete('/:id', controller.destroy);<% } %> module.exports = router; diff --git a/templates/endpoint/index.spec.js b/templates/endpoint/index.spec.js index 2df1b12d3..07b343b6a 100644 --- a/templates/endpoint/index.spec.js +++ b/templates/endpoint/index.spec.js @@ -7,6 +7,7 @@ var <%= cameledName %>CtrlStub = { show: '<%= cameledName %>Ctrl.show', create: '<%= cameledName %>Ctrl.create', upsert: '<%= cameledName %>Ctrl.upsert', + patch: '<%= cameledName %>Ctrl.patch', destroy: '<%= cameledName %>Ctrl.destroy'<% } %> }; @@ -66,9 +67,9 @@ describe('<%= classedName %> API Router:', function() { }); describe('PATCH <%= route %>/:id', function() { - it('should route to <%= cameledName %>.controller.update', function() { + it('should route to <%= cameledName %>.controller.patch', function() { <%= expect() %>routerStub.patch - .withArgs('/:id', '<%= cameledName %>Ctrl.update') + .withArgs('/:id', '<%= cameledName %>Ctrl.patch') <%= to() %>.have.been.calledOnce; }); }); From 185a47eb85e2f877596c57e002a09f32d38b8f15 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 19 Jul 2016 11:26:38 -0400 Subject: [PATCH 3/6] fix(endpoint:test): fix PUT test for upsert --- templates/endpoint/basename.integration.js | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/templates/endpoint/basename.integration.js b/templates/endpoint/basename.integration.js index c651766d1..125e10854 100644 --- a/templates/endpoint/basename.integration.js +++ b/templates/endpoint/basename.integration.js @@ -105,9 +105,27 @@ describe('<%= classedName %> API:', function() { updated<%= classedName %> = {}; }); - it('should respond with the updated <%= cameledName %>', function() { - <%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('Updated <%= classedName %>'); - <%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!'); + it('should respond with the original <%= cameledName %>', function() { + <%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('New <%= classedName %>'); + <%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the brand new <%= cameledName %>!!!'); + }); + + it('should respond with the updated <%= cameledName %> on a subsequent GET', function(done) { + request(app) + .get('<%= route %>/' + new<%= classedName %>._id) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if(err) { + return done(err); + } + let <%= cameledName %> = res.body; + + <%= expect() %><%= cameledName %>.name<%= to() %>.equal('Updated <%= classedName %>'); + <%= expect() %><%= cameledName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!'); + + done(); + }); }); }); From efb69f39310d641aca529a4c05a2f8e11cc595e7 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 19 Jul 2016 11:43:43 -0400 Subject: [PATCH 4/6] test(endpoint): add integration test for PATCH --- templates/endpoint/basename.integration.js | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/templates/endpoint/basename.integration.js b/templates/endpoint/basename.integration.js index 125e10854..b31b4002f 100644 --- a/templates/endpoint/basename.integration.js +++ b/templates/endpoint/basename.integration.js @@ -129,6 +129,37 @@ describe('<%= classedName %> API:', function() { }); }); + describe('PATCH <%= route %>/:id', function() { + var patched<%= classedName %>; + + beforeEach(function(done) { + request(app) + .patch('<%= route %>/' + new<%= classedName %>._id) + .send([ + { "op": "replace", "path": "/name", value: 'Patched <%= classedName %>' }, + { "op": "replace", "path": "/info", value: 'This is the patched <%= cameledName %>!!!' } + ]) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if(err) { + return done(err); + } + patched<%= classedName %> = res.body; + done(); + }); + }); + + afterEach(function() { + patched<%= classedName %> = {}; + }); + + it('should respond with the patched <%= cameledName %>', function() { + <%= expect() %>patched<%= classedName %>.name<%= to() %>.equal('Patched <%= classedName %>'); + <%= expect() %>patched<%= classedName %>.info<%= to() %>.equal('This is the patched <%= cameledName %>!!!'); + }); + }); + describe('DELETE <%= route %>/:id', function() { it('should respond with 204 on successful removal', function(done) { request(app) From 3fd494c7b91b51b0c92a9d9c15e74125242c147c Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 19 Jul 2016 11:58:06 -0400 Subject: [PATCH 5/6] style(endpoint): lint fixes --- templates/endpoint/basename.integration.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/endpoint/basename.integration.js b/templates/endpoint/basename.integration.js index b31b4002f..9f5db9738 100644 --- a/templates/endpoint/basename.integration.js +++ b/templates/endpoint/basename.integration.js @@ -58,7 +58,7 @@ describe('<%= classedName %> API:', function() { beforeEach(function(done) { request(app) - .get('<%= route %>/' + new<%= classedName %>._id) + .get(`<%= route %>/${new<%= classedName %>._id}`) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { @@ -85,7 +85,7 @@ describe('<%= classedName %> API:', function() { beforeEach(function(done) { request(app) - .put('<%= route %>/' + new<%= classedName %>._id) + .put(`<%= route %>/${new<%= classedName %>._id}`) .send({ name: 'Updated <%= classedName %>', info: 'This is the updated <%= cameledName %>!!!' @@ -112,7 +112,7 @@ describe('<%= classedName %> API:', function() { it('should respond with the updated <%= cameledName %> on a subsequent GET', function(done) { request(app) - .get('<%= route %>/' + new<%= classedName %>._id) + .get(`<%= route %>/${new<%= classedName %>._id}`) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { @@ -120,7 +120,7 @@ describe('<%= classedName %> API:', function() { return done(err); } let <%= cameledName %> = res.body; - + <%= expect() %><%= cameledName %>.name<%= to() %>.equal('Updated <%= classedName %>'); <%= expect() %><%= cameledName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!'); @@ -134,10 +134,10 @@ describe('<%= classedName %> API:', function() { beforeEach(function(done) { request(app) - .patch('<%= route %>/' + new<%= classedName %>._id) + .patch(`<%= route %>/${new<%= classedName %>._id}`) .send([ - { "op": "replace", "path": "/name", value: 'Patched <%= classedName %>' }, - { "op": "replace", "path": "/info", value: 'This is the patched <%= cameledName %>!!!' } + { op: 'replace', path: '/name', value: 'Patched <%= classedName %>' }, + { op: 'replace', path: '/info', value: 'This is the patched <%= cameledName %>!!!' } ]) .expect(200) .expect('Content-Type', /json/) @@ -163,7 +163,7 @@ describe('<%= classedName %> API:', function() { describe('DELETE <%= route %>/:id', function() { it('should respond with 204 on successful removal', function(done) { request(app) - .delete('<%= route %>/' + new<%= classedName %>._id) + .delete(`<%= route %>/${new<%= classedName %>._id}`) .expect(204) .end(err => { if(err) { @@ -175,7 +175,7 @@ describe('<%= classedName %> API:', function() { it('should respond with 404 when <%= cameledName %> does not exist', function(done) { request(app) - .delete('<%= route %>/' + new<%= classedName %>._id) + .delete(`<%= route %>/${new<%= classedName %>._id}`) .expect(404) .end(err => { if(err) { From 69dc2a02ffcf22dbfff9934406fa4eb7aa27e9f9 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 20 Jul 2016 11:27:41 -0400 Subject: [PATCH 6/6] fix(gen:test): increase exec maxBuffer to 500KB --- src/test/test-helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test-helpers.js b/src/test/test-helpers.js index 3bf005ab3..16978ab07 100644 --- a/src/test/test-helpers.js +++ b/src/test/test-helpers.js @@ -33,7 +33,7 @@ export function copyAsync(src, dest) { */ export function runCmd(cmd) { return new Promise((resolve, reject) => { - exec(cmd, {}, function(err, stdout, stderr) { + exec(cmd, {maxBuffer: 1024 * 500}, function(err, stdout, stderr) { if(err) { console.error(stdout); return reject(err);