UNPKG

falcor-router

Version:

A router DataSource constructor for falcor that allows you to model all your cloud data sources as a single JSON resource.

150 lines (126 loc) 4.95 kB
var iterateKeySet = require('falcor-path-utils').iterateKeySet; var types = require('./../support/types'); var $ref = types.$ref; var $error = types.$error; var clone = require('./../support/clone'); var cloneArray = require('./../support/cloneArray'); var catAndSlice = require('./../support/catAndSlice'); /** * merges jsong into a seed */ module.exports = function jsongMerge(cache, jsongEnv, routerInstance) { var paths = jsongEnv.paths; var j = jsongEnv.jsonGraph; var references = []; var values = []; paths.forEach(function(p) { merge({ router: routerInstance, cacheRoot: cache, messageRoot: j, references: references, values: values, requestedPath: [], requestIdx: -1, ignoreCount: 0 }, cache, j, 0, p); }); return { references: references, values: values }; }; function merge(config, cache, message, depth, path, fromParent, fromKey) { var cacheRoot = config.cacheRoot; var messageRoot = config.messageRoot; var requestedPath = config.requestedPath; var ignoreCount = config.ignoreCount; var typeOfMessage = typeof message; var requestIdx = config.requestIdx; var updateRequestedPath = ignoreCount <= depth; if (updateRequestedPath) { requestIdx = ++config.requestIdx; } // The message at this point should always be defined. // Reached the end of the JSONG message path if (message === null || typeOfMessage !== 'object' || message.$type) { fromParent[fromKey] = clone(message); // If we notice an error while merging, we'll fire the error hook // for logging purposes. if (message && message.$type === $error) { config.router._pathErrorHook({ path: path, value: message }); } // NOTE: If we have found a reference at our cloning position // and we have resolved our path then add the reference to // the unfulfilledRefernces. if (message && message.$type === $ref) { var references = config.references; references.push({ path: cloneArray(requestedPath), value: message.value }); } // We are dealing with a value. We need this for call // Call needs to report all of its values into the jsongCache // and paths. else { var values = config.values; values.push({ path: cloneArray(requestedPath), value: (message && message.type) ? message.value : message }); } return; } var outerKey = path[depth]; var iteratorNote = {}; var key; key = iterateKeySet(outerKey, iteratorNote); // We always attempt this as a loop. If the memo exists then // we assume that the permutation is needed. do { // If the cache exists and we are not at our height, then // just follow cache, else attempt to follow message. var cacheRes = cache[key]; var messageRes = message[key]; // We no longer materialize inside of jsonGraph merge. Either the // client should specify all of the paths if (messageRes !== undefined) { var nextPath = path; var nextDepth = depth + 1; if (updateRequestedPath) { requestedPath[requestIdx] = key; } // We do not continue with this branch since the cache if (cacheRes === undefined) { cacheRes = cache[key] = {}; } var nextIgnoreCount = ignoreCount; // TODO: Potential performance gain since we know that // references are always pathSets of 1, they can be evaluated // iteratively. // There is only a need to consider message references since the // merge is only for the path that is provided. if (messageRes && messageRes.$type === $ref && depth < path.length - 1) { nextDepth = 0; nextPath = catAndSlice(messageRes.value, path, depth + 1); cache[key] = clone(messageRes); // Reset position in message and cache. nextIgnoreCount = messageRes.value.length; messageRes = messageRoot; cacheRes = cacheRoot; } // move forward down the path progression. config.ignoreCount = nextIgnoreCount; merge(config, cacheRes, messageRes, nextDepth, nextPath, cache, key); config.ignoreCount = ignoreCount; } if (updateRequestedPath) { requestedPath.length = requestIdx; } // Are we done with the loop? key = iterateKeySet(outerKey, iteratorNote); } while (!iteratorNote.done); }