[Persistence] Rename bundle

Rename bundle for queued persistence. WTD-1033.
This commit is contained in:
Victor Woeltjen
2015-03-20 13:13:26 -07:00
parent 62e88abbd2
commit 7b6ecd7bd7
10 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,6 @@
/*global define*/
define({
REVISION_ERROR_KEY: "revision",
OVERWRITE_KEY: "overwrite"
});

View File

@ -0,0 +1,54 @@
/*global define*/
define(
['./PersistenceFailureConstants'],
function (PersistenceFailureConstants) {
"use strict";
var OVERWRITE_CANCEL_OPTIONS = [
{
name: "Overwrite",
key: PersistenceFailureConstants.OVERWRITE_KEY
},
{
name: "Cancel",
key: "cancel"
}
],
OK_OPTIONS = [ { name: "OK", key: "ok" } ];
/**
* Populates a `dialogModel` to pass to `dialogService.getUserChoise`
* in order to choose between Overwrite and Cancel.
*/
function PersistenceFailureDialog(failures) {
var revisionErrors = [],
otherErrors = [];
// Place this failure into an appropriate group
function categorizeFailure(failure) {
// Check if the error is due to object revision
var isRevisionError = ((failure || {}).error || {}).key ===
PersistenceFailureConstants.REVISION_ERROR_KEY;
// Push the failure into the appropriate group
(isRevisionError ? revisionErrors : otherErrors).push(failure);
}
// Separate into revision errors, and other errors
failures.forEach(categorizeFailure);
return {
title: "Save Error",
template: "persistence-failure-dialog",
model: {
revised: revisionErrors,
unrecoverable: otherErrors
},
options: revisionErrors.length > 0 ?
OVERWRITE_CANCEL_OPTIONS : OK_OPTIONS
};
}
return PersistenceFailureDialog;
}
);

View File

@ -0,0 +1,74 @@
/*global define*/
define(
['./PersistenceFailureDialog', './PersistenceFailureConstants'],
function (PersistenceFailureDialog, PersistenceFailureConstants) {
"use strict";
function PersistenceFailureHandler($q, dialogService, persistenceService) {
// Refresh revision information for the domain object associated
// with htis persistence failure
function refresh(failure) {
// Perform a new read; this should update the persistence
// service's local revision records, so that the next request
// should permit the overwrite
return persistenceService.readObject(
failure.persistence.getSpace(),
failure.id,
{ cache: false } // Disallow caching
);
}
// Issue a new persist call for the domain object associated with
// this failure.
function persist(failure) {
var undecoratedPersistence =
failure.domainObject.getCapability('persistence');
return undecoratedPersistence &&
undecoratedPersistence.persist();
}
// Retry persistence for this set of failed attempts
function retry(failures) {
// Refresh all objects within the persistenceService to
// get up-to-date revision information; once complete,
// reissue the persistence request.
return $q.all(failures.map(refresh)).then(function () {
return $q.all(failures.map(persist));
});
}
// Handle failures in persistence
function handleFailures(failures) {
// Prepare dialog for display
var dialogModel = new PersistenceFailureDialog(failures),
revisionErrors = dialogModel.model.revised;
// Handle user input (did they choose to overwrite?)
function handleChoice(key) {
// If so, try again
if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
return retry(revisionErrors);
}
}
// Prompt for user input, the overwrite if they said so.
return dialogService.getUserChoice(dialogModel)
.then(handleChoice);
}
return {
/**
* Handle persistence failures by providing the user with a
* dialog summarizing these failures, and giving the option
* to overwrite/cancel as appropriate.
* @param {Array} failures persistence failures, as prepared
* by PersistenceQueueHandler
*/
handle: handleFailures
};
}
return PersistenceFailureHandler;
}
);

View File

@ -0,0 +1,57 @@
/*global define*/
define(
[
'./PersistenceQueueImpl',
'./PersistenceQueueHandler',
'./PersistenceFailureHandler'
],
function (
PersistenceQueueImpl,
PersistenceQueueHandler,
PersistenceFailureHandler
) {
"use strict";
/**
* The PersistenceQueue is used by the QueuingPersistenceCapability
* to aggregrate calls for object persistence. These are then issued
* in a group, such that if some or all are rejected, this result can
* be shown to the user (again, in a group.)
*
* This constructor is exposed as a service, but handles only the
* wiring of injected dependencies; behavior is implemented in the
* various component parts.
*
* @param $timeout Angular's $timeout
* @param {PersistenceQueueHandler} handler handles actual
* persistence when the queue is flushed
* @param {number} [DELAY] optional; delay in milliseconds between
* attempts to flush the queue
*/
function PersistenceQueue(
$q,
$timeout,
dialogService,
persistenceService,
PERSISTENCE_QUEUE_DELAY
) {
// Wire up injected dependencies
return new PersistenceQueueImpl(
$timeout,
new PersistenceQueueHandler(
$q,
new PersistenceFailureHandler(
$q,
dialogService,
persistenceService
)
),
PERSISTENCE_QUEUE_DELAY
);
}
return PersistenceQueue;
}
);

View File

@ -0,0 +1,79 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* Handles actual persistence invocations for queeud persistence
* attempts, in a group. Handling in a group in this manner
* also allows failure to be handled in a group (e.g. in a single
* summary dialog.)
* @param $q Angular's $q, for promises
* @param {PersistenceFailureHandler} handler to invoke in the event
* that a persistence attempt fails.
*/
function PersistenceQueueHandler($q, failureHandler) {
// Handle a group of persistence invocations
function persistGroup(ids, queue, domainObjects) {
var failures = [];
// Try to persist a specific domain object
function tryPersist(id) {
// Look up its persistence capability from the provided
// id->persistence object.
var persistence = queue[id];
// Handle success
function succeed(value) {
return value;
}
// Handle failure (build up a list of failures)
function fail(error) {
failures.push({
id: id,
persistence: persistence,
domainObject: domainObjects[id],
error: error
});
return false;
}
// Invoke the actual persistence capability, then
// note success or failures
return persistence.persist().then(succeed, fail);
}
// Handle any failures from the full operation
function handleFailure(value) {
return failures.length > 0 ?
failureHandler.handle(failures) : value;
}
// Try to persist everything, then handle any failures
return $q.all(ids.map(tryPersist)).then(handleFailure);
}
return {
/**
* Invoke the persist method on the provided persistence
* capabilities.
* @param {Object.<string,PersistenceCapability>} queue
* capabilities to invoke, in id->capability pairs.
* @param {Object.<string,DomainObject>} domainObjects
* associated domain objects, in id->object pairs.
*/
persist: function (queue, domainObjects) {
var ids = Object.keys(queue);
return persistGroup(ids, queue, domainObjects);
}
};
}
return PersistenceQueueHandler;
}
);

View File

@ -0,0 +1,94 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* The PersistenceQueue is used by the QueuingPersistenceCapability
* to aggregrate calls for object persistence. These are then issued
* in a group, such that if some or all are rejected, this result can
* be shown to the user (again, in a group.)
*
* This implementation is separate out from PersistenceQueue, which
* handles the wiring of injected dependencies into an instance of
* this class.
*
* @param $timeout Angular's $timeout
* @param {PersistenceQueueHandler} handler handles actual
* persistence when the queue is flushed
* @param {number} [DELAY] optional; delay in milliseconds between
* attempts to flush the queue
*/
function PersistenceQueueImpl($timeout, handler, DELAY) {
var queue = {},
objects = {},
lastObservedSize = 0,
pendingTimeout,
flushPromise;
// Check if the queue's size has stopped increasing)
function quiescent() {
return Object.keys(queue).length === lastObservedSize;
}
// Persist all queued objects
function flush() {
// Persist all queued objects
flushPromise = handler.persist(queue, objects);
// When persisted, clear the active promise
flushPromise.then(function () {
flushPromise = undefined;
});
// Reset queue, etc.
queue = {};
objects = {};
lastObservedSize = 0;
pendingTimeout = undefined;
}
// Schedule a flushing of the queue (that is, plan to flush
// all objects in the queue)
function scheduleFlush() {
function maybeFlush() {
// Only flush when we've stopped receiving updates
(quiescent() ? flush : scheduleFlush)();
}
// If we are already flushing the queue...
if (flushPromise) {
// Wait until that's over before considering a flush
flushPromise.then(maybeFlush);
} else {
// Otherwise, schedule a flush on a timeout (to give
// a window for other updates to get aggregated)
pendingTimeout = pendingTimeout ||
$timeout(maybeFlush, DELAY, false);
}
}
// If no delay is provided, use a default
DELAY = DELAY || 0;
return {
/**
* Queue persistence of a domain object.
* @param {DomainObject} domainObject the domain object
* @param {PersistenceCapability} persistence the object's
* undecorated persistence capability
*/
put: function (domainObject, persistence) {
var id = domainObject.getId();
queue[id] = persistence;
objects[id] = domainObject;
scheduleFlush();
}
};
}
return PersistenceQueueImpl;
}
);

View File

@ -0,0 +1,31 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* The QueuingPersistenceCapability places `persist` calls in a queue
* to be handled in batches.
* @param {PersistenceQueue} queue of persistence calls
* @param {PersistenceCapability} persistence the wrapped persistence
* capability
* @param {DomainObject} domainObject the domain object which exposes
* the capability
*/
function QueuingPersistenceCapability(queue, persistence, domainObject) {
var queuingPersistence = Object.create(persistence),
id = domainObject.getId();
// Override persist calls to queue them instead
queuingPersistence.persist = function () {
queue.put(id, persistence);
};
return queuingPersistence;
}
return QueuingPersistenceCapability;
}
);

View File

@ -0,0 +1,75 @@
/*global define,Promise*/
/**
* Module defining CoreCapabilityProvider. Created by vwoeltje on 11/7/14.
*/
define(
['./QueuingPersistenceCapability'],
function (QueuingPersistenceCapability) {
"use strict";
/**
* Capability decorator. Adds queueing support to persistence
* capabilities for domain objects, such that persistence attempts
* will be handled in batches (allowing failure notification to
* also be presented in batches.)
*
* @constructor
*/
function QueuingPersistenceCapabilityDecorator(
persistenceQueue,
capabilityService
) {
function decoratePersistence(capabilities) {
var originalPersistence = capabilities.persistence;
if (originalPersistence) {
capabilities.persistence = function (domainObject) {
// Get/instantiate the original
var original =
(typeof originalPersistence === 'function') ?
originalPersistence(domainObject) :
originalPersistence;
// Provide a decorated version
return new QueuingPersistenceCapability(
persistenceQueue,
original,
domainObject
);
};
}
return capabilities;
}
function getCapabilities(model) {
return capabilityService.getCapabilities(model)
.then(decoratePersistence);
}
return {
/**
* Get all capabilities associated with a given domain
* object.
*
* This returns a promise for an object containing key-value
* pairs, where keys are capability names and values are
* either:
*
* * Capability instances
* * Capability constructors (which take a domain object
* as their argument.)
*
*
* @param {*} model the object model
* @returns {Object.<string,function|Capability>} all
* capabilities known to be valid for this model, as
* key-value pairs
*/
getCapabilities: getCapabilities
};
}
return QueuingPersistenceCapabilityDecorator;
}
);