All files dependingResolvers.js

100% Statements 28/28
100% Branches 6/6
100% Functions 11/11
100% Lines 18/18
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  1x 1x   1x 1x 1x 1x 1x         1x   21x                               69x                 100x 80x                       100x                 32x 12x 10x     16x                         25x        
 
import { push } from 'object-path'
import deepEqual from 'deep-equal'
 
import { nextTick, skip } from './utils'
import { allResolvers } from './allResolvers'
import { pipeResolvers } from './pipeResolvers'
import { combineResolvers } from './combineResolvers'
import { contextMustBeObject } from './miscResolvers'
 
/**
 * Piping resolver to save current value and reference to dependees cache.
 */
const saveDependee = combineResolvers(
  contextMustBeObject,
  (value, args, context, info) => ((
    push(context, '_dependees', { path: info.path, value }),
    value
  )),
)
 
/**
 * Identify a resolver as being a dependee, so other sibling
 * field resolvers might depend on the value resolved by this one.
 *
 * Basically, it will polute "info" during resolving
 * to insert the resolved value and path to this resolver.
 *
 * @param {Function} resolver Resolver implementation.
 * @return {Promise}.
 */
export const isDependee = resolver => (root, ...args) =>
  saveDependee(resolver(root, ...args), ...args)
 
/**
 * Make sure the field name exists on the parent type.
 *
 * @param {String} dependeeName The name of the dependee to check the parent against
 * @return {Function} Resolver to error when no dependee is found.
 */
const dependeeExists = dependeeName =>
  (root, args, context, { fieldName, parentType: { _fields, name: parent } }) =>
    !_fields[dependeeName]
      ? new Error(`Cannot get dependee "${dependeeName}" from field "${fieldName}" on type "${parent}"`)
      : skip
 
/**
 * Resolver implementation to retrieve the resolved value of a dependee sibling field.
 *
 * @param {String} dependeeName The name of the dependee this resolver depends on.
 * @param {Function} resolver Resolver implemenatation.
 * @return {Function} dependee resolver.
 */
export const resolveDependee = dependeeName => combineResolvers(
  contextMustBeObject,
  dependeeExists(dependeeName),
  pipeResolvers(
    // Make sure dependent resolvers occur after
    // dependees have been initialized.
    nextTick,
 
    // Find any currently resolved dependee.
    (root, args, { _dependees = [] }, info) => _dependees
      .filter(({ path: { prev } }) => deepEqual(prev, info.path.prev))
      .find(({ path: { key } }) => key === dependeeName),
 
    // Run field resolution, if resolved value was not found.
    (resolved, args, context, info) => resolved === skip
      ? info.parentType._fields[dependeeName].resolve(info.rootValue, args, context, info)
      : resolved.value,
  ),
)
 
/**
 * Resolver implementation to retrieve the resolved value of multiple dependee sibling fields.
 *
 * @param {[String]} dependeeNames Array of names of the dependees this resolver depends on.
 * @param {Function} resolver Resolver implemenatation.
 * @return {Function} dependee resolver.
 */
export const resolveDependees = dependeeNames => combineResolvers(
  contextMustBeObject,
  allResolvers(dependeeNames.map(resolveDependee)),
)