From 97f904fcf2e93242413c5d82b521709ba0742f5a Mon Sep 17 00:00:00 2001 From: a Date: Sat, 9 Jul 2016 10:14:37 +0200 Subject: [PATCH 1/8] feat(upsert) support hasManyThrough upsert --- lib/deserializer.js | 2 +- lib/relationships.js | 45 ++++++++ test/hasManyThroughUpsert.test.js | 179 ++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 test/hasManyThroughUpsert.test.js diff --git a/lib/deserializer.js b/lib/deserializer.js index 0b22eb2..089a8c2 100644 --- a/lib/deserializer.js +++ b/lib/deserializer.js @@ -76,4 +76,4 @@ function belongsToRelationships (options) { options.result[fkName] = relationship.data.id } }) -} +} \ No newline at end of file diff --git a/lib/relationships.js b/lib/relationships.js index 0d3375c..c8d881a 100644 --- a/lib/relationships.js +++ b/lib/relationships.js @@ -100,6 +100,37 @@ function relationships (id, data, model) { updateRelation(modelTo, idToFind, where) }) + + if (serverRelation.modelThrough) { + var modelThrough = serverRelation.modelThrough + var key = keyByModel(modelThrough, modelTo) + var data = {} + data[fkName] = id + + var payloadIds = _.map(relationship.data, function(item) { + return item.id + }) + + modelThrough.find({where: data}, function(err, instances) { + var serverIds = _.map(instances, function(instance) { + return instance[key] + }) + // to delete + var toDelete = _.difference(serverIds, payloadIds); + _.each(toDelete, function(id) { + data[key] = id + modelThrough.destroyAll(data) + }) + // new + var newAssocs = _.difference(payloadIds, serverIds); + _.each(newAssocs, function(id) { + data[key] = id + modelThrough.create(data) + }) + }) + + } + } else { if (relationship.data === null) { where[fkName] = null @@ -132,3 +163,17 @@ function updateRelation (model, id, data) { } }) } + +function keyByModel(assocModel, model) { + var key = null; + _.each(assocModel.relations, function(relation){ + if (relation.modelTo.modelName === model.modelName) { + key = relation.keyFrom; + } + }) + + if (key === null) { + console.log('Can not find relation for ' + model.modelName); + } + return key +} diff --git a/test/hasManyThroughUpsert.test.js b/test/hasManyThroughUpsert.test.js new file mode 100644 index 0000000..21aec72 --- /dev/null +++ b/test/hasManyThroughUpsert.test.js @@ -0,0 +1,179 @@ +var request = require("supertest"); +var loopback = require('loopback') +var expect = require('chai').expect; +var JSONAPIComponent = require('../') +var RSVP = require('rsvp') + +var app +var Movie, Category, MovieCategoryAssoc; + +describe('hasManyThrough upsert', function () { + beforeEach(function (done) { + app = loopback() + app.set('legacyExplorer', false) + var ds = loopback.createDataSource('memory') + // create models + Movie = ds.createModel('movie', { + id: {type: Number, id: true}, + name: String + }) + + Category = ds.createModel('category', { + id: {type: Number, id: true}, + name: String + }) + + MovieCategoryAssoc = ds.createModel('movieCategoryAssoc', { + id: {type: Number, id: true} + }) + + // add models + app.model(Movie) + app.model(Category) + app.model(MovieCategoryAssoc) + + // set up relationships + Movie.hasMany(Category, { through: MovieCategoryAssoc }) + Category.hasMany(Movie, { through: MovieCategoryAssoc }) + + MovieCategoryAssoc.belongsTo(Movie) + MovieCategoryAssoc.belongsTo(Category) + makeData().then(done()) + app.use(loopback.rest()) + JSONAPIComponent(app, {restApiRoot: ''}) + }) + + it('should make initial data', function (done) { + + request(app) + .get('/movies/1/categories') + .end(function (err, res) { + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) + expect(res).to.have.deep.property('body.data[0]').and.have.property('attributes.name').and.equal('Crime') + done(err) + }) + + }) + + it('should handle POST', function(done){ + var agent = request(app) + agent + .post('/movies') + .send({ + "data": { + "type": "movies", + "attributes": { + "name": "Ace Ventura: Pet Detective" + }, + "relationships": { + "categories": { + "data": [ + {"type": "categories", "id": 4} + ] + } + } + } + }).end(function(){ + agent.get('/movieCategoryAssocs').end(function(err, res){ + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) + done() + }) + }) + }) + + it('should handle PATCH', function(done){ + var agent = request(app) + agent + .patch('/movies/1') + .send({ + "data": { + "id": 1, + "type": "movies", + "attributes": { + "name": "The Shawshank Redemption" + }, + "relationships": { + "categories": { + "data": [ + {"type": "categories", "id": 1}, + {"type": "categories", "id": 2}, + {"type": "categories", "id": 3} + ] + } + } + } + }).end(function(){ + agent.get('/movieCategoryAssocs').end(function(err, res){ + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) + done() + }) + }) + }) + + it('should handle PATCH with less assocs', function(done){ + var agent = request(app) + agent + .patch('/movies/1') + .send({ + "data": { + "id": 1, + "type": "movies", + "attributes": { + "name": "The Shawshank Redemption" + }, + "relationships": { + "categories": { + "data": [ + {"type": "categories", "id": 1}, + {"type": "categories", "id": 4} + ] + } + } + } + }).end(function(err, res){ + expect(err).to.equal(null) + agent.get('/movieCategoryAssocs').end(function(err, res){ + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) + + agent.get('/movies/1/categories').end(function(err, res){ + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) + expect(res).to.have.deep.property('body.data[1].attributes.name').and.equal('Comedy') + done() + }) + }) + }) + }) + +}) + + +function makeData (done) { + var createMovie = denodeifyCreate(Movie) + var createCategory = denodeifyCreate(Category) + var createAssoc = denodeifyCreate(MovieCategoryAssoc) + + return RSVP.hash({ + movie: createMovie({ name: 'The Shawshank Redemption'}), + categories: RSVP.all([ + createCategory({ name: 'Crime' }), + createCategory({ name: 'Drama' }), + createCategory({ name: 'History' }), + createCategory({ name: 'Comedy' }) + ]) + }) + .then(function (models) { + return RSVP.all([ + createAssoc({ movieId: models.movie.id, categoryId: models.categories[0].id }), + createAssoc({ movieId: models.movie.id, categoryId: models.categories[2].id }) + ]) + }) + + function denodeifyCreate (Model) { + return RSVP.denodeify(Model.create.bind(Model)) + } +} \ No newline at end of file From 6dd678499f1434abdacd8bb9d1351e79e63e1a98 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Mon, 11 Jul 2016 09:20:39 +0200 Subject: [PATCH 2/8] chore(linter): linter was not linting all files --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d20e69b..9616757 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "test": "npm run lint && istanbul cover _mocha --report lcovonly --reporter=spec ./test/**/*.test.js && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", "tester": "mocha --reporter=spec ./test/**/*.test.js", "coverage": "istanbul cover _mocha ./test/**/*.test.js", - "lint": "standard .", + "lint": "standard test/**/*.js lib/**/*.js", "semantic-release": "semantic-release pre && npm publish && semantic-release post" }, "repository": { From a8145d64301358253e998f7591d517e88bddcdd9 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Mon, 11 Jul 2016 09:30:15 +0200 Subject: [PATCH 3/8] style(linter): Fix styling issues --- lib/deserializer.js | 14 ++--- lib/relationships.js | 31 +++++----- test/hasManyThroughUpsert.test.js | 95 +++++++++++++++---------------- 3 files changed, 68 insertions(+), 72 deletions(-) diff --git a/lib/deserializer.js b/lib/deserializer.js index 089a8c2..bc1cceb 100644 --- a/lib/deserializer.js +++ b/lib/deserializer.js @@ -24,14 +24,14 @@ function defaultAfterDeserialize (options, cb) { module.exports = function deserializer (options, cb) { var model = options.model - var beforeDeserialize = (typeof model.beforeJsonApiDeserialize === 'function') ? - model.beforeJsonApiDeserialize : defaultBeforeDeserialize + var beforeDeserialize = (typeof model.beforeJsonApiDeserialize === 'function') + ? model.beforeJsonApiDeserialize : defaultBeforeDeserialize - var deserialize = (typeof model.jsonApiDeserialize === 'function') ? - model.jsonApiDeserialize : defaultDeserialize + var deserialize = (typeof model.jsonApiDeserialize === 'function') + ? model.jsonApiDeserialize : defaultDeserialize - var afterDeserialize = (typeof model.afterJsonApiDeserialize === 'function') ? - model.afterJsonApiDeserialize : defaultAfterDeserialize + var afterDeserialize = (typeof model.afterJsonApiDeserialize === 'function') + ? model.afterJsonApiDeserialize : defaultAfterDeserialize var deserializeOptions = _.cloneDeep(options) @@ -76,4 +76,4 @@ function belongsToRelationships (options) { options.result[fkName] = relationship.data.id } }) -} \ No newline at end of file +} diff --git a/lib/relationships.js b/lib/relationships.js index c8d881a..eea067e 100644 --- a/lib/relationships.js +++ b/lib/relationships.js @@ -107,30 +107,29 @@ function relationships (id, data, model) { var data = {} data[fkName] = id - var payloadIds = _.map(relationship.data, function(item) { + var payloadIds = _.map(relationship.data, function (item) { return item.id }) - modelThrough.find({where: data}, function(err, instances) { - var serverIds = _.map(instances, function(instance) { + modelThrough.find({where: data}, function (err, instances) { + if (err) return + var serverIds = _.map(instances, function (instance) { return instance[key] }) // to delete - var toDelete = _.difference(serverIds, payloadIds); - _.each(toDelete, function(id) { + var toDelete = _.difference(serverIds, payloadIds) + _.each(toDelete, function (id) { data[key] = id modelThrough.destroyAll(data) }) // new - var newAssocs = _.difference(payloadIds, serverIds); - _.each(newAssocs, function(id) { + var newAssocs = _.difference(payloadIds, serverIds) + _.each(newAssocs, function (id) { data[key] = id modelThrough.create(data) - }) + }) }) - } - } else { if (relationship.data === null) { where[fkName] = null @@ -164,16 +163,16 @@ function updateRelation (model, id, data) { }) } -function keyByModel(assocModel, model) { - var key = null; - _.each(assocModel.relations, function(relation){ +function keyByModel (assocModel, model) { + var key = null + _.each(assocModel.relations, function (relation) { if (relation.modelTo.modelName === model.modelName) { - key = relation.keyFrom; + key = relation.keyFrom } }) - + if (key === null) { - console.log('Can not find relation for ' + model.modelName); + console.log('Can not find relation for ' + model.modelName) } return key } diff --git a/test/hasManyThroughUpsert.test.js b/test/hasManyThroughUpsert.test.js index 21aec72..de0c4e9 100644 --- a/test/hasManyThroughUpsert.test.js +++ b/test/hasManyThroughUpsert.test.js @@ -1,11 +1,11 @@ -var request = require("supertest"); +var request = require('supertest') var loopback = require('loopback') -var expect = require('chai').expect; +var expect = require('chai').expect var JSONAPIComponent = require('../') var RSVP = require('rsvp') var app -var Movie, Category, MovieCategoryAssoc; +var Movie, Category, MovieCategoryAssoc describe('hasManyThrough upsert', function () { beforeEach(function (done) { @@ -40,7 +40,7 @@ describe('hasManyThrough upsert', function () { MovieCategoryAssoc.belongsTo(Category) makeData().then(done()) app.use(loopback.rest()) - JSONAPIComponent(app, {restApiRoot: ''}) + JSONAPIComponent(app, {restApiRoot: ''}) }) it('should make initial data', function (done) { @@ -53,29 +53,28 @@ describe('hasManyThrough upsert', function () { expect(res).to.have.deep.property('body.data[0]').and.have.property('attributes.name').and.equal('Crime') done(err) }) - }) - it('should handle POST', function(done){ + it('should handle POST', function (done) { var agent = request(app) agent .post('/movies') .send({ - "data": { - "type": "movies", - "attributes": { - "name": "Ace Ventura: Pet Detective" + 'data': { + 'type': 'movies', + 'attributes': { + 'name': 'Ace Ventura: Pet Detective' }, - "relationships": { - "categories": { - "data": [ - {"type": "categories", "id": 4} + 'relationships': { + 'categories': { + 'data': [ + {'type': 'categories', 'id': 4} ] } } } - }).end(function(){ - agent.get('/movieCategoryAssocs').end(function(err, res){ + }).end(function () { + agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) done() @@ -83,29 +82,29 @@ describe('hasManyThrough upsert', function () { }) }) - it('should handle PATCH', function(done){ + it('should handle PATCH', function (done) { var agent = request(app) agent .patch('/movies/1') .send({ - "data": { - "id": 1, - "type": "movies", - "attributes": { - "name": "The Shawshank Redemption" + 'data': { + 'id': 1, + 'type': 'movies', + 'attributes': { + 'name': 'The Shawshank Redemption' }, - "relationships": { - "categories": { - "data": [ - {"type": "categories", "id": 1}, - {"type": "categories", "id": 2}, - {"type": "categories", "id": 3} + 'relationships': { + 'categories': { + 'data': [ + {'type': 'categories', 'id': 1}, + {'type': 'categories', 'id': 2}, + {'type': 'categories', 'id': 3} ] } } } - }).end(function(){ - agent.get('/movieCategoryAssocs').end(function(err, res){ + }).end(function () { + agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) done() @@ -113,33 +112,33 @@ describe('hasManyThrough upsert', function () { }) }) - it('should handle PATCH with less assocs', function(done){ + it('should handle PATCH with less assocs', function (done) { var agent = request(app) agent .patch('/movies/1') .send({ - "data": { - "id": 1, - "type": "movies", - "attributes": { - "name": "The Shawshank Redemption" + 'data': { + 'id': 1, + 'type': 'movies', + 'attributes': { + 'name': 'The Shawshank Redemption' }, - "relationships": { - "categories": { - "data": [ - {"type": "categories", "id": 1}, - {"type": "categories", "id": 4} + 'relationships': { + 'categories': { + 'data': [ + {'type': 'categories', 'id': 1}, + {'type': 'categories', 'id': 4} ] } } } - }).end(function(err, res){ + }).end(function (err, res) { expect(err).to.equal(null) - agent.get('/movieCategoryAssocs').end(function(err, res){ + agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) - - agent.get('/movies/1/categories').end(function(err, res){ + + agent.get('/movies/1/categories').end(function (err, res) { expect(err).to.equal(null) expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) expect(res).to.have.deep.property('body.data[1].attributes.name').and.equal('Comedy') @@ -148,17 +147,15 @@ describe('hasManyThrough upsert', function () { }) }) }) - }) - function makeData (done) { var createMovie = denodeifyCreate(Movie) var createCategory = denodeifyCreate(Category) var createAssoc = denodeifyCreate(MovieCategoryAssoc) return RSVP.hash({ - movie: createMovie({ name: 'The Shawshank Redemption'}), + movie: createMovie({name: 'The Shawshank Redemption'}), categories: RSVP.all([ createCategory({ name: 'Crime' }), createCategory({ name: 'Drama' }), @@ -176,4 +173,4 @@ function makeData (done) { function denodeifyCreate (Model) { return RSVP.denodeify(Model.create.bind(Model)) } -} \ No newline at end of file +} From 04ae93df58f1fafb00072166456f52e80e05ded2 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Mon, 11 Jul 2016 09:41:21 +0200 Subject: [PATCH 4/8] style(cleanup): Remove redundant code --- lib/relationships.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/relationships.js b/lib/relationships.js index eea067e..e821a58 100644 --- a/lib/relationships.js +++ b/lib/relationships.js @@ -64,11 +64,6 @@ function relationships (id, data, model) { var fkName = serverRelation.keyTo - if (type === 'belongsTo') { - fkName = serverRelation.keyFrom - modelTo = serverRelation.modelFrom - } - if (!modelTo) { return false } From bd7fb7ebb825b46dab3afd2f39402f8287f97d0e Mon Sep 17 00:00:00 2001 From: A Date: Wed, 14 Dec 2016 16:33:36 +0100 Subject: [PATCH 5/8] fix(string ids): handle string IDs in request payload --- lib/relationships.js | 15 ++++++++++--- test/hasManyThroughUpsert.test.js | 37 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lib/relationships.js b/lib/relationships.js index e821a58..ae4b86a 100644 --- a/lib/relationships.js +++ b/lib/relationships.js @@ -64,6 +64,11 @@ function relationships (id, data, model) { var fkName = serverRelation.keyTo + if (type === 'belongsTo') { + fkName = serverRelation.keyFrom + modelTo = serverRelation.modelFrom + } + if (!modelTo) { return false } @@ -101,15 +106,19 @@ function relationships (id, data, model) { var key = keyByModel(modelThrough, modelTo) var data = {} data[fkName] = id + var stringIds = false var payloadIds = _.map(relationship.data, function (item) { + if (typeof item.id === 'string') { + stringIds = true + } return item.id }) - modelThrough.find({where: data}, function (err, instances) { + modelThrough.find({where: data, fields: key}, function(err, instances) { if (err) return - var serverIds = _.map(instances, function (instance) { - return instance[key] + var serverIds = _.map(instances, function(instance) { + return stringIds ? instance[key].toString() : instance[key] }) // to delete var toDelete = _.difference(serverIds, payloadIds) diff --git a/test/hasManyThroughUpsert.test.js b/test/hasManyThroughUpsert.test.js index de0c4e9..7baa923 100644 --- a/test/hasManyThroughUpsert.test.js +++ b/test/hasManyThroughUpsert.test.js @@ -38,7 +38,11 @@ describe('hasManyThrough upsert', function () { MovieCategoryAssoc.belongsTo(Movie) MovieCategoryAssoc.belongsTo(Category) - makeData().then(done()) + makeData().then(function () { + done() + }).catch(function (err) { + done(err) + }) app.use(loopback.rest()) JSONAPIComponent(app, {restApiRoot: ''}) }) @@ -112,6 +116,35 @@ describe('hasManyThrough upsert', function () { }) }) + it('should handle string IDs', function(done){ + var agent = request(app) + agent + .patch('/movies/1') + .send({ + "data": { + "id": '1', + "type": "movies", + "attributes": { + "name": "The Shawshank Redemption" + }, + "relationships": { + "categories": { + "data": [ + {"type": "categories", "id": '1'}, + {"type": "categories", "id": '4'} + ] + } + } + } + }).end(function(){ + agent.get('/movieCategoryAssocs/1').end(function(err, res){ + expect(err).to.equal(null) + expect(res).to.have.deep.property('body.data').and.have.property('id').and.equal('1') + done() + }) + }) + }) + it('should handle PATCH with less assocs', function (done) { var agent = request(app) agent @@ -149,7 +182,7 @@ describe('hasManyThrough upsert', function () { }) }) -function makeData (done) { +function makeData () { var createMovie = denodeifyCreate(Movie) var createCategory = denodeifyCreate(Category) var createAssoc = denodeifyCreate(MovieCategoryAssoc) From bcd757b51307d6422f0079c0c50717621e6ba827 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Sun, 11 Jun 2017 11:38:12 +0200 Subject: [PATCH 6/8] fix(tests): Fix broken tests after rebase --- test/hasManyThroughUpsert.test.js | 154 +++++++++++++++--------------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/test/hasManyThroughUpsert.test.js b/test/hasManyThroughUpsert.test.js index 7baa923..a6e7e88 100644 --- a/test/hasManyThroughUpsert.test.js +++ b/test/hasManyThroughUpsert.test.js @@ -1,3 +1,5 @@ +/* global describe, beforeEach, it */ + var request = require('supertest') var loopback = require('loopback') var expect = require('chai').expect @@ -14,17 +16,17 @@ describe('hasManyThrough upsert', function () { var ds = loopback.createDataSource('memory') // create models Movie = ds.createModel('movie', { - id: {type: Number, id: true}, + id: { type: Number, id: true }, name: String }) Category = ds.createModel('category', { - id: {type: Number, id: true}, + id: { type: Number, id: true }, name: String }) MovieCategoryAssoc = ds.createModel('movieCategoryAssoc', { - id: {type: Number, id: true} + id: { type: Number, id: true } }) // add models @@ -38,25 +40,24 @@ describe('hasManyThrough upsert', function () { MovieCategoryAssoc.belongsTo(Movie) MovieCategoryAssoc.belongsTo(Category) - makeData().then(function () { - done() - }).catch(function (err) { - done(err) - }) + makeData() + .then(function () { + done() + }) + .catch(function (err) { + done(err) + }) app.use(loopback.rest()) - JSONAPIComponent(app, {restApiRoot: ''}) + JSONAPIComponent(app, { restApiRoot: '' }) }) it('should make initial data', function (done) { - - request(app) - .get('/movies/1/categories') - .end(function (err, res) { - expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) - expect(res).to.have.deep.property('body.data[0]').and.have.property('attributes.name').and.equal('Crime') - done(err) - }) + request(app).get('/movies/1/categories').end(function (err, res) { + expect(err).to.equal(null) + expect(res.body.data.length).to.equal(2) + expect(res.body.data[0].attributes.name).to.equal('Crime') + done(err) + }) }) it('should handle POST', function (done) { @@ -64,23 +65,22 @@ describe('hasManyThrough upsert', function () { agent .post('/movies') .send({ - 'data': { - 'type': 'movies', - 'attributes': { - 'name': 'Ace Ventura: Pet Detective' + data: { + type: 'movies', + attributes: { + name: 'Ace Ventura: Pet Detective' }, - 'relationships': { - 'categories': { - 'data': [ - {'type': 'categories', 'id': 4} - ] + relationships: { + categories: { + data: [{ type: 'categories', id: 4 }] } } } - }).end(function () { + }) + .end(function () { agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) + expect(res.body.data.length).to.equal(3) done() }) }) @@ -91,55 +91,54 @@ describe('hasManyThrough upsert', function () { agent .patch('/movies/1') .send({ - 'data': { - 'id': 1, - 'type': 'movies', - 'attributes': { - 'name': 'The Shawshank Redemption' + data: { + id: 1, + type: 'movies', + attributes: { + name: 'The Shawshank Redemption' }, - 'relationships': { - 'categories': { - 'data': [ - {'type': 'categories', 'id': 1}, - {'type': 'categories', 'id': 2}, - {'type': 'categories', 'id': 3} + relationships: { + categories: { + data: [ + { type: 'categories', id: 1 }, + { type: 'categories', id: 2 }, + { type: 'categories', id: 3 } ] } } } - }).end(function () { + }) + .end(function () { agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(3) + expect(res.body.data.length).to.equal(3) done() }) }) }) - it('should handle string IDs', function(done){ + it('should handle string IDs', function (done) { var agent = request(app) agent .patch('/movies/1') .send({ - "data": { - "id": '1', - "type": "movies", - "attributes": { - "name": "The Shawshank Redemption" + data: { + id: '1', + type: 'movies', + attributes: { + name: 'The Shawshank Redemption' }, - "relationships": { - "categories": { - "data": [ - {"type": "categories", "id": '1'}, - {"type": "categories", "id": '4'} - ] + relationships: { + categories: { + data: [{ type: 'categories', id: '1' }, { type: 'categories', id: '4' }] } } } - }).end(function(){ - agent.get('/movieCategoryAssocs/1').end(function(err, res){ + }) + .end(function () { + agent.get('/movieCategoryAssocs/1').end(function (err, res) { expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('id').and.equal('1') + expect(res.body.data.id).to.equal('1') done() }) }) @@ -150,31 +149,29 @@ describe('hasManyThrough upsert', function () { agent .patch('/movies/1') .send({ - 'data': { - 'id': 1, - 'type': 'movies', - 'attributes': { - 'name': 'The Shawshank Redemption' + data: { + id: 1, + type: 'movies', + attributes: { + name: 'The Shawshank Redemption' }, - 'relationships': { - 'categories': { - 'data': [ - {'type': 'categories', 'id': 1}, - {'type': 'categories', 'id': 4} - ] + relationships: { + categories: { + data: [{ type: 'categories', id: 1 }, { type: 'categories', id: 4 }] } } } - }).end(function (err, res) { + }) + .end(function (err, res) { expect(err).to.equal(null) agent.get('/movieCategoryAssocs').end(function (err, res) { expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) + expect(res.body.data.length).to.equal(2) agent.get('/movies/1/categories').end(function (err, res) { expect(err).to.equal(null) - expect(res).to.have.deep.property('body.data').and.have.property('length').and.equal(2) - expect(res).to.have.deep.property('body.data[1].attributes.name').and.equal('Comedy') + expect(res.body.data.length).to.equal(2) + expect(res.body.data[1].attributes.name).to.equal('Comedy') done() }) }) @@ -188,20 +185,19 @@ function makeData () { var createAssoc = denodeifyCreate(MovieCategoryAssoc) return RSVP.hash({ - movie: createMovie({name: 'The Shawshank Redemption'}), + movie: createMovie({ name: 'The Shawshank Redemption' }), categories: RSVP.all([ createCategory({ name: 'Crime' }), createCategory({ name: 'Drama' }), createCategory({ name: 'History' }), createCategory({ name: 'Comedy' }) ]) + }).then(function (models) { + return RSVP.all([ + createAssoc({ movieId: models.movie.id, categoryId: models.categories[0].id }), + createAssoc({ movieId: models.movie.id, categoryId: models.categories[2].id }) + ]) }) - .then(function (models) { - return RSVP.all([ - createAssoc({ movieId: models.movie.id, categoryId: models.categories[0].id }), - createAssoc({ movieId: models.movie.id, categoryId: models.categories[2].id }) - ]) - }) function denodeifyCreate (Model) { return RSVP.denodeify(Model.create.bind(Model)) From d31c9f26cb90647d42c0e7532161bab60ecc6537 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Sun, 11 Jun 2017 11:44:13 +0200 Subject: [PATCH 7/8] chore(dependencies): Add snazzy for standard js --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9616757..1a1e2d4 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "test": "npm run lint && istanbul cover _mocha --report lcovonly --reporter=spec ./test/**/*.test.js && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", "tester": "mocha --reporter=spec ./test/**/*.test.js", "coverage": "istanbul cover _mocha ./test/**/*.test.js", - "lint": "standard test/**/*.js lib/**/*.js", + "lint": "standard ./test/**/*.js ./lib/**/*.js --verbose | snazzy", "semantic-release": "semantic-release pre && npm publish && semantic-release post" }, "repository": { @@ -32,6 +32,7 @@ "http-status-codes": "^1.0.5", "inflection": "^1.7.2", "lodash": "^4.17.1", + "snazzy": "^7.0.0", "type-is": "^1.6.14" }, "devDependencies": { From fb53937a89cea90048d4417ce6d531108670475c Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Sun, 11 Jun 2017 11:56:09 +0200 Subject: [PATCH 8/8] Replace debug with console.log --- lib/relationships.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/relationships.js b/lib/relationships.js index ae4b86a..fb3999f 100644 --- a/lib/relationships.js +++ b/lib/relationships.js @@ -2,6 +2,7 @@ var _ = require('lodash') var utils = require('./utils') +var debug = require('debug')('loopback-component-jsonapi') module.exports = function (app, options) { // get remote methods. @@ -176,7 +177,7 @@ function keyByModel (assocModel, model) { }) if (key === null) { - console.log('Can not find relation for ' + model.modelName) + debug('Can not find relation for ' + model.modelName) } return key }