Skip to content

Commit 3f1ce47

Browse files
authored
Merge pull request #30 from dynamiccast/relationships
Add experimental support for relationships
2 parents 526d622 + 44b9c4d commit 3f1ce47

File tree

17 files changed

+436
-19
lines changed

17 files changed

+436
-19
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ This will expect sails Model attributes keys to follow the camelCase naming conv
8686
- [X] DELETE resource
8787
- [X] PATCH resource
8888
- [X] Return proper error if any
89-
- [ ] Relationships
89+
- Relationships
90+
- [X] One to one
91+
- [ ] One way associations
92+
- [ ] Many to many
93+
- [ ] One to many
94+
- [X] Through relationships
9095
- [ ] Fields
9196
- [ ] Sorting
9297
- [ ] Pagination

lib/api/blueprints/_util/actionUtil.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const Promise = require('bluebird');
2+
13
/**
24
* Utility methods used in built-in blueprint actions.
35
*
@@ -81,5 +83,59 @@ module.exports = {
8183
}
8284

8385
return pk;
86+
},
87+
88+
/**
89+
* Parse a JSON API request and set query to be populated with all defined relationships
90+
*
91+
* @param {Object} req node request object
92+
* @param {Object} query query to be executed
93+
* @return {Object} Returns input query with proper populate set
94+
*/
95+
populateEach(req, query) {
96+
97+
if (req.body.data.relationships) {
98+
for (var name in req.body.data.relationships) {
99+
query = query.populate(name);
100+
}
101+
}
102+
103+
return query;
104+
},
105+
106+
/**
107+
* Parse a JSON API relationship object and update given sails record
108+
*
109+
* @param {Object} recordToUpdate a single record for which relationships need to be populated
110+
* @param {String} relationshipName the name of the relationship
111+
* @param {Object} relationships a valid JSON API relationship object (http://jsonapi.org/format/#document-resource-object-relationships)
112+
* @return {Promise[null]} a promise resolving to null if relationship was updated successfully
113+
*/
114+
updateRelationship(recordToUpdate, relationshipName, relationships) {
115+
116+
// First let's remove all current relationships
117+
recordToUpdate[relationshipName].forEach((toDelete) => {
118+
recordToUpdate[relationshipName].remove(toDelete.id);
119+
});
120+
121+
return new Promise((resolve, reject) => {
122+
recordToUpdate.save((err) => {
123+
if (err) {
124+
return reject(err);
125+
}
126+
127+
relationships.data.forEach((relationship) => {
128+
recordToUpdate[relationshipName].add(relationship.id);
129+
});
130+
131+
recordToUpdate.save((err) => {
132+
if (err) {
133+
return reject(err);
134+
}
135+
136+
return resolve(null);
137+
});
138+
});
139+
})
84140
}
85141
};

lib/api/blueprints/update.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const Promise = require('bluebird');
12
const actionUtil = require( './_util/actionUtil' );
23

34
/**
@@ -43,6 +44,38 @@ module.exports = function updateOneRecord(req, res) {
4344
return res.notFound();
4445
}
4546

46-
return res.ok(updatedRecord);
47+
let query = Model.findOne(pk);
48+
query = actionUtil.populateEach(req, query);
49+
return query.exec((err, updatedRecord) => {
50+
51+
if (err) {
52+
return res.negotiate(err);
53+
}
54+
55+
let relationshipsToUpdate = [];
56+
57+
// Let's update relationships if any
58+
if (req.body.data.relationships) {
59+
for (var name in req.body.data.relationships) {
60+
61+
let relationships = req.body.data.relationships[name];
62+
63+
relationshipsToUpdate.push(actionUtil.updateRelationship(updatedRecord, name, relationships));
64+
}
65+
Promise.all(relationshipsToUpdate)
66+
.then(() => {
67+
query = Model.findOne(pk);
68+
query = actionUtil.populateEach(req, query);
69+
return query.exec((err, updatedRecord) => {
70+
if (err) {
71+
return res.negotiate(err);
72+
}
73+
return res.ok(updatedRecord);
74+
});
75+
});
76+
} else {
77+
return res.ok(updatedRecord);
78+
}
79+
});
4780
});
4881
};

lib/api/services/JsonApiService.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,33 @@ module.exports = {
1818
destroyOneRecord: destroyOneRecord,
1919
updateOneRecord: updateOneRecord,
2020

21+
init: function() {
22+
23+
for (let name in sails.models) {
24+
25+
let model = sails.models[name];
26+
let associations = {};
27+
28+
sails.log.verbose("Registering model " + name + " as " + pluralize(model.identity));
29+
model.associations.forEach((association) => {
30+
31+
let alias = association['alias'];
32+
let type = association[association['type']];
33+
34+
sails.log.verbose(name + " has a relation with " + type + " (as " + alias + ")");
35+
associations[alias] = {
36+
type: pluralize(type)
37+
};
38+
});
39+
40+
Serializer.register(pluralize(model.identity), {
41+
id: model.primaryKey || 'id',
42+
convertCase: this.getAttributesSerializedCaseSetting(),
43+
relationships: associations
44+
});
45+
};
46+
},
47+
2148
getAttributesSerializedCaseSetting: function() {
2249

2350
var caseSetting = undefined;
@@ -101,20 +128,6 @@ module.exports = {
101128

102129
serialize: function(modelName, data) {
103130

104-
let Model = this._getModelObjectFromModelName(modelName);
105-
let id = 'id';
106-
107-
if (Model) {
108-
id = Model.primaryKey;
109-
} else {
110-
sails.log.warn('Model ' + modelName + ' could not be found by sails-json-api-blueprints');
111-
}
112-
113-
Serializer.register(modelName, {
114-
convertCase: this.getAttributesSerializedCaseSetting(),
115-
id: id
116-
});
117-
118131
var returnedValue = Serializer.serialize(modelName, data);
119132
delete returnedValue.jsonapi; // Let's ignore the version for now
120133

lib/hook.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ module.exports = function(sails) {
167167

168168
bindShadowRoutes: function() {
169169

170+
JsonApiService.init();
170171
var logWarns = function(warns) {
171172
sails.log.blank();
172173
_.each(warns, function (warn) {

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sails-json-api-blueprints",
3-
"version": "0.10.2",
3+
"version": "0.11.0",
44
"description": "Blueprints to turn a Sails.js API into a JSON API",
55
"main": "index.js",
66
"scripts": {
@@ -25,7 +25,8 @@
2525
},
2626
"homepage": "https://github.com/dynamiccast/sails-json-api-blueprints#readme",
2727
"dependencies": {
28-
"json-api-serializer": "1.1.0",
28+
"bluebird": "^3.4.1",
29+
"json-api-serializer": "1.1.2",
2930
"jsonapi-validator": "^2.0.0",
3031
"lodash": "^4.13.1",
3132
"pluralize": "^2.0.0",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* HouseController
3+
*
4+
* @description :: Server-side logic for managing houses
5+
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
6+
*/
7+
8+
module.exports = {
9+
10+
};
11+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* HusbandController
3+
*
4+
* @description :: Server-side logic for managing husbands
5+
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
6+
*/
7+
8+
module.exports = {
9+
10+
find: function(req, res) {
11+
12+
Husband.find()
13+
.populate("wife")
14+
.then(res.ok);
15+
}
16+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* PetController
3+
*
4+
* @description :: Server-side logic for managing Pets
5+
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
6+
*/
7+
8+
module.exports = {
9+
10+
find: function(req, res) {
11+
12+
Pet.find()
13+
.populate('homes')
14+
.then(res.ok);
15+
}
16+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* WifeController
3+
*
4+
* @description :: Server-side logic for managing wives
5+
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
6+
*/
7+
8+
module.exports = {
9+
10+
};
11+

0 commit comments

Comments
 (0)