sarala-json-api-data-formatter
Version:
Simple and fluent framework agnostic javascript library to transform standard JSON API responses to simple JSON objects and vice versa.
238 lines (183 loc) • 6.25 kB
JavaScript
import {
each,
find,
forEach,
forOwn,
indexOf,
isEmpty,
isUndefined,
isArray,
map
} from 'lodash'
export default class Formatter {
constructor () {
this.data = {}
this.includes = null
this.fields = null
this.includedData = []
}
includeOnly (includes = []) {
this.includes = includes
return this
}
filterFields (fields = {}) {
this.fields = fields
return this
}
shouldIncludeRelation (relation) {
if (this.includes === null) {
return true
}
return indexOf(this.includes, relation) !== -1
}
shouldIncludeField (relation, field) {
if (this.fields === null) {
return true
}
if (!Object.prototype.hasOwnProperty.call(this.fields, relation)) {
return true
}
if (indexOf(this.fields[relation], field) !== -1) {
return true
}
return false
}
deserialize (data) {
this.data = data
if (isArray(data.data)) {
return this.deserializeCollection(data)
}
return this.deserializeOne(data.data)
}
deserializeOne (data) {
const formatted = {}
formatted.id = data.id
formatted.type = data.type
if (data.links) {
formatted.links = data.links
}
if (data.meta) {
formatted.meta = data.meta
}
forOwn(data.attributes, (value, key) => {
if (this.shouldIncludeField(data.type, key)) {
formatted[key] = value
}
})
if (data.relationships) {
formatted.relationships = []
for (const key in data.relationships) {
if (this.shouldIncludeRelation(key)) {
formatted.relationships.push(key)
const relationship = this.mapAndKillProps(data.relationships[key], {}, ['links', 'meta']).to
if (isArray(data.relationships[key].data)) {
relationship.data = data.relationships[key].data ? this.resolveRelationCollection(data.relationships[key].data) : []
} else {
relationship.data = data.relationships[key].data ? this.resolveRelation(data.relationships[key].data) : null
}
formatted[key] = relationship
}
}
}
return formatted
}
deserializeCollection (data) {
data.data = map(data.data, item => {
return this.deserializeOne(item)
})
return data
}
resolveRelation (data) {
return this.deserializeOne(find(this.data.included, data))
}
resolveRelationCollection (relations) {
return map(relations, relation => {
return this.resolveRelation(relation)
})
}
mapAndKillProps (from, to, props) {
each(props, prop => {
if (Object.prototype.hasOwnProperty.call(from, prop)) {
to[prop] = from[prop]
delete from[prop]
}
})
return { from, to }
}
isSerializeableCollection (data) {
return Object.prototype.hasOwnProperty.call(data, 'data') && isArray(data.data)
}
serialize (data) {
this.includedData = []
let serialized = {}
if (Object.prototype.hasOwnProperty.call(data, 'relationships')) {
serialized.included = []
}
if (this.isSerializeableCollection(data)) {
serialized = this.serializeCollection(data)
} else {
serialized.data = this.serializeOne(data)
}
if (this.includedData.length) {
serialized.included = this.includedData
}
return serialized
}
serializeOne (data) {
let serialized = {
attributes: {},
relationships: {}
}
const mapAndKilled = this.mapAndKillProps(data, serialized, ['id', 'type', 'links', 'meta'])
data = mapAndKilled.from
serialized = mapAndKilled.to
if (Object.prototype.hasOwnProperty.call(data, 'relationships')) {
forEach(data.relationships, relationship => {
if (this.shouldIncludeRelation(relationship)) {
const relationshipData = this.mapAndKillProps(data[relationship], {}, ['links', 'meta']).to
if (this.isSerializeableCollection(data[relationship])) {
relationshipData.data = this.serializeRelationshipCollection(data[relationship].data)
} else {
relationshipData.data = data[relationship].data === null ? null : this.serializeRelationship(data[relationship].data)
}
serialized.relationships[relationship] = relationshipData
}
delete data[relationship]
})
delete data.relationships
}
forOwn(data, (value, key) => {
if (this.shouldIncludeField(serialized.type, key)) {
serialized.attributes[key] = value
}
})
if (isEmpty(serialized.relationships)) {
delete serialized.relationships
}
return serialized
}
serializeCollection (data) {
const mapAndKilled = this.mapAndKillProps(data, {}, ['links', 'meta'])
data = mapAndKilled.from
const serialized = mapAndKilled.to
serialized.data = map(data.data, item => {
return this.serializeOne(item)
})
return serialized
}
serializeRelationship (data) {
const serialized = this.serializeOne(data)
this.addToIncludes(serialized)
return { type: serialized.type, id: serialized.id }
}
serializeRelationshipCollection (data) {
return map(data, item => {
return this.serializeRelationship(item)
})
}
addToIncludes (data) {
if (isUndefined(find(this.includedData, { id: data.id, type: data.type }))) {
this.includedData.push(data)
}
}
}