8000 Bump v1.0.0-alpha.6 - Fix #9 PATCH changes instead of PUT to replace · Wikodit/js-data-jsonapi-light@a5c2855 · GitHub
[go: up one dir, main page]

Skip to content

Commit a5c2855

Browse files
committed
Bump v1.0.0-alpha.6 - Fix #9 PATCH changes instead of PUT to replace
1 parent dccd572 commit a5c2855

File tree

10 files changed

+159
-67
lines changed

10 files changed

+159
-67
lines changed

README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,21 @@ Sometime you want to be able to do some custom additional serialization / deseri
148148
});
149149
```
150150

151+
## Options
151152

152-
### Get JSONApi Meta
153+
### Adapter options
153154

154-
To retrieve JSONApi Meta on every call :
155+
#### `beforeDeserialize`, `afterDeserialize`, `beforeSerialize`, `afterSerialize`
156+
157+
Deserialization and serialization hooks, see above.
158+
159+
### CRUD methods options
160+
161+
#### `raw: boolean`
162+
163+
Send back raw response
164+
165+
Eg. To retrieve JSONApi Meta :
155166

156167
```js
157168
store.findAll('User', {}, {
@@ -162,6 +173,22 @@ store.findAll('User', {}, {
162173
})
163174
```
164175

176+
* Compatible with: `all`
177+
178+
#### `forceReplace: boolean`
179+
180+
On update, force all fields to be sent even if they haven't been changed.
181+
It will switch from default PATCH http verb to PUT.
182+
183+
If some properties are passed to the update, only those will be sent. Those who has not changed compared to the record original properties will not be sent.
184+
Be careful, because it means, once the update made, the server will return saved datas, and it will erased possible properties that were changed on the record but not given to the update. Nothing wrong with that, it's the correct behavior, but you should know about this logic.
185+
186+
* Compatible with: `update`, `save`
187+
188+
#### `beforeDeserialize`, `afterDeserialize`, `beforeSerialize`, `afterSerialize`
189+
190+
Deserialization and serialization hooks, see above.
191+
165192
## Development Status
166193

167194
### What is done
@@ -170,11 +197,11 @@ store.findAll('User', {}, {
170197
* Meta handling
171198
* Serialization
172199
* Custom hooks
200+
* By default update PATCH changes instead of PUT the record
173201

174202
### What is remaining
175203

176204
* ManyToMany
177-
* PATCH instead of PUT
178205
* Error handling
179206

180207
## License

dist/js-data-jsonapi-light.js

Lines changed: 32 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/js-data-jsonapi-light.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "js-data-jsonapi-light",
3-
"version": "1.0.0-alpha.5",
3+
"version": "1.0.0-alpha.6",
44
"description": "JsonApi adapter serializer/dezerializer light.",
55
"main": "./dist/js-data-jsonapi-light.js",
66
"author": {

src/adapter.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,28 @@ export class JsonApiAdapter extends HttpAdapter{
7171
// Ensure id is properly set
7272
props[mapper.idAttribute] = id;
7373

74-
if (!opts.replace) {
75-
opts.method = opts.method || 'patch';
74+
// If we don't force the replace, then we will attempt to only update what
75+
// has been changed through PATCH
76+
if (!opts.forceReplace) {
77+
// opts has the finaly say over the method used
78+
opts.method = opts.method || 'patch';
79+
80+
// We need the record to get changes
81+
let record = (<any>mapper).datastore.get(mapper.name, id);
82+
if (record) {
83+
// Now we have two possible cases :
84+
// * either props contain parameters the user want to update, and only
85+
// those should be updated
86+
// * or when record is saved, props contains all data from the record
87+
// even unchanged data
88+
// So we can't use record.changes(), because if first case it will be
89+
// empty or with not the data we want to send. Thus we can do the same
90+
// things the changes method do, but with props rather than with
91+
// current attributes.
92+
93+
// opts.changes = record.changes();
94+
opts.changes = utils.diffObjects(props, record._get('previous'), null);
7695
}
77-
78-
// We need the record to get changes
79-
let record = (<any>mapper).datastore.get(mapper.name, id);
80-
if (record) {
81-
console.info(record.changes());
8296
}
8397

8498
return this.handleBeforeLifecycle(opts).then(() => {

src/deserializer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export function jsonApiDeserialize(mapper:Mapper, res:any, opts:any){
5858
if (!item.relationships || !Object.keys(item.relationships)) continue;
5959

6060
for (let relationField in (item.relationships || {})) {
61-
let relation:any = resource.relationByField[relationField]
61+
let relation:any = resource.relationByField[relationField];
62+
6263
if (!relation || !item.relationships[relationField] || !item.relationships[relationField].data) {
6364
continue;
6465
}

src/serializer.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,54 @@ export function jsonApiSerialize (mapper:any, data:any, opts:any){
2020
// Just cache a pointer to relations for the Resource
2121
mapperCacheRelationByField(mapper);
2222

23-
if (opts.changes && id && mapper.changes(id)) {
24-
let changes = mapper.changes(id);
25-
console.info('changes', changes);
23+
let output:any = { data: { type: mapper.name } };
24+
25+
// Work for update or create, if an id is given, server should accept it
26+
if (id) output.data.id = id;
27+
28+
let relationships:any = {};
29+
let attributes:any = {};
30+
31+
// Only include relationships if needed
32+
if (Object.keys(relationships).length) {
33+
output.data.relationships = relationships;
2634
}
2735

28-
let relationships:any = {}
36+
// opts.changes is there when update method is PATCH
37+
// in this case we change only what has changed
38+
if (!opts.forceReplace && opts.changes && id) {
39+
data = opts.changes.changed;
40+
}
2941

42+
// @todo For the moment sending hasMany items is not supported and maybe
43+
// shouldn't be supported for security reasons (cf. JSON Api Spec)
3044
for (let key in data) {
3145
let relation:any = mapper.relationByFieldId[key];
32-
if (relation) {
33-
relationships[relation.localField] = {
34-
data: {
35-
type: relation.relation,
36-
id: data[key]
37-
}
38-
}
39-
delete data[key]
46+
47+
// No relations means a simple attribute
48+
if (!relation) {
49+
attributes[key] = data[key];
50+
continue;
4051
}
41-
}
4252

43-
let output:any = {
44-
data: {
45-
type: mapper.name,
46-
attributes: data
53+
// Relation that can be in data are only belongsTo since it has a localKey
54+
relationships[relation.localField] = {
55+
data: {
56+
type: relation.relation,
57+
id: data[key]
58+
}
4759
}
4860
}
4961

50-
// Work for update or create, if an id is given, server should accept it
51-
if (id) output.data.id = id;
62+
// Only include relationships if needed
5263
if (Object.keys(relationships).length) {
5364
output.data.relationships = relationships;
5465
}
5566

67+
// Only include attributes if needed
68+
if (Object.keys(attributes).length !== 0) {
69+
output.data.attributes = attributes;
70+
}
71+
5672
return output;
5773
}

test/unit/crud/update.spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ describe('UPDATE', () => {
3232
},
3333
relationships: {
3434
author: {
35-
type: 'User',
36-
id: 'e81fea3d-6379-4137-8068-7d70a90a1a7c'
35+
data: {
36+
type: 'User',
37+
id: 'e81fea3d-6379-4137-8068-7d70a90a1a7c'
38+
}
3739
}
3840
}
3941
}
@@ -52,8 +54,10 @@ describe('UPDATE', () => {
5254
},
5355
relationships: {
5456
author: {
55-
type: 'User',
56-
id: UPDATE_PARAMS.authorId
57+
data: {
58+
type: 'User',
59+
id: UPDATE_PARAMS.authorId
60+
}
5761
}
5862
}
5963
}
@@ -106,7 +110,6 @@ describe('UPDATE', () => {
106110
record.title = UPDATE_PARAMS.title;
107111
return record.save();
108112
}).then((data) => {
109-
console.info(reqPatch.body);
110113
expect(reqPatch.body).to.deep.equal({
111114
data: {
112115
type: MAPPER_NAME,
@@ -124,7 +127,7 @@ describe('UPDATE', () => {
124127

125128
it('the request should send relation when a record is saved.', () => {
126129
return store.update('Article', ID, UPDATE_PARAMS, {
127-
replace: true
130+
forceReplace: true
128131
}).then((data) => {
129132
expect(reqPut.body.data).to.be.an('object');
130133
expect(reqPut.body.data.type).to.equal(MAPPER_NAME);

test/unit/lifecycle.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('Lifecycle', () => {
9595

9696
return store.update('User', USER.ID, {
9797
email: USER.EMAIL
98-
}, { beforeSerialize, replace: true }).then(() => {
98+
}, { beforeSerialize, forceReplace: true }).then(() => {
9999
expect(beforeSerialize.calledOnce).to.be.true;
100100
expect(req.body.data.attributes).to.deep.equal({
101101
email: USER.EMAIL,
@@ -122,7 +122,7 @@ describe('Lifecycle', () => {
122122

123123
return store.update('User', USER.ID, {
124124
email: USER.EMAIL
125-
}, { afterSerialize, replace: true }).then(() => {
125+
}, { afterSerialize, forceReplace: true }).then(() => {
126126
expect(afterSerialize.calledOnce).to.be.true;
127127
expect(req.body.data.attributes).to.deep.equal({
128128
email: USER.EMAIL,

test/unit/relations/belongsTo.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,30 @@ describe('relations/belongsTo', () => {
5757
expect(users[0].email).to.equal(USER.EMAIL);
5858
})
5959
})
60+
61+
describe('when resource is fetched with belongsTo reference to un unknown resource', () => {
62+
const
63+
ARTICLE_LENGTH = 1,
64+
ARTICLE = {
65+
AUTHOR_ID: 'e81fea3d-6379-4137-8068-7d70a90a1a7c'
66+
}
67+
;
68+
69+
let articles:Array<any>;
70+
71+
beforeEach(() => {
72+
return store.findAll('Article').then((datas:Array<any>) => {
73+
articles = datas
74+
})
75+
});
76+
77+
afterEach(() => {
78+
articles = null
79+
});
80+
81+
it('should keep the relation field intact', () => {
82+
expect(articles).to.be.an('array').and.to.have.lengthOf(ARTICLE_LENGTH);
83+
expect(articles[0].authorId).to.equal(ARTICLE.AUTHOR_ID);
84+
});
85+
});
6086
});

0 commit comments

Comments
 (0)
0