falcor
Version:
A JavaScript library for efficient data fetching.
130 lines (108 loc) • 5.04 kB
JavaScript
var emptyArray = [];
var AssignableDisposable = require("./../AssignableDisposable");
var GetResponse = require("./../get/GetResponse");
var setGroupsIntoCache = require("./setGroupsIntoCache");
var getWithPathsAsPathMap = require("./../../get").getWithPathsAsPathMap;
var InvalidSourceError = require("./../../errors/InvalidSourceError");
var MaxRetryExceededError = require("./../../errors/MaxRetryExceededError");
/**
* The request cycle for set. This is responsible for requesting to dataSource
* and allowing disposing inflight requests.
*/
module.exports = function setRequestCycle(model, observer, groups,
isJSONGraph, isProgressive, count) {
var requestedAndOptimizedPaths = setGroupsIntoCache(model, groups);
var optimizedPaths = requestedAndOptimizedPaths.optimizedPaths;
var requestedPaths = requestedAndOptimizedPaths.requestedPaths;
// we have exceeded the maximum retry limit.
if (count > model._maxRetries) {
observer.onError(new MaxRetryExceededError(optimizedPaths));
return {
dispose: function() {}
};
}
var isMaster = model._source === undefined;
// Local set only. We perform a follow up get. If performance is ever
// a requirement simply requiring in checkCacheAndReport and use get request
// internals. Figured this is more "pure".
if (isMaster) {
return subscribeToFollowupGet(model, observer, requestedPaths,
isJSONGraph, isProgressive);
}
// Progressively output the data from the first set.
var prevVersion;
if (isProgressive) {
var results = getWithPathsAsPathMap(model, requestedPaths, [{}]);
if (results.criticalError) {
observer.onError(results.criticalError);
return null;
}
observer.onNext(results.values[0]);
prevVersion = model._root.cache.$_version;
}
var currentJSONGraph = getJSONGraph(model, optimizedPaths);
var disposable = new AssignableDisposable();
// Sends out the setRequest. The Queue will call the callback with the
// JSONGraph envelope / error.
var requestDisposable = model._request.
// TODO: There is error handling that has not been addressed yet.
// If disposed before this point then the sendSetRequest will not
// further any callbacks. Therefore, if we are at this spot, we are
// not disposed yet.
set(currentJSONGraph, count, function(error, jsonGraphEnv) {
if (error instanceof InvalidSourceError) {
observer.onError(error);
return;
}
// TODO: This seems like there are errors with this approach, but
// for sanity sake I am going to keep this logic in here until a
// rethink can be done.
var isCompleted = false;
if (error || optimizedPaths.length === jsonGraphEnv.paths.length) {
isCompleted = true;
}
// If we're in progressive mode and nothing changed in the meantime, we're done
if (isProgressive) {
var nextVersion = model._root.cache.$_version;
var versionChanged = nextVersion !== prevVersion;
if (!versionChanged) {
observer.onCompleted();
return;
}
}
// Happy case. One request to the dataSource will fulfill the
// required paths.
if (isCompleted) {
disposable.currentDisposable =
subscribeToFollowupGet(model, observer, requestedPaths,
isJSONGraph, isProgressive);
}
// TODO: The unhappy case. I am unsure how this can even be
// achieved.
else {
// We need to restart the setRequestCycle.
setRequestCycle(model, observer, groups, isJSONGraph,
isProgressive, count + 1);
}
});
// Sets the current disposable as the requestDisposable.
disposable.currentDisposable = requestDisposable;
return disposable;
};
function getJSONGraph(model, optimizedPaths) {
var boundPath = model._path;
var envelope = {};
model._path = emptyArray;
model._getPathValuesAsJSONG(model._materialize().withoutDataSource(), optimizedPaths, [envelope]);
model._path = boundPath;
return envelope;
}
function subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph,
isProgressive) {
// Creates a new response and subscribes to it with the original observer.
// Also sets forceCollect to true, incase the operation is synchronous and
// exceeds the cache limit size
var response = new GetResponse(model, requestedPaths, isJSONGraph,
isProgressive, true);
return response.subscribe(observer);
}