diff --git a/src/plugins/conditionWidget/components/ConditionWidget.vue b/src/plugins/conditionWidget/components/ConditionWidget.vue index 80220dd754..ef865b1c64 100644 --- a/src/plugins/conditionWidget/components/ConditionWidget.vue +++ b/src/plugins/conditionWidget/components/ConditionWidget.vue @@ -136,8 +136,8 @@ export default { this.url = url; } - const conditionSetIdentifier = domainObject.configuration.objectStyles.conditionSetIdentifier; - if (this.conditionSetIdentifier !== conditionSetIdentifier) { + const conditionSetIdentifier = domainObject.configuration?.objectStyles?.conditionSetIdentifier; + if (conditionSetIdentifier && this.conditionSetIdentifier !== conditionSetIdentifier) { this.conditionSetIdentifier = conditionSetIdentifier; } diff --git a/src/plugins/exportAsJSONAction/ExportAsJSONAction.js b/src/plugins/exportAsJSONAction/ExportAsJSONAction.js index f887b8302e..506c347454 100644 --- a/src/plugins/exportAsJSONAction/ExportAsJSONAction.js +++ b/src/plugins/exportAsJSONAction/ExportAsJSONAction.js @@ -128,6 +128,30 @@ export default class ExportAsJSONAction { return copyOfChild; } + + /** + * @private + * @param {object} child + * @param {object} parent + * @returns {object} + */ + _rewriteLinkForReference(child, parent) { + const childId = this._getId(child); + this.externalIdentifiers.push(childId); + const copyOfChild = JSON.parse(JSON.stringify(child)); + + copyOfChild.identifier.key = uuid(); + const newIdString = this._getId(copyOfChild); + const parentId = this._getId(parent); + + this.idMap[childId] = newIdString; + copyOfChild.location = null; + parent.configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier; + this.tree[newIdString] = copyOfChild; + this.tree[parentId].configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier; + + return copyOfChild; + } /** * @private */ @@ -159,23 +183,27 @@ export default class ExportAsJSONAction { "rootId": this._getId(this.root) }; } + /** * @private * @param {object} parent */ _write(parent) { this.calls++; + //conditional object styles are not saved on the composition, so we need to check for them + let childObjectReferenceId = parent.configuration?.objectStyles?.conditionSetIdentifier; + const composition = this.openmct.composition.get(parent); if (composition !== undefined) { composition.load() .then((children) => { children.forEach((child, index) => { - // Only export if object is creatable + // Only export if object is creatable 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 is a link to something absent from - // tree, generate new id and treat as new object + // If object is a link to something absent from + // tree, generate new id and treat as new object if (this._isLinkedObject(child, parent)) { child = this._rewriteLink(child, parent); } else { @@ -186,18 +214,41 @@ export default class ExportAsJSONAction { } } }); - this.calls--; - if (this.calls === 0) { - this._rewriteReferences(); - this._saveAs(this._wrapTree()); - } + this._decrementCallsAndSave(); }); - } else { - this.calls--; - if (this.calls === 0) { - this._rewriteReferences(); - this._saveAs(this._wrapTree()); - } + } else if (!childObjectReferenceId) { + this._decrementCallsAndSave(); + } + + if (childObjectReferenceId) { + this.openmct.objects.get(childObjectReferenceId) + .then((child) => { + // Only export if object is creatable + if (this._isCreatableAndPersistable(child)) { + // Prevents infinite export of self-contained objs + if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) { + // If object is a link to something absent from + // tree, generate new id and treat as new object + if (this._isLinkedObject(child, parent)) { + child = this._rewriteLinkForReference(child, parent); + } else { + this.tree[this._getId(child)] = child; + } + + this._write(child); + } + } + + this._decrementCallsAndSave(); + }); + } + } + + _decrementCallsAndSave() { + this.calls--; + if (this.calls === 0) { + this._rewriteReferences(); + this._saveAs(this._wrapTree()); } } } diff --git a/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js b/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js index b55feb25af..7ecb3b9637 100644 --- a/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js +++ b/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js @@ -322,4 +322,57 @@ describe('Export as JSON plugin', () => { exportAsJSONAction.invoke([parent]); }); + + it('ExportAsJSONAction exports object references from tree', (done) => { + const parent = { + composition: [], + configuration: { + objectStyles: { + conditionSetIdentifier: { + key: 'child', + namespace: '' + } + } + }, + identifier: { + key: 'parent', + namespace: '' + }, + name: 'Parent', + type: 'folder', + modified: 1503598129176, + location: 'mine', + persisted: 1503598129176 + }; + + const child = { + composition: [], + identifier: { + key: 'child', + namespace: '' + }, + name: 'Child', + type: 'folder', + modified: 1503598132428, + location: null, + persisted: 1503598132428 + }; + + spyOn(openmct.objects, 'get').and.callFake(object => { + return Promise.resolve(child); + }); + + spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => { + expect(Object.keys(completedTree).length).toBe(2); + const conditionSetId = Object.keys(completedTree.openmct)[1]; + expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy(); + expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy(); + expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy(); + expect(completedTree.openmct[conditionSetId].name).toBe('Child'); + + done(); + }); + + exportAsJSONAction.invoke([parent]); + }); }); diff --git a/src/plugins/importFromJSONAction/ImportFromJSONAction.js b/src/plugins/importFromJSONAction/ImportFromJSONAction.js index a24ef281d5..5b28c0f60d 100644 --- a/src/plugins/importFromJSONAction/ImportFromJSONAction.js +++ b/src/plugins/importFromJSONAction/ImportFromJSONAction.js @@ -82,12 +82,14 @@ export default class ImportAsJSONAction { * @param {object} seen */ _deepInstantiate(parent, tree, seen) { - if (this.openmct.composition.get(parent)) { + let objectIdentifiers = this._getObjectReferenceIds(parent); + + if (objectIdentifiers.length) { let newObj; seen.push(parent.id); - parent.composition.forEach(async (childId) => { + objectIdentifiers.forEach(async (childId) => { const keystring = this.openmct.objects.makeKeyString(childId); if (!tree[keystring] || seen.includes(keystring)) { return; @@ -101,6 +103,27 @@ export default class ImportAsJSONAction { }, this); } } + /** + * @private + * @param {object} parent + * @returns [identifiers] + */ + _getObjectReferenceIds(parent) { + let objectIdentifiers = []; + + let parentComposition = this.openmct.composition.get(parent); + if (parentComposition) { + objectIdentifiers = Array.from(parentComposition.domainObject.composition); + } + + //conditional object styles are not saved on the composition, so we need to check for them + let parentObjectReference = parent.configuration?.objectStyles?.conditionSetIdentifier; + if (parentObjectReference) { + objectIdentifiers.push(parentObjectReference); + } + + return objectIdentifiers; + } /** * @private * @param {object} tree