Add extensions to $q for resolving all promises
This patch provides an extension service for the $q service which waits for all promises to settle and then return which promises fail and which promises pass. This was a result of having similar looking code in https://review.openstack.org/#/c/231335/ and https://review.openstack.org/#/c/234408/ The method name was inspired method which exists in $q from Kris Oswal but not in the Angular implementation of it. https://github.com/kriskowal/q/blob/v1/q.js#L1676 Co-Authored-By: Errol Pais<epais@thoughtworks.com> Co-Authored-By: Kyle Olivo<keolivo@thoughtworks.com> Change-Id: Ic3d36bba1886e06d7e7aee50c1ddfa17b51a1a65 Partially-Implements: blueprint angularize-images-table
This commit is contained in:
parent
9010d51484
commit
2d0e491d76
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.framework.util.q')
|
||||
.factory('horizon.framework.util.q.extensions', qExtensions);
|
||||
|
||||
qExtensions.$inject = ['$q'];
|
||||
|
||||
/**
|
||||
* @ngdoc factory
|
||||
* @name horizon.framework.util.q:extensions
|
||||
* @module horizon.framework.util.q
|
||||
* @kind function
|
||||
* @description
|
||||
*
|
||||
* Extends the $q from Angular to provide additional functionality.
|
||||
*
|
||||
*/
|
||||
function qExtensions($q) {
|
||||
var service = {
|
||||
allSettled: allSettled
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
/**
|
||||
* Allow all given promises to settle and returns a collection
|
||||
* of the successful and failed responses allowing the caller
|
||||
* to make decisions based on the individual results.
|
||||
*
|
||||
* This function is typically used if you need all promises
|
||||
* to complete and get all of the success and response messages,
|
||||
* but need to correlate specific success and failure results
|
||||
* to a particular context. To do this, ensure that the passed
|
||||
* in promise has a context attribute available on the input
|
||||
* promise. Each result or failure reason will include the
|
||||
* context in the fail or pass array.
|
||||
*
|
||||
* It will always result in a success callback (resolved),
|
||||
* but will provide a summary object of the results in a pair
|
||||
* of arrays ("pass" and "fail"), even if some of the promises fail.
|
||||
* The "pass" array will contain each of the results of the
|
||||
* successfully resolved promises and the "fail" array
|
||||
* will return each of the reasons for the rejected promises.
|
||||
*
|
||||
* In contrast to the `$q.all` in Angular which will terminate all
|
||||
* promises if any promise is rejected, this will wait for all promises
|
||||
* to settle.
|
||||
*
|
||||
* The order of the resolve or rejection reasons is non-deterministic
|
||||
* and should not be relied upon for correlation to input promises.
|
||||
*
|
||||
* @param promiseList
|
||||
* The list of promises to resolve
|
||||
*
|
||||
* @return
|
||||
* An object with 2 lists, one for promises that got resolved
|
||||
* and one for promises that got rejected.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* var settledPromises = qExtenstions.allSettled([
|
||||
* {promise: promise1, context: context1},
|
||||
* {promise: promise2, context: context2}
|
||||
* ]);
|
||||
* settledPromises.then(onSettled);
|
||||
*
|
||||
* function onSettled(data) {
|
||||
* doSomething(data.pass);
|
||||
* doSomething(data.fail);
|
||||
* }
|
||||
*
|
||||
* function doSomething(resolvedList) {
|
||||
* resolvedList.forEach(function (item) {
|
||||
* console.log("context", item.context, "result", item.data);
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
function allSettled(promiseList) {
|
||||
var deferred = $q.defer();
|
||||
var passList = [];
|
||||
var failList = [];
|
||||
var promises = promiseList.map(resolveSingle);
|
||||
|
||||
$q.all(promises).then(onComplete);
|
||||
return deferred.promise;
|
||||
|
||||
function resolveSingle(singlePromise) {
|
||||
var deferredInner = $q.defer();
|
||||
singlePromise.promise.then(onResolve, onReject);
|
||||
return deferredInner.promise;
|
||||
|
||||
function onResolve(response) {
|
||||
passList.push(formatResponse(response, singlePromise.context));
|
||||
deferredInner.resolve();
|
||||
}
|
||||
|
||||
function onReject(response) {
|
||||
failList.push(formatResponse(response, singlePromise.context));
|
||||
deferredInner.resolve();
|
||||
}
|
||||
|
||||
function formatResponse(response, context) {
|
||||
return {
|
||||
data: response,
|
||||
context: context
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function onComplete() {
|
||||
deferred.resolve({pass: passList, fail: failList});
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.framework.util.q.extensions', function () {
|
||||
var service, $q, $scope;
|
||||
|
||||
var failedPromise = function() {
|
||||
var deferred2 = $q.defer();
|
||||
deferred2.reject('failed');
|
||||
return deferred2.promise;
|
||||
};
|
||||
|
||||
var passedPromise = function() {
|
||||
var deferred1 = $q.defer();
|
||||
deferred1.resolve('passed');
|
||||
return deferred1.promise;
|
||||
};
|
||||
|
||||
beforeEach(module('horizon.framework.util.q'));
|
||||
beforeEach(inject(function($injector, _$rootScope_) {
|
||||
service = $injector.get('horizon.framework.util.q.extensions');
|
||||
$q = $injector.get('$q');
|
||||
$scope = _$rootScope_.$new();
|
||||
}));
|
||||
|
||||
it('should define allSettled', function () {
|
||||
expect(service.allSettled).toBeDefined();
|
||||
});
|
||||
|
||||
it('should resolve all given promises', function() {
|
||||
service.allSettled([{
|
||||
promise: failedPromise(),
|
||||
context: '1'
|
||||
}, {
|
||||
promise: passedPromise(),
|
||||
context: '2'
|
||||
}]).then(onAllSettled, failTest);
|
||||
|
||||
$scope.$apply();
|
||||
|
||||
function onAllSettled(resolvedPromises) {
|
||||
expect(resolvedPromises.fail.length).toEqual(1);
|
||||
expect(resolvedPromises.fail[0]).toEqual({data: 'failed', context: '1'});
|
||||
expect(resolvedPromises.pass.length).toEqual(1);
|
||||
expect(resolvedPromises.pass[0]).toEqual({data: 'passed', context: '2'});
|
||||
}
|
||||
});
|
||||
|
||||
function failTest() {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name horizon.framework.util.q
|
||||
* @description
|
||||
*
|
||||
* # horizon.framework.util.q
|
||||
*
|
||||
* This module provides extensions to the Angular $q service.
|
||||
*
|
||||
*/
|
||||
angular
|
||||
.module('horizon.framework.util.q', []);
|
||||
|
||||
})();
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.framework.util.q module', function () {
|
||||
it('should have been defined', function () {
|
||||
expect(angular.module('horizon.framework.util.q')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
|
@ -8,6 +8,7 @@
|
|||
'horizon.framework.util.http',
|
||||
'horizon.framework.util.i18n',
|
||||
'horizon.framework.util.promise-toggle',
|
||||
'horizon.framework.util.q',
|
||||
'horizon.framework.util.tech-debt',
|
||||
'horizon.framework.util.workflow',
|
||||
'horizon.framework.util.validators',
|
||||
|
|
|
@ -24,10 +24,11 @@
|
|||
'$http',
|
||||
'$q',
|
||||
'$templateCache',
|
||||
'horizon.framework.widgets.basePath'
|
||||
'horizon.framework.widgets.basePath',
|
||||
'horizon.framework.util.q.extensions'
|
||||
];
|
||||
|
||||
function actionsService($compile, $http, $q, $templateCache, basePath) {
|
||||
function actionsService($compile, $http, $q, $templateCache, basePath, $qExtensions) {
|
||||
return function(spec) {
|
||||
return createService(spec.scope, spec.element, spec.listType);
|
||||
};
|
||||
|
@ -42,50 +43,23 @@
|
|||
return service;
|
||||
|
||||
function renderActions(allowedActions) {
|
||||
getPermittedActions(allowedActions).then(renderPermittedActions);
|
||||
}
|
||||
allowedActions.forEach(function(allowedAction) {
|
||||
allowedAction.promise = allowedAction.permissions;
|
||||
allowedAction.context = allowedAction;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the permitted actions from the list of allowed actions
|
||||
* by resolving the promises in the permissions object.
|
||||
*/
|
||||
function getPermittedActions(allowedActions) {
|
||||
var deferred = $q.defer();
|
||||
var permittedActions = [];
|
||||
var promises = allowedActions.map(actionPermitted);
|
||||
|
||||
$q.all(promises).then(onResolved);
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
function actionPermitted(action) {
|
||||
var deferredInner = $q.defer();
|
||||
action.permissions.then(onSuccess, onError);
|
||||
return deferredInner.promise;
|
||||
|
||||
function onSuccess() {
|
||||
permittedActions.push(action);
|
||||
deferredInner.resolve();
|
||||
}
|
||||
|
||||
function onError() {
|
||||
deferredInner.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function onResolved() {
|
||||
deferred.resolve(permittedActions);
|
||||
}
|
||||
$qExtensions.allSettled(allowedActions).then(renderPermittedActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render permitted actions as per the list type
|
||||
*/
|
||||
function renderPermittedActions(permittedActions) {
|
||||
if (permittedActions.length > 0) {
|
||||
var templateFetch = $q.all(permittedActions.map(getTemplate));
|
||||
|
||||
if (listType === 'batch' || permittedActions.length === 1) {
|
||||
if (permittedActions.pass.length > 0) {
|
||||
var templateFetch = $q.all(permittedActions.pass.map(getTemplate));
|
||||
|
||||
if (listType === 'batch' || permittedActions.pass.length === 1) {
|
||||
element.addClass('btn-addon');
|
||||
templateFetch.then(addButtons);
|
||||
} else {
|
||||
|
@ -183,8 +157,9 @@
|
|||
/**
|
||||
* Fetch the HTML Template for the Action
|
||||
*/
|
||||
function getTemplate(action) {
|
||||
function getTemplate(permittedActionResponse) {
|
||||
var defered = $q.defer();
|
||||
var action = permittedActionResponse.context;
|
||||
$http.get(getTemplateUrl(action), {cache: $templateCache}).then(onTemplateGet);
|
||||
return defered.promise;
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
deleteModalService.$inject = [
|
||||
'$q',
|
||||
'horizon.framework.widgets.modal.simple-modal.service',
|
||||
'horizon.framework.widgets.toast.service'
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.framework.util.q.extensions'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -43,7 +44,7 @@
|
|||
* and then raise the event.
|
||||
* On cancel, do nothing.
|
||||
*/
|
||||
function deleteModalService($q, simpleModalService, toast) {
|
||||
function deleteModalService($q, simpleModalService, toast, $qExtensions) {
|
||||
var service = {
|
||||
open: open
|
||||
};
|
||||
|
@ -91,26 +92,32 @@
|
|||
simpleModalService.modal(options).result.then(onModalSubmit);
|
||||
|
||||
function onModalSubmit() {
|
||||
resolveAll(entities.map(deleteEntityPromise)).then(notify);
|
||||
$qExtensions.allSettled(entities.map(deleteEntityPromise)).then(notify);
|
||||
}
|
||||
|
||||
function deleteEntityPromise(entity) {
|
||||
return {promise: context.deleteEntity(entity.id), entity: entity};
|
||||
return {promise: context.deleteEntity(entity.id), context: entity};
|
||||
}
|
||||
|
||||
function notify(result) {
|
||||
if (result.pass.length > 0) {
|
||||
scope.$emit(context.successEvent, result.pass.map(getId));
|
||||
toast.add('success', getMessage(context.labels.success, result.pass));
|
||||
var passEntities = result.pass.map(getEntities);
|
||||
scope.$emit(context.successEvent, passEntities.map(getId));
|
||||
toast.add('success', getMessage(context.labels.success, passEntities));
|
||||
}
|
||||
|
||||
if (result.fail.length > 0) {
|
||||
scope.$emit(context.failedEvent, result.fail.map(getId));
|
||||
toast.add('error', getMessage(context.labels.error, result.fail));
|
||||
var failEntities = result.fail.map(getEntities);
|
||||
scope.$emit(context.failedEvent, failEntities.map(getId));
|
||||
toast.add('error', getMessage(context.labels.error, failEntities));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEntities(passResponse) {
|
||||
return passResponse.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the displayed message
|
||||
*/
|
||||
|
@ -132,40 +139,5 @@
|
|||
return entity.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve all promises.
|
||||
* It asks the backing API Service to suppress errors
|
||||
* and collect all entities to display one
|
||||
* success and one error message.
|
||||
*/
|
||||
function resolveAll(promiseList) {
|
||||
var deferred = $q.defer();
|
||||
var passList = [];
|
||||
var failList = [];
|
||||
var promises = promiseList.map(resolveSingle);
|
||||
|
||||
$q.all(promises).then(onComplete);
|
||||
return deferred.promise;
|
||||
|
||||
function resolveSingle(singlePromise) {
|
||||
var deferredInner = $q.defer();
|
||||
singlePromise.promise.then(success, error);
|
||||
return deferredInner.promise;
|
||||
|
||||
function success() {
|
||||
passList.push(singlePromise.entity);
|
||||
deferredInner.resolve();
|
||||
}
|
||||
|
||||
function error() {
|
||||
failList.push(singlePromise.entity);
|
||||
deferredInner.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function onComplete() {
|
||||
deferred.resolve({pass: passList, fail: failList});
|
||||
}
|
||||
}
|
||||
} // end of batchDeleteService
|
||||
})(); // end of IIFE
|
||||
|
|
Loading…
Reference in New Issue