Correctly use creatable attribute and persistability when working with domainObjects (#4898)

* making move action location check persistability

* adding persistence check instead of creatability for styles

* added check for link action to make sure parent is persistable

* debug

* adding parent to link action and move action form location controls so they can be used in the form

* adding parent persistability check for duplicate

* updating multilple actions appliesTo methods to check for persistability

* updated the tree to not require an initial selection if being used in a form

* remove noneditable folder plugin

* added persistence check for the parent, in the create wizard

* minor name change

* removing noneditabl folder from default plugins as well

* checking the correct parent for persistability in create wizard

* importing file-saver correctly

* updated tests for import as json

* changes addressing PR review: using consts, removing comments, removing unneccessary code

Co-authored-by: Scott Bell <scott@traclabs.com>
This commit is contained in:
Jamie V 2022-03-14 13:20:06 -07:00 committed by GitHub
parent bcd668594d
commit e691a89682
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 67 additions and 124 deletions

View File

@ -269,7 +269,6 @@ define([
this.install(this.plugins.ViewDatumAction()); this.install(this.plugins.ViewDatumAction());
this.install(this.plugins.ViewLargeAction()); this.install(this.plugins.ViewLargeAction());
this.install(this.plugins.ObjectInterceptors()); this.install(this.plugins.ObjectInterceptors());
this.install(this.plugins.NonEditableFolder());
this.install(this.plugins.DeviceClassifier()); this.install(this.plugins.DeviceClassifier());
this.install(this.plugins.UserIndicator()); this.install(this.plugins.UserIndicator());
} }

View File

@ -325,16 +325,7 @@ export default {
return item && (item.type === type); return item && (item.type === type);
}, },
canPersistObject(item) { canPersistObject(item) {
// for now the only way to tell if an object can be persisted is if it is creatable. return this.openmct.objects.isPersistable(item.identifier);
let creatable = false;
if (item) {
const type = this.openmct.types.get(item.type);
if (type && type.definition) {
creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true));
}
}
return creatable;
}, },
hasConditionalStyle(domainObject, layoutItem) { hasConditionalStyle(domainObject, layoutItem) {
const id = layoutItem ? layoutItem.id : undefined; const id = layoutItem ? layoutItem.id : undefined;

View File

@ -97,13 +97,16 @@ export default class DuplicateAction {
validate(currentParent) { validate(currentParent) {
return (data) => { return (data) => {
const parentCandidatePath = data.value; const parentCandidate = data.value[0];
const parentCandidate = parentCandidatePath[0];
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
return false;
}
if (!parentCandidateKeystring || !currentParentKeystring) { if (!parentCandidateKeystring || !currentParentKeystring) {
return false; return false;
} }
@ -122,13 +125,14 @@ export default class DuplicateAction {
} }
appliesTo(objectPath) { appliesTo(objectPath) {
let parent = objectPath[1]; const parent = objectPath[1];
let parentType = parent && this.openmct.types.get(parent.type); const parentType = parent && this.openmct.types.get(parent.type);
let child = objectPath[0]; const child = objectPath[0];
let childType = child && this.openmct.types.get(child.type); const childType = child && this.openmct.types.get(child.type);
let locked = child.locked ? child.locked : parent && parent.locked; const locked = child.locked ? child.locked : parent && parent.locked;
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
if (locked) { if (locked || !isPersistable) {
return false; return false;
} }

View File

@ -52,7 +52,7 @@ export default class ExportAsJSONAction {
appliesTo(objectPath) { appliesTo(objectPath) {
let domainObject = objectPath[0]; let domainObject = objectPath[0];
return this._isCreatable(domainObject); return this._isCreatableAndPersistable(domainObject);
} }
/** /**
* *
@ -80,10 +80,11 @@ export default class ExportAsJSONAction {
* @param {object} domainObject * @param {object} domainObject
* @returns {boolean} * @returns {boolean}
*/ */
_isCreatable(domainObject) { _isCreatableAndPersistable(domainObject) {
const type = this.openmct.types.get(domainObject.type); const type = this.openmct.types.get(domainObject.type);
const isPersistable = this.openmct.objects.isPersistable(domainObject.identifier);
return type && type.definition.creatable; return type && type.definition.creatable && isPersistable;
} }
/** /**
* @private * @private
@ -170,7 +171,7 @@ export default class ExportAsJSONAction {
.then((children) => { .then((children) => {
children.forEach((child, index) => { children.forEach((child, index) => {
// Only export if object is creatable // Only export if object is creatable
if (this._isCreatable(child)) { if (this._isCreatableAndPersistable(child)) {
// Prevents infinite export of self-contained objs // Prevents infinite export of self-contained objs
if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) { if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
// If object is a link to something absent from // If object is a link to something absent from

View File

@ -27,6 +27,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to folder', () => { it('ExportAsJSONAction applies to folder', () => {
domainObject = { domainObject = {
identifier: {
key: 'export-testing',
namespace: ''
},
composition: [], composition: [],
location: 'mine', location: 'mine',
modified: 1640115501237, modified: 1640115501237,
@ -40,6 +44,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to telemetry.plot.overlay', () => { it('ExportAsJSONAction applies to telemetry.plot.overlay', () => {
domainObject = { domainObject = {
identifier: {
key: 'export-testing',
namespace: ''
},
composition: [], composition: [],
location: 'mine', location: 'mine',
modified: 1640115501237, modified: 1640115501237,
@ -53,6 +61,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to telemetry.plot.stacked', () => { it('ExportAsJSONAction applies to telemetry.plot.stacked', () => {
domainObject = { domainObject = {
identifier: {
key: 'export-testing',
namespace: ''
},
composition: [], composition: [],
location: 'mine', location: 'mine',
modified: 1640115501237, modified: 1640115501237,
@ -64,16 +76,24 @@ describe('Export as JSON plugin', () => {
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true); expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
}); });
it('ExportAsJSONAction applies does not applies to non-creatable objects', () => { it('ExportAsJSONAction does not applie to non-persistable objects', () => {
domainObject = { domainObject = {
identifier: {
key: 'export-testing',
namespace: ''
},
composition: [], composition: [],
location: 'mine', location: 'mine',
modified: 1640115501237, modified: 1640115501237,
name: 'Non Editable Folder', name: 'Non Editable Folder',
persisted: 1640115501237, persisted: 1640115501237,
type: 'noneditable.folder' type: 'folder'
}; };
spyOn(openmct.objects, 'getProvider').and.callFake(() => {
return { get: () => domainObject };
});
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false); expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false);
}); });

View File

@ -101,7 +101,10 @@ export default class CreateWizard {
// Ensure there is always a 'save in' section // Ensure there is always a 'save in' section
if (includeLocation) { if (includeLocation) {
function validateLocation(data) { function validateLocation(data) {
return self.openmct.composition.checkPolicy(data.value[0], domainObject); const policyCheck = self.openmct.composition.checkPolicy(data.value[0], domainObject);
const parentIsPersistable = self.openmct.objects.isPersistable(data.value[0].identifier);
return policyCheck && parentIsPersistable;
} }
sections.push({ sections.push({

View File

@ -33,10 +33,7 @@ export default class LinkAction {
} }
appliesTo(objectPath) { appliesTo(objectPath) {
let domainObject = objectPath[0]; return true; // link away!
let type = domainObject && this.openmct.types.get(domainObject.type);
return type && type.definition.creatable;
} }
invoke(objectPath) { invoke(objectPath) {
@ -77,6 +74,7 @@ export default class LinkAction {
{ {
name: "location", name: "location",
control: "locator", control: "locator",
parent: parentDomainObject,
required: true, required: true,
validate: this.validate(parentDomainObject), validate: this.validate(parentDomainObject),
key: 'location' key: 'location'
@ -97,6 +95,10 @@ export default class LinkAction {
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
return false;
}
if (!parentCandidateKeystring || !currentParentKeystring) { if (!parentCandidateKeystring || !currentParentKeystring) {
return false; return false;
} }

View File

@ -126,6 +126,7 @@ export default class MoveAction {
{ {
name: "Location", name: "Location",
control: "locator", control: "locator",
parent: parentDomainObject,
required: true, required: true,
validate: this.validate(parentDomainObject), validate: this.validate(parentDomainObject),
key: 'location' key: 'location'
@ -144,6 +145,10 @@ export default class MoveAction {
const parentCandidatePath = data.value; const parentCandidatePath = data.value;
const parentCandidate = parentCandidatePath[0]; const parentCandidate = parentCandidatePath[0];
if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
return false;
}
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
@ -174,8 +179,9 @@ export default class MoveAction {
let parentType = parent && this.openmct.types.get(parent.type); let parentType = parent && this.openmct.types.get(parent.type);
let child = objectPath[0]; let child = objectPath[0];
let childType = child && this.openmct.types.get(child.type); let childType = child && this.openmct.types.get(child.type);
let isPersistable = this.openmct.objects.isPersistable(child.identifier);
if (child.locked || (parent && parent.locked)) { if (child.locked || (parent && parent.locked) || !isPersistable) {
return false; return false;
} }

View File

@ -1,33 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is 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.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
export default function () {
return function (openmct) {
openmct.types.addType("noneditable.folder", {
name: "Non-Editable Folder",
key: "noneditable.folder",
description: "Create folders to organize other objects or links to objects without the ability to edit it's properties.",
cssClass: "icon-folder",
creatable: false
});
};
}

View File

@ -1,50 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is 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.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
createOpenMct,
resetApplicationState
} from 'utils/testing';
describe("the plugin", () => {
const NON_EDITABLE_FOLDER_KEY = 'noneditable.folder';
let openmct;
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(openmct.plugins.NonEditableFolder());
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('adds the new non-editable folder type', () => {
const type = openmct.types.get(NON_EDITABLE_FOLDER_KEY);
expect(type).toBeDefined();
expect(type.definition.creatable).toBeFalse();
});
});

View File

@ -61,7 +61,6 @@ define([
'./URLTimeSettingsSynchronizer/plugin', './URLTimeSettingsSynchronizer/plugin',
'./notificationIndicator/plugin', './notificationIndicator/plugin',
'./newFolderAction/plugin', './newFolderAction/plugin',
'./nonEditableFolder/plugin',
'./persistence/couch/plugin', './persistence/couch/plugin',
'./defaultRootName/plugin', './defaultRootName/plugin',
'./plan/plugin', './plan/plugin',
@ -119,7 +118,6 @@ define([
URLTimeSettingsSynchronizer, URLTimeSettingsSynchronizer,
NotificationIndicator, NotificationIndicator,
NewFolderAction, NewFolderAction,
NonEditableFolder,
CouchDBPlugin, CouchDBPlugin,
DefaultRootName, DefaultRootName,
PlanLayout, PlanLayout,
@ -197,7 +195,6 @@ define([
plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default; plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default;
plugins.NotificationIndicator = NotificationIndicator.default; plugins.NotificationIndicator = NotificationIndicator.default;
plugins.NewFolderAction = NewFolderAction.default; plugins.NewFolderAction = NewFolderAction.default;
plugins.NonEditableFolder = NonEditableFolder.default;
plugins.ISOTimeFormat = ISOTimeFormat.default; plugins.ISOTimeFormat = ISOTimeFormat.default;
plugins.DefaultRootName = DefaultRootName.default; plugins.DefaultRootName = DefaultRootName.default;
plugins.PlanLayout = PlanLayout.default; plugins.PlanLayout = PlanLayout.default;

View File

@ -106,6 +106,11 @@ export default class RemoveAction {
let child = objectPath[0]; let child = objectPath[0];
let locked = child.locked ? child.locked : parent && parent.locked; let locked = child.locked ? child.locked : parent && parent.locked;
let isEditing = this.openmct.editor.isEditing(); let isEditing = this.openmct.editor.isEditing();
let isPersistable = this.openmct.objects.isPersistable(child.identifier);
if (locked || !isPersistable) {
return false;
}
if (isEditing) { if (isEditing) {
let currentItemInView = this.openmct.router.path[0]; let currentItemInView = this.openmct.router.path[0];
@ -116,10 +121,6 @@ export default class RemoveAction {
} }
} }
if (locked) {
return false;
}
return parentType return parentType
&& parentType.definition.creatable && parentType.definition.creatable
&& Array.isArray(parent.composition); && Array.isArray(parent.composition);

View File

@ -258,11 +258,13 @@ export default {
if (!this.isSelectorTree) { if (!this.isSelectorTree) {
await this.syncTreeOpenItems(); await this.syncTreeOpenItems();
} else { } else {
if (this.initialSelection.identifier) {
const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier); const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier);
const navigationPath = this.buildNavigationPath(objectPath); const navigationPath = this.buildNavigationPath(objectPath);
this.openAndScrollTo(navigationPath); this.openAndScrollTo(navigationPath);
} }
}
}, },
created() { created() {
this.getSearchResults = _.debounce(this.getSearchResults, 400); this.getSearchResults = _.debounce(this.getSearchResults, 400);