simple-jas
Version:
A simple, smart, minimum configuration JSONAPI serializer.
197 lines (160 loc) • 5.56 kB
JavaScript
export default {
serialize (dataObject, other = {}) {
return Object.assign({}, { data: this.serializeData(dataObject) }, other)
},
serializeData (object) {
let data = {
type: object.type,
attributes: {},
relationships: {}
}
if (object.id) {
data.id = object.id
}
Object.keys(object).forEach(key => {
let value = object[key]
// Ignore type attribute
if (key === 'type') { return }
// Match relationship base on key
if (this._endsWith(key, 'Id')) {
let relationshipKey = key.slice(0, -2)
let type = relationshipKey
data.relationships[relationshipKey] = { data: { id: value, type: type } }
return
}
// Match relationship base on key & value
if (this._endsWith(key, 'Ids') && Array.isArray(value)) {
let relationshipKey = key.slice(0, -3)
let type = relationshipKey
let rioArray = []
value.forEach(item => {
// If item is an object we assume there is an id attribute
if (this._isObject(item)) {
let resourceIdentifierObject = { id: item.id, type: item.type }
resourceIdentifierObject.type = resourceIdentifierObject.type || type
rioArray.push(resourceIdentifierObject)
// If item is not an object we assume it is an id
} else {
rioArray.push({ id: item, type: type })
}
})
data.relationships[relationshipKey] = { data: rioArray }
return
}
// Match relationship base on value
if (Array.isArray(value) && this._isObject(value[0])) {
let type = key
let rioArray = []
value.forEach(item => {
let resourceIdentifierObject = { id: item.id, type: item.type }
resourceIdentifierObject.type = resourceIdentifierObject.type || type
rioArray.push(resourceIdentifierObject)
})
data.relationships[key] = { data: rioArray }
return
}
// Match relationship base on value
if (this._isObject(value) && value.id) {
let type = key
let rio = { id: value.id, type: value.type }
rio.type = rio.type || type
data.relationships[key] = { data: rio }
return
}
// Match attributes
data.attributes[key] = value
})
return data
},
deserialize (payload) {
let deserializedData = this.deserializeData(payload.data, payload.included || [])
if (!payload.meta) {
return { data: deserializedData }
}
return { data: deserializedData, meta: payload.meta }
},
deserializeData (data, included = []) {
if (Array.isArray(data)) {
return this._deserializeArrayData(data, included)
}
return this._deserializeResourceData(data, included)
},
deserializeErrors (errors) {
let errorObjects = { }
errors.forEach(item => {
let pointerArray = item.source.pointer.split('/')
let index = 3
if (pointerArray.length === 3) { index = 2 }
errorObjects[pointerArray[index]] = errorObjects[pointerArray[index]] || []
errorObjects[pointerArray[index]].push(item)
})
return errorObjects
},
_deserializeArrayData (data, included = {}) {
let objectArray = []
data.forEach(resourceObject => {
let object = this._deserializeResourceData(resourceObject, included)
objectArray.push(object)
})
return objectArray
},
_deserializeResourceData (data, included = {}, deserializationTree = []) {
let object = {}
object.id = data.id
object.type = data.type
let newTree = deserializationTree.concat([{ type: data.type, id: object.id }])
if (data.attributes) {
Object.keys(data.attributes).forEach(name => {
object[name] = data.attributes[name]
})
}
if (data.relationships) {
Object.keys(data.relationships).forEach(name => {
let value = data.relationships[name]
if (Array.isArray(value.data)) {
object[name] = []
value.data.forEach(rio => {
let relationshipData = this._find(included, { type: rio.type, id: rio.id })
if (relationshipData) {
let relationshipObject = this._deserializeResourceData(relationshipData, included, newTree)
object[name].push(relationshipObject)
}
})
}
if (this._isObject(value.data)) {
if (this._find(deserializationTree, { type: value.data.type, id: value.data.id })) {
object[name] = value.data
} else {
let rio = value.data
let relationshipData = this._find(included, { type: rio.type, id: rio.id })
if (relationshipData) {
object[name] = this._deserializeResourceData(relationshipData, included, newTree)
} else {
object[name] = rio
}
}
}
if (value.data === null) {
object[name] = null
}
})
}
return object
},
_find (collection, target) {
for (var i = 0; i < collection.length; i++) {
if (collection[i].id === target.id && collection[i].type === target.type) {
return collection[i]
}
}
},
_isObject (target) {
return target && !Array.isArray(target) && typeof target === 'object'
},
_endsWith(str, search) {
if (!String.prototype.endsWith) {
return str.substring(str.length - search.length, str.length) === search;
}
return str.endsWith(search)
}
}