diff --git a/e2e-tests/data/valid-view.json b/e2e-tests/data/valid-view.json index 0ea548f100..4d659c3083 100644 --- a/e2e-tests/data/valid-view.json +++ b/e2e-tests/data/valid-view.json @@ -315,7 +315,6 @@ "layers": [ { "activityColor": "#283593", - "activityHeight": 20, "chartType": "activity", "filter": { "activity": { diff --git a/e2e-tests/fixtures/View.ts b/e2e-tests/fixtures/View.ts index 6223989287..5a670b3305 100644 --- a/e2e-tests/fixtures/View.ts +++ b/e2e-tests/fixtures/View.ts @@ -12,6 +12,7 @@ export class View { navButtonViewSaveAsMenuButton: Locator; navButtonViewSavedViewsMenuButton: Locator; navButtonViewUploadViewMenuButton: Locator; + outOfDateViewFilePath: string = 'src/tests/mocks/view/v0/view.json'; renameViewMenuSaveViewButton: Locator; saveAsMenuSaveAsButton: Locator; table: Locator; diff --git a/e2e-tests/tests/view.test.ts b/e2e-tests/tests/view.test.ts index 35529524f9..104ad6868b 100644 --- a/e2e-tests/tests/view.test.ts +++ b/e2e-tests/tests/view.test.ts @@ -106,4 +106,15 @@ test.describe.serial('View', () => { await page.locator('.modal .st-button:has-text("Upload View")').click(); await expect(page.locator('.modal')).not.toBeVisible(); }); + + test(`Selecting an out of date view file should not display an error and not prevent the file from being uploaded`, async () => { + await view.openViewMenu(); + await expect(view.navButtonViewUploadViewMenuButton).toBeVisible(); + await view.navButtonViewUploadViewMenuButton.click(); + await view.fillViewInputName(); + await view.fillViewInputFile(view.outOfDateViewFilePath); + await expect(page.locator('.modal-content .error')).not.toBeVisible(); + await page.locator('.modal .st-button:has-text("Upload View")').click(); + await expect(page.locator('.modal')).not.toBeVisible(); + }); }); diff --git a/src/components/modals/SavedViewsModal.svelte b/src/components/modals/SavedViewsModal.svelte index e375e05514..df17135221 100644 --- a/src/components/modals/SavedViewsModal.svelte +++ b/src/components/modals/SavedViewsModal.svelte @@ -61,9 +61,9 @@ } } - async function getFullView(viewId: number): Promise { + async function getFullView(viewId: number, migrate: boolean = true): Promise { const query = new URLSearchParams(`?${SearchParameters.VIEW_ID}=${viewId}`); - return await effects.getView(query, user); + return await effects.getView(query, user, migrate); } async function openView({ detail: viewId }: CustomEvent) { @@ -78,7 +78,7 @@ } async function downloadView({ detail: viewId }: CustomEvent) { - const view = await getFullView(viewId); + const view = await getFullView(viewId, false); if (view !== null) { downloadViewUtil(view); dispatch('close'); diff --git a/src/components/modals/UploadViewModal.svelte b/src/components/modals/UploadViewModal.svelte index 80c1777066..ec2e008ed0 100644 --- a/src/components/modals/UploadViewModal.svelte +++ b/src/components/modals/UploadViewModal.svelte @@ -77,6 +77,7 @@ name="file" required type="file" + accept="application/json" bind:files on:click={onClick} on:change={onChange} diff --git a/src/constants/view.ts b/src/constants/view.ts index b4611f07a8..2a13c402e2 100644 --- a/src/constants/view.ts +++ b/src/constants/view.ts @@ -69,3 +69,7 @@ export const ViewXRangeLayerSchemePresets: Record { const initialView = await effects.getView( url.searchParams, user, + true, initialActivityTypes, initialResourceTypes, initialExternalEventTypes, diff --git a/src/schemas/index.ts b/src/schemas/index.ts new file mode 100644 index 0000000000..915187e594 --- /dev/null +++ b/src/schemas/index.ts @@ -0,0 +1,7 @@ +import * as v0 from './ui-view-schema-v0.json'; +import * as v1 from './ui-view-schema-v1.json'; + +export default { + v0, + v1, +}; diff --git a/src/schemas/ui-view-schema-v0.json b/src/schemas/ui-view-schema-v0.json new file mode 100644 index 0000000000..c35ac4178a --- /dev/null +++ b/src/schemas/ui-view-schema-v0.json @@ -0,0 +1,550 @@ +{ + "$id": "https://github.com/NASA-AMMOS/aerie-ui/blob/develop/src/schemas/ui-view-schema-v0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "color": { + "description": "RGB color in hex format", + "pattern": "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$", + "type": "string" + }, + "filterResource": { + "additionalProperties": false, + "properties": { + "resource": { + "additionalProperties": false, + "properties": { + "names": { + "description": "Array of resource names to display in this layer", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": ["names"], + "type": "object" + } + }, + "required": ["resource"], + "type": "object" + }, + "id": { + "description": "Numeric unique id >= 0", + "minimum": 0, + "type": "number" + }, + "label": { + "additionalProperties": false, + "description": "Generic label object", + "properties": { + "color": { + "$ref": "#/definitions/color" + }, + "text": { + "description": "Label text content", + "type": "string" + } + }, + "required": ["text"], + "type": "object" + }, + "table": { + "additionalProperties": false, + "description": "Table specification for visualizing tabular data via ag-grid", + "properties": { + "autoSizeColumns": { + "default": "fill", + "description": "The state of which the table should resize automatically", + "type": "string" + }, + "columnDefs": { + "description": "Array of column definition objects that conform to the ag-grid ColumnDef type", + "items": { + "type": "object" + }, + "type": "array" + }, + "columnStates": { + "description": "Array of column state objects that conform to the ag-grid ColumnState type", + "items": { + "type": "object" + }, + "type": "array" + } + }, + "required": ["columnDefs", "columnStates"], + "type": "object" + }, + "viewGridComponent": { + "description": "Set of components available to view in a grid section", + "enum": [ + "ActivityDirectivesTablePanel", + "ActivityFormPanel", + "ActivitySpansTablePanel", + "TimelineItemsPanel", + "ConstraintsPanel", + "ExpansionPanel", + "IFramePanel", + "PlanMetadataPanel", + "SchedulingConditionsPanel", + "SchedulingGoalsPanel", + "SimulationEventsPanel", + "SimulationPanel", + "TimelineEditorPanel" + ] + }, + "yAxisId": { + "description": "Id of the associated y-axis. Can be null for no association.", + "type": ["number", "null"] + } + }, + "description": "JSON schema definition used for configuring the Aerie UI", + "properties": { + "plan": { + "additionalProperties": false, + "description": "View configuration for a plan", + "properties": { + "activityDirectivesTable": { + "$ref": "#/definitions/table" + }, + "activitySpansTable": { + "$ref": "#/definitions/table" + }, + "simulationEventsTable": { + "$ref": "#/definitions/table" + }, + "grid": { + "additionalProperties": false, + "description": "Defines the different visible sections for a plan", + "properties": { + "columnSizes": { + "description": "Size of each column and gutter using CSS grid notation", + "type": "string" + }, + "leftComponentBottom": { + "$ref": "#/definitions/viewGridComponent" + }, + "leftComponentTop": { + "$ref": "#/definitions/viewGridComponent" + }, + "leftHidden": { + "description": "If true hide the left panel, if false show the left panel", + "type": "boolean" + }, + "leftRowSizes": { + "description": "Size of each left row and gutter using CSS grid notation", + "type": "string" + }, + "leftSplit": { + "description": "If true the left panel is split into two, if false show single left panel", + "type": "boolean" + }, + "middleComponentBottom": { + "$ref": "#/definitions/viewGridComponent" + }, + "middleRowSizes": { + "description": "Size of each middle row and gutter using CSS grid notation", + "type": "string" + }, + "middleSplit": { + "description": "If true the middle panel is split into two, if false show single middle panel", + "type": "boolean" + }, + "rightComponentBottom": { + "$ref": "#/definitions/viewGridComponent" + }, + "rightComponentTop": { + "$ref": "#/definitions/viewGridComponent" + }, + "rightHidden": { + "description": "If true hide the right panel, if false show the right panel", + "type": "boolean" + }, + "rightRowSizes": { + "description": "Size of each right row and gutter using CSS grid notation", + "type": "string" + }, + "rightSplit": { + "description": "If true the right panel is split into two, if false show single right panel", + "type": "boolean" + } + }, + "required": [ + "columnSizes", + "leftComponentBottom", + "leftComponentTop", + "leftHidden", + "leftRowSizes", + "leftSplit", + "middleComponentBottom", + "middleRowSizes", + "middleSplit", + "rightComponentBottom", + "rightComponentTop", + "rightHidden", + "rightRowSizes", + "rightSplit" + ], + "type": "object" + }, + "iFrames": { + "description": "IFrame specifications for embedding other web pages", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/id" + }, + "src": { + "description": "The URL of the page to embed", + "type": "string" + }, + "title": { + "description": "The title attribute of the iframe", + "type": "string" + } + }, + "required": ["id", "src", "title"], + "type": "object" + }, + "type": "array" + }, + "timelines": { + "description": "Timeline specifications for visualizing activities or resources in a timeline", + "items": { + "additionalProperties": false, + "description": "Displays a timeline in the section", + "properties": { + "id": { + "$ref": "#/definitions/id" + }, + "marginLeft": { + "description": "Left margin of the timeline in pixels", + "minimum": 0, + "type": "number" + }, + "marginRight": { + "description": "Right margin of the timeline in pixels", + "minimum": 0, + "type": "number" + }, + "rows": { + "description": "Timeline row definitions", + "items": { + "additionalProperties": false, + "properties": { + "activityOptions": { + "additionalProperties": false, + "description": "Defines the options used for rendering activities on the timeline", + "properties": { + "activityHeight": { + "description": "Height of activity subrows", + "type": "number", + "minimum": 12 + }, + "composition": { + "description": "Whether or not to display only directives, only spans, or both in the row", + "enum": ["directives", "spans", "both"] + }, + "displayMode": { + "description": "Describes the primary method in which activities are visualized within this row", + "enum": ["grouped", "compact"] + }, + "hierarchyMode": { + "description": "If 'directive' the activities are grouped starting with directive types, if 'flat' activities are grouped by type regardless of hierarchy", + "enum": ["directive", "flat"] + }, + "labelVisibility": { + "description": "Activity text label behavior", + "enum": ["on", "off", "auto"] + } + }, + "required": ["composition", "displayMode", "hierarchyMode", "labelVisibility"] + }, + "autoAdjustHeight": { + "description": "If true the row height is set automatically to fit the row content", + "type": "boolean" + }, + "expanded": { + "description": "Expanded state of the timeline row", + "type": "boolean" + }, + "height": { + "description": "Height of the row in pixels", + "minimum": 0, + "type": "number" + }, + "horizontalGuides": { + "description": "Row horizontal guide definitions", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/id" + }, + "label": { + "$ref": "#/definitions/label" + }, + "y": { + "description": "Y value the horizontal guide anchors to", + "type": "number" + }, + "yAxisId": { + "$ref": "#/definitions/id" + } + }, + "required": ["id", "label", "y", "yAxisId"], + "type": "object" + }, + "type": "array" + }, + "id": { + "$ref": "#/definitions/id" + }, + "layers": { + "description": "Row layer definitions", + "items": { + "oneOf": [ + { + "additionalProperties": false, + "description": "Activity layer", + "properties": { + "activityColor": { + "$ref": "#/definitions/color" + }, + "activityHeight": { + "description": "Height of each activity", + "type": "number", + "$comment": "deprecated" + }, + "chartType": { + "const": "activity", + "description": "Layer that visualizes activities" + }, + "filter": { + "additionalProperties": false, + "properties": { + "activity": { + "additionalProperties": false, + "properties": { + "types": { + "description": "Array of activity types to display in this layer", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": ["types"], + "type": "object" + } + }, + "required": ["activity"], + "type": "object" + }, + "id": { + "$ref": "#/definitions/id" + }, + "name": { + "description": "Name of the layer", + "type": "string" + }, + "yAxisId": { + "$ref": "#/definitions/yAxisId" + } + }, + "required": ["activityColor", "activityHeight", "chartType", "filter", "id", "yAxisId"], + "type": "object" + }, + { + "additionalProperties": false, + "description": "Line layer", + "properties": { + "chartType": { + "const": "line", + "description": "Layer that visualizes points and a line" + }, + "filter": { + "$ref": "#/definitions/filterResource" + }, + "id": { + "$ref": "#/definitions/id" + }, + "lineColor": { + "$ref": "#/definitions/color" + }, + "lineWidth": { + "description": "Width of the line", + "type": "number" + }, + "name": { + "description": "Name of the layer", + "type": "string" + }, + "pointRadius": { + "description": "Radius of the points", + "type": "number" + }, + "yAxisId": { + "$ref": "#/definitions/yAxisId" + } + }, + "required": [ + "chartType", + "filter", + "id", + "lineColor", + "lineWidth", + "pointRadius", + "yAxisId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "X-range layer", + "properties": { + "chartType": { + "const": "x-range", + "description": "Layer that visualizes range data as full-height colored rectangles" + }, + "colorScheme": { + "description": "https://github.com/d3/d3-scale-chromatic/blob/main/README.md#api-reference", + "enum": [ + "schemeAccent", + "schemeCategory10", + "schemeDark2", + "schemePaired", + "schemePastel1", + "schemePastel2", + "schemeSet1", + "schemeSet2", + "schemeSet3", + "schemeTableau10" + ], + "type": "string" + }, + "filter": { + "$ref": "#/definitions/filterResource" + }, + "id": { + "$ref": "#/definitions/id" + }, + "name": { + "description": "Name of the layer", + "type": "string" + }, + "opacity": { + "type": "number" + }, + "showAsLinePlot": { + "type": "boolean" + }, + "yAxisId": { + "$ref": "#/definitions/yAxisId" + } + }, + "required": ["chartType", "colorScheme", "filter", "id", "opacity", "yAxisId"], + "type": "object" + } + ] + }, + "type": "array" + }, + "name": { + "description": "Name of the row", + "type": "string" + }, + "yAxes": { + "description": "Row y-axes definitions", + "items": { + "additionalProperties": false, + "properties": { + "color": { + "$ref": "#/definitions/color" + }, + "domainFitMode": { + "description": "Describes the domain fitting behavior for the axis", + "enum": ["fitPlan", "fitTimeWindow", "manual"], + "type": "string" + }, + "id": { + "$ref": "#/definitions/id" + }, + "label": { + "$ref": "#/definitions/label" + }, + "renderTickLines": { + "description": "If true render horizontal lines for each y axis tick, if false do not render them", + "type": "boolean" + }, + "scaleDomain": { + "description": "Min and max values of the axis domain: [min, max], only used if domainFitMode is set to manual", + "items": { + "type": "number" + }, + "type": "array" + }, + "tickCount": { + "description": "Number of ticks on the axis", + "minimum": 0, + "type": "number" + } + }, + "required": ["color", "id", "label", "tickCount"], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "autoAdjustHeight", + "expanded", + "height", + "horizontalGuides", + "id", + "layers", + "name", + "yAxes" + ], + "type": "object" + }, + "type": "array" + }, + "verticalGuides": { + "description": "Timeline vertical guide definitions", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/id" + }, + "label": { + "$ref": "#/definitions/label" + }, + "timestamp": { + "description": "DOY timestamp (YYYY-DDDThh:mm:ss) of the vertical guide", + "type": "string" + } + }, + "required": ["id", "label", "timestamp"], + "type": "object" + }, + "type": "array" + } + }, + "required": ["id", "marginLeft", "marginRight", "rows", "verticalGuides"], + "type": "object" + }, + "type": "array" + } + }, + "required": ["activityDirectivesTable", "activitySpansTable", "grid", "iFrames", "timelines"], + "type": "object" + } + }, + "required": ["plan"], + "title": "View", + "type": "object" +} diff --git a/src/schemas/ui-view-schema.json b/src/schemas/ui-view-schema-v1.json similarity index 92% rename from src/schemas/ui-view-schema.json rename to src/schemas/ui-view-schema-v1.json index e8c84c8c7b..5676167e81 100644 --- a/src/schemas/ui-view-schema.json +++ b/src/schemas/ui-view-schema-v1.json @@ -1,5 +1,5 @@ { - "$id": "https://github.com/NASA-AMMOS/aerie-ui/blob/develop/src/schemas/ui-view-schema.json", + "$id": "https://github.com/NASA-AMMOS/aerie-ui/blob/develop/src/schemas/ui-view-schema-v1.json", "$schema": "http://json-schema.org/draft-07/schema", "additionalProperties": false, "definitions": { @@ -101,6 +101,7 @@ }, "description": "JSON schema definition used for configuring the Aerie UI", "properties": { + "version": { "type": "number" }, "plan": { "additionalProperties": false, "description": "View configuration for a plan", @@ -234,34 +235,6 @@ "items": { "additionalProperties": false, "properties": { - "activityOptions": { - "additionalProperties": false, - "description": "Defines the options used for rendering activities on the timeline", - "properties": { - "activityHeight": { - "description": "Height of activity subrows", - "type": "number", - "minimum": 12 - }, - "composition": { - "description": "Whether or not to display only directives, only spans, or both in the row", - "enum": ["directives", "spans", "both"] - }, - "displayMode": { - "description": "Describes the primary method in which activities are visualized within this row", - "enum": ["grouped", "compact"] - }, - "hierarchyMode": { - "description": "If 'directive' the activities are grouped starting with directive types, if 'flat' activities are grouped by type regardless of hierarchy", - "enum": ["directive", "flat"] - }, - "labelVisibility": { - "description": "Activity text label behavior", - "enum": ["on", "off", "auto"] - } - }, - "required": ["composition", "displayMode", "hierarchyMode", "labelVisibility"] - }, "autoAdjustHeight": { "description": "If true the row height is set automatically to fit the row content", "type": "boolean" @@ -311,7 +284,8 @@ "description": "External event text label behavior", "enum": ["on", "off", "auto"] } - } + }, + "type": "object" }, "height": { "description": "Height of the row in pixels", @@ -356,11 +330,6 @@ "activityColor": { "$ref": "#/definitions/color" }, - "activityHeight": { - "description": "Height of each activity", - "type": "number", - "$comment": "deprecated" - }, "chartType": { "const": "activity", "description": "Layer that visualizes activities" @@ -397,7 +366,7 @@ "$ref": "#/definitions/yAxisId" } }, - "required": ["activityColor", "activityHeight", "chartType", "filter", "id", "yAxisId"], + "required": ["activityColor", "chartType", "filter", "id", "yAxisId"], "type": "object" }, { @@ -533,7 +502,8 @@ "yAxisId": { "$ref": "#/definitions/yAxisId" } - } + }, + "type": "object" } ] }, @@ -631,7 +601,7 @@ "type": "object" } }, - "required": ["plan"], + "required": ["plan", "version"], "title": "View", "type": "object" } diff --git a/src/tests/mocks/view/v0/view-migrated.json b/src/tests/mocks/view/v0/view-migrated.json new file mode 100644 index 0000000000..73b5c79c98 --- /dev/null +++ b/src/tests/mocks/view/v0/view-migrated.json @@ -0,0 +1,940 @@ +{ + "plan": { + "grid": { + "leftSplit": false, + "leftHidden": false, + "rightSplit": false, + "columnSizes": "1fr 3px 3fr 3px 1fr", + "middleSplit": true, + "rightHidden": false, + "leftRowSizes": "1fr", + "rightRowSizes": "1fr", + "middleRowSizes": "2fr 3px 1fr", + "leftComponentTop": "TimelineItemsPanel", + "rightComponentTop": "ActivityFormPanel", + "leftComponentBottom": "ConstraintsPanel", + "rightComponentBottom": "TimelineEditorPanel", + "middleComponentBottom": "ActivityDirectivesTablePanel" + }, + "iFrames": [ + { + "id": 0, + "src": "https://eyes.nasa.gov/apps/mars2020/#/home", + "title": "Mars-2020-EDL" + } + ], + "timelines": [ + { + "id": 0, + "rows": [ + { + "id": 0, + "name": "Activities by Type", + "yAxes": [], + "height": 119, + "layers": [ + { + "id": 0, + "name": "", + "filter": { + "activity": { + "types": [ + "BakeBananaBread", + "BananaNap", + "BiteBanana", + "ChangeProducer", + "child", + "ControllableDurationActivity", + "DecomposingSpawnChild", + "DecomposingSpawnParent", + "DownloadBanana", + "DurationParameterActivity", + "ExceptionActivity", + "grandchild", + "GrowBanana", + "LineCount", + "ParameterTest", + "parent", + "PeelBanana", + "PickBanana", + "RipenBanana", + "ThrowBanana" + ] + } + }, + "yAxisId": null, + "chartType": "activity", + "activityColor": "#fcdd8f" + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "grouped", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": true, + "horizontalGuides": [] + }, + { + "id": 1, + "name": "/data/line_count", + "yAxes": [ + { + "id": 0, + "color": "#1b1d1e", + "label": { + "text": "/data/line_count" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 1, + "name": "", + "filter": { + "resource": { + "names": ["/data/line_count"] + } + }, + "yAxisId": 0, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 2, + "name": "/flag", + "yAxes": [ + { + "id": 1, + "color": "#1b1d1e", + "label": { + "text": "/flag" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 2, + "name": "", + "filter": { + "resource": { + "names": ["/flag"] + } + }, + "opacity": 0.8, + "yAxisId": 1, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 3, + "name": "/flag/conflicted", + "yAxes": [ + { + "id": 2, + "color": "#1b1d1e", + "label": { + "text": "/flag/conflicted" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 3, + "name": "", + "filter": { + "resource": { + "names": ["/flag/conflicted"] + } + }, + "opacity": 0.8, + "yAxisId": 2, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 4, + "name": "/fruit", + "yAxes": [ + { + "id": 3, + "color": "#1b1d1e", + "label": { + "text": "/fruit (bananas)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 4, + "name": "", + "filter": { + "resource": { + "names": ["/fruit"] + } + }, + "yAxisId": 3, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 5, + "name": "/peel", + "yAxes": [ + { + "id": 4, + "color": "#1b1d1e", + "label": { + "text": "/peel (kg)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 5, + "name": "", + "filter": { + "resource": { + "names": ["/peel"] + } + }, + "yAxisId": 4, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 6, + "name": "/plant", + "yAxes": [ + { + "id": 5, + "color": "#1b1d1e", + "label": { + "text": "/plant (count)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 6, + "name": "", + "filter": { + "resource": { + "names": ["/plant"] + } + }, + "yAxisId": 5, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 7, + "name": "/producer", + "yAxes": [ + { + "id": 6, + "color": "#1b1d1e", + "label": { + "text": "/producer" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 7, + "name": "", + "filter": { + "resource": { + "names": ["/producer"] + } + }, + "opacity": 0.8, + "yAxisId": 6, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + } + ], + "marginLeft": 250, + "marginRight": 30, + "verticalGuides": [] + } + ], + "activitySpansTable": { + "columnDefs": [ + { + "field": "id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "field": "dataset_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dataset ID" + }, + { + "field": "parent_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Parent ID" + }, + { + "field": "type", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Type" + }, + { + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "field": "duration", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Duration" + } + ], + "columnStates": [], + "autoSizeColumns": "fill" + }, + "simulationEventsTable": { + "columnDefs": [ + { + "field": "id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "field": "dataset_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dataset ID" + }, + { + "hide": true, + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "hide": true, + "field": "dense_time", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dense Time" + }, + { + "field": "topic", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Topic" + }, + { + "field": "value", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Value" + } + ], + "columnStates": [], + "autoSizeColumns": "fit" + }, + "activityDirectivesTable": { + "columnDefs": [ + { + "sort": null, + "colId": "arguments", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "id", + "width": 80, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "sort": null, + "colId": "last_modified_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "metadata", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "name", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Name" + }, + { + "field": "type", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Type" + }, + { + "sort": null, + "colId": "source_scheduling_goal_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "sort": null, + "colId": "tags", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "type", + "pivot": false, + "width": 280, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "anchor_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "applied_preset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "anchored_to_start", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "derived_start_time", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Absolute Start Time (UTC)" + }, + { + "sort": null, + "colId": "start_offset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "hide": true, + "field": "created_at", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Created At (UTC)" + } + ], + "columnStates": [ + { + "flex": null, + "hide": false, + "sort": null, + "colId": "errorCounts", + "pivot": false, + "width": 70, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "anchor_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "anchored_to_start", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "applied_preset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "arguments", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "created_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "created_by", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "id", + "pivot": false, + "width": 85.20000000000005, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "last_modified_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "last_modified_by", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "metadata", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "name", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "source_scheduling_goal_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "start_offset", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "start_time_ms", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "tags", + "pivot": false, + "width": 220, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "type", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + } + ], + "autoSizeColumns": "fill" + } + }, + "version": 1 +} diff --git a/src/tests/mocks/view/v0/view.json b/src/tests/mocks/view/v0/view.json new file mode 100644 index 0000000000..6aaa2209c8 --- /dev/null +++ b/src/tests/mocks/view/v0/view.json @@ -0,0 +1,900 @@ +{ + "plan": { + "activityDirectivesTable": { + "autoSizeColumns": "fill", + "columnDefs": [ + { + "aggFunc": null, + "colId": "arguments", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "field": "id", + "filter": "text", + "headerName": "ID", + "resizable": true, + "sortable": true, + "width": 80 + }, + { + "aggFunc": null, + "colId": "last_modified_at", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "aggFunc": null, + "colId": "metadata", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "field": "name", + "filter": "text", + "headerName": "Name", + "resizable": true, + "sortable": true, + "width": 200 + }, + { + "field": "type", + "filter": "text", + "headerName": "Type", + "resizable": true, + "sortable": true + }, + { + "aggFunc": null, + "colId": "source_scheduling_goal_id", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "field": "start_offset", + "filter": "text", + "headerName": "Start Offset", + "resizable": true, + "sortable": true + }, + { + "aggFunc": null, + "colId": "tags", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "aggFunc": null, + "colId": "type", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 280 + }, + { + "aggFunc": null, + "colId": "anchor_id", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "aggFunc": null, + "colId": "applied_preset", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "aggFunc": null, + "colId": "anchored_to_start", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "field": "derived_start_time", + "filter": "text", + "headerName": "Absolute Start Time (UTC)", + "resizable": true, + "sortable": true, + "width": 200 + }, + { + "aggFunc": null, + "colId": "start_offset", + "pinned": null, + "pivot": false, + "pivotIndex": null, + "rowGroup": false, + "rowGroupIndex": null, + "sort": null, + "sortIndex": null, + "width": 200 + }, + { + "field": "created_at", + "filter": "text", + "headerName": "Created At (UTC)", + "hide": true, + "resizable": true, + "sortable": true, + "width": 200 + } + ], + "columnStates": [ + { + "colId": "errorCounts", + "width": 70, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "anchor_id", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "anchored_to_start", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "applied_preset", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "arguments", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "created_at", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "created_by", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "id", + "width": 85.20000000000005, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "last_modified_at", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "last_modified_by", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "metadata", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "name", + "width": 211, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "source_scheduling_goal_id", + "width": 200, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "start_offset", + "width": 211, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "start_time_ms", + "width": 211, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "tags", + "width": 220, + "hide": true, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + }, + { + "colId": "type", + "width": 211, + "hide": false, + "pinned": null, + "sort": null, + "sortIndex": null, + "aggFunc": null, + "rowGroup": false, + "rowGroupIndex": null, + "pivot": false, + "pivotIndex": null, + "flex": null + } + ] + }, + "activitySpansTable": { + "autoSizeColumns": "fill", + "columnDefs": [ + { + "field": "id", + "filter": "text", + "headerName": "ID", + "resizable": true, + "sortable": true + }, + { + "field": "dataset_id", + "filter": "text", + "headerName": "Dataset ID", + "resizable": true, + "sortable": true + }, + { + "field": "parent_id", + "filter": "text", + "headerName": "Parent ID", + "resizable": true, + "sortable": true + }, + { + "field": "type", + "filter": "text", + "headerName": "Type", + "resizable": true, + "sortable": true + }, + { + "field": "start_offset", + "filter": "text", + "headerName": "Start Offset", + "resizable": true, + "sortable": true + }, + { + "field": "duration", + "filter": "text", + "headerName": "Duration", + "resizable": true, + "sortable": true + } + ], + "columnStates": [] + }, + "grid": { + "columnSizes": "1fr 3px 3fr 3px 1fr", + "leftComponentBottom": "ConstraintViolationsPanel", + "leftComponentTop": "ActivityTypesPanel", + "leftHidden": false, + "leftRowSizes": "1fr", + "leftSplit": false, + "middleComponentBottom": "ActivityDirectivesTablePanel", + "middleRowSizes": "2fr 3px 1fr", + "middleSplit": true, + "rightComponentBottom": "TimelineEditorPanel", + "rightComponentTop": "ActivityFormPanel", + "rightHidden": false, + "rightRowSizes": "1fr", + "rightSplit": false + }, + "iFrames": [ + { + "id": 0, + "src": "https://eyes.nasa.gov/apps/mars2020/#/home", + "title": "Mars-2020-EDL" + } + ], + "simulationEventsTable": { + "autoSizeColumns": "fit", + "columnDefs": [ + { + "field": "id", + "filter": "text", + "headerName": "ID", + "resizable": true, + "sortable": true + }, + { + "field": "dataset_id", + "filter": "text", + "headerName": "Dataset ID", + "resizable": true, + "sortable": true + }, + { + "field": "start_offset", + "filter": "text", + "headerName": "Start Offset", + "hide": true, + "resizable": true, + "sortable": true + }, + { + "field": "dense_time", + "filter": "text", + "headerName": "Dense Time", + "hide": true, + "resizable": true, + "sortable": true + }, + { + "field": "topic", + "filter": "text", + "headerName": "Topic", + "resizable": true, + "sortable": true + }, + { + "field": "value", + "filter": "text", + "headerName": "Value", + "resizable": true, + "sortable": true + } + ], + "columnStates": [] + }, + "timelines": [ + { + "id": 0, + "marginLeft": 250, + "marginRight": 30, + "rows": [ + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "grouped", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": true, + "expanded": true, + "height": 119, + "horizontalGuides": [], + "id": 0, + "layers": [ + { + "activityColor": "#fcdd8f", + "activityHeight": 16, + "chartType": "activity", + "filter": { + "activity": { + "types": [ + "BakeBananaBread", + "BananaNap", + "BiteBanana", + "ChangeProducer", + "child", + "ControllableDurationActivity", + "DecomposingSpawnChild", + "DecomposingSpawnParent", + "DownloadBanana", + "DurationParameterActivity", + "ExceptionActivity", + "grandchild", + "GrowBanana", + "LineCount", + "ParameterTest", + "parent", + "PeelBanana", + "PickBanana", + "RipenBanana", + "ThrowBanana" + ] + } + }, + "id": 0, + "name": "", + "yAxisId": null + } + ], + "name": "Activities by Type", + "yAxes": [] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 1, + "layers": [ + { + "chartType": "line", + "filter": { + "resource": { + "names": ["/data/line_count"] + } + }, + "id": 1, + "lineColor": "#283593", + "lineWidth": 1, + "name": "", + "pointRadius": 2, + "yAxisId": 0 + } + ], + "name": "/data/line_count", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 0, + "label": { + "text": "/data/line_count" + }, + "renderTickLines": true, + "tickCount": 5 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 2, + "layers": [ + { + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "filter": { + "resource": { + "names": ["/flag"] + } + }, + "id": 2, + "name": "", + "opacity": 0.8, + "showAsLinePlot": false, + "yAxisId": 1 + } + ], + "name": "/flag", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 1, + "label": { + "text": "/flag" + }, + "renderTickLines": true, + "tickCount": 0 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 3, + "layers": [ + { + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "filter": { + "resource": { + "names": ["/flag/conflicted"] + } + }, + "id": 3, + "name": "", + "opacity": 0.8, + "showAsLinePlot": false, + "yAxisId": 2 + } + ], + "name": "/flag/conflicted", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 2, + "label": { + "text": "/flag/conflicted" + }, + "renderTickLines": true, + "tickCount": 0 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 4, + "layers": [ + { + "chartType": "line", + "filter": { + "resource": { + "names": ["/fruit"] + } + }, + "id": 4, + "lineColor": "#283593", + "lineWidth": 1, + "name": "", + "pointRadius": 2, + "yAxisId": 3 + } + ], + "name": "/fruit", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 3, + "label": { + "text": "/fruit (bananas)" + }, + "renderTickLines": true, + "tickCount": 5 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 5, + "layers": [ + { + "chartType": "line", + "filter": { + "resource": { + "names": ["/peel"] + } + }, + "id": 5, + "lineColor": "#283593", + "lineWidth": 1, + "name": "", + "pointRadius": 2, + "yAxisId": 4 + } + ], + "name": "/peel", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 4, + "label": { + "text": "/peel (kg)" + }, + "renderTickLines": true, + "tickCount": 5 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 6, + "layers": [ + { + "chartType": "line", + "filter": { + "resource": { + "names": ["/plant"] + } + }, + "id": 6, + "lineColor": "#283593", + "lineWidth": 1, + "name": "", + "pointRadius": 2, + "yAxisId": 5 + } + ], + "name": "/plant", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 5, + "label": { + "text": "/plant (count)" + }, + "renderTickLines": true, + "tickCount": 5 + } + ] + }, + { + "activityOptions": { + "activityHeight": 16, + "composition": "both", + "displayMode": "compact", + "hierarchyMode": "flat", + "labelVisibility": "auto" + }, + "autoAdjustHeight": false, + "expanded": true, + "height": 100, + "horizontalGuides": [], + "id": 7, + "layers": [ + { + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "filter": { + "resource": { + "names": ["/producer"] + } + }, + "id": 7, + "name": "", + "opacity": 0.8, + "showAsLinePlot": false, + "yAxisId": 6 + } + ], + "name": "/producer", + "yAxes": [ + { + "color": "#1b1d1e", + "domainFitMode": "fitTimeWindow", + "id": 6, + "label": { + "text": "/producer" + }, + "renderTickLines": true, + "tickCount": 0 + } + ] + } + ], + "verticalGuides": [] + } + ] + } +} diff --git a/src/tests/mocks/view/v1/view.json b/src/tests/mocks/view/v1/view.json new file mode 100644 index 0000000000..73b5c79c98 --- /dev/null +++ b/src/tests/mocks/view/v1/view.json @@ -0,0 +1,940 @@ +{ + "plan": { + "grid": { + "leftSplit": false, + "leftHidden": false, + "rightSplit": false, + "columnSizes": "1fr 3px 3fr 3px 1fr", + "middleSplit": true, + "rightHidden": false, + "leftRowSizes": "1fr", + "rightRowSizes": "1fr", + "middleRowSizes": "2fr 3px 1fr", + "leftComponentTop": "TimelineItemsPanel", + "rightComponentTop": "ActivityFormPanel", + "leftComponentBottom": "ConstraintsPanel", + "rightComponentBottom": "TimelineEditorPanel", + "middleComponentBottom": "ActivityDirectivesTablePanel" + }, + "iFrames": [ + { + "id": 0, + "src": "https://eyes.nasa.gov/apps/mars2020/#/home", + "title": "Mars-2020-EDL" + } + ], + "timelines": [ + { + "id": 0, + "rows": [ + { + "id": 0, + "name": "Activities by Type", + "yAxes": [], + "height": 119, + "layers": [ + { + "id": 0, + "name": "", + "filter": { + "activity": { + "types": [ + "BakeBananaBread", + "BananaNap", + "BiteBanana", + "ChangeProducer", + "child", + "ControllableDurationActivity", + "DecomposingSpawnChild", + "DecomposingSpawnParent", + "DownloadBanana", + "DurationParameterActivity", + "ExceptionActivity", + "grandchild", + "GrowBanana", + "LineCount", + "ParameterTest", + "parent", + "PeelBanana", + "PickBanana", + "RipenBanana", + "ThrowBanana" + ] + } + }, + "yAxisId": null, + "chartType": "activity", + "activityColor": "#fcdd8f" + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "grouped", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": true, + "horizontalGuides": [] + }, + { + "id": 1, + "name": "/data/line_count", + "yAxes": [ + { + "id": 0, + "color": "#1b1d1e", + "label": { + "text": "/data/line_count" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 1, + "name": "", + "filter": { + "resource": { + "names": ["/data/line_count"] + } + }, + "yAxisId": 0, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 2, + "name": "/flag", + "yAxes": [ + { + "id": 1, + "color": "#1b1d1e", + "label": { + "text": "/flag" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 2, + "name": "", + "filter": { + "resource": { + "names": ["/flag"] + } + }, + "opacity": 0.8, + "yAxisId": 1, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 3, + "name": "/flag/conflicted", + "yAxes": [ + { + "id": 2, + "color": "#1b1d1e", + "label": { + "text": "/flag/conflicted" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 3, + "name": "", + "filter": { + "resource": { + "names": ["/flag/conflicted"] + } + }, + "opacity": 0.8, + "yAxisId": 2, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 4, + "name": "/fruit", + "yAxes": [ + { + "id": 3, + "color": "#1b1d1e", + "label": { + "text": "/fruit (bananas)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 4, + "name": "", + "filter": { + "resource": { + "names": ["/fruit"] + } + }, + "yAxisId": 3, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 5, + "name": "/peel", + "yAxes": [ + { + "id": 4, + "color": "#1b1d1e", + "label": { + "text": "/peel (kg)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 5, + "name": "", + "filter": { + "resource": { + "names": ["/peel"] + } + }, + "yAxisId": 4, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 6, + "name": "/plant", + "yAxes": [ + { + "id": 5, + "color": "#1b1d1e", + "label": { + "text": "/plant (count)" + }, + "tickCount": 5, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 6, + "name": "", + "filter": { + "resource": { + "names": ["/plant"] + } + }, + "yAxisId": 5, + "chartType": "line", + "lineColor": "#283593", + "lineWidth": 1, + "pointRadius": 2 + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + }, + { + "id": 7, + "name": "/producer", + "yAxes": [ + { + "id": 6, + "color": "#1b1d1e", + "label": { + "text": "/producer" + }, + "tickCount": 0, + "domainFitMode": "fitTimeWindow", + "renderTickLines": true + } + ], + "height": 100, + "layers": [ + { + "id": 7, + "name": "", + "filter": { + "resource": { + "names": ["/producer"] + } + }, + "opacity": 0.8, + "yAxisId": 6, + "chartType": "x-range", + "colorScheme": "schemeTableau10", + "showAsLinePlot": false + } + ], + "expanded": true, + "discreteOptions": { + "height": 16, + "displayMode": "compact", + "activityOptions": { + "composition": "both", + "hierarchyMode": "flat" + }, + "labelVisibility": "auto", + "externalEventOptions": { + "groupBy": "event_type_name" + } + }, + "autoAdjustHeight": false, + "horizontalGuides": [] + } + ], + "marginLeft": 250, + "marginRight": 30, + "verticalGuides": [] + } + ], + "activitySpansTable": { + "columnDefs": [ + { + "field": "id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "field": "dataset_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dataset ID" + }, + { + "field": "parent_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Parent ID" + }, + { + "field": "type", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Type" + }, + { + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "field": "duration", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Duration" + } + ], + "columnStates": [], + "autoSizeColumns": "fill" + }, + "simulationEventsTable": { + "columnDefs": [ + { + "field": "id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "field": "dataset_id", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dataset ID" + }, + { + "hide": true, + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "hide": true, + "field": "dense_time", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Dense Time" + }, + { + "field": "topic", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Topic" + }, + { + "field": "value", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Value" + } + ], + "columnStates": [], + "autoSizeColumns": "fit" + }, + "activityDirectivesTable": { + "columnDefs": [ + { + "sort": null, + "colId": "arguments", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "id", + "width": 80, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "ID" + }, + { + "sort": null, + "colId": "last_modified_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "metadata", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "name", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Name" + }, + { + "field": "type", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Type" + }, + { + "sort": null, + "colId": "source_scheduling_goal_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "start_offset", + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Start Offset" + }, + { + "sort": null, + "colId": "tags", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "type", + "pivot": false, + "width": 280, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "anchor_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "applied_preset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "sort": null, + "colId": "anchored_to_start", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "field": "derived_start_time", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Absolute Start Time (UTC)" + }, + { + "sort": null, + "colId": "start_offset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "hide": true, + "field": "created_at", + "width": 200, + "filter": "text", + "sortable": true, + "resizable": true, + "headerName": "Created At (UTC)" + } + ], + "columnStates": [ + { + "flex": null, + "hide": false, + "sort": null, + "colId": "errorCounts", + "pivot": false, + "width": 70, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "anchor_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "anchored_to_start", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "applied_preset", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "arguments", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "created_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "created_by", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "id", + "pivot": false, + "width": 85.20000000000005, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "last_modified_at", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "last_modified_by", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "metadata", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "name", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "source_scheduling_goal_id", + "pivot": false, + "width": 200, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "start_offset", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "start_time_ms", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": true, + "sort": null, + "colId": "tags", + "pivot": false, + "width": 220, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + }, + { + "flex": null, + "hide": false, + "sort": null, + "colId": "type", + "pivot": false, + "width": 211, + "pinned": null, + "aggFunc": null, + "rowGroup": false, + "sortIndex": null, + "pivotIndex": null, + "rowGroupIndex": null + } + ], + "autoSizeColumns": "fill" + } + }, + "version": 1 +} diff --git a/src/types/timeline.ts b/src/types/timeline.ts index 51830b5495..4f2ce30b08 100644 --- a/src/types/timeline.ts +++ b/src/types/timeline.ts @@ -25,7 +25,6 @@ export type DiscreteTreeExpansionMap = Record; export interface ActivityLayer extends Layer { activityColor: string; - activityHeight: number; // @deprecated TODO how should we deprecate view properties? } export interface ExternalEventLayer extends Layer { externalEventColor: string; diff --git a/src/types/view.ts b/src/types/view.ts index 55e989850e..5f50669406 100644 --- a/src/types/view.ts +++ b/src/types/view.ts @@ -87,6 +87,7 @@ export type ViewDefinition = { simulationEventsTable: ViewTable; timelines: Timeline[]; }; + version: number; }; export type View = { diff --git a/src/utilities/effects.ts b/src/utilities/effects.ts index cca500bb5c..d73c0b3e35 100644 --- a/src/utilities/effects.ts +++ b/src/utilities/effects.ts @@ -251,7 +251,12 @@ import { } from './time'; import { createRow, duplicateRow } from './timeline'; import { showFailureToast, showSuccessToast } from './toast'; -import { generateDefaultView, validateViewJSONAgainstSchema } from './view'; +import { + applyViewDefinitionMigrations, + applyViewMigrations, + generateDefaultView, + validateViewJSONAgainstSchema, +} from './view'; function throwPermissionError(attemptedAction: string): never { throw Error(`You do not have permission to: ${attemptedAction}.`); @@ -4638,11 +4643,12 @@ const effects = { /** * Try and get the view from the query parameters, otherwise check if there's a default view set at the - * mission model level, otherwise just return a generated default view. + * mission model level, otherwise just return a generated default view. Performs view migration if requested. */ async getView( query: URLSearchParams | null, user: User | null, + migrate: boolean = true, activityTypes: ActivityType[] = [], resourceTypes: ResourceType[] = [], externalEventTypes: ExternalEventType[] = [], @@ -4652,15 +4658,40 @@ const effects = { if (query !== null) { const viewIdAsNumber = getSearchParameterNumber(SearchParameters.VIEW_ID, query); + // Derive view from url or model default + let view; if (viewIdAsNumber !== null) { const data = await reqHasura(gql.GET_VIEW, { id: viewIdAsNumber }, user); - const { view } = data; + const { view: fetchedView } = data; + view = fetchedView; + } else if (defaultView !== null && defaultView !== undefined) { + view = defaultView; + } - if (view !== null) { + if (view) { + // Return view if not asked to migrate the view + if (!migrate) { return view; } - } else if (defaultView !== null && defaultView !== undefined) { - return defaultView; + + // Otherwise perform any needed migrations + const { migratedView, error, anyMigrationsApplied } = await applyViewMigrations(view); + if (migratedView && anyMigrationsApplied) { + await effects.updateView( + migratedView.id, + { definition: migratedView.definition }, + 'View Automatically Migrated', + user, + ); + } + + // If migration failed catch the error and return default view + if (!migratedView) { + catchError('Unable to automatically migrate view', error as Error); + showFailureToast(`Unable to automatically migrate view: ${view.name}`); + } else { + return migratedView; + } } } return generateDefaultView(activityTypes, resourceTypes, externalEventTypes); @@ -4843,10 +4874,14 @@ const effects = { }); const viewJSON = JSON.parse(viewFileString); - const { errors, valid } = await effects.validateViewJSON(viewJSON); + const { migratedViewDefinition, error } = await applyViewDefinitionMigrations(viewJSON); + if (error) { + return { definition: null, errors: [(error.stack || error).toString()] }; + } + const { errors, valid } = await effects.validateViewJSON(migratedViewDefinition); if (valid) { - return { definition: viewJSON }; + return { definition: migratedViewDefinition }; } else { return { definition: null, @@ -6288,7 +6323,7 @@ const effects = { } }, - async updateView(id: number, view: Partial, user: User | null): Promise { + async updateView(id: number, view: Partial, message: string | null, user: User | null): Promise { try { if (!queryPermissions.UPDATE_VIEW(user, { owner: view.owner ?? null })) { throwPermissionError('update this view'); @@ -6296,7 +6331,7 @@ const effects = { const data = await reqHasura>(gql.UPDATE_VIEW, { id, view }, user); if (data.updatedView) { - showSuccessToast('View Updated Successfully'); + showSuccessToast(message ?? 'View Updated Successfully'); return true; } else { throw Error(`Unable to update view with ID: "${id}"`); diff --git a/src/utilities/timeline.ts b/src/utilities/timeline.ts index b41c1d6c7c..d46d5d2e1e 100644 --- a/src/utilities/timeline.ts +++ b/src/utilities/timeline.ts @@ -550,7 +550,6 @@ export function createTimelineActivityLayer(timelines: Timeline[], args: Partial return { activityColor: ViewDiscreteLayerColorPresets[0], - activityHeight: 16, chartType: 'activity', filter: { activity: { diff --git a/src/utilities/view.test.ts b/src/utilities/view.test.ts index b40ddb7b5f..657d94592a 100644 --- a/src/utilities/view.test.ts +++ b/src/utilities/view.test.ts @@ -1,5 +1,13 @@ import { describe, expect, test } from 'vitest'; -import { generateDefaultView, validateViewJSONAgainstSchema } from './view'; +import viewV0Migrated from '../tests/mocks/view/v0/view-migrated.json'; +import viewV0 from '../tests/mocks/view/v0/view.json'; +import viewV1 from '../tests/mocks/view/v1/view.json'; +import { + applyViewDefinitionMigrations, + generateDefaultView, + migrateViewDefinitionV0toV1, + validateViewJSONAgainstSchema, +} from './view'; describe('generateDefaultView', () => { test('Should generate a valid view', async () => { @@ -38,3 +46,34 @@ describe('generateDefaultViewWithEvents', () => { expect(layers[0].filter.externalEvent?.event_types).toEqual(['external-event-type_1', 'external-event-type_2']); }); }); + +describe('applyViewDefinitionMigrations', () => { + test('Should migrate a view from v0 -> v1', async () => { + const migratedView = migrateViewDefinitionV0toV1(viewV0 as any); + expect(migratedView).to.deep.eq(viewV0Migrated); + }); +}); + +describe('migrateViewDefinition', () => { + test('Should apply view migrations to an old view', async () => { + const { anyMigrationsApplied, error, migratedViewDefinition } = applyViewDefinitionMigrations(viewV0 as any); + expect(anyMigrationsApplied).toBeTruthy(); + expect(error).toBeNull(); + expect(migratedViewDefinition).to.deep.eq(viewV1); + }); + test('Should apply no view migrations to a migration matching current version', async () => { + const { anyMigrationsApplied, error, migratedViewDefinition } = applyViewDefinitionMigrations(viewV1 as any); + expect(anyMigrationsApplied).toBeFalsy(); + expect(error).toBeNull(); + expect(migratedViewDefinition).to.deep.eq(viewV1); + }); + test('Should return errors if migration fails', async () => { + const invalidView = structuredClone(viewV0); + // @ts-expect-error forcing this to be invalid + invalidView.plan.grid = null; + const { anyMigrationsApplied, error, migratedViewDefinition } = applyViewDefinitionMigrations(invalidView as any); + expect(anyMigrationsApplied).toBeFalsy(); + expect(error).not.toBeNull(); + expect(migratedViewDefinition).toBeNull(); + }); +}); diff --git a/src/utilities/view.ts b/src/utilities/view.ts index 9852a74fbe..d30e7da4d7 100644 --- a/src/utilities/view.ts +++ b/src/utilities/view.ts @@ -1,10 +1,10 @@ import Ajv from 'ajv'; -import { ViewDefaultDiscreteOptions } from '../constants/view'; -import jsonSchema from '../schemas/ui-view-schema.json'; +import { ViewDefaultDiscreteOptions, viewSchemaVersion, viewSchemaVersionName } from '../constants/view'; +import jsonSchema from '../schemas'; import type { ActivityType } from '../types/activity'; import type { ExternalEventType } from '../types/external-event'; import type { ResourceType } from '../types/simulation'; -import type { View, ViewGridColumns, ViewGridRows } from '../types/view'; +import type { View, ViewDefinition, ViewGridColumns, ViewGridRows } from '../types/view'; import { createRow, createTimeline, @@ -375,6 +375,7 @@ export function generateDefaultView( }, timelines, }, + version: viewSchemaVersion, }, id: 0, name: 'Default View', @@ -465,7 +466,12 @@ export function createRowSizes({ row1 = '1fr', row2 = '1fr' }: ViewGridRows, col export function validateViewJSONAgainstSchema(json: any) { try { const ajv = new Ajv(); - const validate = ajv.compile(jsonSchema); + // Ensure json schema is found for the current version + const currentSchema = (jsonSchema as Record)[viewSchemaVersionName]; + if (!currentSchema) { + throw new Error(`Schema not found for version: ${viewSchemaVersionName}`); + } + const validate = ajv.compile(currentSchema); const valid = validate(json); const errors = valid ? [] : validate.errors; return { errors, valid }; @@ -481,3 +487,130 @@ export function downloadView(view: View) { a.download = view.name; a.click(); } + +export async function applyViewMigrations(view: View): Promise<{ + anyMigrationsApplied: boolean; + error: Error | null; + migratedView: View | null; +}> { + try { + const { anyMigrationsApplied, error, migratedViewDefinition } = await applyViewDefinitionMigrations( + view.definition, + ); + if (!migratedViewDefinition || error) { + return { anyMigrationsApplied: false, error, migratedView: null }; + } + const migratedView: View = { ...view, definition: migratedViewDefinition }; + return { anyMigrationsApplied, error, migratedView }; + } catch (error) { + return { anyMigrationsApplied: false, error: error as Error, migratedView: null }; + } +} + +export function applyViewDefinitionMigrations(viewDefinition: ViewDefinition): { + anyMigrationsApplied: boolean; + error: Error | null; + migratedViewDefinition: ViewDefinition | null; +} { + try { + // If the view version does not exist we will consider it to be version 0 + const version = viewDefinition.version ?? 0; + const upMigrations: Record ViewDefinition> = { + 0: migrateViewDefinitionV0toV1, + }; + + // Iterate through versions between view version and latest view version + // and apply any migrations if found + let migratedViewDefinition = viewDefinition; + let anyMigrationsApplied = false; + for (let i = version; i < viewSchemaVersion; i++) { + if (upMigrations[i]) { + migratedViewDefinition = upMigrations[i](viewDefinition); + anyMigrationsApplied = true; + } + } + + return { anyMigrationsApplied, error: null, migratedViewDefinition }; + } catch (error) { + return { anyMigrationsApplied: false, error: error as Error, migratedViewDefinition: null }; + } +} + +export function migrateViewDefinitionV0toV1(viewDefinition: ViewDefinition) { + /* + Summary of migrations: + - External events changes to row activity options + - ActivityTypesPanel rename to TimelineItemsPanel + - ConstraintViolationsPanel rename to ConstraintsPanel + - Remove deprecated ActivityLayer.activityHeight + */ + + const updatedGrid = structuredClone(viewDefinition.plan.grid); + const gridKeysToUpdate = [ + 'leftComponentTop', + 'rightComponentTop', + 'leftComponentBottom', + 'rightComponentBottom', + 'middleComponentBottom', + ]; + const gridKeysToSwap: Record = { + ActivityTypesPanel: 'TimelineItemsPanel', + ConstraintViolationsPanel: 'ConstraintsPanel', + }; + Object.entries(updatedGrid).forEach(([key, value]) => { + if (gridKeysToUpdate.indexOf(key) > -1 && gridKeysToSwap[value as string]) { + // @ts-expect-error cannot resolve types here but this is safe + updatedGrid[key] = gridKeysToSwap[value]; + } + return value; + }); + + return { + ...viewDefinition, + plan: { + ...viewDefinition.plan, + grid: updatedGrid, + timelines: viewDefinition.plan.timelines.map(timeline => { + return { + ...timeline, + rows: timeline.rows.map(row => { + const newRow = structuredClone(row); + // @ts-expect-error deprecated type def + if (row.activityOptions) { + newRow.discreteOptions = { + ...(newRow.discreteOptions ?? {}), + activityOptions: { + // @ts-expect-error deprecated type def + composition: newRow.activityOptions.composition, + // @ts-expect-error deprecated type def + hierarchyMode: newRow.activityOptions.hierarchyMode, + }, + // @ts-expect-error deprecated type def + displayMode: newRow.activityOptions.displayMode, + externalEventOptions: { + groupBy: 'event_type_name', + }, + // @ts-expect-error deprecated type def + height: newRow.activityOptions.activityHeight, + // @ts-expect-error deprecated type def + labelVisibility: newRow.activityOptions.labelVisibility, + }; + // @ts-expect-error deprecated type def + delete newRow.activityOptions; + } + newRow.layers = newRow.layers.map(layer => { + const newLayer = structuredClone(layer); + if (newLayer.chartType === 'activity') { + // @ts-expect-error deprecated type def + delete newLayer.activityHeight; + } + return newLayer; + }); + return newRow; + }), + }; + }), + }, + version: 1, + }; +}