Last active
June 15, 2016 15:09
-
-
Save jcreamer898/e8ca5acd454eb9c3741200288c69285c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import stringify from "json-stringify-safe"; | |
import clone from "lodash/clone"; | |
import each from "lodash/each"; | |
import find from "lodash/find"; | |
import camelCase from "lodash/camelCase"; | |
const _ = { | |
clone, each, find, camelCase, | |
}; | |
/** | |
* Recursively process a document | |
* @param {object} document A JsonAPI Document | |
* @return {Resource | [Resource]} Returns a resorce or an array of resources | |
*/ | |
function createResourceFromDocument(document) { | |
if (Array.isArray(document.data)) { | |
return document.data.map((d) => createResourceFromDocument({ | |
...d, | |
included: document.included, | |
links: d.links, | |
})); | |
} | |
const doc = document.data ? document.data : document; | |
const links = document.links; | |
const included = document.included; | |
if (!doc.type || !doc.id) { | |
return null; | |
} | |
const { id, type, attributes, relationships, meta } = doc; | |
return new Resource({ | |
id, | |
type, | |
attributes, | |
relationships, | |
meta, | |
links, | |
included, | |
}); | |
} | |
/** | |
* An abstraction over JsonAPI resources | |
* A Resource's attributes will be added as instance properties | |
*/ | |
export default class Resource { | |
static get ignoreRelationships() { | |
return this._ignoreRelationships || ["to"]; | |
} | |
static set ignoreRelationships(value) { | |
this._ignoreRelationships = value; | |
} | |
/** | |
* Creates an instance of a Resource based on a JsonAPI compliant document | |
* @param {object} document The JsonAPI compliant document | |
* @return {Resource} An instance of the Resource class | |
*/ | |
static from(document) { | |
const resource = createResourceFromDocument(document); | |
return Array.isArray(resource) ? | |
resource.map(r => r.toJs()) : | |
resource.toJs(); | |
} | |
/** | |
* Constructor for a resource | |
* @param {string} options | |
* @param {string} options.id="" | |
* @param {string} options.type="" | |
* @param {string} [options.attributes=""] | |
* @param {string} [options.relationships={}] | |
* @param {string} [options.links={}] | |
* @param {string} [options.meta={}] | |
* @param {string} [options.included=[]] | |
* @return {Resource} An instance of a Resource | |
*/ | |
constructor({ | |
id, | |
type, | |
attributes = {}, | |
relationships = {}, | |
links = {}, | |
meta = {}, | |
included = [], | |
}) { | |
this.id = id; | |
this._type = type; | |
this._attributes = attributes; | |
this.relationships = relationships; | |
this.links = links; | |
this._meta = meta; | |
this._included = included; | |
this._create(); | |
} | |
/** | |
* Recursively parses the document and adds relationships and attributes to the instance | |
* @private | |
*/ | |
_create() { | |
_.each(this._attributes, (attribute, key) => { | |
this[_.camelCase(key)] = _.clone(attribute); | |
}); | |
_.each(this.relationships, (rel, key) => { | |
const camelKey = _.camelCase(key); | |
// Admitedly don't love this, but inifinite loops happen when recursing. TM | |
if (!rel.data || Resource.ignoreRelationships.indexOf(key) > -1) { | |
return; | |
} | |
if (Array.isArray(rel.data)) { | |
_.each(rel.data, (relatedResource) => { | |
const related = _.find(this._included, i => | |
i.type === relatedResource.type && | |
i.id === relatedResource.id); | |
if (related) { | |
this[camelKey] = this[camelKey] || []; | |
this[camelKey].push(createResourceFromDocument({ | |
...related, | |
included: this._included, | |
})); | |
} | |
}); | |
} else { | |
const related = _.find(this._included, i => | |
i.type === rel.data.type && | |
i.id === rel.data.id); | |
if (related) { | |
this[camelKey] = createResourceFromDocument({ | |
...related, | |
included: this._included, | |
}); | |
} | |
} | |
}); | |
} | |
/** | |
* Return the JavaScript object for this resource | |
* @return {[type]} [description] | |
*/ | |
toJs() { | |
return JSON.parse(stringify(this, (k, v) => { | |
if (k[0] === "_") return void 0; | |
return v; | |
})); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe("JsonApiResource", () => { | |
it("should perform well", () => { | |
const iterations = 1000; | |
const start = new Date(); | |
for (let i = 0; i < iterations; i++) { | |
Resource.from(placeDoc); | |
} | |
const end = new Date(); | |
const perMs = (end - start) / iterations; // (684ms / 1000) = ops/ms | |
const perSec = perMs * 1000; | |
console.log(perSec); | |
expect(perSec).to.be.lessThan(1000); | |
expect(perSec).to.be.greaterThan(500); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment