Home Reference Source Repository

lib/mixins/link-getters.js

import map from 'lodash/map'
import each from 'lodash/each'
import find from 'lodash/find'
import get from 'lodash/get'
import partial from 'lodash/partial'
import memoize from 'lodash/memoize'

let resolveAllLocales = false
/**
 * Sets getters on links for a given response
 * @private
 * @param {Array<Entry|Asset|DeletedEntry|DeletedAsset>} items
 * @param {Object} includes - Object with lists of Entry, Asset, DeletedEntry and DeletedAsset
 */
export default function mixinLinkGetters (items, includes, resolveForAllLocales) {
  resolveAllLocales = resolveForAllLocales
  const linkGetter = memoize(getLinksFromIncludes, memoizationResolver)
  each(items, (item) => {
    // TODO: workaround the preview endpoint extra locale this should be removed when
    // it is fixed on the backend
    if (resolveForAllLocales && item.sys.locale) {
      delete item.sys.locale
    }
    setLocalizedFieldGetters(item.fields, !!item.sys.locale)
  })

  /**
   * If a field does not have a locale defined in sys, the content of that field
   * is an object where the keys are each available locale, and we need to iterate
   * over each of those
   * @private
   * @param {Object} fields - Fields object
   * @param {boolean} hasLocale - If entry has been requested with a locale
   */
  function setLocalizedFieldGetters (fields, hasLocale) {
    if (hasLocale) {
      setFieldGettersForFields(fields)
    } else {
      each(fields, (localizedField) => setFieldGettersForFields(localizedField))
    }
  }

  /**
   * Sets getters on each link field or list of link fields for each item
   * @private
   * @param {Object} fields - Fields object
   */
  function setFieldGettersForFields (fields) {
    each(fields, (field, fieldKey) => {
      if (Array.isArray(field)) {
        addGetterForLinkArray(field, fieldKey, fields)
      } else {
        addGetterForLink(field, fieldKey, fields)
      }
    })
  }

  /**
   * Sets a getter which resolves the link of the given fieldKey in fields
   * @private
   * @param {Object} field - Field object
   * @param {string} fieldKey
   * @param {Object} fields - Fields object
   */
  function addGetterForLink (field, fieldKey, fields) {
    if (get(field, 'sys.type') === 'Link') {
      Object.defineProperty(fields, fieldKey, {
        get: partial(linkGetter, field)
      })
    }
  }

  /**
   * Sets a getter which resolves the array of links of the given fieldKey in fields
   * @private
   * @param {Array<Object>} field - List field array
   * @param {string} fieldKey
   * @param {Object} fields - Fields object
   */
  function addGetterForLinkArray (listField, fieldKey, fields) {
    if (get(listField[0], 'sys.type') === 'Link') {
      Object.defineProperty(fields, fieldKey, {
        get: function () {
          return map(listField, partial(linkGetter))
        }
      })
    }
  }

  /**
   * Looks for given link field in includes.
   * If linked entity is not found, it returns the original link.
   * This method shouldn't be used directly, and it's memoized whenever this
   * module's main method is used.
   * This is done to prevent the same link being resolved multiple times.
   * @private
   * @param {Object} field - Field object
   * @return {Object} Field, or link if field not resolved
   */
  function getLinksFromIncludes (field) {
    var link = find(includes[field.sys.linkType], ['sys.id', field.sys.id])
    if (link && link.fields) {
      // TODO: workaround the preview endpoint extra locale this should be removed when
      // it is fixed on the backend
      if (resolveAllLocales && link.sys.locale) {
        delete link.sys.locale
      }
      setLocalizedFieldGetters(link.fields, !!link.sys.locale)
      return link
    }
    return field
  }

  function memoizationResolver (link) {
    return link.sys.id
  }
}