Skip to content

Commit f8ce5cb

Browse files
authored
Merge pull request #2033 from angular-fullstack/put-patch
Put patch
2 parents e0ab407 + 69dc2a0 commit f8ce5cb

File tree

6 files changed

+96
-27
lines changed

6 files changed

+96
-27
lines changed

Diff for: src/test/test-helpers.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function copyAsync(src, dest) {
3333
*/
3434
export function runCmd(cmd) {
3535
return new Promise((resolve, reject) => {
36-
exec(cmd, {}, function(err, stdout, stderr) {
36+
exec(cmd, {maxBuffer: 1024 * 500}, function(err, stdout, stderr) {
3737
if(err) {
3838
console.error(stdout);
3939
return reject(err);

Diff for: templates/app/_package.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"errorhandler": "^1.4.2",
3131
"compression": "^1.5.2",
3232
"composable-middleware": "^0.3.0",
33+
"fast-json-patch": "^1.0.0",
3334
"lodash": "^4.6.1",
3435
"lusca": "^1.3.0",
3536
"babel-runtime": "^6.6.1",

Diff for: templates/endpoint/basename.controller.js

+30-12
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
* GET <%= route %> -> index<% if(filters.models) { %>
44
* POST <%= route %> -> create
55
* GET <%= route %>/:id -> show
6-
* PUT <%= route %>/:id -> update
6+
* PUT <%= route %>/:id -> upsert
7+
* PATCH <%= route %>/:id -> patch
78
* DELETE <%= route %>/:id -> destroy<% } %>
89
*/
910

1011
'use strict';<% if(filters.models) { %>
1112

12-
import _ from 'lodash';<% if(filters.mongooseModels) { %>
13+
import jsonpatch from 'fast-json-patch';<% if(filters.mongooseModels) { %>
1314
import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequelizeModels) { %>
1415
import {<%= classedName %>} from '<%= relativeRequire(config.get('registerModelsFile')) %>';<% } %>
1516

@@ -22,15 +23,15 @@ function respondWithResult(res, statusCode) {
2223
};
2324
}
2425

25-
function saveUpdates(updates) {
26+
function patchUpdates(patches) {
2627
return function(entity) {
27-
<%_ if(filters.mongooseModels) { -%>
28-
var updated = _.merge(entity, updates);
29-
return updated.save();
30-
<%_ } -%>
31-
<%_ if(filters.sequelizeModels) { -%>
32-
return entity.updateAttributes(updates);
33-
<%_ } -%>
28+
try {
29+
jsonpatch.apply(entity, patches, /*validate*/ true);
30+
} catch(err) {
31+
return Promise.reject(err);
32+
}
33+
34+
return entity.save();
3435
};
3536
}
3637

@@ -93,8 +94,25 @@ export function create(req, res) {
9394
.catch(handleError(res));
9495
}
9596

97+
// Upserts the given <%= classedName %> in the DB at the specified ID
98+
export function upsert(req, res) {
99+
if(req.body._id) {
100+
delete req.body._id;
101+
}
102+
<%_ if(filters.mongooseModels) { -%>
103+
return <%= classedName %>.findOneAndUpdate(req.params.id, req.body, {upsert: true, setDefaultsOnInsert: true, runValidators: true}).exec()<% } %>
104+
<%_ if(filters.sequelizeModels) { -%>
105+
return <%= classedName %>.upsert(req.body, {
106+
where: {
107+
_id: req.params.id
108+
}
109+
})<% } %>
110+
.then(respondWithResult(res))
111+
.catch(handleError(res));
112+
}
113+
96114
// Updates an existing <%= classedName %> in the DB
97-
export function update(req, res) {
115+
export function patch(req, res) {
98116
if(req.body._id) {
99117
delete req.body._id;
100118
}
@@ -105,7 +123,7 @@ export function update(req, res) {
105123
}
106124
})<% } %>
107125
.then(handleEntityNotFound(res))
108-
.then(saveUpdates(req.body))
126+
.then(patchUpdates(req.body))
109127
.then(respondWithResult(res))
110128
.catch(handleError(res));
111129
}

Diff for: templates/endpoint/basename.integration.js

+56-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('<%= classedName %> API:', function() {
5858

5959
beforeEach(function(done) {
6060
request(app)
61-
.get('<%= route %>/' + new<%= classedName %>._id)
61+
.get(`<%= route %>/${new<%= classedName %>._id}`)
6262
.expect(200)
6363
.expect('Content-Type', /json/)
6464
.end((err, res) => {
@@ -85,7 +85,7 @@ describe('<%= classedName %> API:', function() {
8585
8686
beforeEach(function(done) {
8787
request(app)
88-
.put('<%= route %>/' + new<%= classedName %>._id)
88+
.put(`<%= route %>/${new<%= classedName %>._id}`)
8989
.send({
9090
name: 'Updated <%= classedName %>',
9191
info: 'This is the updated <%= cameledName %>!!!'
@@ -105,16 +105,65 @@ describe('<%= classedName %> API:', function() {
105105
updated<%= classedName %> = {};
106106
});
107107
108-
it('should respond with the updated <%= cameledName %>', function() {
109-
<%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('Updated <%= classedName %>');
110-
<%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!');
108+
it('should respond with the original <%= cameledName %>', function() {
109+
<%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('New <%= classedName %>');
110+
<%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the brand new <%= cameledName %>!!!');
111+
});
112+
113+
it('should respond with the updated <%= cameledName %> on a subsequent GET', function(done) {
114+
request(app)
115+
.get(`<%= route %>/${new<%= classedName %>._id}`)
116+
.expect(200)
117+
.expect('Content-Type', /json/)
118+
.end((err, res) => {
119+
if(err) {
120+
return done(err);
121+
}
122+
let <%= cameledName %> = res.body;
123+
124+
<%= expect() %><%= cameledName %>.name<%= to() %>.equal('Updated <%= classedName %>');
125+
<%= expect() %><%= cameledName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!');
126+
127+
done();
128+
});
129+
});
130+
});
131+
132+
describe('PATCH <%= route %>/:id', function() {
133+
var patched<%= classedName %>;
134+
135+
beforeEach(function(done) {
136+
request(app)
137+
.patch(`<%= route %>/${new<%= classedName %>._id}`)
138+
.send([
139+
{ op: 'replace', path: '/name', value: 'Patched <%= classedName %>' },
140+
{ op: 'replace', path: '/info', value: 'This is the patched <%= cameledName %>!!!' }
141+
])
142+
.expect(200)
143+
.expect('Content-Type', /json/)
144+
.end(function(err, res) {
145+
if(err) {
146+
return done(err);
147+
}
148+
patched<%= classedName %> = res.body;
149+
done();
150+
});
151+
});
152+
153+
afterEach(function() {
154+
patched<%= classedName %> = {};
155+
});
156+
157+
it('should respond with the patched <%= cameledName %>', function() {
158+
<%= expect() %>patched<%= classedName %>.name<%= to() %>.equal('Patched <%= classedName %>');
159+
<%= expect() %>patched<%= classedName %>.info<%= to() %>.equal('This is the patched <%= cameledName %>!!!');
111160
});
112161
});
113162
114163
describe('DELETE <%= route %>/:id', function() {
115164
it('should respond with 204 on successful removal', function(done) {
116165
request(app)
117-
.delete('<%= route %>/' + new<%= classedName %>._id)
166+
.delete(`<%= route %>/${new<%= classedName %>._id}`)
118167
.expect(204)
119168
.end(err => {
120169
if(err) {
@@ -126,7 +175,7 @@ describe('<%= classedName %> API:', function() {
126175
127176
it('should respond with 404 when <%= cameledName %> does not exist', function(done) {
128177
request(app)
129-
.delete('<%= route %>/' + new<%= classedName %>._id)
178+
.delete(`<%= route %>/${new<%= classedName %>._id}`)
130179
.expect(404)
131180
.end(err => {
132181
if(err) {

Diff for: templates/endpoint/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ var router = express.Router();
88
router.get('/', controller.index);<% if (filters.models) { %>
99
router.get('/:id', controller.show);
1010
router.post('/', controller.create);
11-
router.put('/:id', controller.update);
12-
router.patch('/:id', controller.update);
11+
router.put('/:id', controller.upsert);
12+
router.patch('/:id', controller.patch);
1313
router.delete('/:id', controller.destroy);<% } %>
1414

1515
module.exports = router;

Diff for: templates/endpoint/index.spec.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ var <%= cameledName %>CtrlStub = {
66
index: '<%= cameledName %>Ctrl.index'<% if(filters.models) { %>,
77
show: '<%= cameledName %>Ctrl.show',
88
create: '<%= cameledName %>Ctrl.create',
9-
update: '<%= cameledName %>Ctrl.update',
9+
upsert: '<%= cameledName %>Ctrl.upsert',
10+
patch: '<%= cameledName %>Ctrl.patch',
1011
destroy: '<%= cameledName %>Ctrl.destroy'<% } %>
1112
};
1213

@@ -58,17 +59,17 @@ describe('<%= classedName %> API Router:', function() {
5859
});
5960

6061
describe('PUT <%= route %>/:id', function() {
61-
it('should route to <%= cameledName %>.controller.update', function() {
62+
it('should route to <%= cameledName %>.controller.upsert', function() {
6263
<%= expect() %>routerStub.put
63-
.withArgs('/:id', '<%= cameledName %>Ctrl.update')
64+
.withArgs('/:id', '<%= cameledName %>Ctrl.upsert')
6465
<%= to() %>.have.been.calledOnce;
6566
});
6667
});
6768

6869
describe('PATCH <%= route %>/:id', function() {
69-
it('should route to <%= cameledName %>.controller.update', function() {
70+
it('should route to <%= cameledName %>.controller.patch', function() {
7071
<%= expect() %>routerStub.patch
71-
.withArgs('/:id', '<%= cameledName %>Ctrl.update')
72+
.withArgs('/:id', '<%= cameledName %>Ctrl.patch')
7273
<%= to() %>.have.been.calledOnce;
7374
});
7475
});

0 commit comments

Comments
 (0)