API Docs for: 0.2.1
Show:

File: addon/utils/related-proxy.js

/**
  @module ember-jsonapi-resources
  @submodule utils
  @main RelatedProxyUtil
**/

import Ember from 'ember';
import RSVP from 'rsvp';
import { pluralize } from 'ember-inflector';

/**
  Utility for creating promise proxy objects for related resources

  @class RelatedProxyUtil
  @static
  @final
*/
const RelatedProxyUtil = Ember.Object.extend({

  /**
    Checks for required `relationship` property

    @method init
  */
  init: function () {
    this._super();
    if (typeof this.get('relationship') !== 'string') {
      throw new Error('RelatedProxyUtil#init expects `relationship` property to exist.');
    }
    return this;
  },

  /**
    The name of the relationship

    @property resource
    @type String
    @required
  */
  relationship: null,

  /**
    The name of the type of resource

    @property type
    @type String
    @required
  */
  type: null,

  /**
    Proxy for the requested relation, resolves w/ content from fulfilled promise

    @method createProxy
    @param {Resource} resource
    @param {String} kind 'toMany' or 'toOne'
    @return {PromiseProxy|ObjectProxy|ArrayProxy} proxy instance, new resource uses mock relations
  */
  createProxy(resource, kind) {
    let proxyFactory, newContent;
    if (kind === 'toMany') {
      proxyFactory = Ember.ArrayProxy;
      newContent = Ember.A([]);
    } else if (kind === 'toOne') {
      proxyFactory = Ember.ObjectProxy;
      newContent = Ember.Object.create();
    }
    if (resource.get('isNew')) {
      return proxyFactory.create({ content: newContent });
    } else {
      let proxy = this.proxySetup(resource, kind, proxyFactory);
      return this.proxyResolution(resource, proxy);
    }
  },

  /**
    @method proxySetup
    @param {Resource} resource
    @param {String} kind 'toMany' or 'toOne'
    @param {Ember.ObjectProxy|Ember.ArrayProxy} proxyFactory
    @return {PromiseProxy} proxy
  */
  proxySetup(resource, kind, proxyFactory) {
    let relation = this.get('relationship');
    let type = this.get('type');
    let url = this.proxyUrl(resource, relation);
    let owner = Ember.getOwner(resource);
    let service = owner.lookup('service:' + pluralize(type));
    let promise = this.promiseFromCache(resource, relation, service);
    promise = promise || service.findRelated({'relation': relation, 'type': type}, url);
    let proxyProto = proxyFactory.extend(Ember.PromiseProxyMixin, {
      'promise': promise, 'type': relation, 'kind': kind
    });
    return proxyProto.create({
      content: (kind === 'toOne') ? Ember.Object.create() : Ember.A([])
    });
  },

  /**
    @method proxyResolution
    @param {proxy} resource
    @return {PromiseProxy} proxy
  */
  proxyResolution(resource, proxy) {
    proxy.then(
      function (resources) {
        proxy.set('content', resources);
        let relation = proxy.get('type');
        let kind = proxy.get('kind');
        resource.didResolveProxyRelation(relation, kind, resources);
        return resources;
      },
      function (error) {
        Ember.Logger.error(error);
        throw error;
      }
    );
    return proxy;
  },

  /**
    Proxy url to fetch for the resource's relation

    @method proxyUrl
    @param {Resource} resource
    @param {String} relation
    @return {PromiseProxy} proxy
  */
  proxyUrl(resource, relation) {
    let related = linksPath(relation);
    let url = resource.get(related);
    if (typeof url !== 'string') {
      throw new Error('RelatedProxyUtil#_proxyUrl expects `model.'+ related +'` property to exist.');
    }
    return url;
  },

  /**
    Lookup relation from service cache and pomisify result

    @method promiseFromCache
    @param {Resource} resource
    @param {String} relation
    @param {Object} service
    @return {Promise|null}
  */
  promiseFromCache(resource, relation, service) {
    let data = resource.get('relationships.' + relation + '.data');
    if (!data) { return; }
    let content, found;
    if (Array.isArray(data)) {
      content = Ember.A([]);
      for (let i = 0; i < data.length; i++) {
        found = this.serviceCacheLookup(service, data[i]);
        if (found) {
          content.push(found);
        }
      }
      content = (data.length && data.length === content.length) ? content : null;
    } else {
      content = this.serviceCacheLookup(service, data);
    }
    return (content) ? RSVP.Promise.resolve(content) : null;
  },

  /**
    Lookup data in service cache

    @method serviceCacheLookup
    @param {Object} service
    @param {Object} data
    @return {Resource|undefined}
  */
  serviceCacheLookup(service, data) {
    return (typeof data === 'object' && data.id) ? service.cacheLookup(data.id) : undefined;
  }
});

export default RelatedProxyUtil;

/**
  @method linksPath
  @param {String} relation
  @return {String} path to the related link
*/
export function linksPath(relation) {
  return ['relationships', relation, 'links', 'related'].join('.');
}