Remove deprecated scope handling

The `initScope` function is deprecated since ocata, so this patch
replaces it into `initAction` if it needed, or removes it.
Also, gets scope from second parameter of perform function.
Futher more, removes deprecated `scope` parameter for `modal`
function of wizard-modal-service.

Change-Id: I8979b699a9b4383b894db9bdcbad80f15c1df150
Closes-Bug: #1640049
This commit is contained in:
Shu Muto 2017-08-29 19:09:36 +09:00
parent f911d0dd40
commit 9a758638ee
21 changed files with 120 additions and 341 deletions

View File

@ -149,7 +149,7 @@
* *
* If an action does not have an initAction() function, it is ignored. * If an action does not have an initAction() function, it is ignored.
*/ */
function initActions(scope) { function initActions() {
angular.forEach(self.itemActions, setActionScope); angular.forEach(self.itemActions, setActionScope);
angular.forEach(self.batchActions, setActionScope); angular.forEach(self.batchActions, setActionScope);
angular.forEach(self.globalActions, setActionScope); angular.forEach(self.globalActions, setActionScope);
@ -157,14 +157,6 @@
function setActionScope(action) { function setActionScope(action) {
if (action.service.initAction) { if (action.service.initAction) {
action.service.initAction(); action.service.initAction();
} else if (action.service.initScope) {
// The use of scope in action services breaks the singleton nature
// of the services, and should be stopped. State should be held on
// controllers instead; scope is now passed into allow() and perform()
// methods.
$log.warn('The initScope() method is deprecated. ' +
'Invocation of it will stop in Queens.');
action.service.initScope(scope.$new());
} }
} }
} }

View File

@ -170,9 +170,8 @@
}); });
it('initActions calls initAction on item and batch actions', function () { it('initActions calls initAction on item and batch actions', function () {
var action = {service: {initAction: angular.noop, initScope: angular.noop}}; var action = {service: {initAction: angular.noop}};
spyOn(action.service, 'initAction'); spyOn(action.service, 'initAction');
spyOn(action.service, 'initScope');
type.batchActions.push(action); type.batchActions.push(action);
type.initActions({ type.initActions({
'$new': function () { '$new': function () {
@ -180,22 +179,9 @@
} }
}); });
expect(action.service.initAction).toHaveBeenCalled(); expect(action.service.initAction).toHaveBeenCalled();
expect(action.service.initScope).not.toHaveBeenCalled();
}); });
it('initActions calls initScope if initAction is not defined', function () { it('initActions ignores initAction when not present', function () {
var action = {service: {initScope: angular.noop}};
spyOn(action.service, 'initScope');
type.batchActions.push(action);
type.initActions({
'$new': function () {
return 4;
}
});
expect(action.service.initScope).toHaveBeenCalledWith(4);
});
it('initActions ignores initAction and initScope when not present', function () {
var action = {service: {}}; var action = {service: {}};
type.batchActions.push(action); type.batchActions.push(action);
var returned = type.initActions({}); var returned = type.initActions({});

View File

@ -115,7 +115,7 @@
* - When using 'delete-selected' for 'batch' type, all selected rows are passed as * - When using 'delete-selected' for 'batch' type, all selected rows are passed as
* an array. * an array.
* *
* There is a third optional function, initAction (which was previously called initScope) * There is a third optional function, initAction.
* Actions may perform post-config (in the angular sense) initialization by * Actions may perform post-config (in the angular sense) initialization by
* providing an initAction method. This might be typically invoked by initActions() * providing an initAction method. This might be typically invoked by initActions()
* on a ResourceType. Actions should not perform blocking operations in their * on a ResourceType. Actions should not perform blocking operations in their

View File

@ -25,8 +25,7 @@
'horizon.framework.util.actions.action-result.service', 'horizon.framework.util.actions.action-result.service',
'horizon.framework.widgets.modal-wait-spinner.service', 'horizon.framework.widgets.modal-wait-spinner.service',
'$q', '$q',
'$routeParams', '$routeParams'
'$rootScope'
]; ];
function controller( function controller(
@ -34,8 +33,7 @@
resultService, resultService,
spinnerService, spinnerService,
$q, $q,
$routeParams, $routeParams
$rootScope
) { ) {
var ctrl = this; var ctrl = this;
@ -54,7 +52,7 @@
function loadData(response) { function loadData(response) {
spinnerService.hideModalSpinner(); spinnerService.hideModalSpinner();
ctrl.showDetails = true; ctrl.showDetails = true;
ctrl.resourceType.initActions($rootScope.$new()); ctrl.resourceType.initActions();
ctrl.itemData = response.data; ctrl.itemData = response.data;
ctrl.itemName = ctrl.resourceType.itemName(response.data); ctrl.itemName = ctrl.resourceType.itemName(response.data);
} }

View File

@ -28,13 +28,11 @@
* .controller('modalExampleCtrl', ExampleCtrl); * .controller('modalExampleCtrl', ExampleCtrl);
* *
* ExampleCtrl.$inject = [ * ExampleCtrl.$inject = [
* '$scope',
* 'horizon.framework.widgets.modal.wizard-modal.service' * 'horizon.framework.widgets.modal.wizard-modal.service'
* ]; * ];
* *
* function ExampleCtrl($scope, wizardModalService) { * function ExampleCtrl($scope, wizardModalService) {
* var options = { * var options = {
* scope: scope, // the scope to use for the Wizard
* workflow: workflow, // the workflow used in the wizard * workflow: workflow, // the workflow used in the wizard
* submit: submit // callback to call on a wizard submit * submit: submit // callback to call on a wizard submit
* }; * };
@ -81,13 +79,6 @@
} }
}; };
// backwards compatibility
if (angular.isDefined(params.scope)) {
$log.warn('The "scope" param to modal() is deprecated.' +
'Handling of it will stop in Queens.');
options.scope = params.scope;
}
return $uibModal.open(options); return $uibModal.open(options);
} }
} }

View File

@ -16,7 +16,7 @@
"use strict"; "use strict";
describe('horizon.framework.widgets.wizard-modal.service', function() { describe('horizon.framework.widgets.wizard-modal.service', function() {
var service, modal, $scope; var service, modal;
beforeEach(module('horizon.framework')); beforeEach(module('horizon.framework'));
beforeEach(module(function($provide) { beforeEach(module(function($provide) {
@ -26,8 +26,7 @@
$provide.value('$uibModal', modal); $provide.value('$uibModal', modal);
})); }));
beforeEach(inject(function($injector, _$rootScope_) { beforeEach(inject(function($injector) {
$scope = _$rootScope_.$new();
service = $injector.get('horizon.framework.widgets.modal.wizard-modal.service'); service = $injector.get('horizon.framework.widgets.modal.wizard-modal.service');
})); }));
@ -43,7 +42,7 @@
it('should open the modal if called with required parameters', function() { it('should open the modal if called with required parameters', function() {
spyOn(modal, 'open').and.callThrough(); spyOn(modal, 'open').and.callThrough();
service.modal({scope: $scope, workflow: {}, submit: {}}); service.modal({workflow: {}, submit: {}});
expect(modal.open).toHaveBeenCalled(); expect(modal.open).toHaveBeenCalled();
}); });
@ -51,11 +50,10 @@
spyOn(modal, 'open').and.callThrough(); spyOn(modal, 'open').and.callThrough();
var workflow = {id: 'w'}; var workflow = {id: 'w'};
var submit = {id: 's'}; var submit = {id: 's'};
service.modal({scope: $scope, workflow: workflow, submit: submit}); service.modal({workflow: workflow, submit: submit});
expect(modal.open).toHaveBeenCalled(); expect(modal.open).toHaveBeenCalled();
var modalOpenArgs = modal.open.calls.argsFor(0)[0]; var modalOpenArgs = modal.open.calls.argsFor(0)[0];
expect(modalOpenArgs.controller).toEqual('WizardModalController as modalCtrl'); expect(modalOpenArgs.controller).toEqual('WizardModalController as modalCtrl');
expect(modalOpenArgs.scope).toEqual($scope);
expect(modalOpenArgs.resolve.workflow()).toEqual(workflow); expect(modalOpenArgs.resolve.workflow()).toEqual(workflow);
expect(modalOpenArgs.resolve.submit()).toEqual(submit); expect(modalOpenArgs.resolve.submit()).toEqual(submit);
}); });

View File

@ -193,11 +193,11 @@
} }
function onGlobalActionSelected(action) { function onGlobalActionSelected(action) {
if (action.service.initScope) { if (action.service.initAction) {
action.service.initScope($scope.$new()); action.service.initAction();
} }
return action.service.perform(); return action.service.perform(null, $scope.$new());
} }
function onActionSelected(action) { function onActionSelected(action) {
@ -206,11 +206,11 @@
typeData.load(ctrl.resourceId).then(performAction, loadFailed); typeData.load(ctrl.resourceId).then(performAction, loadFailed);
function performAction(resource) { function performAction(resource) {
if (action.service.initScope) { if (action.service.initAction) {
action.service.initScope($scope.$new()); action.service.initAction();
} }
return action.service.perform(resource.data); return action.service.perform(resource.data, $scope.$new());
} }
function loadFailed(reason) { function loadFailed(reason) {

View File

@ -55,12 +55,9 @@
success: gettext('Image %s was successfully created.') success: gettext('Image %s was successfully created.')
}; };
var model = {};
var scope; var scope;
var service = { var service = {
initScope: initScope,
perform: perform, perform: perform,
allowed: allowed allowed: allowed
}; };
@ -68,48 +65,25 @@
return service; return service;
////////////// //////////////
function initScope($scope) {
var watchImageChange = $scope.$on(events.IMAGE_CHANGED, onImageChange);
var watchMetadataChange = $scope.$on(events.IMAGE_METADATA_CHANGED, onMetadataChange);
scope = $scope;
$scope.$on('$destroy', destroy);
function destroy() {
watchImageChange();
watchMetadataChange();
}
}
function onImageChange(e, image) {
model.image = image;
e.stopPropagation();
}
function onMetadataChange(e, metadata) {
model.metadata = metadata;
e.stopPropagation();
}
function allowed() { function allowed() {
return policy.ifAllowed({ rules: [['image', 'add_image']] }); return policy.ifAllowed({ rules: [['image', 'add_image']] });
} }
function perform() { function perform(selected, $scope) {
model.image = {}; scope = $scope;
model.metadata = {};
scope.image = {};
return wizardModalService.modal({ return wizardModalService.modal({
scope: scope,
workflow: createWorkflow, workflow: createWorkflow,
submit: submit submit: submit
}).result; }).result;
} }
function submit() { function submit(stepModels) {
var finalModel = angular.extend({}, model.image, model.metadata); var finalModel = angular.extend(
{},
stepModels.imageForm,
stepModels.updateMetadataForm);
if (finalModel.source_type === 'url') { if (finalModel.source_type === 'url') {
delete finalModel.data; delete finalModel.data;
} else { } else {

View File

@ -29,10 +29,8 @@
}; };
var wizardModalService = { var wizardModalService = {
modal: function (config) { modal: function () {
deferredModal = $q.defer(); return { result: {catch: angular.noop} };
deferredModal.resolve(config.scope.image);
return {result: deferredModal.promise};
} }
}; };
@ -54,7 +52,7 @@
} }
}; };
var service, events, $scope, deferredModal, deferredCreate, $q; var service, $scope, deferredCreate, $q;
/////////////////////// ///////////////////////
@ -71,7 +69,6 @@
beforeEach(inject(function($injector, _$rootScope_, _$q_) { beforeEach(inject(function($injector, _$rootScope_, _$q_) {
$scope = _$rootScope_.$new(); $scope = _$rootScope_.$new();
service = $injector.get('horizon.app.core.images.actions.create.service'); service = $injector.get('horizon.app.core.images.actions.create.service');
events = $injector.get('horizon.app.core.images.events');
$q = _$q_; $q = _$q_;
})); }));
@ -85,14 +82,11 @@
it('open the modal with the correct parameters', function() { it('open the modal with the correct parameters', function() {
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
expect(wizardModalService.modal).toHaveBeenCalled(); expect(wizardModalService.modal).toHaveBeenCalled();
expect($scope.image).toEqual({});
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
expect(modalArgs.scope).toEqual($scope);
expect(modalArgs.workflow).toBeDefined(); expect(modalArgs.workflow).toBeDefined();
expect(modalArgs.submit).toBeDefined(); expect(modalArgs.submit).toBeDefined();
}); });
@ -105,14 +99,10 @@
spyOn(glanceAPI, 'createImage').and.callThrough(); spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
$scope.$emit(events.IMAGE_CHANGED, image);
$scope.$emit(events.IMAGE_METADATA_CHANGED, newMetadata);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image, updateMetadataForm: newMetadata});
$scope.$apply(); $scope.$apply();
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual( expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual(
@ -128,12 +118,10 @@
spyOn(glanceAPI, 'createImage').and.callThrough(); spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
$scope.$emit(events.IMAGE_CHANGED, image);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image});
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test', expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test',
source_type: 'file-direct', data: {name: 'test_file'}}); source_type: 'file-direct', data: {name: 'test_file'}});
@ -148,12 +136,10 @@
spyOn(glanceAPI, 'createImage').and.callThrough(); spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
$scope.$emit(events.IMAGE_CHANGED, image);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image});
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test', expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test',
source_type: 'url', image_url: 'http://somewhere'}); source_type: 'url', image_url: 'http://somewhere'});
@ -168,12 +154,10 @@
spyOn(glanceAPI, 'createImage').and.callThrough(); spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
$scope.$emit(events.IMAGE_CHANGED, image);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image});
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test', expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test',
source_type: 'file-direct', data: {name: 'test_file'}}); source_type: 'file-direct', data: {name: 'test_file'}});
@ -188,71 +172,15 @@
spyOn(glanceAPI, 'createImage').and.callThrough(); spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(null, $scope);
service.perform();
$scope.$emit(events.IMAGE_CHANGED, image);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image});
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test', expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({ name: 'Test',
source_type: 'url', image_url: 'http://somewhere'}); source_type: 'url', image_url: 'http://somewhere'});
}); });
it('should raise event even if update metadata fails', function() {
var image = { name: 'Test', id: '2' };
var failedPromise = function() {
return {
then: function(callback, errorCallback) {
errorCallback();
}
};
};
spyOn(wizardModalService, 'modal').and.callThrough();
spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(metadataService, 'editMetadata').and.callFake(failedPromise);
spyOn($scope, '$emit').and.callThrough();
service.initScope($scope);
service.perform();
$scope.$apply();
$scope.$emit(events.IMAGE_CHANGED, image);
$scope.$emit(events.IMAGE_METADATA_CHANGED, newMetadata);
var newMetadata = {prop1: '11', prop3: '3'};
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit();
$scope.$apply();
expect($scope.$emit).toHaveBeenCalledWith(
'horizon.app.core.images.IMAGE_METADATA_CHANGED', undefined);
});
it('should destroy the event watchers', function() {
var newImage = { name: 'Test2', id: '2' };
var newMetadata = {p1: '11', p3: '3'};
spyOn(wizardModalService, 'modal').and.callThrough();
spyOn(glanceAPI, 'createImage').and.callThrough();
spyOn(metadataService, 'editMetadata').and.callThrough();
service.initScope($scope);
service.perform();
$scope.$apply();
$scope.$emit('$destroy');
$scope.$emit(events.IMAGE_CHANGED, newImage);
$scope.$emit(events.IMAGE_METADATA_CHANGED, newMetadata);
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit();
$scope.$apply();
expect(glanceAPI.createImage.calls.argsFor(0)[0]).toEqual({});
});
}); });
})(); })();

View File

@ -52,11 +52,9 @@
toast, toast,
imagesResourceType imagesResourceType
) { ) {
var scope, context, deleteImagePromise;
var notAllowedMessage = gettext("You are not allowed to delete images: %s"); var notAllowedMessage = gettext("You are not allowed to delete images: %s");
var service = { var service = {
initScope: initScope,
allowed: allowed, allowed: allowed,
perform: perform perform: perform
}; };
@ -65,33 +63,13 @@
////////////// //////////////
function initScope(newScope) { function perform(items, newScope) {
scope = newScope; var scope = newScope;
context = { }; var context = { };
deleteImagePromise = policy.ifAllowed({rules: [['image', 'delete_image']]});
}
function perform(items) {
var images = angular.isArray(items) ? items : [items]; var images = angular.isArray(items) ? items : [items];
context.labels = labelize(images.length); context.labels = labelize(images.length);
context.deleteEntity = deleteImage; context.deleteEntity = deleteImage;
return $qExtensions.allSettled(images.map(checkPermission)).then(afterCheck); return $qExtensions.allSettled(images.map(checkPermission)).then(afterCheck);
}
function allowed(image) {
// only row actions pass in image
// otherwise, assume it is a batch action
if (image) {
return $q.all([
notProtected(image),
deleteImagePromise,
policy.ifAllowed({ rules: [['image', 'delete_image']] }),
notDeleted(image)
]);
} else {
return policy.ifAllowed({ rules: [['image', 'delete_image']] });
}
}
function checkPermission(image) { function checkPermission(image) {
return {promise: allowed(image), context: image}; return {promise: allowed(image), context: image};
@ -108,6 +86,21 @@
} }
return outcome; return outcome;
} }
}
function allowed(image) {
// only row actions pass in image
// otherwise, assume it is a batch action
if (image) {
return $q.all([
notProtected(image),
policy.ifAllowed({ rules: [['image', 'delete_image']] }),
notDeleted(image)
]);
} else {
return policy.ifAllowed({ rules: [['image', 'delete_image']] });
}
}
function createResult(deleteModalResult) { function createResult(deleteModalResult) {
// To make the result of this action generically useful, reformat the return // To make the result of this action generically useful, reformat the return

View File

@ -91,19 +91,8 @@
beforeEach(function() { beforeEach(function() {
spyOn(deleteModalService, 'open').and.callThrough(); spyOn(deleteModalService, 'open').and.callThrough();
service.initScope($scope, labelize);
}); });
function labelize(count) {
return {
title: ngettext('title', 'titles', count),
message: ngettext('message', 'messages', count),
submit: ngettext('submit', 'submits', count),
success: ngettext('success', 'successs', count),
error: ngettext('error', 'errors', count)
};
}
//////////// ////////////
it('should open the delete modal and show correct labels', testSingleLabels); it('should open the delete modal and show correct labels', testSingleLabels);
@ -221,7 +210,6 @@
beforeEach(function() { beforeEach(function() {
spyOn(resolver, 'success'); spyOn(resolver, 'success');
spyOn(resolver, 'error'); spyOn(resolver, 'error');
service.initScope($scope);
}); });
//////////// ////////////

View File

@ -56,15 +56,8 @@
var message = { var message = {
success: gettext('Image %s was successfully updated.') success: gettext('Image %s was successfully updated.')
}; };
var modifyImagePolicyCheck, scope;
var model = {
image: {},
metadata: {}
};
var service = { var service = {
initScope: initScope,
allowed: allowed, allowed: allowed,
perform: perform perform: perform
}; };
@ -73,15 +66,9 @@
////////////// //////////////
function initScope($scope) {
scope = $scope;
$scope.$on(events.IMAGE_METADATA_CHANGED, onMetadataChange);
modifyImagePolicyCheck = policy.ifAllowed({rules: [['image', 'modify_image']]});
}
function allowed(image) { function allowed(image) {
return $q.all([ return $q.all([
modifyImagePolicyCheck, policy.ifAllowed({rules: [['image', 'modify_image']]}),
isActive(image) isActive(image)
]); ]);
} }
@ -89,62 +76,59 @@
function perform(image) { function perform(image) {
var deferred = glance.getImage(image.id); var deferred = glance.getImage(image.id);
deferred.then(onLoad); deferred.then(onLoad);
scope.imagePromise = deferred; var data = {};
data.imagePromise = deferred;
function onLoad(response) { function onLoad(response) {
var localImage = response.data; var localImage = response.data;
model.image = localImage; data.image = localImage;
} }
return wizardModalService.modal({ return wizardModalService.modal({
scope: scope, data: data,
workflow: editWorkflow, workflow: editWorkflow,
submit: submit submit: submit
}).result; }).result;
} }
function onMetadataChange(e, metadata) { function submit(stepModels) {
model.metadata = metadata; var image = stepModels.imageForm;
e.stopPropagation(); var metadata = stepModels.updateMetadataForm;
}
function submit() {
return updateMetadata().then(updateImage, onUpdateImageFail); return updateMetadata().then(updateImage, onUpdateImageFail);
}
function updateImage() {
var finalModel = model.image;
return glance.updateImage(finalModel).then(onUpdateImageSuccess, onUpdateImageFail);
}
function onUpdateImageSuccess() {
toast.add('success', interpolate(message.success, [model.image.name]));
return actionResultService.getActionResult()
.updated(imageResourceType, model.image.id)
.result;
}
function onUpdateImageFail() {
return actionResultService.getActionResult()
.failed(imageResourceType, model.image.id)
.result;
}
function updateMetadata() { function updateMetadata() {
return metadataService return metadataService
.getMetadata('image', model.image.id) .getMetadata('image', image.id)
.then(onMetadataGet); .then(onMetadataGet);
function onMetadataGet(response) { function onMetadataGet(response) {
var updated = model.metadata; var updated = metadata;
var removed = angular.copy(response.data); var removed = angular.copy(response.data);
angular.forEach(updated, function(value, key) { angular.forEach(updated, function(value, key) {
delete removed[key]; delete removed[key];
}); });
return metadataService return metadataService
.editMetadata('image', model.image.id, updated, Object.keys(removed)); .editMetadata('image', image.id, updated, Object.keys(removed));
}
}
function updateImage() {
return glance.updateImage(image).then(onUpdateImageSuccess, onUpdateImageFail);
}
function onUpdateImageSuccess() {
toast.add('success', interpolate(message.success, [image.name]));
return actionResultService.getActionResult()
.updated(imageResourceType, image.id)
.result;
}
function onUpdateImageFail() {
return actionResultService.getActionResult()
.failed(imageResourceType, image.id)
.result;
} }
} }

View File

@ -18,7 +18,8 @@
'use strict'; 'use strict';
describe('horizon.app.core.images.actions.edit.service', function() { describe('horizon.app.core.images.actions.edit.service', function() {
var service, $scope, $q, deferred, testImage, $timeout, updateImageDeferred; var service, $scope, $q, deferred, $timeout, updateImageDeferred;
var image = {id: 1, name: 'Original'};
var existingMetadata = {p1: '1', p2: '2'}; var existingMetadata = {p1: '1', p2: '2'};
var metadataService = { var metadataService = {
@ -52,7 +53,7 @@
}, },
getImage: function() { getImage: function() {
var imageLoad = $q.defer(); var imageLoad = $q.defer();
imageLoad.resolve({data: {id: 1, name: 'Test'}}); imageLoad.resolve({data: image});
return imageLoad.promise; return imageLoad.promise;
} }
}; };
@ -91,7 +92,6 @@
$scope = _$rootScope_.$new(); $scope = _$rootScope_.$new();
$q = _$q_; $q = _$q_;
service = $injector.get('horizon.app.core.images.actions.edit.service'); service = $injector.get('horizon.app.core.images.actions.edit.service');
service.initScope($scope);
deferred = $q.defer(); deferred = $q.defer();
updateImageDeferred = $q.defer(); updateImageDeferred = $q.defer();
$timeout = _$timeout_; $timeout = _$timeout_;
@ -101,16 +101,12 @@
it('should open the modal with the correct parameters', function() { it('should open the modal with the correct parameters', function() {
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
testImage = {id: '12'}; service.perform(image, $scope);
service.initScope($scope);
service.perform(testImage);
$timeout.flush(); $timeout.flush();
expect(wizardModalService.modal).toHaveBeenCalled(); expect(wizardModalService.modal).toHaveBeenCalled();
expect($scope.imagePromise).toBeDefined();
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
expect(modalArgs.scope).toEqual($scope);
expect(modalArgs.workflow).toBeDefined(); expect(modalArgs.workflow).toBeDefined();
}); });
@ -124,45 +120,32 @@
describe('submit', function() { describe('submit', function() {
beforeEach(function() { beforeEach(function() {
var image = {id: 1, name: 'Original'};
spyOn(glanceAPI, 'updateImage').and.callThrough(); spyOn(glanceAPI, 'updateImage').and.callThrough();
spyOn(wizardModalService, 'modal').and.callThrough(); spyOn(wizardModalService, 'modal').and.callThrough();
service.initScope($scope); service.perform(image, $scope);
service.perform(image);
$timeout.flush(); $timeout.flush();
}); });
it('passes the image from the model to updateImage', function() { it('passes the image from the model to updateImage', function() {
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit(); modalArgs.submit({imageForm: image});
updateImageDeferred.resolve(); updateImageDeferred.resolve();
$timeout.flush(); $timeout.flush();
expect(glanceAPI.updateImage.calls.argsFor(0)[0]).toEqual({id: 1, name: 'Test'}); expect(glanceAPI.updateImage.calls.argsFor(0)[0]).toEqual(image);
}); });
it('returns a failed result if API call fails', function() { it('returns a failed result if API call fails', function() {
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0]; var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
var result = modalArgs.submit(); var result = modalArgs.submit({imageForm: image});
updateImageDeferred.reject(); updateImageDeferred.reject();
result.then(function err(data) { result.then(function err(data) {
expect(data.failed.length).toBe(1); expect(data.failed.length).toBe(1);
}); });
$timeout.flush(); $timeout.flush();
}); });
it('updates metadata on event', function() {
$scope.$emit('horizon.app.core.images.IMAGE_METADATA_CHANGED', {i_am: 'metadata'});
$scope.$apply();
spyOn(metadataService, 'editMetadata').and.callThrough();
var modalArgs = wizardModalService.modal.calls.argsFor(0)[0];
modalArgs.submit();
expect(metadataService.editMetadata.calls.argsFor(0)[2]).toEqual({i_am: 'metadata'});
});
}); });
function permissionShouldFail(permissions) { function permissionShouldFail(permissions) {

View File

@ -269,8 +269,6 @@
*/ */
function events() { function events() {
return { return {
IMAGE_CHANGED: 'horizon.app.core.images.IMAGE_CHANGED',
IMAGE_METADATA_CHANGED: 'horizon.app.core.images.IMAGE_METADATA_CHANGED',
IMAGE_UPLOAD_PROGRESS: 'horizon.app.core.images.IMAGE_UPLOAD_PROGRESS' IMAGE_UPLOAD_PROGRESS: 'horizon.app.core.images.IMAGE_UPLOAD_PROGRESS'
}; };
} }

View File

@ -56,7 +56,7 @@
ctrl.apiVersion = 0; ctrl.apiVersion = 0;
ctrl.allowPublicizeImage = true; ctrl.allowPublicizeImage = true;
ctrl.image = { $scope.stepModels.imageForm = ctrl.image = {
source_type: '', source_type: '',
image_url: '', image_url: '',
data: {}, data: {},
@ -96,11 +96,9 @@
init(); init();
var imageChangedWatcher = $scope.$watchCollection('ctrl.image', watchImageCollection);
var watchUploadProgress = $scope.$on(events.IMAGE_UPLOAD_PROGRESS, watchImageUpload); var watchUploadProgress = $scope.$on(events.IMAGE_UPLOAD_PROGRESS, watchImageUpload);
$scope.$on('$destroy', function() { $scope.$on('$destroy', function() {
imageChangedWatcher();
watchUploadProgress(); watchUploadProgress();
}); });
@ -148,13 +146,6 @@
return (type === 'file-legacy' || type === 'file-direct'); return (type === 'file-legacy' || type === 'file-direct');
} }
// emits new data to parent listeners
function watchImageCollection(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.$emit(events.IMAGE_CHANGED, newValue);
}
}
function init() { function init() {
glance.getImages({paginate: false}).success(onGetImages); glance.getImages({paginate: false}).success(onGetImages);
policyAPI.ifAllowed({rules: [['image', 'publicize_image']]}).then( policyAPI.ifAllowed({rules: [['image', 'publicize_image']]}).then(

View File

@ -49,6 +49,7 @@
beforeEach(inject(function ($injector, _$rootScope_, _$q_, _$timeout_) { beforeEach(inject(function ($injector, _$rootScope_, _$q_, _$timeout_) {
$scope = _$rootScope_.$new(); $scope = _$rootScope_.$new();
$scope.stepModels = {imageForm: {}, updateMetadataForm: {}};
$q = _$q_; $q = _$q_;
$timeout = _$timeout_; $timeout = _$timeout_;
@ -93,19 +94,6 @@
expect(ctrl.ramdiskImages).toEqual([{disk_format: 'ari'}]); expect(ctrl.ramdiskImages).toEqual([{disk_format: 'ari'}]);
}); });
it('should emit events on image change', function() {
spyOn($scope, '$emit').and.callThrough();
var ctrl = createController();
ctrl.image = 1;
$scope.$apply();
ctrl.image = 2;
$scope.$apply();
expect($scope.$emit).toHaveBeenCalledWith('horizon.app.core.images.IMAGE_CHANGED', 2);
});
it('should have options for visibility, protected and copying', function() { it('should have options for visibility, protected and copying', function() {
var ctrl = createController(); var ctrl = createController();

View File

@ -76,7 +76,7 @@
} }
function init(response) { function init(response) {
ctrl.image = response.data; $scope.stepModels.imageForm = ctrl.image = response.data;
ctrl.image.kernel = ctrl.image.properties.kernel_id; ctrl.image.kernel = ctrl.image.properties.kernel_id;
ctrl.image.ramdisk = ctrl.image.properties.ramdisk_id; ctrl.image.ramdisk = ctrl.image.properties.ramdisk_id;
ctrl.image.architecture = ctrl.image.properties.architecture; ctrl.image.architecture = ctrl.image.properties.architecture;

View File

@ -30,6 +30,7 @@
beforeEach(inject(function ($injector, _$rootScope_, _$q_, _$timeout_) { beforeEach(inject(function ($injector, _$rootScope_, _$q_, _$timeout_) {
$scope = _$rootScope_.$new(); $scope = _$rootScope_.$new();
$scope.stepModels = {imageForm: {}, updateMetadataForm: {}};
$q = _$q_; $q = _$q_;
$timeout = _$timeout_; $timeout = _$timeout_;
@ -132,22 +133,5 @@
expect(ctrl.image.container_format).toEqual('ari'); expect(ctrl.image.container_format).toEqual('ari');
}); });
it("should destroy the image changed watcher when the controller is destroyed", function() {
setImagePromise({id: '1', container_format: 'bare', properties: []});
spyOn($scope, '$emit').and.callThrough();
var ctrl = createController();
ctrl.image = 1;
$scope.$apply();
$scope.$emit("$destroy");
$scope.$emit.calls.reset();
ctrl.image = 2;
$scope.$apply();
expect($scope.$emit).not.toHaveBeenCalled();
});
}); });
})(); })();

View File

@ -92,7 +92,7 @@
function onMetadataChanged(newValue, oldValue) { function onMetadataChanged(newValue, oldValue) {
if (newValue !== oldValue) { if (newValue !== oldValue) {
$scope.$emit(events.IMAGE_METADATA_CHANGED, newValue); $scope.stepModels.updateMetadataForm = newValue;
} }
} }

View File

@ -68,6 +68,7 @@
beforeEach(inject(function($injector, _$rootScope_, _$q_) { beforeEach(inject(function($injector, _$rootScope_, _$q_) {
$controller = $injector.get('$controller'); $controller = $injector.get('$controller');
$scope = _$rootScope_.$new(); $scope = _$rootScope_.$new();
$scope.stepModels = {imageForm: {}, updateMetadataForm: {}};
$q = _$q_; $q = _$q_;
})); }));
@ -118,7 +119,7 @@
expect(metadataTreeService.Tree).toHaveBeenCalledWith(availableMetadata, []); expect(metadataTreeService.Tree).toHaveBeenCalledWith(availableMetadata, []);
}); });
it('should emit imageMetadataChanged event when metadata changes', function() { it('should update stepModels.updateMetadataForm when metadata changes', function() {
var deferred = $q.defer(); var deferred = $q.defer();
deferred.resolve({data: {id: '1'}}); deferred.resolve({data: {id: '1'}});
$scope.imagePromise = deferred.promise; $scope.imagePromise = deferred.promise;
@ -135,10 +136,7 @@
mockGetExisting.and.returnValue('2'); mockGetExisting.and.returnValue('2');
$scope.$apply(); $scope.$apply();
expect($scope.$emit).toHaveBeenCalledWith( expect($scope.stepModels.updateMetadataForm).toEqual('2');
'horizon.app.core.images.IMAGE_METADATA_CHANGED',
'2'
);
}); });
function createController() { function createController() {

View File

@ -0,0 +1,5 @@
---
deprecations:
- Remove formerly deprecated initScope() function for
Angularized actions. Instead use initAction() and get
scope from second parameter of perform() function.