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.

256 lines (224 loc) 9.94 kB
var isJSONG = require('./../../support/isJSONG'); var outputToObservable = require('./../conversion/outputToObservable'); var noteToJsongOrPV = require('./../conversion/noteToJsongOrPV'); var CallRequiresPathsError = require('./../../errors/CallRequiresPathsError'); var mCGRI = require('./../mergeCacheAndGatherRefsAndInvalidations'); var Observable = require('../../RouterRx.js').Observable; module.exports = outerRunCallAction; function outerRunCallAction(routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary) { return function innerRunCallAction(matchAndPath) { return runCallAction(matchAndPath, routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary); }; } function runCallAction(matchAndPath, routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary) { var match = matchAndPath.match; var matchedPath = matchAndPath.path; var out; // We are at out destination. Its time to get out // the pathValues from the if (match.isCall) { // This is where things get interesting out = Observable. defer(function() { var next; try { next = match. action.call( routerInstance, matchedPath, args, suffixes, paths); } catch (e) { e.throwToNext = true; throw e; } var output = outputToObservable(next); if (methodSummary) { var route = { start: routerInstance._now(), route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, results: [] }; methodSummary.routes.push(route); output = output.do( function (response) { route.results.push({ time: routerInstance._now(), value: response }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); } ) } return output.toArray(); }). // Required to get the references from the outputting jsong // and pathValues. map(function(res) { // checks call for isJSONG and if there is jsong without paths // throw errors. var refs = []; var values = []; // Will flatten any arrays of jsong/pathValues. var callOutput = res. // Filters out any falsy values filter(function(x) { return x; }). reduce(function(flattenedRes, next) { return flattenedRes.concat(next); }, []); // An empty output from call if (callOutput.length === 0) { return []; } var refLen = -1; callOutput.forEach(function(r) { // its json graph. if (isJSONG(r)) { // This is a hard error and must fully stop the server if (!r.paths) { var err = new CallRequiresPathsError(); err.throwToNext = true; throw err; } } }); var invsRefsAndValues = mCGRI(jsongCache, callOutput, routerInstance); invsRefsAndValues.references.forEach(function(ref) { refs[++refLen] = ref; }); values = invsRefsAndValues.values.map(function(pv) { return pv.path; }); var callLength = callOutput.length; var callPathSave1 = callPath.slice(0, callPath.length - 1); var hasSuffixes = suffixes && suffixes.length; var hasPaths = paths && paths.length; // We are going to use recurseMatchAndExecute to run // the paths and suffixes for call. For that to happen // we must send a message to the outside to switch from // call to get. callOutput[++callLength] = {isMessage: true, method: 'get'}; // If there are paths to add then push them into the next // paths through 'additionalPaths' message. if (hasPaths && (callLength + 1)) { paths.forEach(function(path) { callOutput[++callLength] = { isMessage: true, additionalPath: callPathSave1.concat(path) }; }); } // Suffix is the same as paths except for how to calculate // a path per reference found from the callPath. if (hasSuffixes) { // matchedPath is the optimized path to call. // e.g: // callPath: [genreLists, 0, add] -> // matchedPath: [lists, 'abc', add] var optimizedPathLength = matchedPath.length - 1; // For every reference build the complete path // from the callPath - 1 and concat remaining // path from the PathReference (path is where the // reference was found, not the value of the reference). // e.g: from the above example the output is: // output = {path: [lists, abc, 0], value: [titles, 123]} // // This means the refs object = [output]; // callPathSave1: [genreLists, 0], // optimizedPathLength: 3 - 1 = 2 // ref.path.slice(2): [lists, abc, 0].slice(2) = [0] // deoptimizedPath: [genreLists, 0, 0] // // Add the deoptimizedPath to the callOutput messages. // This will make the outer expand run those as a 'get' refs.forEach(function(ref) { var deoptimizedPath = callPathSave1.concat( ref.path.slice(optimizedPathLength)); suffixes.forEach(function(suffix) { var additionalPath = deoptimizedPath.concat(suffix); callOutput[++callLength] = { isMessage: true, additionalPath: additionalPath }; }); }); } // If there are no suffixes but there are references, report // the paths to the references. There may be values as well, // add those to the output. if (refs.length && !hasSuffixes || values.length) { var additionalPaths = []; if (refs.length && !hasSuffixes) { additionalPaths = refs. map(function(x) { return x.path; }); } additionalPaths. concat(values). forEach(function(path) { callOutput[++callLength] = { isMessage: true, additionalPath: path }; }); } return callOutput; }). // When call has an error it needs to be propagated to the next // level instead of onCompleted'ing do(null, function(e) { e.throwToNext = true; throw e; }); } else { out = Observable.defer(function () { return outputToObservable( match.action.call(routerInstance, matchAndPath.path) ); }); if (methodSummary) { var route = { start: routerInstance._now(), route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, results: [] }; methodSummary.routes.push(route); out = out.do( function (response) { route.results.push({ time: routerInstance._now(), value: response }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); } ) } } return out. materialize(). filter(function(note) { return note.kind !== 'C'; }). map(noteToJsongOrPV(matchAndPath.path, false, routerInstance)). map(function(jsonGraphOrPV) { return [matchAndPath.match, jsonGraphOrPV]; }); }