Skip to content

Commit 70339db

Browse files
Fixes #24, When sending patch only send data chnages by default
1 parent 564ba0a commit 70339db

File tree

4 files changed

+86
-59
lines changed

4 files changed

+86
-59
lines changed

scripts/typings/js-data/JsonApiAdapter.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ declare module JsonApiAdapter {
66
// JsonApi does not support PUT semantics, so use PATCH by default
77
usePATCH?: boolean;
88

9-
// Set to true force objects relations to be sent when updating an objet
9+
// Set to true to force objects relationships to be sent when updating an objet, default false
1010
updateRelationships: boolean,
1111

1212
// Do not set globally, used to override the url for a resource
@@ -18,6 +18,9 @@ declare module JsonApiAdapter {
1818
log?: (message?: any, ...optionalParams: any[]) => void;
1919
error?: (message?: any, ...optionalParams: any[]) => void;
2020

21+
// Js-data send changes only
22+
changes?: boolean;
23+
2124
// DSHTTPSpecific Options
2225
http?: any;
2326
headers?: any;

src/JsonApiAdapter.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ export class JsonApiAdapter implements JSData.IDSAdapter {
315315
public create(config: JSData.DSResourceDefinition<any>, attrs: Object, options?: JSData.DSConfiguration): JSData.JSDataPromise<any> {
316316
let localOptions = this.configureSerializers(options);
317317

318+
// Create semantics require sending relationships
319+
//localOptions.jsonApi.updateRelationships = true;
320+
318321
// Id
319322
if (attrs[config.idAttribute]) {
320323
attrs[config.idAttribute] = attrs[config.idAttribute].toString();
@@ -389,9 +392,16 @@ export class JsonApiAdapter implements JSData.IDSAdapter {
389392
}
390393

391394
let localOptions = this.configureSerializers(options);
392-
if (!localOptions.method && localOptions.jsonApi.usePATCH === true) {
395+
if (localOptions.jsonApi.usePATCH === true) {
393396
// Use Jsonapi PATCH symantics
394-
localOptions.method = 'patch';
397+
localOptions.method = localOptions.method || 'patch';
398+
} else {
399+
localOptions.jsonApi.updateRelationships = true;
400+
}
401+
402+
//If we are using patch then just send changes by default
403+
if (localOptions.method === 'patch') {
404+
localOptions.changes = (localOptions.changes === false) ? false : true;
395405
}
396406

397407
return this.adapter.update(config, idString, attrs, localOptions).then(

src/JsonApiSerializer.ts

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ export class SerializationOptions {
295295

296296
let relationlower = relationType.toLowerCase();
297297
let matchIndex = -1;
298-
let relationList = this.resourceDef['relationlist'];
298+
let relationList = this.resourceDef['relationList'];
299299

300300
DSUTILS.forEach<JSData.RelationDefinition>(relationList, (relation: JSData.RelationDefinition, index: number) => {
301301
if (relation.type === jsDataHasMany || relation.type === jsDataHasOne) {
@@ -903,50 +903,60 @@ export class JsonApiHelper {
903903

904904
var data = new JsonApi.JsonApiData(options.type);
905905

906+
var changes = attrs;
906907
//JsonApi id is always a string, it can be empty for a new unstored object!
907908
if (attrs[options.idAttribute]) {
908909
data.WithId(attrs[options.idAttribute]);
910+
911+
// TODO : Update tests
912+
//if (config.method === 'patch') {
913+
// changes = (<JSData.DSResourceDefinition<any>>options.def()).changes(attrs[options.idAttribute]);
914+
//}
915+
916+
changes = (config.changes === true) ? (<JSData.DSResourceDefinition<any>>options.def()).changes(attrs[options.idAttribute]) : changes;
909917
}
910918

911919
// Take object attributes
912-
DSUTILS.forOwn(attrs, (value: any, prop: string) => {
913-
// Skip id attribute as it has already been copied to the id field out side of the attributes collection
914-
// Skip any non-json api compliant tags
915-
if (prop !== options.idAttribute && prop !== JSONAPI_META && prop.indexOf('$') < 0) {
916-
data.WithAttribute(prop, value);
917-
}
918-
});
920+
DSUTILS.forOwn(changes, (value: any, prop: string) => {
921+
// Skip id attribute as it has already been copied to the id field out side of the attributes collection
922+
// Skip any non-json api compliant tags
923+
if (prop !== options.idAttribute && prop !== JSONAPI_META && prop.indexOf('$') < 0) {
924+
data.WithAttribute(prop, value);
925+
}
926+
});
919927

920-
// Get object related data
921-
if (config.jsonApi.updateRelationships === true) {
922-
DSUTILS.forEach(DSUTILS.get<JSData.RelationDefinition[]>(options.def(), 'relationList'), (relation: JSData.RelationDefinition) => {
923-
// Get child definition
924-
var relatedDef = options.getResource(relation.relation);
925-
926-
if (relation.type === jsDataHasMany) {
927-
var relatedObjects = DSUTILS.get<any[]>(attrs, relation.localField);
928-
if (relatedObjects) {
929-
//This is a relationship so add data as relationsship as apposed to inling data structure
930-
var relationship = new JsonApi.JsonApiRelationship(true);
931-
DSUTILS.forEach(relatedObjects, (item: any) => {
932-
relationship.WithData(relation.relation, item[relatedDef.idAttribute]);
933-
});
934-
935-
data.WithRelationship(relation.localField, relationship);
936-
}
928+
// Get object related data
929+
// Create should always send relationships
930+
if (config.jsonApi.updateRelationships === true) {
931+
DSUTILS.forEach(DSUTILS.get<JSData.RelationDefinition[]>(options.def(), 'relationList'), (relation: JSData.RelationDefinition) => {
932+
// Get child definition
933+
var relatedDef = options.getResource(relation.relation);
934+
935+
if (relation.type === jsDataHasMany) {
936+
var relationship = new JsonApi.JsonApiRelationship(true);
937+
var relatedObjects = DSUTILS.get<any[]>(changes, relation.localField);
938+
if (relatedObjects) {
939+
//This is a relationship so add data as relationship
940+
DSUTILS.forEach(relatedObjects, (item: any) => {
941+
relationship.WithData(relation.relation, item[relatedDef.idAttribute]);
942+
});
937943
}
938944

939-
if (relation.type === jsDataHasOne) {
940-
var relatedObject = DSUTILS.get<any>(attrs, relation.localField);
941-
if (relatedObject) {
942-
var relationship = new JsonApi.JsonApiRelationship(false)
943-
.WithData(relation.relation, relatedObject[relatedDef.idAttribute]);
945+
data.WithRelationship(relation.localField, relationship);
946+
}
944947

945-
data.WithRelationship(relation.localField, relationship);
946-
}
948+
if (relation.type === jsDataHasOne) {
949+
var relationship: JsonApi.JsonApiRelationship = null;
950+
var relatedObject = DSUTILS.get<any>(changes, relation.localField);
951+
if (relatedObject) {
952+
relationship = new JsonApi.JsonApiRelationship(false)
953+
.WithData(relation.relation, relatedObject[relatedDef.idAttribute]);
947954
}
948-
});
949-
}
955+
956+
data.WithRelationship(relation.localField, relationship);
957+
}
958+
});
959+
}
950960

951961
return data;
952962
}

test/update.spec.js

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('Update Tests', function () {
2222
_this.requests[0].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
2323
}, 30);
2424

25-
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }).then(function (data) {
25+
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, {changes:false}).then(function (data) {
2626
// We are not testing meta data yet
2727
ignoreMetaData(data);
2828

@@ -34,19 +34,19 @@ describe('Update Tests', function () {
3434
assert.equal(_this.requests[1].method, 'PATCH');
3535
assert.isDefined(_this.requests[1].requestHeaders);
3636
assert.include(_this.requests[1].requestHeaders['Accept'], 'application/vnd.api+json', 'Contains json api content-type header');
37-
assert.equal(_this.requests[1].requestBody, DSUtils.toJson({ data: { id: "1", type: 'posts', attributes: { author: 'John', age: 30 } } }));
37+
assert.equal(_this.requests[1].requestBody, DSUtils.toJson({ data: { id: "1", type: 'posts', attributes: { author: 'John', age: 30 } } }));
3838

39-
_this.requests[1].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
39+
_this.requests[1].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
4040

4141
}, 30);
4242

43-
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, { basePath: 'api2' });
44-
}).then(function (data) {
45-
// We are not testing meta data yet
46-
ignoreMetaData(data);
47-
48-
assert.deepEqual(data, p1.model, 'post 1 should have been updated#2');
49-
assert.equal(queryTransform.callCount, 2, 'queryTransform should have been called twice');
43+
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, { basePath: 'api2', changes:false }).then(function (data) {
44+
// We are not testing meta data yet
45+
ignoreMetaData(data);
46+
47+
assert.deepEqual(data, p1.model, 'post 1 should have been updated#2');
48+
assert.equal(queryTransform.callCount, 2, 'queryTransform should have been called twice');
49+
});
5050
});
5151
});
5252

@@ -59,7 +59,9 @@ describe('Update Tests', function () {
5959
assert.equal(_this.requests[0].method, 'PUT');
6060
assert.isDefined(_this.requests[0].requestHeaders);
6161
assert.include(_this.requests[0].requestHeaders['Accept'], 'application/vnd.api+json', 'Contains json api content-type header');
62-
assert.equal(_this.requests[0].requestBody, DSUtils.toJson({ data: { id: '1', type: 'posts', attributes: { author: 'John', age: 30 } } }));
62+
assert.equal(_this.requests[0].requestBody, DSUtils.toJson(
63+
{ data: { id: '1', type: 'posts', attributes: { author: 'John', age: 30 } } })
64+
);
6365

6466

6567
_this.requests[0].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
@@ -77,19 +79,21 @@ describe('Update Tests', function () {
7779
assert.equal(_this.requests[1].method, 'PATCH');
7880
assert.isDefined(_this.requests[1].requestHeaders);
7981
assert.include(_this.requests[1].requestHeaders['Accept'], 'application/vnd.api+json', 'Contains json api content-type header');
80-
assert.equal(_this.requests[1].requestBody, DSUtils.toJson({ data: { id: "1", type: 'posts', attributes: { author: 'John', age: 30 } } }));
82+
assert.equal(_this.requests[1].requestBody, DSUtils.toJson(
83+
{ data: { id: "1", type: 'posts', attributes: { author: 'John', age: 30 } } })
84+
);
8185

82-
_this.requests[1].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
86+
_this.requests[1].respond(200, { 'Content-Type': 'application/vnd.api+json' }, DSUtils.toJson(p1.jsonApiData));
8387

8488
}, 30);
8589

86-
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, { basePath: 'api2' });
87-
}).then(function (data) {
88-
// We are not testing meta data yet
89-
ignoreMetaData(data);
90-
91-
assert.deepEqual(data, p1.model, 'post 1 should have been updated#2');
92-
assert.equal(queryTransform.callCount, 2, 'queryTransform should have been called twice');
90+
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, { basePath: 'api2', changes:false }).then(function (data) {
91+
// We are not testing meta data yet
92+
ignoreMetaData(data);
93+
94+
assert.deepEqual(data, p1.model, 'post 1 should have been updated#2');
95+
assert.equal(queryTransform.callCount, 2, 'queryTransform should have been called twice');
96+
});
9397
});
9498
});
9599

@@ -131,7 +135,7 @@ describe('Update Tests', function () {
131135
_this.requests[0].respond(204);//{ 'Content-Type': 'application/vnd.api+json' }
132136
}, 30);
133137

134-
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30, type: 'person' }).then(function (data) {
138+
return dsHttpAdapter.update(Post, 1, { author: 'John', age: 30, type: 'person' }, {changes:false}).then(function (data) {
135139
// We are not testing meta data yet
136140
ignoreMetaData(data);
137141

@@ -275,7 +279,7 @@ describe('Update Tests', function () {
275279
var author = testData.config.Author.get(1);
276280
assert.isDefined(author, 'Author should be in DS');
277281
author.name = 'New Author';
278-
return ds.save('author', author.id, {jsonApi: { updateRelationships: true }}).then(function (data) {
282+
return ds.save('author', author.id, {changes:false, jsonApi: { updateRelationships: true }}).then(function (data) {
279283
assert.isDefined(data, 'Result Should exists');
280284
assert.equal(data.name, 'New Author');
281285

0 commit comments

Comments
 (0)