lib/Siren.js
import Promise from 'bluebird';
import Immutable from 'immutable';
import LinkedSubEntity from './LinkedSubEntity';
import SirenLink from './SirenLink';
import SirenAction from './SirenAction';
import _ from 'lodash';
import Client from './Client';
import SirenHelpers from './SirenHelpers';
Client.addParser('application/vnd.siren+json', (text, requestUrl) => Siren.fromJson(JSON.parse(text), requestUrl));
Client.addHeader('Cache-Control', 'no-cache,no-store,must-revalidate,max-age=-1');
Client.addHeader('Accept', 'application/vnd.siren+json');
Client.addHeader('Content-Type', 'application/json');
var client = new Client();
/**
* @class Siren
* Immutable Siren entity. This is the main entrypoint for all Siren operations.
*/
class Siren extends Immutable.Record({
classes: Immutable.Set(),
properties: Immutable.Map(),
entities: Immutable.Set(),
actions: Immutable.Map(),
links: Immutable.Set()
}) {
constructor(args) {
if (!args && Siren.empty) {
return Siren.empty;
}
else {
super(args);
}
}
/**
* Finds the @see {@link SirenAction} referenced by the provided rel.
*
* @param {String} name The name of the action to find.
* @return {SirenAction} SirenAction matching the requested name. null if none is found.
*/
findActionByName(name) {
return this.actions.get(name) || null;
}
/**
* Finds the first @See {@link SirenLink} referenced by the provided rel.
*
* @param {String} rel The relation to this Siren entity for the requested link.
* @return {SirenLink} SirenLink matching the requested rel. null if none is found.
*/
findLinkByRel(rel) {
return this.links.filter(link => link.rels.contains(rel)).first() || null;
}
/**
* Finds the @See {@link EmbeddedSubEntity}|{@link LinkedSubEntity} entities referenced by the provided rel.
*
* @param {String} rel The relation to this Siren entity for the requested sub-entity.
* @return {Immutable.Set} Set of sub-entities matching the requested rel.
*/
findEntitiesByRel(rel) {
return this.entities.filter(item => item.rels.contains(rel));
}
/**
* Returns the sub-entities on this Siren object which are embedded sub-entities.
*
* @return {Immutable.Set} Set of embedded sub-entities.
*/
get embeddedEntities() {
return this.entities
.filter(item => item instanceof EmbeddedSubEntity)
.toSet();
}
/**
* Returns the sub-entities on this Siren object which are embedded sub-entities.
*
* @param {String} rel Only entities with a relation to the parent siren matching this should be returned.
* @return {Immutable.Set} Set of embedded sub-entities which match thes provided rel.
*/
embeddedEntitiesByRel(rel) {
return this.embeddedEntities
.filter(item => item.rels.contains(rel));
}
/**
* Returns the sub-entities on the Siren object which are linked sub-entities.
*
* @return {Immutable.Set} Set of linked sub-entities on this Siren object.
*/
get linkedEntities() {
return this.entities
.filter(item => item instanceof LinkedSubEntity)
.toSet();
}
/**
* Returns the set of linked sub-entities on the Siren object which match the requested rel.
*
* @param {String} rel Only entities with this relation to the parent siren should be returned.
* @return {Immutable.Set} Set of linked sub-entities which match the provided rel.
*/
linkedEntitiesByRel(rel) {
return this.linkedEntities
.filter(item => item.rels.contains(rel));
}
/**
* Returns the self link for this entity
*
* @return {SirenLink} link represented by the self rel, null if no self link is found.
*/
get selfLink() {
return this.findLinkByRel('self');
}
//TODO: add a validate method and use it while parsing
/**
* Parses a JSON representation of a Siren entity
* and returns the Siren representation.
* @param {Object} [obj] The JSON object to be parsed as Siren
* @param {String} [baseUrl=null] Optional base URL to use for relative URL parsing
* @return {Siren} Parsed Siren entity
*/
static fromJson(obj, baseUrl = null) {
return Siren.empty.withMutations(map => {
map.set('classes', map.classes.union(obj.class ? Immutable.fromJS(obj.class) : new Immutable.List()));
for (var key in obj.properties) {
map.set('properties', map.properties.set(key, obj.properties[key]));
}
map.set('links',
new Immutable.Set(
_.map(obj.links || [], (item) => new SirenLink(item.rel, SirenHelpers.processUrl(item.href, baseUrl)))
)
);
map.set('actions',
new Immutable.Map(
_.map(obj.actions || [], (item) => SirenAction.fromJson(item, baseUrl))
.map(action => [action.name, action])
)
);
map.set('entities',
new Immutable.Set(
_.map(obj.entities || [], (item) => item.href ? LinkedSubEntity.fromJson(item, baseUrl) : EmbeddedSubEntity.fromJson(item, baseUrl))
)
);
});
}
/**
* Returns an empty siren representation. This Siren entity
* contains no afforances.
* @return {Siren} Empty siren structure
*/
static get empty() {
return emptySiren;
}
/**
* Returns a Superagent Promise instance which will perform an HTTP Get against
* the provided href returning the response as a SuperAgent response.
* If the response is Siren ('application/vnd.siren+json'),
* then the body should be a Siren instance.
*
* @param {String} href The URL to perform an HTTP get against
* @return {superagent-promise} Superagent Promise Object
*/
static get(href) {
return Client.get(href);
}
}
var emptySiren = new Siren();
/**
* @class EmbeddedSubEntity
* Entity which has been embedded within a parent Siren instance.
*
* @param {Array} options.rels: new Immutable.Set() array of strings to identify how this
* embedded entity is related to it's parent.
* @param {Object} options.entity: Siren.empty embedded entity instance
*/
class EmbeddedSubEntity extends Immutable.Record({
rels: new Immutable.Set(),
entity: Siren.empty
}) {
/**
* Parses the provided JSON representation of the Siren sub entity
* into an instance of an EmbeddedSubEntity.
*
* @param {Object} json The JSON representation of a siren embedded sub entity.
* @param {String} [baseUrl=null] Optional base URL to use in case URLs are relative URLs.
* @return {EmbeddedSubEntity} The representation of the parsed JSON.
*/
static fromJson(json, baseUrl=null) {
if (!json.rel || !_.isArray(json.rel) || json.rel.length === 0) {
throw new Error('A rel array is required to parse an embedded sub entity');
}
return EmbeddedSubEntity.empty.withMutations(map => {
map.set('rels', new Immutable.Set(json.rel));
map.set('entity', Siren.fromJson(json, baseUrl));
});
}
/**
* Returns the default empty instance of an EmbeddedSubEntity.
*
* @return {EmbeddedSubEntity} default dembedded sub entity.
*/
static get empty() {
return emptyEmbedded;
}
}
var emptyEmbedded = new EmbeddedSubEntity();
Siren.Link = SirenLink;
Siren.Action = SirenAction;
Siren.LinkedSubEntity = LinkedSubEntity;
Siren.EmbeddedSubEntity = EmbeddedSubEntity;
Siren.Client = Client;
Siren.Helper = SirenHelpers;
export default Siren;