Skip to content

Commit

Permalink
Merge pull request #248 from digitalsadhu/fix_and_refactor_updating_o…
Browse files Browse the repository at this point in the history
…f_relationships

Fix and refactor updating of relationships
  • Loading branch information
digitalsadhu authored Sep 14, 2017
2 parents a6ee62d + 06b6247 commit e4dd6b1
Show file tree
Hide file tree
Showing 45 changed files with 672 additions and 248 deletions.
2 changes: 2 additions & 0 deletions lib/create.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var url = require('url')
var utils = require('./utils')
var statusCodes = require('http-status-codes')
Expand Down
2 changes: 2 additions & 0 deletions lib/delete.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var utils = require('./utils')
var statusCodes = require('http-status-codes')

Expand Down
2 changes: 2 additions & 0 deletions lib/deserialize.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

/* global require,module */
var deserializer = require('./deserializer')
var RelUtils = require('./utilities/relationship-utils')
Expand Down
2 changes: 2 additions & 0 deletions lib/deserializer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var _ = require('lodash')

function defaultBeforeDeserialize (options, cb) {
Expand Down
2 changes: 2 additions & 0 deletions lib/errors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var debug
var statusCodes = require('http-status-codes')

Expand Down
1 change: 1 addition & 0 deletions lib/headers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict'

var is = require('type-is')
var _ = require('lodash')
var utils = require('./utils')
Expand Down
2 changes: 2 additions & 0 deletions lib/patch.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var statusCodes = require('http-status-codes')

module.exports = function (app, options) {
Expand Down
2 changes: 2 additions & 0 deletions lib/querystring.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var utils = require('./utils')

module.exports = function (app, options) {
Expand Down
162 changes: 30 additions & 132 deletions lib/relationships.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

var _ = require('lodash')
var utils = require('./utils')
var debug = require('debug')('loopback-component-jsonapi')
const linkRelatedModels = require(
'./utilities/relationship-utils'
).linkRelatedModels

module.exports = function (app, options) {
// get remote methods.
Expand All @@ -24,9 +26,7 @@ module.exports = function (app, options) {
data = options.data
model = utils.getModelFromContext(ctx, app)

relationships(id, data, model)

next()
relationships(model, id, data).then(() => next()).catch(err => next(err))
})

// for create
Expand All @@ -41,144 +41,42 @@ module.exports = function (app, options) {
id = ctx.result.data.id
data = options.data
model = utils.getModelFromContext(ctx, app)
relationships(id, data, model)
return relationships(model, id, data)
.then(() => next())
.catch(err => next(err))
}

next()
})
}

function relationships (id, data, model) {
if (!data || !data.data || !id || !model || !data.data.relationships) {
return
function extractIdsFromResource (resource) {
if (_.isArray(resource)) {
return _.map(resource, 'id')
}

_.each(data.data.relationships, function (relationship, name) {
var serverRelation = model.relations[name]
if (!serverRelation) return
var type = serverRelation.type

// don't handle belongsTo in relationships function
if (type === 'belongsTo') return

var modelTo = serverRelation.modelTo

var fkName = serverRelation.keyTo

if (type === 'belongsTo') {
fkName = serverRelation.keyFrom
modelTo = serverRelation.modelFrom
}

if (!modelTo) {
return false
}

var setTo = {}
setTo[fkName] = null
var where = {}
where[fkName] = id

// remove all relations to the model (eg .: post/1)
if (type !== 'belongsTo') {
modelTo.updateAll(where, setTo, function (err, info) {
if (err) console.log(err)
})
}

var idToFind = null

if (_.isArray(relationship.data)) {
// find all instance from the relation data eg
// [{type: "comments", id: 1}, {type: "comments", id: 2}]
_.each(relationship.data, function (item) {
idToFind = item.id

if (type === 'belongsTo') {
where[fkName] = item.id
idToFind = id
}

updateRelation(modelTo, idToFind, where)
})

if (serverRelation.modelThrough) {
var modelThrough = serverRelation.modelThrough
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, fields: key }, function (
err,
instances
) {
if (err) return
var serverIds = _.map(instances, function (instance) {
return stringIds ? instance[key].toString() : 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
updateRelation(model, id, where)
return
}

idToFind = relationship.data.id

if (type === 'belongsTo') {
idToFind = id
where[fkName] = relationship.data.id
}
// relationship: {data: {type: "comments": id: 1}}
updateRelation(modelTo, idToFind, where)
}
})
return _.get(resource, 'id', null)
}

// if the instance exist, then update it (create relationship),
// according to JSON API spec we MUST NOT create new ones
function updateRelation (model, id, data) {
model.findById(id, function (err, instance) {
if (err) console.log(err)
function relationships (model, id, payload) {
if (!id || !model) return
const relationships = _.get(payload, 'data.relationships', {})

if (instance) {
instance.updateAttributes(data)
}
})
}
return Promise.all(
Object.keys(relationships).map(relationName => {
const relationship = relationships[relationName]
const relationDefn = model.relations[relationName]
if (!relationDefn) return

function keyByModel (assocModel, model) {
var key = null
_.each(assocModel.relations, function (relation) {
if (relation.modelTo.modelName === model.modelName) {
key = relation.keyFrom
}
})
const type = relationDefn.type
const modelTo = relationDefn.modelTo

if (key === null) {
debug('Can not find relation for ' + model.modelName)
}
return key
// don't handle belongsTo in relationships function
if (!modelTo || type === 'belongsTo') return

const data = extractIdsFromResource(relationship.data)
const from = { model, id }
const to = { model: modelTo, data }
return linkRelatedModels(relationName, from, to)
})
)
}
2 changes: 2 additions & 0 deletions lib/removeRemoteMethods.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

module.exports = function (app, options) {
var models = app.models

Expand Down
13 changes: 10 additions & 3 deletions lib/serialize.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

/* global module,require */
var serializer = require('./serializer')
var utils = require('./utils')
Expand Down Expand Up @@ -83,7 +85,8 @@ module.exports = function (app, defaults) {
var relation = model.relations[relationName]
if (relationName && relation) {
if (relation.polymorphic && utils.relationFkOnModelFrom(relation)) {
var discriminator = utils.clone(ctx.instance)[relation.polymorphic.discriminator]
let discriminator = relation.polymorphic.discriminator
discriminator = utils.clone(ctx.instance)[discriminator]
relatedModel = app.models[discriminator]
} else {
relatedModel = relation.modelTo
Expand All @@ -102,7 +105,9 @@ module.exports = function (app, defaults) {

// If we're sideloading, we need to add the includes
if (ctx.req.isSideloadingRelationships) {
requestedIncludes = utils.setRequestedIncludes(ctx.req.remotingContext.args.filter.include)
requestedIncludes = utils.setRequestedIncludes(
ctx.req.remotingContext.args.filter.include
)
}

if (model.definition.settings.scope) {
Expand All @@ -119,7 +124,9 @@ module.exports = function (app, defaults) {
if (typeof include === 'string') {
requestedIncludes.push(include)
} else if (_.isArray(include)) {
requestedIncludes = requestedIncludes.concat(utils.setRequestedIncludes(include))
requestedIncludes = requestedIncludes.concat(
utils.setRequestedIncludes(include)
)
}
}

Expand Down
32 changes: 22 additions & 10 deletions lib/serializer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var _ = require('lodash')
var RelUtils = require('./utilities/relationship-utils')
var utils = require('./utils')
Expand Down Expand Up @@ -403,14 +405,13 @@ function handleIncludes (resp, includes, relations, options) {
)
})
embeds = _.compact(embeds)

resource.relationships[include].data = resource.attributes[include]
.map(function (relData) {
return {
id: String(relData[propertyKey]),
type: plural
}
})
const included = resource.attributes[include]
resource.relationships[include].data = included.map(relData => {
return {
id: String(relData[propertyKey]),
type: plural
}
})
} else {
var rel = utils.clone(resource.attributes[include])
var compoundIncludes = createCompoundIncludes(
Expand Down Expand Up @@ -471,14 +472,25 @@ function handleIncludes (resp, includes, relations, options) {
* @param {Object} options
* @return {Object}
*/
function createCompoundIncludes (relationship, key, fk, type, includedRelations, options) {
function createCompoundIncludes (
relationship,
key,
fk,
type,
includedRelations,
options
) {
var compoundInclude = makeRelation(type, String(relationship[key]))

if (options && !_.isEmpty(includedRelations)) {
var defaultModelPath = options.modelPath
options.modelPath = type

compoundInclude.relationships = parseRelations(relationship, includedRelations, options)
compoundInclude.relationships = parseRelations(
relationship,
includedRelations,
options
)

options.modelPath = defaultModelPath
}
Expand Down
2 changes: 2 additions & 0 deletions lib/update.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

var utils = require('./utils')

module.exports = function (app, options) {
Expand Down
Loading

0 comments on commit e4dd6b1

Please sign in to comment.