From 327fc826c14fdcd703287e74b1e534b69a0cc407 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 28 Dec 2022 11:12:00 -0800 Subject: [PATCH 1/6] fix(imagery): Unblock 'latest' strategy requests for Related Telemetry in realtime mode (#6080) * fix: use ephemeral timeContext for thumbnail metadata requests * fix(TEMP): use `eval-source-map` - **!!! REVERT THIS CHANGE BEFORE MERGE !!!** * fix: only mutate if object supports mutation * fix: pass identifier instead of whole domainObject * fix: add start and end bounds to request * Revert "fix(TEMP): use `eval-source-map`" This reverts commit 7972d8c33ab621e8852d65743e2adf946e5c14fb. * docs: add comments --- .../imagery/components/ImageryView.vue | 2 +- .../RelatedTelemetry/RelatedTelemetry.js | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/plugins/imagery/components/ImageryView.vue b/src/plugins/imagery/components/ImageryView.vue index 4eb987cc735..80958986990 100644 --- a/src/plugins/imagery/components/ImageryView.vue +++ b/src/plugins/imagery/components/ImageryView.vue @@ -788,7 +788,7 @@ export default { } }, persistVisibleLayers() { - if (this.domainObject.configuration) { + if (this.domainObject.configuration && this.openmct.objects.supportsMutation(this.domainObject.identifier)) { this.openmct.objects.mutate(this.domainObject, 'configuration.layers', this.layers); } diff --git a/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js b/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js index f874d812997..f06091d3953 100644 --- a/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js +++ b/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js @@ -28,6 +28,7 @@ function copyRelatedMetadata(metadata) { return copiedMetadata; } +import IndependentTimeContext from "@/api/time/IndependentTimeContext"; export default class RelatedTelemetry { constructor(openmct, domainObject, telemetryKeys) { @@ -88,9 +89,31 @@ export default class RelatedTelemetry { this[key].historicalDomainObject = await this._openmct.objects.get(this[key].historical.telemetryObjectId); this[key].requestLatestFor = async (datum) => { - const options = { + // We need to create a throwaway time context and pass it along + // as a request option. We do this to "trick" the Time API + // into thinking we are in fixed time mode in order to bypass this logic: + // https://github.com/akhenry/openmct-yamcs/blob/1060d42ebe43bf346dac0f6a8068cb288ade4ba4/src/providers/historical-telemetry-provider.js#L59 + // Context: https://github.com/akhenry/openmct-yamcs/pull/217 + const ephemeralContext = new IndependentTimeContext( + this._openmct, + this._openmct.time, + [this[key].historicalDomainObject] + ); + + // Stop following the global context, stop the clock, + // and set bounds. + ephemeralContext.resetContext(); + const newBounds = { start: this._openmct.time.bounds().start, - end: this._parseTime(datum), + end: this._parseTime(datum) + }; + ephemeralContext.stopClock(); + ephemeralContext.bounds(newBounds); + + const options = { + start: newBounds.start, + end: newBounds.end, + timeContext: ephemeralContext, strategy: 'latest' }; let results = await this._openmct.telemetry From 9ed9e62202cdb40943bcca7775af9bda24b7a44d Mon Sep 17 00:00:00 2001 From: Shefali Joshi Date: Wed, 28 Dec 2022 14:18:47 -0800 Subject: [PATCH 2/6] Use the current clock's timestamp to show the now line in the timestrip (#6082) --- src/ui/components/TimeSystemAxis.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/components/TimeSystemAxis.vue b/src/ui/components/TimeSystemAxis.vue index d580b884e6e..50943e32814 100644 --- a/src/ui/components/TimeSystemAxis.vue +++ b/src/ui/components/TimeSystemAxis.vue @@ -101,7 +101,8 @@ export default { if (nowMarker) { nowMarker.classList.remove('hidden'); nowMarker.style.height = this.contentHeight + 'px'; - const now = this.xScale(Date.now()); + const nowTimeStamp = this.openmct.time.clock().currentValue(); + const now = this.xScale(nowTimeStamp); nowMarker.style.left = now + this.offset + 'px'; } } From 5424a62db570fc1f5ff759571531da41b0f023ff Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 29 Dec 2022 14:11:08 -0800 Subject: [PATCH 3/6] [Notebook] Handle conflicts properly (#6067) * making a revert on failed save more clear * only notify conflicts for non sync items in object api, spruce up notebook with better transaction tracking and observing and unobserving during transactions, structuredClone backup in monkeypatch * WIP * WIP debuggin * fresh start * dont observe in transaction objects, small changes to notebook vue to indicate saving/prevent spamming, added forceRemote flag to objects.get * updating readability of code as well as fix issue of stuck transaction for same value entry edits * once entry is created, click out to blur * quick revert ; * click outside of entry to blur and commit * switched to enter... as suggested :) * removing unused variable * initializing transaction to null as we are using that now for no transaction * fix: ensure EventSource is closed so it recovers - Make sure to close the CouchDB EventSource as well, so that it can recover in the case where two tabs or windows are on Open MCT and one refreshes. The check on line 81 was preventing recovery since the EventSource was not closed properly. * enhance, enhance, enhance readability Co-authored-by: Jesse Mazzella --- .../plugins/notebook/tags.e2e.spec.js | 1 + src/api/objects/ObjectAPI.js | 34 ++++++----- src/plugins/notebook/components/Notebook.vue | 59 ++++++++++++++++--- .../monkeyPatchObjectAPIForNotebooks.js | 20 +++++-- src/plugins/objectMigration/plugin.js | 4 +- .../persistence/couch/CouchChangesFeed.js | 1 + .../persistence/couch/CouchObjectProvider.js | 14 ++++- 7 files changed, 99 insertions(+), 34 deletions(-) diff --git a/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js b/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js index a8a3dd239b0..6a650c60769 100644 --- a/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/tags.e2e.spec.js @@ -44,6 +44,7 @@ async function createNotebookAndEntry(page, iterations = 1) { const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = ${iteration}`; await page.locator(entryLocator).click(); await page.locator(entryLocator).fill(`Entry ${iteration}`); + await page.locator(entryLocator).press('Enter'); } return notebook; diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index f5031ec6df5..1f23b305f41 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -193,23 +193,27 @@ export default class ObjectAPI { * @memberof module:openmct.ObjectProvider# * @param {string} key the key for the domain object to load * @param {AbortController.signal} abortSignal (optional) signal to abort fetch requests + * @param {boolean} forceRemote defaults to false. If true, will skip cached and + * dirty/in-transaction objects use and the provider.get method * @returns {Promise} a promise which will resolve when the domain object * has been saved, or be rejected if it cannot be saved */ - get(identifier, abortSignal) { + get(identifier, abortSignal, forceRemote = false) { let keystring = this.makeKeyString(identifier); - if (this.cache[keystring] !== undefined) { - return this.cache[keystring]; - } + if (!forceRemote) { + if (this.cache[keystring] !== undefined) { + return this.cache[keystring]; + } - identifier = utils.parseKeyString(identifier); + identifier = utils.parseKeyString(identifier); - if (this.isTransactionActive()) { - let dirtyObject = this.transaction.getDirtyObject(identifier); + if (this.isTransactionActive()) { + let dirtyObject = this.transaction.getDirtyObject(identifier); - if (dirtyObject) { - return Promise.resolve(dirtyObject); + if (dirtyObject) { + return Promise.resolve(dirtyObject); + } } } @@ -391,7 +395,6 @@ export default class ObjectAPI { lastPersistedTime = domainObject.persisted; const persistedTime = Date.now(); this.#mutate(domainObject, 'persisted', persistedTime); - savedObjectPromise = provider.update(domainObject); } @@ -399,7 +402,7 @@ export default class ObjectAPI { savedObjectPromise.then(response => { savedResolve(response); }).catch((error) => { - if (lastPersistedTime !== undefined) { + if (!isNewObject) { this.#mutate(domainObject, 'persisted', lastPersistedTime); } @@ -412,11 +415,12 @@ export default class ObjectAPI { return result.catch(async (error) => { if (error instanceof this.errors.Conflict) { - this.openmct.notifications.error(`Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}`); + // Synchronized objects will resolve their own conflicts + if (this.SYNCHRONIZED_OBJECT_TYPES.includes(domainObject.type)) { + this.openmct.notifications.info(`Conflict detected while saving "${this.makeKeyString(domainObject.name)}", attempting to resolve`); + } else { + this.openmct.notifications.error(`Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}`); - // Synchronized objects will resolve their own conflicts, so - // bypass the refresh here and throw the error. - if (!this.SYNCHRONIZED_OBJECT_TYPES.includes(domainObject.type)) { if (this.isTransactionActive()) { this.endTransaction(); } diff --git a/src/plugins/notebook/components/Notebook.vue b/src/plugins/notebook/components/Notebook.vue index 91bdbebc71b..5a029c51c69 100644 --- a/src/plugins/notebook/components/Notebook.vue +++ b/src/plugins/notebook/components/Notebook.vue @@ -50,7 +50,7 @@
+
{ Object.entries(pagesInSection).forEach(([pageKey, localEntries]) => { - const remoteEntries = mutable.configuration.entries[sectionKey][pageKey]; + const remoteEntries = remoteObject.configuration.entries[sectionKey][pageKey]; const mergedEntries = [].concat(remoteEntries); let shouldMutate = false; @@ -110,8 +113,13 @@ function applyLocalEntries(mutable, entries, openmct) { }); if (shouldMutate) { - openmct.objects.mutate(mutable, `configuration.entries.${sectionKey}.${pageKey}`, mergedEntries); + shouldSave = true; + openmct.objects.mutate(remoteObject, `configuration.entries.${sectionKey}.${pageKey}`, mergedEntries); } }); }); + + if (shouldSave) { + return openmct.objects.save(remoteObject); + } } diff --git a/src/plugins/objectMigration/plugin.js b/src/plugins/objectMigration/plugin.js index 715d70418bb..8c0db540793 100644 --- a/src/plugins/objectMigration/plugin.js +++ b/src/plugins/objectMigration/plugin.js @@ -36,8 +36,8 @@ export default function () { } let wrappedFunction = openmct.objects.get; - openmct.objects.get = function migrate(identifier) { - return wrappedFunction.apply(openmct.objects, [identifier]) + openmct.objects.get = function migrate() { + return wrappedFunction.apply(openmct.objects, [...arguments]) .then(function (object) { if (needsMigration(object)) { migrateObject(object) diff --git a/src/plugins/persistence/couch/CouchChangesFeed.js b/src/plugins/persistence/couch/CouchChangesFeed.js index 4547c6c9e4b..86059841ca3 100644 --- a/src/plugins/persistence/couch/CouchChangesFeed.js +++ b/src/plugins/persistence/couch/CouchChangesFeed.js @@ -28,6 +28,7 @@ connected = false; // stop listening for events couchEventSource.removeEventListener('message', self.onCouchMessage); + couchEventSource.close(); console.debug('🚪 Closed couch connection 🚪'); return; diff --git a/src/plugins/persistence/couch/CouchObjectProvider.js b/src/plugins/persistence/couch/CouchObjectProvider.js index 0b98d713a25..9ba083674ac 100644 --- a/src/plugins/persistence/couch/CouchObjectProvider.js +++ b/src/plugins/persistence/couch/CouchObjectProvider.js @@ -96,8 +96,13 @@ class CouchObjectProvider { let keyString = this.openmct.objects.makeKeyString(objectIdentifier); //TODO: Optimize this so that we don't 'get' the object if it's current revision (from this.objectQueue) is the same as the one we already have. let observersForObject = this.observers[keyString]; + let isInTransaction = false; - if (observersForObject) { + if (this.openmct.objects.isTransactionActive()) { + isInTransaction = this.openmct.objects.transaction.getDirtyObject(objectIdentifier); + } + + if (observersForObject && !isInTransaction) { observersForObject.forEach(async (observer) => { const updatedObject = await this.get(objectIdentifier); if (this.isSynchronizedObject(updatedObject)) { @@ -219,7 +224,12 @@ class CouchObjectProvider { console.error(error.message); throw new Error(`CouchDB Error - No response"`); } else { - console.error(error.message); + if (body?.model && isNotebookOrAnnotationType(body.model)) { + // warn since we handle conflicts for notebooks + console.warn(error.message); + } else { + console.error(error.message); + } throw error; } From 04ef4b369c889d5eb9f87e800bf8674a58ad5c3c Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Tue, 3 Jan 2023 09:50:28 -0800 Subject: [PATCH 4/6] chore: bump version to `2.1.6-SNAPSHOT` (#6092) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2977b186276..7a8886249dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openmct", - "version": "2.1.5-SNAPSHOT", + "version": "2.1.6-SNAPSHOT", "description": "The Open MCT core platform", "devDependencies": { "@babel/eslint-parser": "7.18.9", From 93b5519c4b9cbc653d6012a3e3d3cf6a81ab1414 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:32:19 -0800 Subject: [PATCH 5/6] Bump karma-spec-reporter from 0.0.34 to 0.0.36 (#6058) Bumps [karma-spec-reporter](https://github.com/tmcgee123/karma-spec-reporter) from 0.0.34 to 0.0.36. - [Release notes](https://github.com/tmcgee123/karma-spec-reporter/releases) - [Commits](https://github.com/tmcgee123/karma-spec-reporter/compare/v0.0.34...v0.0.36) --- updated-dependencies: - dependency-name: karma-spec-reporter dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a8886249dd..3646a994855 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "karma-jasmine": "5.1.0", "karma-junit-reporter": "2.0.1", "karma-sourcemap-loader": "0.3.8", - "karma-spec-reporter": "0.0.34", + "karma-spec-reporter": "0.0.36", "karma-webpack": "5.0.0", "location-bar": "3.0.1", "lodash": "4.17.21", From e6bdaa957adb39c98d75e835e274689ccb99da11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 16:27:54 -0800 Subject: [PATCH 6/6] Bump plotly.js-basic-dist from 2.14.0 to 2.17.0 (#6078) Bumps [plotly.js-basic-dist](https://github.com/plotly/plotly.js) from 2.14.0 to 2.17.0. - [Release notes](https://github.com/plotly/plotly.js/releases) - [Changelog](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.js/compare/v2.14.0...v2.17.0) --- updated-dependencies: - dependency-name: plotly.js-basic-dist dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3646a994855..74243408712 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nyc": "15.1.0", "painterro": "1.2.78", "playwright-core": "1.29.0", - "plotly.js-basic-dist": "2.14.0", + "plotly.js-basic-dist": "2.17.0", "plotly.js-gl2d-dist": "2.14.0", "printj": "1.3.1", "resolve-url-loader": "5.0.0",