Merge branch 'master' into ladtableset-name-clarity

This commit is contained in:
Jamie V 2020-09-10 10:22:03 -07:00 committed by GitHub
commit 35692ae4b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 763 additions and 101 deletions

View File

@ -79,12 +79,6 @@ function (
return this.objectService; return this.objectService;
}; };
function resolveWith(object) {
return function () {
return object;
};
}
/** /**
* Save changes and conclude editing. * Save changes and conclude editing.
* *
@ -102,7 +96,6 @@ function (
SaveAsAction.prototype.save = function () { SaveAsAction.prototype.save = function () {
var self = this, var self = this,
domainObject = this.domainObject, domainObject = this.domainObject,
copyService = this.copyService,
dialog = new SaveInProgressDialog(this.dialogService), dialog = new SaveInProgressDialog(this.dialogService),
toUndirty = []; toUndirty = [];
@ -139,19 +132,22 @@ function (
return fetchObject(object.getModel().location); return fetchObject(object.getModel().location);
} }
function allowClone(objectToClone) { function saveObject(parent) {
var allowed = return self.openmct.editor.save().then(() => {
(objectToClone.getId() === domainObject.getId()) // Force mutation for search indexing
|| objectToClone.getCapability('location').isOriginal(); return parent;
if (allowed) { });
toUndirty.push(objectToClone);
} }
return allowed; function addSavedObjectToParent(parent) {
} return parent.getCapability("composition")
.add(domainObject)
function cloneIntoParent(parent) { .then(function (addedObject) {
return copyService.perform(domainObject, parent, allowClone); return parent.getCapability("persistence").persist()
.then(function () {
return addedObject;
});
});
} }
function undirty(object) { function undirty(object) {
@ -160,26 +156,17 @@ function (
function undirtyOriginals(object) { function undirtyOriginals(object) {
return Promise.all(toUndirty.map(undirty)) return Promise.all(toUndirty.map(undirty))
.then(resolveWith(object)); .then(() => {
} return object;
function saveAfterClone(clonedObject) {
return self.openmct.editor.save().then(() => {
// Force mutation for search indexing
return clonedObject;
}); });
} }
function finishEditing(clonedObject) { function indexForSearch(addedObject) {
return fetchObject(clonedObject.getId()); addedObject.useCapability('mutation', (model) => {
}
function indexForSearch(savedObject) {
savedObject.useCapability('mutation', (model) => {
return model; return model;
}); });
return savedObject; return addedObject;
} }
function onSuccess(object) { function onSuccess(object) {
@ -201,10 +188,12 @@ function (
.then(doWizardSave) .then(doWizardSave)
.then(showBlockingDialog) .then(showBlockingDialog)
.then(getParent) .then(getParent)
.then(cloneIntoParent) .then(saveObject)
.then(addSavedObjectToParent)
.then(undirtyOriginals) .then(undirtyOriginals)
.then(saveAfterClone) .then((addedObject) => {
.then(finishEditing) return fetchObject(addedObject.getId());
})
.then(indexForSearch) .then(indexForSearch)
.then(hideBlockingDialog) .then(hideBlockingDialog)
.then(onSuccess) .then(onSuccess)

View File

@ -35,7 +35,8 @@ define([
'./services/LegacyObjectAPIInterceptor', './services/LegacyObjectAPIInterceptor',
'./views/installLegacyViews', './views/installLegacyViews',
'./policies/LegacyCompositionPolicyAdapter', './policies/LegacyCompositionPolicyAdapter',
'./actions/LegacyActionAdapter' './actions/LegacyActionAdapter',
'./services/LegacyPersistenceAdapter'
], function ( ], function (
ActionDialogDecorator, ActionDialogDecorator,
AdapterCapability, AdapterCapability,
@ -51,7 +52,8 @@ define([
LegacyObjectAPIInterceptor, LegacyObjectAPIInterceptor,
installLegacyViews, installLegacyViews,
legacyCompositionPolicyAdapter, legacyCompositionPolicyAdapter,
LegacyActionAdapter LegacyActionAdapter,
LegacyPersistenceAdapter
) { ) {
return { return {
name: 'src/adapter', name: 'src/adapter',
@ -114,6 +116,15 @@ define([
"instantiate", "instantiate",
"topic" "topic"
] ]
},
{
provides: "persistenceService",
type: "provider",
priority: "fallback",
implementation: function legacyPersistenceProvider(openmct) {
return new LegacyPersistenceAdapter.default(openmct);
},
depends: ["openmct"]
} }
], ],
policies: [ policies: [

View File

@ -0,0 +1,47 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 objectUtils from 'objectUtils';
export default class LegacyPersistenceAdapter {
constructor(openmct) {
this.openmct = openmct;
}
listObjects() {
return Promise.resolve([]);
}
listSpaces() {
return Promise.resolve(Object.keys(this.openmct.objects.providers));
}
updateObject(legacyDomainObject) {
return this.openmct.objects.save(legacyDomainObject.useCapability('adapter'));
}
readObject(keystring) {
let identifier = objectUtils.parseKeyString(keystring);
return this.openmct.legacyObject(this.openmct.objects.get(identifier));
}
}

View File

@ -29,7 +29,7 @@ class RootObjectProvider {
key: "ROOT", key: "ROOT",
namespace: "" namespace: ""
}, },
name: 'The root object', name: 'Open MCT',
type: 'root', type: 'root',
composition: [] composition: []
}; };

View File

@ -22,7 +22,7 @@
import RootObjectProvider from '../RootObjectProvider'; import RootObjectProvider from '../RootObjectProvider';
describe('RootObjectProvider', function () { describe('RootObjectProvider', function () {
// let rootRegistry; const ROOT_NAME = 'Open MCT';
let rootObjectProvider; let rootObjectProvider;
let roots = ['some root']; let roots = ['some root'];
let rootRegistry = { let rootRegistry = {
@ -43,7 +43,7 @@ describe('RootObjectProvider', function () {
key: "ROOT", key: "ROOT",
namespace: "" namespace: ""
}, },
name: 'The root object', name: ROOT_NAME,
type: 'root', type: 'root',
composition: ['some root'] composition: ['some root']
}); });

View File

@ -0,0 +1,47 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 class ISOTimeFormat {
constructor() {
this.key = 'iso';
}
format(value) {
if (value !== undefined) {
return new Date(value).toISOString();
} else {
return value;
}
}
parse(text) {
if (typeof text === 'number' || text === undefined) {
return text;
}
return Date.parse(text);
}
validate(text) {
return !isNaN(Date.parse(text));
}
}

View File

@ -0,0 +1,29 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 ISOTimeFormat from './ISOTimeFormat';
export default function () {
return function install(openmct) {
openmct.telemetry.addFormat(new ISOTimeFormat());
};
}

View File

@ -0,0 +1,55 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 ISOTimeFormat from './ISOTimeFormat.js';
describe("the plugin", () => {
const ISO_KEY = 'iso';
const JUNK = "junk";
const MOON_LANDING_TIMESTAMP = -14256000000;
const MOON_LANDING_DATESTRING = '1969-07-20T00:00:00.000Z';
let isoFormatter;
beforeEach(() => {
isoFormatter = new ISOTimeFormat();
});
describe("creates a new ISO based formatter", function () {
it("with the key 'iso'", () => {
expect(isoFormatter.key).toBe(ISO_KEY);
});
it("that will format a timestamp in ISO standard format", () => {
expect(isoFormatter.format(MOON_LANDING_TIMESTAMP)).toBe(MOON_LANDING_DATESTRING);
});
it("that will parse an ISO Date String into milliseconds", () => {
expect(isoFormatter.parse(MOON_LANDING_DATESTRING)).toBe(MOON_LANDING_TIMESTAMP);
});
it("that will validate correctly", () => {
expect(isoFormatter.validate(MOON_LANDING_DATESTRING)).toBe(true);
expect(isoFormatter.validate(JUNK)).toBe(false);
});
});
});

View File

@ -125,12 +125,18 @@ export default class ConditionManager extends EventEmitter {
} }
} }
updateCondition(conditionConfiguration, index) { updateCondition(conditionConfiguration) {
let condition = this.conditions[index]; let condition = this.findConditionById(conditionConfiguration.id);
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration; if (condition) {
condition.update(conditionConfiguration); condition.update(conditionConfiguration);
}
let index = this.conditionSetDomainObject.configuration.conditionCollection.findIndex(item => item.id === conditionConfiguration.id);
if (index > -1) {
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
this.persistConditions(); this.persistConditions();
} }
}
updateConditionDescription(condition) { updateConditionDescription(condition) {
const found = this.conditionSetDomainObject.configuration.conditionCollection.find(conditionConfiguration => (conditionConfiguration.id === condition.id)); const found = this.conditionSetDomainObject.configuration.conditionCollection.find(conditionConfiguration => (conditionConfiguration.id === condition.id));
@ -202,13 +208,19 @@ export default class ConditionManager extends EventEmitter {
this.persistConditions(); this.persistConditions();
} }
removeCondition(index) { removeCondition(id) {
let condition = this.conditions[index]; let index = this.conditions.findIndex(item => item.id === id);
condition.destroy(); if (index > -1) {
this.conditions[index].destroy();
this.conditions.splice(index, 1); this.conditions.splice(index, 1);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1); }
let conditionCollectionIndex = this.conditionSetDomainObject.configuration.conditionCollection.findIndex(item => item.id === id);
if (conditionCollectionIndex > -1) {
this.conditionSetDomainObject.configuration.conditionCollection.splice(conditionCollectionIndex, 1);
this.persistConditions(); this.persistConditions();
} }
}
findConditionById(id) { findConditionById(id) {
return this.conditions.find(condition => condition.id === id); return this.conditions.find(condition => condition.id === id);
@ -220,8 +232,8 @@ export default class ConditionManager extends EventEmitter {
reorderPlan.forEach((reorderEvent) => { reorderPlan.forEach((reorderEvent) => {
let item = oldConditions[reorderEvent.oldIndex]; let item = oldConditions[reorderEvent.oldIndex];
newCollection.push(item); newCollection.push(item);
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
}); });
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
this.persistConditions(); this.persistConditions();
} }

View File

@ -27,13 +27,32 @@ describe('ConditionManager', () => {
let conditionMgr; let conditionMgr;
let mockListener; let mockListener;
let openmct = {}; let openmct = {};
let mockCondition = { let mockDefaultCondition = {
isDefault: true, isDefault: true,
id: '1234-5678', id: '1234-5678',
configuration: { configuration: {
criteria: [] criteria: []
} }
}; };
let mockCondition1 = {
id: '2345-6789',
configuration: {
criteria: []
}
};
let updatedMockCondition1 = {
id: '2345-6789',
configuration: {
trigger: 'xor',
criteria: []
}
};
let mockCondition2 = {
id: '3456-7890',
configuration: {
criteria: []
}
};
let conditionSetDomainObject = { let conditionSetDomainObject = {
identifier: { identifier: {
namespace: "", namespace: "",
@ -43,7 +62,9 @@ describe('ConditionManager', () => {
location: "mine", location: "mine",
configuration: { configuration: {
conditionCollection: [ conditionCollection: [
mockCondition mockCondition1,
mockCondition2,
mockDefaultCondition
] ]
} }
}; };
@ -59,7 +80,7 @@ describe('ConditionManager', () => {
let mockDomainObject = { let mockDomainObject = {
useCapability: function () { useCapability: function () {
return mockCondition; return mockDefaultCondition;
} }
}; };
mockInstantiate.and.callFake(function () { mockInstantiate.and.callFake(function () {
@ -107,7 +128,11 @@ describe('ConditionManager', () => {
openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) { openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) {
resolve(conditionSetDomainObject); resolve(conditionSetDomainObject);
}), new Promise(function (resolve, reject) { }), new Promise(function (resolve, reject) {
resolve(mockCondition); resolve(mockCondition1);
}), new Promise(function (resolve, reject) {
resolve(mockCondition2);
}), new Promise(function (resolve, reject) {
resolve(mockDefaultCondition);
})); }));
openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key); openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key);
openmct.objects.observe.and.returnValue(function () {}); openmct.objects.observe.and.returnValue(function () {});
@ -126,9 +151,65 @@ describe('ConditionManager', () => {
}); });
it('creates a conditionCollection with a default condition', function () { it('creates a conditionCollection with a default condition', function () {
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1); expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(3);
let defaultConditionId = conditionMgr.conditions[0].id; let defaultConditionId = conditionMgr.conditions[2].id;
expect(defaultConditionId).toEqual(mockCondition.id); expect(defaultConditionId).toEqual(mockDefaultCondition.id);
});
it('reorders a conditionCollection', function () {
let reorderPlan = [{
oldIndex: 1,
newIndex: 0
},
{
oldIndex: 0,
newIndex: 1
},
{
oldIndex: 2,
newIndex: 2
}];
conditionMgr.reorderConditions(reorderPlan);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(3);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection[0].id).toEqual(mockCondition2.id);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection[1].id).toEqual(mockCondition1.id);
});
it('updates the right condition after reorder', function () {
let reorderPlan = [{
oldIndex: 1,
newIndex: 0
},
{
oldIndex: 0,
newIndex: 1
},
{
oldIndex: 2,
newIndex: 2
}];
conditionMgr.reorderConditions(reorderPlan);
conditionMgr.updateCondition(updatedMockCondition1);
expect(conditionMgr.conditions[1].trigger).toEqual(updatedMockCondition1.configuration.trigger);
});
it('removes the right condition after reorder', function () {
let reorderPlan = [{
oldIndex: 1,
newIndex: 0
},
{
oldIndex: 0,
newIndex: 1
},
{
oldIndex: 2,
newIndex: 2
}];
conditionMgr.reorderConditions(reorderPlan);
conditionMgr.removeCondition(mockCondition1.id);
expect(conditionMgr.conditions.length).toEqual(2);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection[0].id).toEqual(mockCondition2.id);
}); });
}); });

View File

@ -308,15 +308,15 @@ export default {
}; };
this.condition.configuration.criteria.push(criteriaObject); this.condition.configuration.criteria.push(criteriaObject);
}, },
dragStart(e) { dragStart(event) {
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag event.dataTransfer.clearData();
e.dataTransfer.effectAllowed = "copyMove"; event.dataTransfer.setData('dragging', event.target); // required for FF to initiate drag
e.dataTransfer.setDragImage(e.target.closest('.c-condition-h'), 0, 0); event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.setDragImage(event.target.closest('.c-condition-h'), 0, 0);
this.$emit('setMoveIndex', this.conditionIndex); this.$emit('setMoveIndex', this.conditionIndex);
}, },
dragEnd(event) { dragEnd() {
this.dragStarted = false; this.dragStarted = false;
event.dataTransfer.clearData();
this.$emit('dragComplete'); this.$emit('dragComplete');
}, },
dropCondition(event, targetIndex) { dropCondition(event, targetIndex) {
@ -359,10 +359,10 @@ export default {
}, },
destroy() { destroy() {
}, },
removeCondition(ev) { removeCondition() {
this.$emit('removeCondition', this.conditionIndex); this.$emit('removeCondition', this.condition.id);
}, },
cloneCondition(ev) { cloneCondition() {
this.$emit('cloneCondition', { this.$emit('cloneCondition', {
condition: this.condition, condition: this.condition,
index: this.conditionIndex index: this.conditionIndex
@ -380,8 +380,7 @@ export default {
}, },
persist() { persist() {
this.$emit('updateCondition', { this.$emit('updateCondition', {
condition: this.condition, condition: this.condition
index: this.conditionIndex
}); });
}, },
initCap(str) { initCap(str) {

View File

@ -223,10 +223,10 @@ export default {
this.conditionManager.addCondition(); this.conditionManager.addCondition();
}, },
updateCondition(data) { updateCondition(data) {
this.conditionManager.updateCondition(data.condition, data.index); this.conditionManager.updateCondition(data.condition);
}, },
removeCondition(index) { removeCondition(id) {
this.conditionManager.removeCondition(index); this.conditionManager.removeCondition(id);
}, },
reorder(reorderPlan) { reorder(reorderPlan) {
this.conditionManager.reorderConditions(reorderPlan); this.conditionManager.reorderConditions(reorderPlan);

View File

@ -0,0 +1,53 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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.
*****************************************************************************/
/**
* A CouchDocument describes domain object model in a format
* which is easily read-written to CouchDB. This includes
* Couch's _id and _rev fields, as well as a separate
* metadata field which contains a subset of information found
* in the model itself (to support search optimization with
* CouchDB views.)
* @memberof platform/persistence/couch
* @constructor
* @param {string} id the id under which to store this mode
* @param {object} model the model to store
* @param {string} rev the revision to include (or undefined,
* if no revision should be noted for couch)
* @param {boolean} whether or not to mark this document as
* deleted (see CouchDB docs for _deleted)
*/
export default function CouchDocument(id, model, rev, markDeleted) {
return {
"_id": id,
"_rev": rev,
"_deleted": markDeleted,
"metadata": {
"category": "domain object",
"type": model.type,
"owner": "admin",
"name": model.name,
"created": Date.now()
},
"model": model
};
}

View File

@ -0,0 +1,156 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 CouchDocument from "./CouchDocument";
import CouchObjectQueue from "./CouchObjectQueue";
const REV = "_rev";
const ID = "_id";
export default class CouchObjectProvider {
constructor(openmct, url, namespace) {
this.openmct = openmct;
this.url = url;
this.namespace = namespace;
this.objectQueue = {};
}
request(subPath, method, value) {
return fetch(this.url + '/' + subPath, {
method: method,
body: JSON.stringify(value)
}).then(response => response.json())
.then(function (response) {
return response;
}, function () {
return undefined;
});
}
// Check the response to a create/update/delete request;
// track the rev if it's valid, otherwise return false to
// indicate that the request failed.
// persist any queued objects
checkResponse(response, intermediateResponse) {
let requestSuccess = false;
const id = response ? response.id : undefined;
let rev;
if (response && response.ok) {
rev = response.rev;
requestSuccess = true;
}
intermediateResponse.resolve(requestSuccess);
if (id) {
if (!this.objectQueue[id]) {
this.objectQueue[id] = new CouchObjectQueue(undefined, rev);
}
this.objectQueue[id].updateRevision(rev);
this.objectQueue[id].pending = false;
if (this.objectQueue[id].hasNext()) {
this.updateQueued(id);
}
}
}
getModel(response) {
if (response && response.model) {
let key = response[ID];
let object = response.model;
object.identifier = {
namespace: this.namespace,
key: key
};
if (!this.objectQueue[key]) {
this.objectQueue[key] = new CouchObjectQueue(undefined, response[REV]);
}
this.objectQueue[key].updateRevision(response[REV]);
return object;
} else {
return undefined;
}
}
get(identifier) {
return this.request(identifier.key, "GET").then(this.getModel.bind(this));
}
getIntermediateResponse() {
let intermediateResponse = {};
intermediateResponse.promise = new Promise(function (resolve, reject) {
intermediateResponse.resolve = resolve;
intermediateResponse.reject = reject;
});
return intermediateResponse;
}
enqueueObject(key, model, intermediateResponse) {
if (this.objectQueue[key]) {
this.objectQueue[key].enqueue({
model,
intermediateResponse
});
} else {
this.objectQueue[key] = new CouchObjectQueue({
model,
intermediateResponse
});
}
}
create(model) {
let intermediateResponse = this.getIntermediateResponse();
const key = model.identifier.key;
this.enqueueObject(key, model, intermediateResponse);
this.objectQueue[key].pending = true;
const queued = this.objectQueue[key].dequeue();
this.request(key, "PUT", new CouchDocument(key, queued.model)).then((response) => {
this.checkResponse(response, queued.intermediateResponse);
});
return intermediateResponse.promise;
}
updateQueued(key) {
if (!this.objectQueue[key].pending) {
this.objectQueue[key].pending = true;
const queued = this.objectQueue[key].dequeue();
this.request(key, "PUT", new CouchDocument(key, queued.model, this.objectQueue[key].rev)).then((response) => {
this.checkResponse(response, queued.intermediateResponse);
});
}
}
update(model) {
let intermediateResponse = this.getIntermediateResponse();
const key = model.identifier.key;
this.enqueueObject(key, model, intermediateResponse);
this.updateQueued(key);
return intermediateResponse.promise;
}
}

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 class CouchObjectQueue {
constructor(object, rev) {
this.rev = rev;
this.objects = object ? [object] : [];
this.pending = false;
}
updateRevision(rev) {
this.rev = rev;
}
hasNext() {
return this.objects.length;
}
enqueue(item) {
this.objects.push(item);
}
dequeue() {
return this.objects.shift();
}
clear() {
this.rev = undefined;
this.objects = [];
}
}

View File

@ -0,0 +1,30 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 CouchObjectProvider from './CouchObjectProvider';
const NAMESPACE = '';
export default function CouchPlugin(url) {
return function install(openmct) {
openmct.objects.addProvider(NAMESPACE, new CouchObjectProvider(openmct, url, NAMESPACE));
};
}

View File

@ -0,0 +1,116 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 CouchPlugin from './plugin.js';
import {
createOpenMct,
resetApplicationState, spyOnBuiltins
} from 'utils/testing';
import CouchObjectProvider from './CouchObjectProvider';
describe('the plugin', () => {
let openmct;
let element;
let child;
let provider;
let testSpace = 'testSpace';
let testPath = '/test/db';
let mockDomainObject = {
identifier: {
namespace: '',
key: 'some-value'
}
};
beforeEach((done) => {
openmct = createOpenMct(false);
openmct.install(new CouchPlugin(testSpace, testPath));
element = document.createElement('div');
child = document.createElement('div');
element.appendChild(child);
openmct.on('start', done);
openmct.startHeadless();
provider = openmct.objects.getProvider(mockDomainObject.identifier);
spyOn(provider, 'get').and.callThrough();
spyOn(provider, 'create').and.callThrough();
spyOn(provider, 'update').and.callThrough();
spyOnBuiltins(['fetch'], window);
fetch.and.returnValue(Promise.resolve({
json: () => {
return {
ok: true,
_id: 'some-value',
_rev: 1,
model: {}
};
}
}));
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('gets an object', () => {
openmct.objects.get(mockDomainObject.identifier).then((result) => {
expect(result.identifier.key).toEqual(mockDomainObject.identifier.key);
});
});
it('creates an object', () => {
openmct.objects.save(mockDomainObject).then((result) => {
expect(provider.create).toHaveBeenCalled();
expect(result).toBeTrue();
});
});
it('updates an object', () => {
openmct.objects.save(mockDomainObject).then((result) => {
expect(result).toBeTrue();
expect(provider.create).toHaveBeenCalled();
openmct.objects.save(mockDomainObject).then((updatedResult) => {
expect(updatedResult).toBeTrue();
expect(provider.update).toHaveBeenCalled();
});
});
});
it('updates queued objects', () => {
let couchProvider = new CouchObjectProvider(openmct, 'http://localhost', '');
let intermediateResponse = couchProvider.getIntermediateResponse();
spyOn(couchProvider, 'updateQueued');
couchProvider.enqueueObject(mockDomainObject.identifier.key, mockDomainObject, intermediateResponse);
couchProvider.objectQueue[mockDomainObject.identifier.key].updateRevision(1);
couchProvider.update(mockDomainObject);
expect(couchProvider.objectQueue[mockDomainObject.identifier.key].hasNext()).toBe(2);
couchProvider.checkResponse({
ok: true,
rev: 2,
id: mockDomainObject.identifier.key
}, intermediateResponse);
expect(couchProvider.updateQueued).toHaveBeenCalledTimes(2);
});
});

View File

@ -24,6 +24,7 @@ define([
'lodash', 'lodash',
'./utcTimeSystem/plugin', './utcTimeSystem/plugin',
'./localTimeSystem/plugin', './localTimeSystem/plugin',
'./ISOTimeFormat/plugin',
'../../example/generator/plugin', '../../example/generator/plugin',
'./autoflow/AutoflowTabularPlugin', './autoflow/AutoflowTabularPlugin',
'./timeConductor/plugin', './timeConductor/plugin',
@ -55,11 +56,13 @@ define([
'./URLTimeSettingsSynchronizer/plugin', './URLTimeSettingsSynchronizer/plugin',
'./notificationIndicator/plugin', './notificationIndicator/plugin',
'./newFolderAction/plugin', './newFolderAction/plugin',
'./persistence/couch/plugin',
'./defaultRootName/plugin' './defaultRootName/plugin'
], function ( ], function (
_, _,
UTCTimeSystem, UTCTimeSystem,
LocalTimeSystem, LocalTimeSystem,
ISOTimeFormat,
GeneratorPlugin, GeneratorPlugin,
AutoflowPlugin, AutoflowPlugin,
TimeConductorPlugin, TimeConductorPlugin,
@ -91,12 +94,12 @@ define([
URLTimeSettingsSynchronizer, URLTimeSettingsSynchronizer,
NotificationIndicator, NotificationIndicator,
NewFolderAction, NewFolderAction,
CouchDBPlugin,
DefaultRootName DefaultRootName
) { ) {
const bundleMap = { const bundleMap = {
LocalStorage: 'platform/persistence/local', LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items', MyItems: 'platform/features/my-items',
CouchDB: 'platform/persistence/couch',
Elasticsearch: 'platform/persistence/elastic' Elasticsearch: 'platform/persistence/elastic'
}; };
@ -128,27 +131,7 @@ define([
plugins.Conductor = TimeConductorPlugin.default; plugins.Conductor = TimeConductorPlugin.default;
plugins.CouchDB = function (url) { plugins.CouchDB = CouchDBPlugin.default;
return function (openmct) {
if (url) {
const bundleName = "config/couch";
openmct.legacyRegistry.register(bundleName, {
"extensions": {
"constants": [
{
"key": "COUCHDB_PATH",
"value": url,
"priority": "mandatory"
}
]
}
});
openmct.legacyRegistry.enable(bundleName);
}
openmct.legacyRegistry.enable(bundleMap.CouchDB);
};
};
plugins.Elasticsearch = function (url) { plugins.Elasticsearch = function (url) {
return function (openmct) { return function (openmct) {
@ -203,6 +186,7 @@ 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.ISOTimeFormat = ISOTimeFormat.default;
plugins.DefaultRootName = DefaultRootName.default; plugins.DefaultRootName = DefaultRootName.default;
return plugins; return plugins;

View File

@ -260,7 +260,7 @@ export default {
this.isZooming = false; this.isZooming = false;
if (bounds) { if (bounds) {
this.handleNewBounds(bounds); this.openmct.time.bounds(bounds);
} else { } else {
this.setViewFromBounds(this.bounds); this.setViewFromBounds(this.bounds);
} }

View File

@ -2,8 +2,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 1 auto; flex: 1 1 auto;
//TODO: Do we need this???
//padding-right: $interiorMarginSm;
overflow: auto; overflow: auto;
> * + * { margin-top: $interiorMargin; } > * + * { margin-top: $interiorMargin; }
@ -245,6 +243,7 @@
border: 1px solid $colorInteriorBorder; border: 1px solid $colorInteriorBorder;
border-radius: $controlCr; border-radius: $controlCr;
padding: $interiorMargin; padding: $interiorMargin;
overflow: auto;
} }
} }

View File

@ -76,7 +76,7 @@
@expanded="handleExpanded" @expanded="handleExpanded"
/> />
<li <li
v-if="visibleItems.length === 0" v-if="visibleItems.length === 0 && !noVisibleItems"
:style="emptyStyles()" :style="emptyStyles()"
class="c-tree__item c-tree__item--empty" class="c-tree__item c-tree__item--empty"
> >
@ -140,7 +140,8 @@ export default {
getChildHeight: false, getChildHeight: false,
settingChildrenHeight: false, settingChildrenHeight: false,
isMobile: isMobile.mobileName, isMobile: isMobile.mobileName,
multipleRootChildren: false multipleRootChildren: false,
noVisibleItems: false
}; };
}, },
computed: { computed: {
@ -318,6 +319,7 @@ export default {
this.noScroll = true; this.noScroll = true;
} }
this.noVisibleItems = false;
this.updatevisibleItems(); this.updatevisibleItems();
}); });
} else { } else {
@ -565,6 +567,7 @@ export default {
return; return;
} }
this.noVisibleItems = true;
this.visibleItems = []; this.visibleItems = [];
await this.$nextTick(); // prevents "ghost" image of visibleItems await this.$nextTick(); // prevents "ghost" image of visibleItems
this.childrenSlideClass = 'slide-left'; this.childrenSlideClass = 'slide-left';