From 3cc93c0656b5893b3969ea30cfe49906c5c1b296 Mon Sep 17 00:00:00 2001 From: Scott Bell Date: Fri, 9 Jun 2023 19:21:44 +0200 Subject: [PATCH 01/25] Add time context for telemetry collections (#6543) * add time context for telemetry collections * move time context to options * clean up jsdoc * clean up jsdoc * Update src/api/telemetry/TelemetryAPI.js * clean up comments * use time context bounds if defined for start and end * refactor: format with prettier --- src/api/telemetry/TelemetryAPI.js | 63 +++++++++++++++++++---- src/api/telemetry/TelemetryCollection.js | 44 +++++++++++----- src/plugins/imagery/mixins/imageryData.js | 4 +- 3 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index d016f06697..6ad0cbcf76 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -28,6 +28,36 @@ import TelemetryValueFormatter from './TelemetryValueFormatter'; import DefaultMetadataProvider from './DefaultMetadataProvider'; import objectUtils from 'objectUtils'; +/** + * @typedef {import('../time/TimeContext').TimeContext} TimeContext + */ + +/** + * Describes and bounds requests for telemetry data. + * + * @typedef TelemetryRequestOptions + * @property {String} [sort] the key of the property to sort by. This may + * be prefixed with a "+" or a "-" sign to sort in ascending + * or descending order respectively. If no prefix is present, + * ascending order will be used. + * @property {Number} [start] the lower bound for values of the sorting property + * @property {Number} [end] the upper bound for values of the sorting property + * @property {String} [strategy] symbolic identifier for strategies + * (such as `latest` or `minmax`) which may be recognized by providers; + * these will be tried in order until an appropriate provider + * is found + * @property {AbortController} [signal] an AbortController which can be used + * to cancel a telemetry request + * @property {String} [domain] the domain key of the request + * @property {TimeContext} [timeContext] the time context to use for this request + * @memberof module:openmct.TelemetryAPI~ + */ + +/** + * Utilities for telemetry + * @interface TelemetryAPI + * @memberof module:openmct + */ export default class TelemetryAPI { #isGreedyLAD; @@ -169,25 +199,35 @@ export default class TelemetryAPI { } /** - * @private - * Though used in TelemetryCollection as well + * @param {TelemetryRequestOptions} options options for the telemetry request + * @returns {TelemetryRequestOptions} the options, with defaults filled in */ - standardizeRequestOptions(options) { - if (!Object.prototype.hasOwnProperty.call(options, 'start')) { - options.start = this.openmct.time.bounds().start; + standardizeRequestOptions(options = {}) { + if (!Object.hasOwn(options, 'start')) { + if (options.timeContext?.bounds()) { + options.start = options.timeContext.bounds().start; + } else { + options.start = this.openmct.time.bounds().start; + } } - if (!Object.prototype.hasOwnProperty.call(options, 'end')) { - options.end = this.openmct.time.bounds().end; + if (!Object.hasOwn(options, 'end')) { + if (options.timeContext?.bounds()) { + options.end = options.timeContext.bounds().end; + } else { + options.end = this.openmct.time.bounds().end; + } } - if (!Object.prototype.hasOwnProperty.call(options, 'domain')) { + if (!Object.hasOwn(options, 'domain')) { options.domain = this.openmct.time.timeSystem().key; } - if (!Object.prototype.hasOwnProperty.call(options, 'timeContext')) { + if (!Object.hasOwn(options, 'timeContext')) { options.timeContext = this.openmct.time; } + + return options; } /** @@ -265,7 +305,7 @@ export default class TelemetryAPI { * @memberof module:openmct.TelemetryAPI~TelemetryProvider# * @param {module:openmct.DomainObject} domainObject the object * which has associated telemetry - * @param {module:openmct.TelemetryAPI~TelemetryRequest} options + * @param {TelemetryRequestOptions} options * options for this telemetry collection request * @returns {TelemetryCollection} a TelemetryCollection instance */ @@ -283,7 +323,7 @@ export default class TelemetryAPI { * @memberof module:openmct.TelemetryAPI~TelemetryProvider# * @param {module:openmct.DomainObject} domainObject the object * which has associated telemetry - * @param {module:openmct.TelemetryAPI~TelemetryRequest} options + * @param {TelemetryRequestOptions} options * options for this historical request * @returns {Promise.} a promise for an array of * telemetry data @@ -339,6 +379,7 @@ export default class TelemetryAPI { * @memberof module:openmct.TelemetryAPI~TelemetryProvider# * @param {module:openmct.DomainObject} domainObject the object * which has associated telemetry + * @param {TelemetryRequestOptions} options configuration items for subscription * @param {Function} callback the callback to invoke with new data, as * it becomes available * @returns {Function} a function which may be called to terminate diff --git a/src/api/telemetry/TelemetryCollection.js b/src/api/telemetry/TelemetryCollection.js index e052355a7a..97a7dc1621 100644 --- a/src/api/telemetry/TelemetryCollection.js +++ b/src/api/telemetry/TelemetryCollection.js @@ -24,6 +24,22 @@ import _ from 'lodash'; import EventEmitter from 'EventEmitter'; import { LOADED_ERROR, TIMESYSTEM_KEY_NOTIFICATION, TIMESYSTEM_KEY_WARNING } from './constants'; +/** + * @typedef {import('../objects/ObjectAPI').DomainObject} DomainObject + */ + +/** + * @typedef {import('../time/TimeContext').TimeContext} TimeContext + */ + +/** + * @typedef {import('./TelemetryAPI').TelemetryRequestOptions} TelemetryRequestOptions + */ + +/** + * @typedef {import('../../../openmct').OpenMCT} OpenMCT + */ + /** Class representing a Telemetry Collection. */ export default class TelemetryCollection extends EventEmitter { @@ -31,10 +47,10 @@ export default class TelemetryCollection extends EventEmitter { * Creates a Telemetry Collection * * @param {OpenMCT} openmct - Open MCT - * @param {module:openmct.DomainObject} domainObject - Domain Object to use for telemetry collection - * @param {object} options - Any options passed in for request/subscribe + * @param {DomainObject} domainObject - Domain Object to use for telemetry collection + * @param {TelemetryRequestOptions} options - Any options passed in for request/subscribe */ - constructor(openmct, domainObject, options) { + constructor(openmct, domainObject, options = {}) { super(); this.loaded = false; @@ -45,7 +61,7 @@ export default class TelemetryCollection extends EventEmitter { this.parseTime = undefined; this.metadata = this.openmct.telemetry.getMetadata(domainObject); this.unsubscribe = undefined; - this.options = options; + this.options = this.openmct.telemetry.standardizeRequestOptions(options); this.pageState = undefined; this.lastBounds = undefined; this.requestAbort = undefined; @@ -62,8 +78,8 @@ export default class TelemetryCollection extends EventEmitter { this._error(LOADED_ERROR); } - this._setTimeSystem(this.openmct.time.timeSystem()); - this.lastBounds = this.openmct.time.bounds(); + this._setTimeSystem(this.options.timeContext.timeSystem()); + this.lastBounds = this.options.timeContext.bounds(); this._watchBounds(); this._watchTimeSystem(); @@ -106,10 +122,10 @@ export default class TelemetryCollection extends EventEmitter { */ async _requestHistoricalTelemetry() { let options = { ...this.options }; - let historicalProvider; - - this.openmct.telemetry.standardizeRequestOptions(options); - historicalProvider = this.openmct.telemetry.findRequestProvider(this.domainObject, options); + const historicalProvider = this.openmct.telemetry.findRequestProvider( + this.domainObject, + options + ); if (!historicalProvider) { return; @@ -438,7 +454,7 @@ export default class TelemetryCollection extends EventEmitter { * @private */ _watchBounds() { - this.openmct.time.on('bounds', this._bounds, this); + this.options.timeContext.on('bounds', this._bounds, this); } /** @@ -446,7 +462,7 @@ export default class TelemetryCollection extends EventEmitter { * @private */ _unwatchBounds() { - this.openmct.time.off('bounds', this._bounds, this); + this.options.timeContext.off('bounds', this._bounds, this); } /** @@ -454,7 +470,7 @@ export default class TelemetryCollection extends EventEmitter { * @private */ _watchTimeSystem() { - this.openmct.time.on('timeSystem', this._setTimeSystemAndFetchData, this); + this.options.timeContext.on('timeSystem', this._setTimeSystemAndFetchData, this); } /** @@ -462,7 +478,7 @@ export default class TelemetryCollection extends EventEmitter { * @private */ _unwatchTimeSystem() { - this.openmct.time.off('timeSystem', this._setTimeSystemAndFetchData, this); + this.options.timeContext.off('timeSystem', this._setTimeSystemAndFetchData, this); } /** diff --git a/src/plugins/imagery/mixins/imageryData.js b/src/plugins/imagery/mixins/imageryData.js index aa594c4759..56a3ce4a65 100644 --- a/src/plugins/imagery/mixins/imageryData.js +++ b/src/plugins/imagery/mixins/imageryData.js @@ -60,7 +60,9 @@ export default { this.timeKey = this.timeSystem.key; this.timeFormatter = this.getFormatter(this.timeKey); - this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {}); + this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, { + timeContext: this.timeContext + }); this.telemetryCollection.on('add', this.dataAdded); this.telemetryCollection.on('remove', this.dataRemoved); this.telemetryCollection.on('clear', this.dataCleared); From f77287530bf14e46c2fd4f9f96b5850ced344aac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 20:22:28 -0700 Subject: [PATCH 02/25] chore(deps-dev): bump vue-eslint-parser from 9.3.0 to 9.3.1 (#6722) Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 9.3.0 to 9.3.1. - [Release notes](https://github.com/vuejs/vue-eslint-parser/releases) - [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v9.3.0...v9.3.1) --- updated-dependencies: - dependency-name: vue-eslint-parser dependency-type: direct:development update-type: version-update:semver-patch ... 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 06df36c912..e09590b5c1 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "typescript": "5.1.3", "uuid": "9.0.0", "vue": "2.6.14", - "vue-eslint-parser": "9.3.0", + "vue-eslint-parser": "9.3.1", "vue-loader": "15.9.8", "vue-template-compiler": "2.6.14", "webpack": "5.85.1", From 040ef0b998d6026a8624e2be53e08f5b6b68ba40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 03:30:43 +0000 Subject: [PATCH 03/25] chore(deps-dev): bump webpack from 5.85.1 to 5.86.0 (#6726) Bumps [webpack](https://github.com/webpack/webpack) from 5.85.1 to 5.86.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.85.1...v5.86.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... 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 e09590b5c1..5860929eeb 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "vue-eslint-parser": "9.3.1", "vue-loader": "15.9.8", "vue-template-compiler": "2.6.14", - "webpack": "5.85.1", + "webpack": "5.86.0", "webpack-cli": "5.1.1", "webpack-dev-server": "4.13.3", "webpack-merge": "5.9.0" From 5a1e544a4cf57661770a480aafea29eeb10d0273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 20:16:09 +0000 Subject: [PATCH 04/25] chore(deps-dev): bump webpack-dev-server from 4.13.3 to 4.15.1 (#6723) Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 4.13.3 to 4.15.1. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.13.3...v4.15.1) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-type: direct:development update-type: version-update:semver-minor ... 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 5860929eeb..7ece8b30fb 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "vue-template-compiler": "2.6.14", "webpack": "5.86.0", "webpack-cli": "5.1.1", - "webpack-dev-server": "4.13.3", + "webpack-dev-server": "4.15.1", "webpack-merge": "5.9.0" }, "scripts": { From 60df9e79c1ff151fa834de8dd7a207a7b8b57010 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 20:27:43 +0000 Subject: [PATCH 05/25] chore(deps-dev): bump @percy/cli from 1.24.2 to 1.26.0 (#6727) Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.24.2 to 1.26.0. - [Release notes](https://github.com/percy/cli/releases) - [Commits](https://github.com/percy/cli/commits/v1.26.0/packages/cli) --- updated-dependencies: - dependency-name: "@percy/cli" dependency-type: direct:development update-type: version-update:semver-minor ... 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 7ece8b30fb..a6ad4de269 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@babel/eslint-parser": "7.21.8", "@braintree/sanitize-url": "6.0.2", "@deploysentinel/playwright": "0.3.4", - "@percy/cli": "1.24.2", + "@percy/cli": "1.26.0", "@percy/playwright": "1.0.4", "@playwright/test": "1.32.3", "@types/eventemitter3": "1.2.0", From 8b2d3b06226ed1277aff9d63c3ae49912e718816 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 21:23:06 +0000 Subject: [PATCH 06/25] chore(deps-dev): bump sass from 1.62.1 to 1.63.3 (#6729) Bumps [sass](https://github.com/sass/dart-sass) from 1.62.1 to 1.63.3. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.62.1...1.63.3) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor ... 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 a6ad4de269..f83cff5d10 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "printj": "1.3.1", "resolve-url-loader": "5.0.0", "sanitize-html": "2.10.0", - "sass": "1.62.1", + "sass": "1.63.3", "sass-loader": "13.3.1", "sinon": "15.1.0", "style-loader": "3.3.3", From 9a01cee5fae13818639aafa30eab58d7ffe7f503 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 14 Jun 2023 12:33:26 -0700 Subject: [PATCH 07/25] feat: Annotation API changes to support Geospatial (Map) annotations (#6703) * feat: `getAnnotations` can take an `abortSignal` * feat: add `MAP` annotationType * fix: handle `MAP` annotations in search results * fix: have `loadAnnotationForTargetObject` take an `abortSignal` * fix(#5646): abort pending annotations requests on nav away from notebooks or plots * fix: handle AbortErrors gracefully * fix: remove redundant `MAP` annotation type * docs: add comment * fix: navigate before selection for geospatial results * feat: comparators for annotation target equality - Adds `addTargetComparator()` to the Annotation API, allowing plugins to define additional comparators for certain annotation types. - Update usage of `_.isEqual()` for targets to use the `areAnnotationTargetsEqual()` method, which uses any additional comparators before falling back to a deep equality check. - Handle aborted `getAnnotations()` calls gracefully in the AnnotationInspectorView * test: add unit tests for target comparators --------- Co-authored-by: Scott Bell --- src/api/annotation/AnnotationAPI.js | 43 +++++++++++++++-- src/api/annotation/AnnotationAPISpec.js | 48 +++++++++++++++++++ .../annotations/AnnotationsInspectorView.vue | 42 ++++++++++------ src/plugins/notebook/components/Notebook.vue | 26 ++++++++-- src/plugins/plot/MctPlot.vue | 13 ++++- .../layout/search/AnnotationSearchResult.vue | 10 +++- 6 files changed, 157 insertions(+), 25 deletions(-) diff --git a/src/api/annotation/AnnotationAPI.js b/src/api/annotation/AnnotationAPI.js index 72f3d74d95..f13a26de58 100644 --- a/src/api/annotation/AnnotationAPI.js +++ b/src/api/annotation/AnnotationAPI.js @@ -76,6 +76,9 @@ const ANNOTATION_LAST_CREATED = 'annotationLastCreated'; * @constructor */ export default class AnnotationAPI extends EventEmitter { + /** @type {Map boolean >>} */ + #targetComparatorMap; + /** * @param {OpenMCT} openmct */ @@ -84,6 +87,7 @@ export default class AnnotationAPI extends EventEmitter { this.openmct = openmct; this.availableTags = {}; this.namespaceToSaveAnnotations = ''; + this.#targetComparatorMap = new Map(); this.ANNOTATION_TYPES = ANNOTATION_TYPES; this.ANNOTATION_TYPE = ANNOTATION_TYPE; @@ -246,15 +250,16 @@ export default class AnnotationAPI extends EventEmitter { /** * @method getAnnotations * @param {Identifier} domainObjectIdentifier - The domain object identifier to use to search for annotations. For example, a telemetry object identifier. + * @param {AbortSignal} abortSignal - An abort signal to cancel the search * @returns {DomainObject[]} Returns an array of annotations that match the search query */ - async getAnnotations(domainObjectIdentifier) { + async getAnnotations(domainObjectIdentifier, abortSignal = null) { const keyStringQuery = this.openmct.objects.makeKeyString(domainObjectIdentifier); const searchResults = ( await Promise.all( this.openmct.objects.search( keyStringQuery, - null, + abortSignal, this.openmct.objects.SEARCH_TYPES.ANNOTATIONS ) ) @@ -384,7 +389,8 @@ export default class AnnotationAPI extends EventEmitter { const combinedResults = []; results.forEach((currentAnnotation) => { const existingAnnotation = combinedResults.find((annotationToFind) => { - return _.isEqual(currentAnnotation.targets, annotationToFind.targets); + const { annotationType, targets } = currentAnnotation; + return this.areAnnotationTargetsEqual(annotationType, targets, annotationToFind.targets); }); if (!existingAnnotation) { combinedResults.push(currentAnnotation); @@ -460,4 +466,35 @@ export default class AnnotationAPI extends EventEmitter { return breakApartSeparateTargets; } + + /** + * Adds a comparator function for a given annotation type. + * The comparator functions will be used to determine if two annotations + * have the same target. + * @param {ANNOTATION_TYPES} annotationType + * @param {(t1, t2) => boolean} comparator + */ + addTargetComparator(annotationType, comparator) { + const comparatorList = this.#targetComparatorMap.get(annotationType) ?? []; + comparatorList.push(comparator); + this.#targetComparatorMap.set(annotationType, comparatorList); + } + + /** + * Compare two sets of targets to see if they are equal. First checks if + * any targets comparators evaluate to true, then falls back to a deep + * equality check. + * @param {ANNOTATION_TYPES} annotationType + * @param {*} targets + * @param {*} otherTargets + * @returns true if the targets are equal, false otherwise + */ + areAnnotationTargetsEqual(annotationType, targets, otherTargets) { + const targetComparatorList = this.#targetComparatorMap.get(annotationType); + return ( + (targetComparatorList?.length && + targetComparatorList.some((targetComparator) => targetComparator(targets, otherTargets))) || + _.isEqual(targets, otherTargets) + ); + } } diff --git a/src/api/annotation/AnnotationAPISpec.js b/src/api/annotation/AnnotationAPISpec.js index 7be711d235..318220ba2e 100644 --- a/src/api/annotation/AnnotationAPISpec.js +++ b/src/api/annotation/AnnotationAPISpec.js @@ -265,4 +265,52 @@ describe('The Annotation API', () => { expect(results.length).toEqual(0); }); }); + + describe('Target Comparators', () => { + let targets; + let otherTargets; + let comparator; + + beforeEach(() => { + targets = { + fooTarget: { + foo: 42 + } + }; + otherTargets = { + fooTarget: { + bar: 42 + } + }; + comparator = (t1, t2) => t1.fooTarget.foo === t2.fooTarget.bar; + }); + + it('can add a comparator function', () => { + const notebookAnnotationType = openmct.annotation.ANNOTATION_TYPES.NOTEBOOK; + expect( + openmct.annotation.areAnnotationTargetsEqual(notebookAnnotationType, targets, otherTargets) + ).toBeFalse(); // without a comparator, these should NOT be equal + // Register a comparator function for the notebook annotation type + openmct.annotation.addTargetComparator(notebookAnnotationType, comparator); + expect( + openmct.annotation.areAnnotationTargetsEqual(notebookAnnotationType, targets, otherTargets) + ).toBeTrue(); // the comparator should make these equal + }); + + it('falls back to deep equality check if no comparator functions', () => { + const annotationTypeWithoutComparator = openmct.annotation.ANNOTATION_TYPES.GEOSPATIAL; + const areEqual = openmct.annotation.areAnnotationTargetsEqual( + annotationTypeWithoutComparator, + targets, + targets + ); + const areNotEqual = openmct.annotation.areAnnotationTargetsEqual( + annotationTypeWithoutComparator, + targets, + otherTargets + ); + expect(areEqual).toBeTrue(); + expect(areNotEqual).toBeFalse(); + }); + }); }); diff --git a/src/plugins/inspectorViews/annotations/AnnotationsInspectorView.vue b/src/plugins/inspectorViews/annotations/AnnotationsInspectorView.vue index fcf2f29653..5e990943fc 100644 --- a/src/plugins/inspectorViews/annotations/AnnotationsInspectorView.vue +++ b/src/plugins/inspectorViews/annotations/AnnotationsInspectorView.vue @@ -41,7 +41,6 @@ diff --git a/src/plugins/imagery/components/Compass/compass.scss b/src/plugins/imagery/components/Compass/compass.scss index 357e88754c..368c1062ab 100644 --- a/src/plugins/imagery/components/Compass/compass.scss +++ b/src/plugins/imagery/components/Compass/compass.scss @@ -19,7 +19,7 @@ $elemBg: rgba(black, 0.7); position: absolute; left: 0; top: 0; - z-index: 2; + z-index: 3; @include userSelectNone; } diff --git a/src/plugins/imagery/components/ImageThumbnail.vue b/src/plugins/imagery/components/ImageThumbnail.vue index 598abd6183..d15ec361a7 100644 --- a/src/plugins/imagery/components/ImageThumbnail.vue +++ b/src/plugins/imagery/components/ImageThumbnail.vue @@ -38,6 +38,11 @@ fetchpriority="low" @load="imageLoadCompleted" /> + +
{{ image.formattedTime }}
@@ -66,6 +71,12 @@ export default { type: Boolean, required: true }, + imageryAnnotations: { + type: Array, + default() { + return []; + } + }, viewableArea: { type: Object, default: function () { @@ -125,6 +136,11 @@ export default { width: `${width}px`, height: `${height}px` }; + }, + showAnnotationIndicator() { + return this.imageryAnnotations.some((annotation) => { + return !annotation._deleted; + }); } }, methods: { diff --git a/src/plugins/imagery/components/ImageryView.vue b/src/plugins/imagery/components/ImageryView.vue index 0ac1840fec..f1564ec0e1 100644 --- a/src/plugins/imagery/components/ImageryView.vue +++ b/src/plugins/imagery/components/ImageryView.vue @@ -88,6 +88,13 @@ :image="focusedImage" :sized-image-dimensions="sizedImageDimensions" /> + @@ -173,6 +180,7 @@ :key="`${image.thumbnailUrl || image.url}-${image.time}-${index}`" :image="image" :active="focusedImageIndex === index" + :imagery-annotations="imageryAnnotations[image.time]" :selected="focusedImageIndex === index && isPaused" :real-time="!isFixed" :viewable-area="focusedImageIndex === index ? viewableArea : null" @@ -200,6 +208,7 @@ import Compass from './Compass/Compass.vue'; import ImageControls from './ImageControls.vue'; import ImageThumbnail from './ImageThumbnail.vue'; import imageryData from '../../imagery/mixins/imageryData'; +import AnnotationsCanvas from './AnnotationsCanvas.vue'; const REFRESH_CSS_MS = 500; const DURATION_TRACK_MS = 1000; @@ -232,7 +241,8 @@ export default { components: { Compass, ImageControls, - ImageThumbnail + ImageThumbnail, + AnnotationsCanvas }, mixins: [imageryData], inject: ['openmct', 'domainObject', 'objectPath', 'currentView', 'imageFreshnessOptions'], @@ -295,7 +305,8 @@ export default { animateZoom: true, imagePanned: false, forceShowThumbnails: false, - animateThumbScroll: false + animateThumbScroll: false, + imageryAnnotations: {} }; }, computed: { @@ -425,6 +436,19 @@ export default { return result; }, + shouldDisplayAnnotations() { + const imageHeightAndWidth = this.sizedImageHeight !== 0 && this.sizedImageWidth !== 0; + const display = + this.focusedImage !== undefined && + this.focusedImageNaturalAspectRatio !== undefined && + this.imageContainerWidth !== undefined && + this.imageContainerHeight !== undefined && + imageHeightAndWidth && + this.zoomFactor === 1 && + this.imagePanned !== true; + + return display; + }, shouldDisplayCompass() { const imageHeightAndWidth = this.sizedImageHeight !== 0 && this.sizedImageWidth !== 0; const display = @@ -631,6 +655,9 @@ export default { } } }, + created() { + this.abortController = new AbortController(); + }, async mounted() { eventHelpers.extend(this); this.focusedImageWrapper = this.$refs.focusedImageWrapper; @@ -689,8 +716,12 @@ export default { this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this); this.loadVisibleLayers(); + this.loadAnnotations(); + + this.openmct.selection.on('change', this.updateSelection); }, beforeDestroy() { + this.abortController.abort(); this.persistVisibleLayers(); this.stopFollowingTimeContext(); @@ -716,6 +747,15 @@ export default { } this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this); + + Object.keys(this.imageryAnnotations).forEach((time) => { + const imageAnnotationsForTime = this.imageryAnnotations[time]; + imageAnnotationsForTime.forEach((imageAnnotation) => { + this.openmct.objects.destroyMutable(imageAnnotation); + }); + }); + + this.openmct.selection.off('change', this.updateSelection); }, methods: { calculateViewHeight() { @@ -743,6 +783,15 @@ export default { this.timeContext.off('clock', this.trackDuration); } }, + updateSelection(selection) { + const selectionType = selection?.[0]?.[0]?.context?.type; + const validSelectionTypes = ['annotation-search-result']; + + if (!validSelectionTypes.includes(selectionType)) { + // wrong type of selection + return; + } + }, expand() { // check for modifier keys so it doesnt interfere with the layout if (this.cursorStates.modifierKeyPressed) { @@ -832,6 +881,41 @@ export default { }); } }, + async loadAnnotations(existingAnnotations) { + if (!this.openmct.annotation.getAvailableTags().length) { + // don't bother loading annotations if there are no tags + return; + } + let foundAnnotations = existingAnnotations; + if (!foundAnnotations) { + // attempt to load + foundAnnotations = await this.openmct.annotation.getAnnotations( + this.domainObject.identifier, + this.abortController.signal + ); + } + foundAnnotations.forEach((foundAnnotation) => { + const targetId = Object.keys(foundAnnotation.targets)[0]; + const timeForAnnotation = foundAnnotation.targets[targetId].time; + if (!this.imageryAnnotations[timeForAnnotation]) { + this.$set(this.imageryAnnotations, timeForAnnotation, []); + } + + const annotationExtant = this.imageryAnnotations[timeForAnnotation].some( + (existingAnnotation) => { + return this.openmct.objects.areIdsEqual( + existingAnnotation.identifier, + foundAnnotation.identifier + ); + } + ); + if (!annotationExtant) { + const annotationArray = this.imageryAnnotations[timeForAnnotation]; + const mutableAnnotation = this.openmct.objects.toMutable(foundAnnotation); + annotationArray.push(mutableAnnotation); + } + }); + }, persistVisibleLayers() { if ( this.domainObject.configuration && @@ -979,7 +1063,9 @@ export default { } await Vue.nextTick(); - this.$refs.thumbsWrapper.scrollLeft = scrollWidth; + if (this.$refs.thumbsWrapper) { + this.$refs.thumbsWrapper.scrollLeft = scrollWidth; + } }, scrollHandler() { if (this.isPaused) { diff --git a/src/plugins/imagery/components/imagery-view.scss b/src/plugins/imagery/components/imagery-view.scss index f14a6cebb0..09e8cb7d81 100644 --- a/src/plugins/imagery/components/imagery-view.scss +++ b/src/plugins/imagery/components/imagery-view.scss @@ -293,6 +293,13 @@ width: 100%; } + &__annotation-indicator { + color: $colorClickIconButton; + position: absolute; + top: 6px; + right: 8px; + } + &__timestamp { flex: 0 0 auto; padding: 2px 3px; @@ -540,3 +547,11 @@ align-self: flex-end; } } + +.c-image-canvas { + pointer-events: auto; // This allows the image element to receive a browser-level context click + position: absolute; + left: 0; + top: 0; + z-index: 2; +} diff --git a/src/plugins/plot/MctPlot.vue b/src/plugins/plot/MctPlot.vue index 7d00b934fc..899af22c72 100644 --- a/src/plugins/plot/MctPlot.vue +++ b/src/plugins/plot/MctPlot.vue @@ -183,7 +183,7 @@ import MctTicks from './MctTicks.vue'; import MctChart from './chart/MctChart.vue'; import XAxis from './axis/XAxis.vue'; import YAxis from './axis/YAxis.vue'; -import KDBush from 'kdbush'; +import Flatbush from 'flatbush'; import _ from 'lodash'; const OFFSET_THRESHOLD = 10; @@ -414,8 +414,8 @@ export default { // on clicking on a search result we highlight the annotation and zoom - we know it's an annotation result when isAnnotationSearchResult === true // We shouldn't zoom when we're selecting existing annotations to view them or creating new annotations. const selectionType = selection?.[0]?.[0]?.context?.type; - const validSelectionTypes = ['clicked-on-plot-selection', 'plot-annotation-search-result']; - const isAnnotationSearchResult = selectionType === 'plot-annotation-search-result'; + const validSelectionTypes = ['clicked-on-plot-selection', 'annotation-search-result']; + const isAnnotationSearchResult = selectionType === 'annotation-search-result'; if (!validSelectionTypes.includes(selectionType)) { // wrong type of selection @@ -1398,6 +1398,24 @@ export default { return annotationsByPoints.flat(); }, + searchWithFlatbush(seriesData, seriesModel, boundingBox) { + const flatbush = new Flatbush(seriesData.length); + seriesData.forEach((point) => { + const x = seriesModel.getXVal(point); + const y = seriesModel.getYVal(point); + flatbush.add(x, y, x, y); + }); + flatbush.finish(); + + const rangeResults = flatbush.search( + boundingBox.minX, + boundingBox.minY, + boundingBox.maxX, + boundingBox.maxY + ); + + return rangeResults; + }, getPointsInBox(boundingBoxPerYAxis, rawAnnotation) { // load series models in KD-Trees const seriesKDTrees = []; @@ -1413,22 +1431,8 @@ export default { const seriesData = seriesModel.getSeriesData(); if (seriesData && seriesData.length) { - const kdTree = new KDBush( - seriesData, - (point) => { - return seriesModel.getXVal(point); - }, - (point) => { - return seriesModel.getYVal(point); - } - ); const searchResults = []; - const rangeResults = kdTree.range( - boundingBox.minX, - boundingBox.minY, - boundingBox.maxX, - boundingBox.maxY - ); + const rangeResults = this.searchWithFlatbush(seriesData, seriesModel, boundingBox); rangeResults.forEach((id) => { const seriesDatum = seriesData[id]; if (seriesDatum) { diff --git a/src/ui/layout/search/AnnotationSearchResult.vue b/src/ui/layout/search/AnnotationSearchResult.vue index 1f0fe22449..cff1f05662 100644 --- a/src/ui/layout/search/AnnotationSearchResult.vue +++ b/src/ui/layout/search/AnnotationSearchResult.vue @@ -122,11 +122,11 @@ export default { mounted() { this.previewAction = new PreviewAction(this.openmct); this.previewAction.on('isVisible', this.togglePreviewState); - this.clickedPlotAnnotation = this.clickedPlotAnnotation.bind(this); + this.fireAnnotationSelection = this.fireAnnotationSelection.bind(this); }, destroyed() { this.previewAction.off('isVisible', this.togglePreviewState); - this.openmct.selection.off('change', this.clickedPlotAnnotation); + this.openmct.selection.off('change', this.fireAnnotationSelection); }, methods: { clickedResult(event) { @@ -139,18 +139,15 @@ export default { if (!this.openmct.router.isNavigatedObject(objectPath)) { // if we're not on the correct page, navigate to the object, // then wait for the selection event to fire before issuing a new selection - if ( - this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL || - this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.GEOSPATIAL - ) { - this.openmct.selection.on('change', this.clickedPlotAnnotation); + if (this.result.annotationType) { + this.openmct.selection.on('change', this.fireAnnotationSelection); } this.openmct.router.navigate(resultUrl); } else { // if this is the navigated object, then we are already on the correct page // and just need to issue the selection event - this.clickedPlotAnnotation(); + this.fireAnnotationSelection(); } } }, @@ -159,8 +156,8 @@ export default { this.previewAction.invoke(objectPath); } }, - clickedPlotAnnotation() { - this.openmct.selection.off('change', this.clickedPlotAnnotation); + fireAnnotationSelection() { + this.openmct.selection.off('change', this.fireAnnotationSelection); const targetDetails = {}; const targetDomainObjects = {}; @@ -176,11 +173,11 @@ export default { element: this.$el, context: { item: this.result.targetModels[0], - type: 'plot-annotation-search-result', + type: 'annotation-search-result', targetDetails, targetDomainObjects, annotations: [this.result], - annotationType: this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL, + annotationType: this.result.annotationType, onAnnotationChange: () => {} } } From 9423591e4d9b869f86694685a7c8693ef958afff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 15:57:01 +0000 Subject: [PATCH 14/25] chore(deps-dev): bump sass-loader from 13.3.1 to 13.3.2 (#6728) Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 13.3.1 to 13.3.2. - [Release notes](https://github.com/webpack-contrib/sass-loader/releases) - [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/sass-loader/compare/v13.3.1...v13.3.2) --- updated-dependencies: - dependency-name: sass-loader dependency-type: direct:development update-type: version-update:semver-patch ... 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 2afcd7229d..33a2f5a24a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "resolve-url-loader": "5.0.0", "sanitize-html": "2.10.0", "sass": "1.63.3", - "sass-loader": "13.3.1", + "sass-loader": "13.3.2", "sinon": "15.1.0", "style-loader": "3.3.3", "typescript": "5.1.3", From c75a82dca52608839b7df1d3d479ee747335559c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 16:54:20 +0000 Subject: [PATCH 15/25] chore(deps-dev): bump eslint from 8.42.0 to 8.43.0 (#6744) Bumps [eslint](https://github.com/eslint/eslint) from 8.42.0 to 8.43.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.42.0...v8.43.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... 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 33a2f5a24a..a9e44e6ac4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "d3-axis": "3.0.0", "d3-scale": "3.3.0", "d3-selection": "3.0.0", - "eslint": "8.42.0", + "eslint": "8.43.0", "eslint-plugin-compat": "4.1.4", "eslint-config-prettier": "8.8.0", "eslint-plugin-playwright": "0.12.0", From f254d4f0787c6538bed2257ad551979cc84f9aeb Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 21 Jun 2023 10:16:36 -0700 Subject: [PATCH 16/25] chore: bump version to 2.2.6-SNAPSHOT (#6752) Co-authored-by: John Hill --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9e44e6ac4..f0333f9ddb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openmct", - "version": "2.2.5-SNAPSHOT", + "version": "2.2.6-SNAPSHOT", "description": "The Open MCT core platform", "devDependencies": { "@babel/eslint-parser": "7.21.8", From d8ac209a96dbd295ff3987b4ffff82178231efc7 Mon Sep 17 00:00:00 2001 From: Scott Bell Date: Wed, 21 Jun 2023 20:20:35 +0200 Subject: [PATCH 17/25] Fix race condition in image annotations loading and drawing them on the canvas (#6751) fix race condition between annotation loading and drawing the annotations --- src/plugins/imagery/components/AnnotationsCanvas.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/imagery/components/AnnotationsCanvas.vue b/src/plugins/imagery/components/AnnotationsCanvas.vue index a4e1a1632b..d750c761fb 100644 --- a/src/plugins/imagery/components/AnnotationsCanvas.vue +++ b/src/plugins/imagery/components/AnnotationsCanvas.vue @@ -89,6 +89,11 @@ export default { } } }, + watch: { + imageryAnnotations() { + this.drawAnnotations(); + } + }, mounted() { this.canvas = this.$refs.canvas; this.context = this.canvas.getContext('2d'); From b685b9582e46c5f518b44e0676d57f65cebb59f3 Mon Sep 17 00:00:00 2001 From: John Hill Date: Wed, 21 Jun 2023 13:54:14 -0700 Subject: [PATCH 18/25] [CI]Add docker and npm caching (#6748) --- .github/workflows/e2e-couchdb.yml | 33 ++++++++++++++--- .github/workflows/e2e-pr.yml | 55 +++++++++------------------- .github/workflows/npm-prerelease.yml | 4 +- .github/workflows/pr-platform.yml | 46 ++++++++++++++++++++--- .prettierrc | 3 +- 5 files changed, 89 insertions(+), 52 deletions(-) diff --git a/.github/workflows/e2e-couchdb.yml b/.github/workflows/e2e-couchdb.yml index e442800b72..016003c6b7 100644 --- a/.github/workflows/e2e-couchdb.yml +++ b/.github/workflows/e2e-couchdb.yml @@ -7,15 +7,33 @@ on: - opened jobs: e2e-couchdb: - if: ${{ github.event.label.name == 'pr:e2e:couchdb' }} || ${{ github.event.action == 'opened' }} + if: github.event.label.name == 'pr:e2e:couchdb' || github.event.action == 'opened' && github.actor == 'dependabot[bot]' runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 'lts/gallium' + node-version: 'lts/hydrogen' + + - name: Cache NPM dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - run: npm install --cache ~/.npm --prefer-offline --no-audit --progress=false + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - run: npx playwright@1.32.3 install - - run: npm install + - name: Start CouchDB Docker Container and Init with Setup Scripts run: | export $(cat src/plugins/persistence/couch/.env.ci | xargs) @@ -23,26 +41,31 @@ jobs: sleep 3 bash src/plugins/persistence/couch/setup-couchdb.sh bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh + - name: Run CouchDB Tests and publish to deploysentinel env: DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }} run: npm run test:e2e:couchdb + - name: Publish Results to Codecov.io env: SUPER_SECRET: ${{ secrets.CODECOV_TOKEN }} run: npm run cov:e2e:full:publish + - name: Archive test results if: success() || failure() uses: actions/upload-artifact@v3 with: path: test-results + - name: Archive html test results if: success() || failure() uses: actions/upload-artifact@v3 with: path: html-test-results + - name: Remove pr:e2e:couchdb label (if present) - if: ${{ contains(github.event.pull_request.labels.*.name, 'pr:e2e:couchdb') }} + if: always() uses: actions/github-script@v6 with: script: | @@ -56,5 +79,5 @@ jobs: name: labelToRemove }); } catch (error) { - core.warning(`Failed to remove 'pr:e2e:couchdb' label: ${error.message}`); + core.warning(`Failed to remove ' + labelToRemove + ' label: ${error.message}`); } diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml index 62cd32dba9..0e4a8d9a9d 100644 --- a/.github/workflows/e2e-pr.yml +++ b/.github/workflows/e2e-pr.yml @@ -7,31 +7,31 @@ on: - opened jobs: e2e-full: - if: ${{ github.event.label.name == 'pr:e2e' }} + if: github.event.label.name == 'pr:e2e' || github.event.action == 'opened' && github.actor == 'dependabot[bot]' runs-on: ${{ matrix.os }} + timeout-minutes: 60 strategy: matrix: os: - ubuntu-latest - windows-latest steps: - - name: Trigger Success - uses: actions/github-script@v6 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: "nasa", - repo: "openmct", - body: 'Started e2e Run. Follow along: https://github.com/nasa/openmct/actions/runs/' + context.runId - }) - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: 'lts/hydrogen' + + - name: Cache NPM dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npx playwright@1.32.3 install - run: npx playwright install chrome-beta - - run: npm install + - run: npm install --cache ~/.npm --prefer-offline --no-audit --progress=false - run: npm run test:e2e:full -- --max-failures=40 - run: npm run cov:e2e:report || true - shell: bash @@ -44,30 +44,9 @@ jobs: uses: actions/upload-artifact@v3 with: path: test-results - - name: Test success - if: ${{ success() }} - uses: actions/github-script@v6 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: "nasa", - repo: "openmct", - body: 'Success ✅ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId - }) - - name: Test failure - if: ${{ failure() }} - uses: actions/github-script@v6 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: "nasa", - repo: "openmct", - body: 'Failure ❌ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId - }) + - name: Remove pr:e2e label (if present) - if: ${{ contains(github.event.pull_request.labels.*.name, 'pr:e2e') }} + if: always() uses: actions/github-script@v6 with: script: | @@ -81,5 +60,5 @@ jobs: name: labelToRemove }); } catch (error) { - core.warning(`Failed to remove 'pr:e2e' label: ${error.message}`); - } + core.warning(`Failed to remove ' + labelToRemove + ' label: ${error.message}`); + } \ No newline at end of file diff --git a/.github/workflows/npm-prerelease.yml b/.github/workflows/npm-prerelease.yml index a9321ac569..605ebe8bba 100644 --- a/.github/workflows/npm-prerelease.yml +++ b/.github/workflows/npm-prerelease.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: lts/hydrogen - run: npm install - run: | echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" >> ~/.npmrc @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: lts/hydrogen registry-url: https://registry.npmjs.org/ - run: npm install - run: npm publish --access=public --tag unstable diff --git a/.github/workflows/pr-platform.yml b/.github/workflows/pr-platform.yml index f56ff576c2..1f1cdfbaa1 100644 --- a/.github/workflows/pr-platform.yml +++ b/.github/workflows/pr-platform.yml @@ -2,12 +2,15 @@ name: 'pr-platform' on: workflow_dispatch: pull_request: - types: [labeled] + types: + - labeled + - opened jobs: - e2e-full: - if: ${{ github.event.label.name == 'pr:platform' }} + pr-platform: + if: github.event.label.name == 'pr:platform' || github.event.action == 'opened' && github.actor == 'dependabot[bot]' runs-on: ${{ matrix.os }} + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -16,18 +19,49 @@ jobs: - macos-latest - windows-latest node_version: - - 16 - - 18 + - lts/gallium + - lts/hydrogen architecture: - x64 + name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }} steps: - uses: actions/checkout@v3 + - name: Setup node uses: actions/setup-node@v3 with: node-version: ${{ matrix.node_version }} architecture: ${{ matrix.architecture }} - - run: npm install + + - name: Cache NPM dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.node_version }}- + + - run: npm install --cache ~/.npm --prefer-offline --no-audit --progress=false + - run: npm test + - run: npm run lint -- --quiet + + - name: Remove pr:platform label (if present) + if: always() + uses: actions/github-script@v6 + with: + script: | + const { owner, repo, number } = context.issue; + const labelToRemove = 'pr:platform'; + try { + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number: number, + name: labelToRemove + }); + } catch (error) { + core.warning(`Failed to remove ' + labelToRemove + ' label: ${error.message}`); + } diff --git a/.prettierrc b/.prettierrc index aaae3c5c5f..479112ec1d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "trailingComma": "none", "singleQuote": true, - "printWidth": 100 + "printWidth": 100, + "endOfLine": "auto" } From 4b39ef32358893e0b1f077df4a1fc0c3708039bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 16:15:46 -0700 Subject: [PATCH 19/25] chore(deps): bump docker/login-action from 1 to 2 (#6754) Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/e2e-couchdb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-couchdb.yml b/.github/workflows/e2e-couchdb.yml index 016003c6b7..cada516eb8 100644 --- a/.github/workflows/e2e-couchdb.yml +++ b/.github/workflows/e2e-couchdb.yml @@ -27,7 +27,7 @@ jobs: - run: npm install --cache ~/.npm --prefer-offline --no-audit --progress=false - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} From 68ed7bf0e53941d8839801a498c2a618f0db1f1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 16:23:11 -0700 Subject: [PATCH 20/25] chore(deps-dev): bump eslint-plugin-vue from 9.14.1 to 9.15.0 (#6746) Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.14.1 to 9.15.0. - [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases) - [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.14.1...v9.15.0) --- updated-dependencies: - dependency-name: eslint-plugin-vue dependency-type: direct:development update-type: version-update:semver-minor ... 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 f0333f9ddb..135146a0f1 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "eslint-config-prettier": "8.8.0", "eslint-plugin-playwright": "0.12.0", "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-vue": "9.14.1", + "eslint-plugin-vue": "9.15.0", "eslint-plugin-you-dont-need-lodash-underscore": "6.12.0", "eventemitter3": "1.2.0", "file-saver": "2.0.5", From 1d7cd64652ccc9f74accd38ba6e5ec8a7e4283d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:09:44 +0000 Subject: [PATCH 21/25] chore(deps-dev): bump @babel/eslint-parser from 7.21.8 to 7.22.5 (#6747) Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.21.8 to 7.22.5. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.22.5/eslint/babel-eslint-parser) --- updated-dependencies: - dependency-name: "@babel/eslint-parser" dependency-type: direct:development update-type: version-update:semver-minor ... 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 135146a0f1..70643be7f3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "2.2.6-SNAPSHOT", "description": "The Open MCT core platform", "devDependencies": { - "@babel/eslint-parser": "7.21.8", + "@babel/eslint-parser": "7.22.5", "@braintree/sanitize-url": "6.0.2", "@deploysentinel/playwright": "0.3.4", "@percy/cli": "1.26.0", From 834a19f99609c6eaf376873bbaeea345071ea2d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:07:33 -0700 Subject: [PATCH 22/25] chore(deps-dev): bump sass from 1.63.3 to 1.63.4 (#6743) Bumps [sass](https://github.com/sass/dart-sass) from 1.63.3 to 1.63.4. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.63.3...1.63.4) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Mazzella --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 70643be7f3..bc13c42066 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "printj": "1.3.1", "resolve-url-loader": "5.0.0", "sanitize-html": "2.10.0", - "sass": "1.63.3", + "sass": "1.63.4", "sass-loader": "13.3.2", "sinon": "15.1.0", "style-loader": "3.3.3", From 58f869b21bea1dffc2ff9baf27c0e841a17b8d70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 23:47:38 +0000 Subject: [PATCH 23/25] chore(deps-dev): bump webpack from 5.86.0 to 5.88.0 (#6764) Bumps [webpack](https://github.com/webpack/webpack) from 5.86.0 to 5.88.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.86.0...v5.88.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... 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 bc13c42066..bc2c260005 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "vue-eslint-parser": "9.3.1", "vue-loader": "15.9.8", "vue-template-compiler": "2.6.14", - "webpack": "5.86.0", + "webpack": "5.88.0", "webpack-cli": "5.1.1", "webpack-dev-server": "4.15.1", "webpack-merge": "5.9.0" From ff7f55574d69c548dcc1dd0958b39a5925e8fced Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 08:12:02 +0000 Subject: [PATCH 24/25] chore(deps-dev): bump flatbush from 4.1.0 to 4.2.0 (#6762) Bumps [flatbush](https://github.com/mourner/flatbush) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/mourner/flatbush/releases) - [Commits](https://github.com/mourner/flatbush/compare/v4.1.0...v4.2.0) --- updated-dependencies: - dependency-name: flatbush dependency-type: direct:development update-type: version-update:semver-minor ... 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 bc2c260005..5cfec6db86 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "6.12.0", "eventemitter3": "1.2.0", "file-saver": "2.0.5", - "flatbush": "4.1.0", + "flatbush": "4.2.0", "git-rev-sync": "3.0.2", "html2canvas": "1.4.1", "imports-loader": "4.0.1", From 3b0e05ed14d0f187a9fa2f85e9099888f35ec81d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:39:24 -0700 Subject: [PATCH 25/25] chore(deps-dev): bump sanitize-html from 2.10.0 to 2.11.0 (#6766) Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.10.0 to 2.11.0. - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.10.0...2.11.0) --- updated-dependencies: - dependency-name: sanitize-html dependency-type: direct:development update-type: version-update:semver-minor ... 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 5cfec6db86..e0a06d6f43 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "prettier": "2.8.7", "printj": "1.3.1", "resolve-url-loader": "5.0.0", - "sanitize-html": "2.10.0", + "sanitize-html": "2.11.0", "sass": "1.63.4", "sass-loader": "13.3.2", "sinon": "15.1.0",