Merge pull request #666 from nasa/open656b
[Create] Utilize copyService during Save As
This commit is contained in:
@@ -156,14 +156,11 @@ define([
|
|||||||
"name": "Save",
|
"name": "Save",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
|
||||||
"$location",
|
|
||||||
"$injector",
|
"$injector",
|
||||||
"urlService",
|
|
||||||
"navigationService",
|
|
||||||
"policyService",
|
"policyService",
|
||||||
"dialogService",
|
"dialogService",
|
||||||
"creationService"
|
"creationService",
|
||||||
|
"copyService"
|
||||||
],
|
],
|
||||||
"priority": "mandatory"
|
"priority": "mandatory"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,18 +36,22 @@ define(
|
|||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
*/
|
*/
|
||||||
function SaveAction($q, $location, $injector, urlService, navigationService, policyService, dialogService, creationService, context) {
|
function SaveAction(
|
||||||
|
$injector,
|
||||||
|
policyService,
|
||||||
|
dialogService,
|
||||||
|
creationService,
|
||||||
|
copyService,
|
||||||
|
context
|
||||||
|
) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
this.$location = $location;
|
|
||||||
this.injectObjectService = function(){
|
this.injectObjectService = function(){
|
||||||
this.objectService = $injector.get("objectService");
|
this.objectService = $injector.get("objectService");
|
||||||
};
|
};
|
||||||
this.urlService = urlService;
|
|
||||||
this.navigationService = navigationService;
|
|
||||||
this.policyService = policyService;
|
this.policyService = policyService;
|
||||||
this.dialogService = dialogService;
|
this.dialogService = dialogService;
|
||||||
this.creationService = creationService;
|
this.creationService = creationService;
|
||||||
this.$q = $q;
|
this.copyService = copyService;
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveAction.prototype.getObjectService = function(){
|
SaveAction.prototype.getObjectService = function(){
|
||||||
@@ -67,35 +71,29 @@ define(
|
|||||||
*/
|
*/
|
||||||
SaveAction.prototype.perform = function () {
|
SaveAction.prototype.perform = function () {
|
||||||
var domainObject = this.domainObject,
|
var domainObject = this.domainObject,
|
||||||
$location = this.$location,
|
copyService = this.copyService,
|
||||||
urlService = this.urlService,
|
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
function resolveWith(object){
|
function resolveWith(object){
|
||||||
return function() {
|
return function () {
|
||||||
return object;
|
return object;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function doWizardSave(parent) {
|
function doWizardSave(parent) {
|
||||||
var context = domainObject.getCapability("context"),
|
var context = domainObject.getCapability("context"),
|
||||||
wizard = new CreateWizard(domainObject, parent, self.policyService);
|
wizard = new CreateWizard(
|
||||||
|
domainObject,
|
||||||
|
parent,
|
||||||
|
self.policyService
|
||||||
|
);
|
||||||
|
|
||||||
return self.dialogService
|
return self.dialogService
|
||||||
.getUserInput(wizard.getFormStructure(true), wizard.getInitialFormValue())
|
.getUserInput(
|
||||||
.then(function(formValue){
|
wizard.getFormStructure(true),
|
||||||
return wizard.populateObjectFromInput(formValue, domainObject);
|
wizard.getInitialFormValue()
|
||||||
});
|
)
|
||||||
}
|
.then(wizard.populateObjectFromInput.bind(wizard));
|
||||||
|
|
||||||
|
|
||||||
function persistObject(object){
|
|
||||||
|
|
||||||
//Persist first to mark dirty
|
|
||||||
return object.getCapability('persistence').persist().then(function(){
|
|
||||||
//then save permanently
|
|
||||||
return object.getCapability('editor').save();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchObject(objectId){
|
function fetchObject(objectId){
|
||||||
@@ -108,16 +106,18 @@ define(
|
|||||||
return fetchObject(object.getModel().location);
|
return fetchObject(object.getModel().location);
|
||||||
}
|
}
|
||||||
|
|
||||||
function locateObjectInParent(parent){
|
function allowClone(objectToClone) {
|
||||||
parent.getCapability('composition').add(domainObject.getId());
|
return (objectToClone.getId() === domainObject.getId()) ||
|
||||||
return parent.getCapability('persistence').persist().then(function() {
|
objectToClone.getCapability('location').isOriginal();
|
||||||
return parent;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doNothing() {
|
function cloneIntoParent(parent) {
|
||||||
// Create cancelled, do nothing
|
return copyService.perform(domainObject, parent, allowClone);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
function cancelEditingAfterClone(clonedObject) {
|
||||||
|
return domainObject.getCapability("editor").cancel()
|
||||||
|
.then(resolveWith(clonedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke any save behavior introduced by the editor capability;
|
// Invoke any save behavior introduced by the editor capability;
|
||||||
@@ -127,17 +127,13 @@ define(
|
|||||||
function doSave() {
|
function doSave() {
|
||||||
//This is a new 'virtual object' that has not been persisted
|
//This is a new 'virtual object' that has not been persisted
|
||||||
// yet.
|
// yet.
|
||||||
if (!domainObject.getModel().persisted){
|
if (domainObject.getModel().persisted === undefined){
|
||||||
return getParent(domainObject)
|
return getParent(domainObject)
|
||||||
.then(doWizardSave)
|
.then(doWizardSave)
|
||||||
.then(persistObject)
|
.then(getParent)
|
||||||
.then(getParent)//Parent may have changed based
|
.then(cloneIntoParent)
|
||||||
// on user selection
|
.then(cancelEditingAfterClone)
|
||||||
.then(locateObjectInParent)
|
.catch(resolveWith(false));
|
||||||
.then(function(){
|
|
||||||
return fetchObject(domainObject.getId());
|
|
||||||
})
|
|
||||||
.catch(doNothing);
|
|
||||||
} else {
|
} else {
|
||||||
return domainObject.getCapability("editor").save()
|
return domainObject.getCapability("editor").save()
|
||||||
.then(resolveWith(domainObject.getOriginalObject()));
|
.then(resolveWith(domainObject.getOriginalObject()));
|
||||||
@@ -148,7 +144,7 @@ define(
|
|||||||
// UI, which will have been pushed atop the Browse UI.)
|
// UI, which will have been pushed atop the Browse UI.)
|
||||||
function returnToBrowse(object) {
|
function returnToBrowse(object) {
|
||||||
if (object) {
|
if (object) {
|
||||||
self.navigationService.setNavigation(object);
|
object.getCapability("action").perform("navigate");
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,20 +54,52 @@ define(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function used to check if a domain object should be cloned
|
||||||
|
* or not.
|
||||||
|
* @callback platform/entanglement.CopyService~filter
|
||||||
|
* @param {DomainObject} domainObject the object to be cloned
|
||||||
|
* @returns {boolean} true if the object should be cloned; false
|
||||||
|
* if it should be linked
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a duplicate of the object tree starting at domainObject to
|
* Creates a duplicate of the object tree starting at domainObject to
|
||||||
* the new parent specified.
|
* the new parent specified.
|
||||||
* @param domainObject
|
*
|
||||||
* @param parent
|
* Any domain objects which cannot be created will not be cloned;
|
||||||
* @param progress
|
* instead, these will appear as links. If a filtering function
|
||||||
|
* is provided, any objects which fail that check will also be
|
||||||
|
* linked instead of cloned
|
||||||
|
*
|
||||||
|
* @param {DomainObject} domainObject the object to duplicate
|
||||||
|
* @param {DomainObject} parent the destination for the clone
|
||||||
|
* @param {platform/entanglement.CopyService~filter} [filter]
|
||||||
|
* an optional function used to filter out objects from
|
||||||
|
* the cloning process
|
||||||
* @returns a promise that will be completed with the clone of
|
* @returns a promise that will be completed with the clone of
|
||||||
* domainObject when the duplication is successful.
|
* domainObject when the duplication is successful.
|
||||||
*/
|
*/
|
||||||
CopyService.prototype.perform = function (domainObject, parent) {
|
CopyService.prototype.perform = function (domainObject, parent, filter) {
|
||||||
var $q = this.$q,
|
var policyService = this.policyService;
|
||||||
copyTask = new CopyTask(domainObject, parent, this.policyService, this.$q);
|
|
||||||
|
// Combines caller-provided filter (if any) with the
|
||||||
|
// baseline behavior of respecting creation policy.
|
||||||
|
function filterWithPolicy(domainObject) {
|
||||||
|
return (!filter || filter(domainObject)) &&
|
||||||
|
policyService.allow(
|
||||||
|
"creation",
|
||||||
|
domainObject.getCapability("type")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.validate(domainObject, parent)) {
|
if (this.validate(domainObject, parent)) {
|
||||||
return copyTask.perform();
|
return new CopyTask(
|
||||||
|
domainObject,
|
||||||
|
parent,
|
||||||
|
filterWithPolicy,
|
||||||
|
this.$q
|
||||||
|
).perform();
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Tried to copy objects without validating first."
|
"Tried to copy objects without validating first."
|
||||||
|
|||||||
@@ -31,18 +31,21 @@ define(
|
|||||||
* This class encapsulates the process of copying a domain object
|
* This class encapsulates the process of copying a domain object
|
||||||
* and all of its children.
|
* and all of its children.
|
||||||
*
|
*
|
||||||
* @param domainObject The object to copy
|
* @param {DomainObject} domainObject The object to copy
|
||||||
* @param parent The new location of the cloned object tree
|
* @param {DomainObject} parent The new location of the cloned object tree
|
||||||
* @param $q
|
* @param {platform/entanglement.CopyService~filter} filter
|
||||||
|
* a function used to filter out objects from
|
||||||
|
* the cloning process
|
||||||
|
* @param $q Angular's $q, for promises
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function CopyTask (domainObject, parent, policyService, $q){
|
function CopyTask (domainObject, parent, filter, $q){
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.firstClone = undefined;
|
this.firstClone = undefined;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.deferred = undefined;
|
this.deferred = undefined;
|
||||||
this.policyService = policyService;
|
this.filter = filter;
|
||||||
this.persisted = 0;
|
this.persisted = 0;
|
||||||
this.clones = [];
|
this.clones = [];
|
||||||
this.idMap = {};
|
this.idMap = {};
|
||||||
@@ -101,9 +104,14 @@ define(
|
|||||||
* Will add a list of clones to the specified parent's composition
|
* Will add a list of clones to the specified parent's composition
|
||||||
*/
|
*/
|
||||||
function addClonesToParent(self) {
|
function addClonesToParent(self) {
|
||||||
self.parent.getCapability("composition").add(self.firstClone.getId());
|
return self.parent.getCapability("composition")
|
||||||
return self.parent.getCapability("persistence").persist()
|
.add(self.firstClone)
|
||||||
.then(function(){return self.firstClone;});
|
.then(function (addedClone) {
|
||||||
|
return self.parent.getCapability("persistence").persist()
|
||||||
|
.then(function () {
|
||||||
|
return addedClone;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,7 +201,7 @@ define(
|
|||||||
//Check if the type of the object being copied allows for
|
//Check if the type of the object being copied allows for
|
||||||
// creation of new instances. If it does not, then a link to the
|
// creation of new instances. If it does not, then a link to the
|
||||||
// original will be created instead.
|
// original will be created instead.
|
||||||
if (this.policyService.allow("creation", originalObject.getCapability("type"))){
|
if (this.filter(originalObject)) {
|
||||||
//create a new clone of the original object. Use the
|
//create a new clone of the original object. Use the
|
||||||
// creation capability of the targetParent to create the
|
// creation capability of the targetParent to create the
|
||||||
// new clone. This will ensure that the correct persistence
|
// new clone. This will ensure that the correct persistence
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ define(
|
|||||||
'compositionCapability',
|
'compositionCapability',
|
||||||
['invoke', 'add']
|
['invoke', 'add']
|
||||||
);
|
);
|
||||||
|
compositionCapability.add.andCallFake(synchronousPromise);
|
||||||
|
|
||||||
locationCapability = jasmine.createSpyObj(
|
locationCapability = jasmine.createSpyObj(
|
||||||
'locationCapability',
|
'locationCapability',
|
||||||
@@ -387,6 +388,7 @@ define(
|
|||||||
expect(childObjectClone.getModel().location).toEqual(objectClone.getId());
|
expect(childObjectClone.getModel().location).toEqual(objectClone.getId());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when cloning non-creatable objects", function() {
|
describe("when cloning non-creatable objects", function() {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
policyService.allow.andCallFake(function(category){
|
policyService.allow.andCallFake(function(category){
|
||||||
@@ -401,8 +403,33 @@ define(
|
|||||||
it ("creates link instead of clone", function() {
|
it ("creates link instead of clone", function() {
|
||||||
var copiedObject = copyFinished.calls[0].args[0];
|
var copiedObject = copyFinished.calls[0].args[0];
|
||||||
expect(copiedObject).toBe(object);
|
expect(copiedObject).toBe(object);
|
||||||
expect(compositionCapability.add).toHaveBeenCalledWith(copiedObject.getId());
|
expect(compositionCapability.add)
|
||||||
//expect(newParent.getModel().composition).toContain(copiedObject.getId());
|
.toHaveBeenCalledWith(copiedObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when provided a filtering function", function () {
|
||||||
|
function accept() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function reject() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
it("does not create new instances of objects " +
|
||||||
|
"rejected by the filter", function() {
|
||||||
|
copyService.perform(object, newParent, reject)
|
||||||
|
.then(copyFinished);
|
||||||
|
expect(copyFinished.mostRecentCall.args[0])
|
||||||
|
.toBe(object);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does create new instances of objects " +
|
||||||
|
"accepted by the filter", function() {
|
||||||
|
copyService.perform(object, newParent, accept)
|
||||||
|
.then(copyFinished);
|
||||||
|
expect(copyFinished.mostRecentCall.args[0])
|
||||||
|
.not.toBe(object);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,11 +44,10 @@ define(
|
|||||||
describe("CopyTask", function () {
|
describe("CopyTask", function () {
|
||||||
var mockDomainObject,
|
var mockDomainObject,
|
||||||
mockParentObject,
|
mockParentObject,
|
||||||
mockPolicyService,
|
mockFilter,
|
||||||
mockQ,
|
mockQ,
|
||||||
mockDeferred,
|
mockDeferred,
|
||||||
testModel,
|
testModel,
|
||||||
mockCallback,
|
|
||||||
counter,
|
counter,
|
||||||
cloneIds,
|
cloneIds,
|
||||||
task;
|
task;
|
||||||
@@ -119,17 +118,14 @@ define(
|
|||||||
mockParentObject = domainObjectFactory({
|
mockParentObject = domainObjectFactory({
|
||||||
capabilities: makeMockCapabilities()
|
capabilities: makeMockCapabilities()
|
||||||
});
|
});
|
||||||
mockPolicyService = jasmine.createSpyObj(
|
mockFilter = jasmine.createSpy('filter');
|
||||||
'policyService',
|
|
||||||
[ 'allow' ]
|
|
||||||
);
|
|
||||||
mockQ = jasmine.createSpyObj('$q', ['when', 'defer', 'all']);
|
mockQ = jasmine.createSpyObj('$q', ['when', 'defer', 'all']);
|
||||||
mockDeferred = jasmine.createSpyObj(
|
mockDeferred = jasmine.createSpyObj(
|
||||||
'deferred',
|
'deferred',
|
||||||
[ 'notify', 'resolve', 'reject' ]
|
[ 'notify', 'resolve', 'reject' ]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockPolicyService.allow.andReturn(true);
|
mockFilter.andReturn(true);
|
||||||
|
|
||||||
mockQ.when.andCallFake(synchronousPromise);
|
mockQ.when.andCallFake(synchronousPromise);
|
||||||
mockQ.defer.andReturn(mockDeferred);
|
mockQ.defer.andReturn(mockDeferred);
|
||||||
@@ -156,7 +152,7 @@ define(
|
|||||||
task = new CopyTask(
|
task = new CopyTask(
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockParentObject,
|
mockParentObject,
|
||||||
mockPolicyService,
|
mockFilter,
|
||||||
mockQ
|
mockQ
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -218,7 +214,7 @@ define(
|
|||||||
task = new CopyTask(
|
task = new CopyTask(
|
||||||
mockComposingObject,
|
mockComposingObject,
|
||||||
mockParentObject,
|
mockParentObject,
|
||||||
mockPolicyService,
|
mockFilter,
|
||||||
mockQ
|
mockQ
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user