Skip to content

Instantly share code, notes, and snippets.

@jcreamer898
Last active June 15, 2016 15:09
Show Gist options
  • Save jcreamer898/e8ca5acd454eb9c3741200288c69285c to your computer and use it in GitHub Desktop.
Save jcreamer898/e8ca5acd454eb9c3741200288c69285c to your computer and use it in GitHub Desktop.
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;
}));
}
}
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