From 6ceab051fc0e1f7e71659a74817a3ce96f0416d6 Mon Sep 17 00:00:00 2001 From: Jon Lamb Date: Fri, 8 Mar 2024 09:54:13 -0800 Subject: [PATCH 1/2] Add no-content messaging to the tree views --- vscode/src/events.ts | 10 ++++++++++ vscode/src/experiments.ts | 11 +++++++++++ vscode/src/mutations.ts | 11 +++++++++++ vscode/src/mutators.ts | 11 +++++++++++ vscode/src/segments.ts | 11 +++++++++++ vscode/src/specs.ts | 11 +++++++++++ vscode/src/timelines.ts | 10 ++++++++++ 7 files changed, 75 insertions(+) diff --git a/vscode/src/events.ts b/vscode/src/events.ts index 10b3958..b48ffd9 100644 --- a/vscode/src/events.ts +++ b/vscode/src/events.ts @@ -62,6 +62,16 @@ export class EventsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = "Select a timeline to view its events."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: EventsTreeItemData): Promise { if (!this.selectedTimelineId) { return []; } diff --git a/vscode/src/experiments.ts b/vscode/src/experiments.ts index 0a6aa47..c2cb566 100644 --- a/vscode/src/experiments.ts +++ b/vscode/src/experiments.ts @@ -203,6 +203,17 @@ export class ExperimentsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = + "No experiments available. Create a new experiment file or upload an existing one to get started."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: ExperimentsTreeItemData): Promise { if (!element) { const experimentNames = await this.apiClient.experiments().list(); const items = await Promise.all( diff --git a/vscode/src/mutations.ts b/vscode/src/mutations.ts index 2bfec92..76ad259 100644 --- a/vscode/src/mutations.ts +++ b/vscode/src/mutations.ts @@ -113,6 +113,17 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = + "The active data scope doesn't contain any mutations. Create a mutation in the Mutators view or the Experiments view."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: MutationsTreeItemData): Promise { if (this.uiState.getFilterBySelectedMutator() && this.selectedMutatorId == null) { // Need a selected mutator to populate with return []; diff --git a/vscode/src/mutators.ts b/vscode/src/mutators.ts index 687ca4e..b799cc9 100644 --- a/vscode/src/mutators.ts +++ b/vscode/src/mutators.ts @@ -177,6 +177,17 @@ export class MutatorsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = + "The active data scope doesn't contain any mutators. Select a different data scope or refresh the view after mutators have announced themselves."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: MutatorsTreeItemData): Promise { if (!element) { this.data = []; diff --git a/vscode/src/segments.ts b/vscode/src/segments.ts index 9eab123..c2035e7 100644 --- a/vscode/src/segments.ts +++ b/vscode/src/segments.ts @@ -92,6 +92,17 @@ export class SegmentsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.activeView.message = + "The active workspace contains no segments. Record some data using one of our provided reflector plugins or the Auxon SDK."; + } else { + this.activeView.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: SegmentTreeItemData): Promise { // only the root has children if (element != null) { return []; diff --git a/vscode/src/specs.ts b/vscode/src/specs.ts index c920541..8fcde2c 100644 --- a/vscode/src/specs.ts +++ b/vscode/src/specs.ts @@ -143,6 +143,17 @@ export class SpecsTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = + "No specs available. Create a new SpeQTr file or upload an existing one to get started."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: SpecsTreeItemData): Promise { if (!element) { this.data = []; const specs = await this.apiClient.specs().list(); diff --git a/vscode/src/timelines.ts b/vscode/src/timelines.ts index d000336..38c8ea2 100644 --- a/vscode/src/timelines.ts +++ b/vscode/src/timelines.ts @@ -92,6 +92,16 @@ export class TimelinesTreeDataProvider implements vscode.TreeDataProvider { + const children = await this.getChildrenInner(element); + if (children.length === 0) { + this.view.message = "Select one or more segments to view their timelines."; + } else { + this.view.message = undefined; + } + return children; + } + + private async getChildrenInner(element?: TimelineTreeItemData): Promise { if (element) { return element.children(); } From 19ed1ad172a30c72ab28c2c204f7eae96903fa86 Mon Sep 17 00:00:00 2001 From: Jon Lamb Date: Fri, 8 Mar 2024 09:55:46 -0800 Subject: [PATCH 2/2] Update openapi schema --- vscode/src/generated/src/modality-api.ts | 52 +++++++- vscode/src/modality-api.json | 149 ++++++++++++++++++++++- 2 files changed, 199 insertions(+), 2 deletions(-) diff --git a/vscode/src/generated/src/modality-api.ts b/vscode/src/generated/src/modality-api.ts index 13aebc0..830c4ea 100644 --- a/vscode/src/generated/src/modality-api.ts +++ b/vscode/src/generated/src/modality-api.ts @@ -280,6 +280,26 @@ export type webhooks = Record; export interface components { schemas: { + ApiError: "Internal" | { + NotFound: { + kind_of_thing: string; + specific_thing: string; + }; + } | { + InvalidRequest: string; + } | { + InvalidParameter: { + parameter: string; + reason: string; + }; + } | { + InvalidParameterLocated: { + located_errors: components["schemas"]["LocatedErrors"]; + parameter: string; + }; + } | { + AuthError: components["schemas"]["TokenAuthError"]; + }; AttrVal: string | number | boolean | { TimelineId?: components["schemas"]["TimelineId"]; } | { @@ -470,6 +490,16 @@ export interface components { */ count?: number | null; }; + LocatedError: { + end: number; + message: string; + start: number; + }; + /** @description A wire-compatible encoding for located errors */ + LocatedErrors: { + errors: components["schemas"]["LocatedError"][]; + text: string; + }; LogicalTime: number[]; /** * @description A serialization helper type, for when you actually want Option. (We're @@ -678,6 +708,8 @@ export interface components { /** @description Internal Server Error */ Internal: string; }]>; + /** @enum {string} */ + TokenAuthError: "TokenMissing" | "TokenNotValid" | "TokenUnderPermitted"; /** * @description Stringy representation of an unparsed, unstructured DSL for expressing how to filter mutators, * likely through attribute evaluation. @@ -1660,14 +1692,32 @@ export interface operations { "application/json": components["schemas"]["Workspace"][]; }; }; + /** @description Invalid Parameter */ + 400: { + content: { + "application/json": components["schemas"]["ApiError"]; + }; + }; + /** @description Unauthorized Not Found */ + 401: { + content: { + "application/json": components["schemas"]["ApiError"]; + }; + }; /** @description Operation not authorized */ 403: { content: never; }; + /** @description Workspace Not Found */ + 404: { + content: { + "application/json": components["schemas"]["ApiError"]; + }; + }; /** @description Internal Server Error */ 500: { content: { - "application/json": components["schemas"]["WorkspacesError"]; + "application/json": components["schemas"]["ApiError"]; }; }; }; diff --git a/vscode/src/modality-api.json b/vscode/src/modality-api.json index eabe73f..46b9677 100644 --- a/vscode/src/modality-api.json +++ b/vscode/src/modality-api.json @@ -1702,15 +1702,45 @@ } } }, + "400": { + "description": "Invalid Parameter", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, + "401": { + "description": "Unauthorized Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, "403": { "description": "Operation not authorized" }, + "404": { + "description": "Workspace Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, "500": { "description": "Internal Server Error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WorkspacesError" + "$ref": "#/components/schemas/ApiError" } } } @@ -2846,6 +2876,86 @@ }, "components": { "schemas": { + "ApiError": { + "oneOf": [ + { + "type": "string", + "enum": ["Internal"] + }, + { + "type": "object", + "required": ["NotFound"], + "properties": { + "NotFound": { + "type": "object", + "required": ["kind_of_thing", "specific_thing"], + "properties": { + "kind_of_thing": { + "type": "string" + }, + "specific_thing": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "required": ["InvalidRequest"], + "properties": { + "InvalidRequest": { + "type": "string" + } + } + }, + { + "type": "object", + "required": ["InvalidParameter"], + "properties": { + "InvalidParameter": { + "type": "object", + "required": ["parameter", "reason"], + "properties": { + "parameter": { + "type": "string" + }, + "reason": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "required": ["InvalidParameterLocated"], + "properties": { + "InvalidParameterLocated": { + "type": "object", + "required": ["parameter", "located_errors"], + "properties": { + "located_errors": { + "$ref": "#/components/schemas/LocatedErrors" + }, + "parameter": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "required": ["AuthError"], + "properties": { + "AuthError": { + "$ref": "#/components/schemas/TokenAuthError" + } + } + } + ] + }, "AttrVal": { "oneOf": [ { @@ -3535,6 +3645,39 @@ } } }, + "LocatedError": { + "type": "object", + "required": ["start", "end", "message"], + "properties": { + "end": { + "type": "integer", + "minimum": 0 + }, + "message": { + "type": "string" + }, + "start": { + "type": "integer", + "minimum": 0 + } + } + }, + "LocatedErrors": { + "type": "object", + "description": "A wire-compatible encoding for located errors", + "required": ["errors", "text"], + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocatedError" + } + }, + "text": { + "type": "string" + } + } + }, "LogicalTime": { "type": "array", "items": { @@ -4233,6 +4376,10 @@ ], "description": "Timelines operation errors" }, + "TokenAuthError": { + "type": "string", + "enum": ["TokenMissing", "TokenNotValid", "TokenUnderPermitted"] + }, "UnstructuredMutatorFilter": { "type": "string", "description": "Stringy representation of an unparsed, unstructured DSL for expressing how to filter mutators,\nlikely through attribute evaluation."