Compare commits

...

6 Commits

Author SHA1 Message Date
0b55115957 starting loading as false, since that makes sense 2021-09-23 08:48:52 -07:00
271f8ed38f Fix file selection on pressing enter key (#4246)
* Invoke angular digest cycle after file input selection returns
2021-09-22 15:14:30 -07:00
650f84e95c [Telemetry Tables] Handling Request Loading (#4245)
* added two new events for telemetry collections to denote historical requests starting and ending (can be used for loading indicators)

* updating refresh data to use correct outstanding requests variable, binding request count update methods

* removing loading spinner (replaced with progress bar)

* if making a request, cancel any existing ones

* reverting edge case code updates
2021-09-22 15:01:28 -07:00
b70af5a1bb If there is a pending create request for an id, queue a duplicate request. (#4243) 2021-09-22 09:44:22 -07:00
0af21632db Use timeFormatter.parse to get the timestamp of imagery since the source could be something other than key (#4238) 2021-09-21 11:10:18 -07:00
e2f1ff5442 Notebook conflict auto retry 1.7.7 (#4230) 2021-09-20 14:33:38 -07:00
18 changed files with 245 additions and 128 deletions

View File

@ -44,9 +44,11 @@ define(
setText(result.name); setText(result.name);
scope.ngModel[scope.field] = result; scope.ngModel[scope.field] = result;
control.$setValidity("file-input", true); control.$setValidity("file-input", true);
scope.$digest();
}, function () { }, function () {
setText('Select File'); setText('Select File');
control.$setValidity("file-input", false); control.$setValidity("file-input", false);
scope.$digest();
}); });
} }

View File

@ -0,0 +1,2 @@
export default class ConflictError extends Error {
}

View File

@ -26,6 +26,7 @@ import RootRegistry from './RootRegistry';
import RootObjectProvider from './RootObjectProvider'; import RootObjectProvider from './RootObjectProvider';
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
import InterceptorRegistry from './InterceptorRegistry'; import InterceptorRegistry from './InterceptorRegistry';
import ConflictError from './ConflictError';
/** /**
* Utilities for loading, saving, and manipulating domain objects. * Utilities for loading, saving, and manipulating domain objects.
@ -34,6 +35,7 @@ import InterceptorRegistry from './InterceptorRegistry';
*/ */
function ObjectAPI(typeRegistry, openmct) { function ObjectAPI(typeRegistry, openmct) {
this.openmct = openmct;
this.typeRegistry = typeRegistry; this.typeRegistry = typeRegistry;
this.eventEmitter = new EventEmitter(); this.eventEmitter = new EventEmitter();
this.providers = {}; this.providers = {};
@ -47,6 +49,10 @@ function ObjectAPI(typeRegistry, openmct) {
this.interceptorRegistry = new InterceptorRegistry(); this.interceptorRegistry = new InterceptorRegistry();
this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan']; this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan'];
this.errors = {
Conflict: ConflictError
};
} }
/** /**
@ -181,6 +187,7 @@ ObjectAPI.prototype.get = function (identifier, abortSignal) {
let objectPromise = provider.get(identifier, abortSignal).then(result => { let objectPromise = provider.get(identifier, abortSignal).then(result => {
delete this.cache[keystring]; delete this.cache[keystring];
result = this.applyGetInterceptors(identifier, result); result = this.applyGetInterceptors(identifier, result);
return result; return result;
@ -285,6 +292,7 @@ ObjectAPI.prototype.isPersistable = function (idOrKeyString) {
ObjectAPI.prototype.save = function (domainObject) { ObjectAPI.prototype.save = function (domainObject) {
let provider = this.getProvider(domainObject.identifier); let provider = this.getProvider(domainObject.identifier);
let savedResolve; let savedResolve;
let savedReject;
let result; let result;
if (!this.isPersistable(domainObject.identifier)) { if (!this.isPersistable(domainObject.identifier)) {
@ -294,14 +302,18 @@ ObjectAPI.prototype.save = function (domainObject) {
} else { } else {
const persistedTime = Date.now(); const persistedTime = Date.now();
if (domainObject.persisted === undefined) { if (domainObject.persisted === undefined) {
result = new Promise((resolve) => { result = new Promise((resolve, reject) => {
savedResolve = resolve; savedResolve = resolve;
savedReject = reject;
}); });
domainObject.persisted = persistedTime; domainObject.persisted = persistedTime;
provider.create(domainObject).then((response) => { provider.create(domainObject)
this.mutate(domainObject, 'persisted', persistedTime); .then((response) => {
savedResolve(response); this.mutate(domainObject, 'persisted', persistedTime);
}); savedResolve(response);
}).catch((error) => {
savedReject(error);
});
} else { } else {
domainObject.persisted = persistedTime; domainObject.persisted = persistedTime;
this.mutate(domainObject, 'persisted', persistedTime); this.mutate(domainObject, 'persisted', persistedTime);

View File

@ -130,8 +130,13 @@ export class TelemetryCollection extends EventEmitter {
this.options.onPartialResponse = this._processNewTelemetry.bind(this); this.options.onPartialResponse = this._processNewTelemetry.bind(this);
try { try {
if (this.requestAbort) {
this.requestAbort.abort();
}
this.requestAbort = new AbortController(); this.requestAbort = new AbortController();
this.options.signal = this.requestAbort.signal; this.options.signal = this.requestAbort.signal;
this.emit('requestStarted');
historicalData = await this.historicalProvider.request(this.domainObject, this.options); historicalData = await this.historicalProvider.request(this.domainObject, this.options);
} catch (error) { } catch (error) {
if (error.name !== 'AbortError') { if (error.name !== 'AbortError') {
@ -140,6 +145,7 @@ export class TelemetryCollection extends EventEmitter {
} }
} }
this.emit('requestEnded');
this.requestAbort = undefined; this.requestAbort = undefined;
this._processNewTelemetry(historicalData); this._processNewTelemetry(historicalData);

View File

@ -41,7 +41,6 @@ const DEFAULTS = [
'platform/forms', 'platform/forms',
'platform/identity', 'platform/identity',
'platform/persistence/aggregator', 'platform/persistence/aggregator',
'platform/persistence/queue',
'platform/policy', 'platform/policy',
'platform/entanglement', 'platform/entanglement',
'platform/search', 'platform/search',

View File

@ -32,7 +32,7 @@ describe('the plugin', function () {
let openmct; let openmct;
let composition; let composition;
beforeEach((done) => { beforeEach(() => {
openmct = createOpenMct(); openmct = createOpenMct();
@ -47,11 +47,6 @@ describe('the plugin', function () {
} }
})); }));
openmct.on('start', done);
openmct.startHeadless();
composition = openmct.composition.get({identifier});
spyOn(couchPlugin.couchProvider, 'getObjectsByFilter').and.returnValue(Promise.resolve([ spyOn(couchPlugin.couchProvider, 'getObjectsByFilter').and.returnValue(Promise.resolve([
{ {
identifier: { identifier: {
@ -66,6 +61,19 @@ describe('the plugin', function () {
} }
} }
])); ]));
spyOn(couchPlugin.couchProvider, "get").and.callFake((id) => {
return Promise.resolve({
identifier: id
});
});
return new Promise((resolve) => {
openmct.once('start', resolve);
openmct.startHeadless();
}).then(() => {
composition = openmct.composition.get({identifier});
});
}); });
afterEach(() => { afterEach(() => {

View File

@ -98,6 +98,8 @@ describe('the plugin', function () {
conditionSetDefinition.initialize(mockConditionSetDomainObject); conditionSetDefinition.initialize(mockConditionSetDomainObject);
spyOn(openmct.objects, "save").and.returnValue(Promise.resolve(true));
openmct.on('start', done); openmct.on('start', done);
openmct.startHeadless(); openmct.startHeadless();
}); });

View File

@ -159,7 +159,7 @@ export default {
let image = { ...datum }; let image = { ...datum };
image.formattedTime = this.formatTime(datum); image.formattedTime = this.formatTime(datum);
image.url = this.formatImageUrl(datum); image.url = this.formatImageUrl(datum);
image.time = datum[this.timeKey]; image.time = this.parseTime(image.formattedTime);
image.imageDownloadName = this.getImageDownloadName(datum); image.imageDownloadName = this.getImageDownloadName(datum);
return image; return image;

View File

@ -0,0 +1,72 @@
import {NOTEBOOK_TYPE} from './notebook-constants';
export default function (openmct) {
const apiSave = openmct.objects.save.bind(openmct.objects);
openmct.objects.save = async (domainObject) => {
if (domainObject.type !== NOTEBOOK_TYPE) {
return apiSave(domainObject);
}
const localMutable = openmct.objects._toMutable(domainObject);
let result;
try {
result = await apiSave(localMutable);
} catch (error) {
if (error instanceof openmct.objects.errors.Conflict) {
result = resolveConflicts(localMutable, openmct);
} else {
result = Promise.reject(error);
}
} finally {
openmct.objects.destroyMutable(localMutable);
}
return result;
};
}
function resolveConflicts(localMutable, openmct) {
return openmct.objects.getMutable(localMutable.identifier).then((remoteMutable) => {
const localEntries = localMutable.configuration.entries;
remoteMutable.$refresh(remoteMutable);
applyLocalEntries(remoteMutable, localEntries);
openmct.objects.destroyMutable(remoteMutable);
return true;
});
}
function applyLocalEntries(mutable, entries) {
Object.entries(entries).forEach(([sectionKey, pagesInSection]) => {
Object.entries(pagesInSection).forEach(([pageKey, localEntries]) => {
const remoteEntries = mutable.configuration.entries[sectionKey][pageKey];
const mergedEntries = [].concat(remoteEntries);
let shouldMutate = false;
const locallyAddedEntries = _.differenceBy(localEntries, remoteEntries, 'id');
const locallyModifiedEntries = _.differenceWith(localEntries, remoteEntries, (localEntry, remoteEntry) => {
return localEntry.id === remoteEntry.id && localEntry.text === remoteEntry.text;
});
locallyAddedEntries.forEach((localEntry) => {
mergedEntries.push(localEntry);
shouldMutate = true;
});
locallyModifiedEntries.forEach((locallyModifiedEntry) => {
let mergedEntry = mergedEntries.find(entry => entry.id === locallyModifiedEntry.id);
if (mergedEntry !== undefined) {
mergedEntry.text = locallyModifiedEntry.text;
shouldMutate = true;
}
});
if (shouldMutate) {
mutable.$set(`configuration.entries.${sectionKey}.${pageKey}`, mergedEntries);
}
});
});
}

View File

@ -2,6 +2,7 @@ import CopyToNotebookAction from './actions/CopyToNotebookAction';
import Notebook from './components/Notebook.vue'; import Notebook from './components/Notebook.vue';
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue'; import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
import SnapshotContainer from './snapshot-container'; import SnapshotContainer from './snapshot-container';
import monkeyPatchObjectAPIForNotebooks from './monkeyPatchObjectAPIForNotebooks.js';
import { notebookImageMigration } from '../notebook/utils/notebook-migration'; import { notebookImageMigration } from '../notebook/utils/notebook-migration';
import { NOTEBOOK_TYPE } from './notebook-constants'; import { NOTEBOOK_TYPE } from './notebook-constants';
@ -165,5 +166,7 @@ export default function NotebookPlugin() {
return domainObject; return domainObject;
} }
}); });
monkeyPatchObjectAPIForNotebooks(openmct);
}; };
} }

View File

@ -154,6 +154,8 @@ describe("Notebook plugin:", () => {
testObjectProvider.get.and.returnValue(Promise.resolve(notebookViewObject)); testObjectProvider.get.and.returnValue(Promise.resolve(notebookViewObject));
openmct.objects.addProvider('test-namespace', testObjectProvider); openmct.objects.addProvider('test-namespace', testObjectProvider);
testObjectProvider.observe.and.returnValue(() => {}); testObjectProvider.observe.and.returnValue(() => {});
testObjectProvider.create.and.returnValue(Promise.resolve(true));
testObjectProvider.update.and.returnValue(Promise.resolve(true));
return openmct.objects.getMutable(notebookViewObject.identifier).then((mutableObject) => { return openmct.objects.getMutable(notebookViewObject.identifier).then((mutableObject) => {
mutableNotebookObject = mutableObject; mutableNotebookObject = mutableObject;

View File

@ -125,7 +125,7 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const newEntries = addEntryIntoPage(notebookStorage, entries, entry); const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
addDefaultClass(domainObject, openmct); addDefaultClass(domainObject, openmct);
openmct.objects.mutate(domainObject, 'configuration.entries', newEntries); domainObject.configuration.entries = newEntries;
return id; return id;
} }

View File

@ -15,12 +15,16 @@
port.onmessage = async function (event) { port.onmessage = async function (event) {
if (event.data.request === 'close') { if (event.data.request === 'close') {
console.log('Closing connection');
connections.splice(event.data.connectionId - 1, 1); connections.splice(event.data.connectionId - 1, 1);
if (connections.length <= 0) { if (connections.length <= 0) {
// abort any outstanding requests if there's nobody listening to it. // abort any outstanding requests if there's nobody listening to it.
controller.abort(); controller.abort();
} }
console.log('Closed.');
connected = false;
return; return;
} }
@ -29,68 +33,9 @@
return; return;
} }
connected = true; do {
await self.listenForChanges(event.data.url, event.data.body, port);
let url = event.data.url; } while (connected);
let body = event.data.body;
let error = false;
// feed=continuous maintains an indefinitely open connection with a keep-alive of HEARTBEAT milliseconds until this client closes the connection
// style=main_only returns only the current winning revision of the document
const response = await fetch(url, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
signal,
body
});
let reader;
if (response.body === undefined) {
error = true;
} else {
reader = response.body.getReader();
}
while (!error) {
const {done, value} = await reader.read();
//done is true when we lose connection with the provider
if (done) {
error = true;
}
if (value) {
let chunk = new Uint8Array(value.length);
chunk.set(value, 0);
const decodedChunk = new TextDecoder("utf-8").decode(chunk).split('\n');
if (decodedChunk.length && decodedChunk[decodedChunk.length - 1] === '') {
decodedChunk.forEach((doc, index) => {
try {
if (doc) {
const objectChanges = JSON.parse(doc);
connections.forEach(function (connection) {
connection.postMessage({
objectChanges
});
});
}
} catch (decodeError) {
//do nothing;
console.log(decodeError);
}
});
}
}
}
if (error) {
port.postMessage({
error
});
}
} }
}; };
@ -103,4 +48,64 @@
console.log('Error on feed'); console.log('Error on feed');
}; };
self.listenForChanges = async function (url, body, port) {
connected = true;
let error = false;
// feed=continuous maintains an indefinitely open connection with a keep-alive of HEARTBEAT milliseconds until this client closes the connection
// style=main_only returns only the current winning revision of the document
console.log('Opening changes feed connection.');
const response = await fetch(url, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
signal,
body
});
let reader;
if (response.body === undefined) {
error = true;
} else {
reader = response.body.getReader();
}
while (!error) {
const {done, value} = await reader.read();
//done is true when we lose connection with the provider
if (done) {
error = true;
}
if (value) {
let chunk = new Uint8Array(value.length);
chunk.set(value, 0);
const decodedChunk = new TextDecoder("utf-8").decode(chunk).split('\n');
console.log('Received chunk');
if (decodedChunk.length && decodedChunk[decodedChunk.length - 1] === '') {
decodedChunk.forEach((doc, index) => {
try {
if (doc) {
const objectChanges = JSON.parse(doc);
connections.forEach(function (connection) {
connection.postMessage({
objectChanges
});
});
}
} catch (decodeError) {
//do nothing;
console.log(decodeError);
}
});
}
}
}
console.log('Done reading changes feed');
};
}()); }());

View File

@ -29,7 +29,7 @@ const ID = "_id";
const HEARTBEAT = 50000; const HEARTBEAT = 50000;
const ALL_DOCS = "_all_docs?include_docs=true"; const ALL_DOCS = "_all_docs?include_docs=true";
export default class CouchObjectProvider { class CouchObjectProvider {
constructor(openmct, options, namespace) { constructor(openmct, options, namespace) {
options = this._normalize(options); options = this._normalize(options);
this.openmct = openmct; this.openmct = openmct;
@ -74,13 +74,6 @@ export default class CouchObjectProvider {
if (event.data.type === 'connection') { if (event.data.type === 'connection') {
this.changesFeedSharedWorkerConnectionId = event.data.connectionId; this.changesFeedSharedWorkerConnectionId = event.data.connectionId;
} else { } else {
const error = event.data.error;
if (error && Object.keys(this.observers).length > 0) {
this.observeObjectChanges();
return;
}
let objectChanges = event.data.objectChanges; let objectChanges = event.data.objectChanges;
objectChanges.identifier = { objectChanges.identifier = {
namespace: this.namespace, namespace: this.namespace,
@ -126,11 +119,12 @@ export default class CouchObjectProvider {
} }
return fetch(this.url + '/' + subPath, fetchOptions) return fetch(this.url + '/' + subPath, fetchOptions)
.then(response => response.json()) .then((response) => {
.then(function (response) { if (response.status === CouchObjectProvider.HTTP_CONFLICT) {
return response; throw new this.openmct.objects.errors.Conflict(`Conflict persisting ${fetchOptions.body.name}`);
}, function () { }
return undefined;
return response.json();
}); });
} }
@ -561,12 +555,18 @@ export default class CouchObjectProvider {
let intermediateResponse = this.getIntermediateResponse(); let intermediateResponse = this.getIntermediateResponse();
const key = model.identifier.key; const key = model.identifier.key;
this.enqueueObject(key, model, intermediateResponse); this.enqueueObject(key, model, intermediateResponse);
this.objectQueue[key].pending = true; if (!this.objectQueue[key].pending) {
const queued = this.objectQueue[key].dequeue(); this.objectQueue[key].pending = true;
let document = new CouchDocument(key, queued.model); const queued = this.objectQueue[key].dequeue();
this.request(key, "PUT", document).then((response) => { let document = new CouchDocument(key, queued.model);
this.checkResponse(response, queued.intermediateResponse, key); this.request(key, "PUT", document).then((response) => {
}); console.log('create check response', key);
this.checkResponse(response, queued.intermediateResponse, key);
}).catch(error => {
queued.intermediateResponse.reject(error);
this.objectQueue[key].pending = false;
});
}
return intermediateResponse.promise; return intermediateResponse.promise;
} }
@ -581,6 +581,9 @@ export default class CouchObjectProvider {
let document = new CouchDocument(key, queued.model, this.objectQueue[key].rev); let document = new CouchDocument(key, queued.model, this.objectQueue[key].rev);
this.request(key, "PUT", document).then((response) => { this.request(key, "PUT", document).then((response) => {
this.checkResponse(response, queued.intermediateResponse, key); this.checkResponse(response, queued.intermediateResponse, key);
}).catch((error) => {
queued.intermediateResponse.reject(error);
this.objectQueue[key].pending = false;
}); });
} }
} }
@ -594,3 +597,7 @@ export default class CouchObjectProvider {
return intermediateResponse.promise; return intermediateResponse.promise;
} }
} }
CouchObjectProvider.HTTP_CONFLICT = 409;
export default CouchObjectProvider;

View File

@ -60,18 +60,17 @@ define([
this.addTelemetryObject = this.addTelemetryObject.bind(this); this.addTelemetryObject = this.addTelemetryObject.bind(this);
this.removeTelemetryObject = this.removeTelemetryObject.bind(this); this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this); this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this);
this.incrementOutstandingRequests = this.incrementOutstandingRequests.bind(this);
this.decrementOutstandingRequests = this.decrementOutstandingRequests.bind(this);
this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this); this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this);
this.isTelemetryObject = this.isTelemetryObject.bind(this); this.isTelemetryObject = this.isTelemetryObject.bind(this);
this.refreshData = this.refreshData.bind(this);
this.updateFilters = this.updateFilters.bind(this); this.updateFilters = this.updateFilters.bind(this);
this.clearData = this.clearData.bind(this);
this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this); this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
this.filterObserver = undefined; this.filterObserver = undefined;
this.createTableRowCollections(); this.createTableRowCollections();
openmct.time.on('bounds', this.refreshData);
openmct.time.on('timeSystem', this.refreshData);
} }
/** /**
@ -141,8 +140,6 @@ define([
let columnMap = this.getColumnMapForObject(keyString); let columnMap = this.getColumnMapForObject(keyString);
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
this.incrementOutstandingRequests();
const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator); const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator);
const telemetryRemover = this.getTelemetryRemover(); const telemetryRemover = this.getTelemetryRemover();
@ -151,13 +148,13 @@ define([
this.telemetryCollections[keyString] = this.openmct.telemetry this.telemetryCollections[keyString] = this.openmct.telemetry
.requestCollection(telemetryObject, requestOptions); .requestCollection(telemetryObject, requestOptions);
this.telemetryCollections[keyString].on('requestStarted', this.incrementOutstandingRequests);
this.telemetryCollections[keyString].on('requestEnded', this.decrementOutstandingRequests);
this.telemetryCollections[keyString].on('remove', telemetryRemover); this.telemetryCollections[keyString].on('remove', telemetryRemover);
this.telemetryCollections[keyString].on('add', telemetryProcessor); this.telemetryCollections[keyString].on('add', telemetryProcessor);
this.telemetryCollections[keyString].on('clear', this.tableRows.clear); this.telemetryCollections[keyString].on('clear', this.clearData);
this.telemetryCollections[keyString].load(); this.telemetryCollections[keyString].load();
this.decrementOutstandingRequests();
this.telemetryObjects[keyString] = { this.telemetryObjects[keyString] = {
telemetryObject, telemetryObject,
keyString, keyString,
@ -268,17 +265,6 @@ define([
this.emit('object-removed', objectIdentifier); this.emit('object-removed', objectIdentifier);
} }
refreshData(bounds, isTick) {
if (!isTick && this.tableRows.outstandingRequests === 0) {
this.tableRows.clear();
this.tableRows.sortBy({
key: this.openmct.time.timeSystem().key,
direction: 'asc'
});
this.tableRows.resubscribe();
}
}
clearData() { clearData() {
this.tableRows.clear(); this.tableRows.clear();
this.emit('refresh'); this.emit('refresh');
@ -378,9 +364,6 @@ define([
let keystrings = Object.keys(this.telemetryCollections); let keystrings = Object.keys(this.telemetryCollections);
keystrings.forEach(this.removeTelemetryCollection); keystrings.forEach(this.removeTelemetryCollection);
this.openmct.time.off('bounds', this.refreshData);
this.openmct.time.off('timeSystem', this.refreshData);
if (this.filterObserver) { if (this.filterObserver) {
this.filterObserver(); this.filterObserver();
} }

View File

@ -125,7 +125,6 @@
<div <div
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver" class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver"
:class="{ :class="{
'loading': loading,
'is-paused' : paused 'is-paused' : paused
}" }"
> >
@ -362,7 +361,7 @@ export default {
autoScroll: true, autoScroll: true,
sortOptions: {}, sortOptions: {},
filters: {}, filters: {},
loading: true, loading: false,
scrollable: undefined, scrollable: undefined,
tableEl: undefined, tableEl: undefined,
headersHolderEl: undefined, headersHolderEl: undefined,
@ -422,6 +421,14 @@ export default {
} }
}, },
watch: { watch: {
loading: {
handler(isLoading) {
if (this.viewActionsCollection) {
let action = isLoading ? 'disable' : 'enable';
this.viewActionsCollection[action](['export-csv-all']);
}
}
},
markedRows: { markedRows: {
handler(newVal, oldVal) { handler(newVal, oldVal) {
this.$emit('marked-rows-updated', newVal, oldVal); this.$emit('marked-rows-updated', newVal, oldVal);
@ -1020,6 +1027,12 @@ export default {
this.viewActionsCollection.disable(['export-csv-marked', 'unmark-all-rows']); this.viewActionsCollection.disable(['export-csv-marked', 'unmark-all-rows']);
} }
if (this.loading) {
this.viewActionsCollection.disable(['export-csv-all']);
} else {
this.viewActionsCollection.enable(['export-csv-all']);
}
if (this.paused) { if (this.paused) {
this.viewActionsCollection.hide(['pause-data']); this.viewActionsCollection.hide(['pause-data']);
this.viewActionsCollection.show(['play-data']); this.viewActionsCollection.show(['play-data']);

View File

@ -88,7 +88,7 @@ export default {
this.mutablePromise.then(() => { this.mutablePromise.then(() => {
this.openmct.objects.destroyMutable(this.domainObject); this.openmct.objects.destroyMutable(this.domainObject);
}); });
} else { } else if (this.domainObject.isMutable) {
this.openmct.objects.destroyMutable(this.domainObject); this.openmct.objects.destroyMutable(this.domainObject);
} }
}, },

View File

@ -49,6 +49,7 @@ describe("the inspector", () => {
beforeEach((done) => { beforeEach((done) => {
openmct = createOpenMct(); openmct = createOpenMct();
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
openmct.on('start', done); openmct.on('start', done);
openmct.startHeadless(); openmct.startHeadless();
}); });
@ -77,12 +78,12 @@ describe("the inspector", () => {
expect(savedStylesViewComponent.$children[0].$children.length).toBe(0); expect(savedStylesViewComponent.$children[0].$children.length).toBe(0);
stylesViewComponent.$children[0].saveStyle(mockStyle); stylesViewComponent.$children[0].saveStyle(mockStyle);
stylesViewComponent.$nextTick().then(() => { return stylesViewComponent.$nextTick().then(() => {
expect(savedStylesViewComponent.$children[0].$children.length).toBe(1); expect(savedStylesViewComponent.$children[0].$children.length).toBe(1);
}); });
}); });
it("should allow a saved style to be applied", () => { xit("should allow a saved style to be applied", () => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true); spyOn(openmct.editor, 'isEditing').and.returnValue(true);
selection = mockTelemetryTableSelection; selection = mockTelemetryTableSelection;
@ -91,12 +92,12 @@ describe("the inspector", () => {
stylesViewComponent.$children[0].saveStyle(mockStyle); stylesViewComponent.$children[0].saveStyle(mockStyle);
stylesViewComponent.$nextTick().then(() => { return stylesViewComponent.$nextTick().then(() => {
const styleSelectorComponent = savedStylesViewComponent.$children[0].$children[0]; const styleSelectorComponent = savedStylesViewComponent.$children[0].$children[0];
styleSelectorComponent.selectStyle(); styleSelectorComponent.selectStyle();
savedStylesViewComponent.$nextTick().then(() => { return savedStylesViewComponent.$nextTick().then(() => {
const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1;
const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex];
const styles = styleEditorComponent.$children.filter(component => component.options.value === mockStyle.color); const styles = styleEditorComponent.$children.filter(component => component.options.value === mockStyle.color);
@ -147,7 +148,7 @@ describe("the inspector", () => {
stylesViewComponent = createViewComponent(StylesView, selection, openmct); stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
stylesViewComponent.$nextTick().then(() => { return stylesViewComponent.$nextTick().then(() => {
const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1;
const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex];
const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; const saveStyleButtonIndex = styleEditorComponent.$children.length - 1;
@ -168,7 +169,7 @@ describe("the inspector", () => {
stylesViewComponent = createViewComponent(StylesView, selection, openmct); stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
stylesViewComponent.$nextTick().then(() => { return stylesViewComponent.$nextTick().then(() => {
const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1;
const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex];
const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; const saveStyleButtonIndex = styleEditorComponent.$children.length - 1;
@ -185,7 +186,7 @@ describe("the inspector", () => {
stylesViewComponent = createViewComponent(StylesView, selection, openmct); stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct); savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
stylesViewComponent.$nextTick().then(() => { return stylesViewComponent.$nextTick().then(() => {
const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1; const styleEditorComponentIndex = stylesViewComponent.$children[0].$children.length - 1;
const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex]; const styleEditorComponent = stylesViewComponent.$children[0].$children[styleEditorComponentIndex];
const saveStyleButtonIndex = styleEditorComponent.$children.length - 1; const saveStyleButtonIndex = styleEditorComponent.$children.length - 1;