All files / module/resource ResourceBuilder.js

82.35% Statements 28/34
71.43% Branches 10/14
55.56% Functions 5/9
81.82% Lines 27/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                          7x                         11x 2x   2x     9x 9x 11x     9x       13x 13x 13x   13x   13x 2x     13x                   2x   2x 6x 4x   4x       4x 4x   4x 2x     4x       6x     6x                                                                
import { hasRelationship } from './hasRelationship'
import { hasLoadableRelationship } from './hasLoadableRelationship'
import { hasLoadedRelationship } from './hasLoadedRelationship'
import { listRelationship } from './listRelationship'
import { loadRelationship } from './loadRelationship'
import { getRelationship } from './getRelationship'
import { deref, hasOwn } from '../../shared/utils'
 
/**
 * @class ResourceBuilder
 */
export class ResourceBuilder {
  constructor (store) {
    this.store = store
  }
 
  /**
  * Returns a new functional resource object
  *
  * Functional resource objects are structurally identical to
  * normal JSON:API resource objects but are enhanced with
  * methods to simplify access to relationships.
  *
  * @param {Object} jsonResourceObject
  */
  build (jsonResourceObject) {
    if (hasOwn(jsonResourceObject, 'id')) {
      const item = deref(jsonResourceObject) // why tho?
 
      return this.functionalizeItem(item)
    }
 
    const items = {}
    for (const itemId in jsonResourceObject) {
      items[itemId] = this.functionalizeItem(deref(jsonResourceObject[itemId]))
    }
 
    return items
  }
 
  functionalizeItem (item) {
    item.hasRelationship = hasRelationship(item)
    item.hasLoadableRelationship = hasLoadableRelationship(item)
    item.hasLoadedRelationship = hasLoadedRelationship(item)
 
    item.get = (attributeName) => hasOwn(item.attributes, attributeName) ? item.attributes[attributeName] : new Error(`attribute "${attributeName}" not found`)
 
    if (hasOwn(item, 'relationships')) {
      this.buildRelationshipMethods(item)
    }
 
    return item
  }
 
  /**
   * Adds Methods (get, load, list) to the relationships for getting / loading them
   * Adds Shorthand Methods (rel / loadRel)
   *
   * @param {Object} jsonResourceObject
   */
  buildRelationshipMethods (obj) {
    const relationships = obj.relationships
 
    for (const currentRelationshipName in relationships) {
      if (obj.hasRelationship(currentRelationshipName)) {
        const relatedObject = relationships[currentRelationshipName]
 
        const relationshipConfig = {
          isToMany: relatedObject.data.constructor === Array
        }
 
        relatedObject.get = getRelationship(this.store, relatedObject, relationshipConfig)
        relatedObject.load = loadRelationship(this.store, obj.id, obj.type, relatedObject, relationshipConfig)
 
        if (relationshipConfig.isToMany) {
          relatedObject.list = listRelationship(this.store, relatedObject)
        }
 
        relationships[currentRelationshipName] = relatedObject
      }
 
      // shorthand variant
      obj.loadRel = (relationshipName) => {
        return loadRelationship(this.store, obj.id, obj.type, relationships[relationshipName], getRelationshipConfig(relationships[relationshipName]))()
      }
      obj.rel = (relationshipName) => {
        if (getRelationshipConfig(relationships[relationshipName]).isToMany) {
          return listRelationship(this.store, relationships[relationshipName])()
        } else {
          return getRelationship(this.store, relationships[relationshipName], getRelationshipConfig(relationships[relationshipName]))()
        }
      }
    }
  }
 
  /**
  * Convenience method to get a spec-conforming version of
  * a resource object
  *
  * @param {Object} functionalResourceObject
  */
  static strip (functionalResourceObject) {
    return deref(functionalResourceObject)
  }
}
 
/**
 * Config Getter for the RelationshipMethod
 *
 * @param relatedObject
 * @return {{isToMany: boolean}}
 */
function getRelationshipConfig (relatedObject) {
  return {
    isToMany: relatedObject.data && relatedObject.data.constructor === Array
  }
}