From 68a9238937c158eadf9ac6ced28f31e66a4f6a88 Mon Sep 17 00:00:00 2001 From: ryanxwelch Date: Fri, 27 Jan 2017 11:26:49 -0500 Subject: [PATCH 1/3] fix(serializer): remove duplicate includes from serializer Proposed resolution to the following issue: https://github.com/digitalsadhu/loopback-component-jsonapi/issues/111 --- lib/serializer.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/serializer.js b/lib/serializer.js index de94d14..fe9ccfa 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -379,7 +379,25 @@ function handleIncludes (resp, includes, relations, app) { }) if (embedded.length) { - resp.included = _.flattenDeep(embedded) + // This array may contain duplicate models if the same item is referenced multiple times in 'data' + let duplicate = _.flattenDeep(embedded) + // So begin with an empty array that will only contain unique items + let unique = [] + // Iterate through each item in the first array + duplicate.forEach(function(d){ + // Count the number of items in the unique array with matching 'type' AND 'id' + // Since we're adhering to the JSONAPI spec, both 'type' and 'id' can assumed to be present + // Both 'type' and 'id' are needed for comparison because we could theoretically have objects of different + // types who happen to have the same 'id', and those would not be considered duplicates + let count = unique.filter(function(u){ + return (u.type === d.type) && (u.id === d.id) + }).length; + // If there are no matching entries, then add the item to the unique array + if (count === 0) { + unique.push(d) + } + }) + resp.included = unique } } From 6c8ac19ec26d533a5ace0a20e99ad8a23c2f5f71 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Sat, 10 Jun 2017 09:18:11 +0200 Subject: [PATCH 2/3] fix(serializer): switch let to var --- lib/serializer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/serializer.js b/lib/serializer.js index fe9ccfa..33b8155 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -380,18 +380,18 @@ function handleIncludes (resp, includes, relations, app) { if (embedded.length) { // This array may contain duplicate models if the same item is referenced multiple times in 'data' - let duplicate = _.flattenDeep(embedded) + var duplicate = _.flattenDeep(embedded) // So begin with an empty array that will only contain unique items - let unique = [] + var unique = [] // Iterate through each item in the first array - duplicate.forEach(function(d){ + duplicate.forEach(function (d) { // Count the number of items in the unique array with matching 'type' AND 'id' // Since we're adhering to the JSONAPI spec, both 'type' and 'id' can assumed to be present // Both 'type' and 'id' are needed for comparison because we could theoretically have objects of different // types who happen to have the same 'id', and those would not be considered duplicates - let count = unique.filter(function(u){ + var count = unique.filter(function (u) { return (u.type === d.type) && (u.id === d.id) - }).length; + }).length // If there are no matching entries, then add the item to the unique array if (count === 0) { unique.push(d) From 4152a3a213a2ade166de81e49b9947ba13b786a0 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Sun, 11 Jun 2017 10:00:02 +0200 Subject: [PATCH 3/3] chore(tests): Add test for duplicate fix --- test/hasManyRelationships.test.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/hasManyRelationships.test.js b/test/hasManyRelationships.test.js index 6549e78..2747ed7 100644 --- a/test/hasManyRelationships.test.js +++ b/test/hasManyRelationships.test.js @@ -113,4 +113,29 @@ describe('loopback json api hasMany relationships', function () { }) }) }) + + describe('Duplicate models in `includes`', function () { + beforeEach(function () { + const Foo = ds.createModel('foo', {title: String}) + app.model(Foo) + const Bar = ds.createModel('bar', {title: String}) + app.model(Bar) + Foo.hasMany(Bar) + Foo.belongsTo(Bar) + + return Promise.all([ + Foo.create({title: 'one', barId: 1}), + Bar.create({title: 'one', barId: 1, fooId: 1}) + ]) + }) + + it('should not occur', function () { + return request(app).get('/foos/1/?include=bars,bar') + .expect(200) + .then(function (res) { + expect(res.body.included.length).to.equal(1, + 'Should be exactly 1 item in included array') + }) + }) + }) })