Skip to content

Commit 84a8ffd

Browse files
authored
Only allow one open model alerts view (#3461)
1 parent 3005dac commit 84a8ffd

File tree

6 files changed

+102
-6
lines changed

6 files changed

+102
-6
lines changed

extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,25 @@ import type { App } from "../../common/app";
1111
import { redactableError } from "../../common/errors";
1212
import { extLogger } from "../../common/logging/vscode";
1313
import { showAndLogExceptionWithTelemetry } from "../../common/logging";
14+
import type { ModelingEvents } from "../modeling-events";
15+
import type { ModelingStore } from "../modeling-store";
16+
import type { DatabaseItem } from "../../databases/local-databases";
1417

1518
export class ModelAlertsView extends AbstractWebview<
1619
ToModelAlertsMessage,
1720
FromModelAlertsMessage
1821
> {
1922
public static readonly viewType = "codeQL.modelAlerts";
2023

21-
public constructor(app: App) {
24+
public constructor(
25+
app: App,
26+
private readonly modelingEvents: ModelingEvents,
27+
private readonly modelingStore: ModelingStore,
28+
private readonly dbItem: DatabaseItem,
29+
) {
2230
super(app);
31+
32+
this.registerToModelingEvents();
2333
}
2434

2535
public async showView() {
@@ -40,7 +50,7 @@ export class ModelAlertsView extends AbstractWebview<
4050
}
4151

4252
protected onPanelDispose(): void {
43-
// Nothing to dispose
53+
this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, false);
4454
}
4555

4656
protected async onMessage(msg: FromModelAlertsMessage): Promise<void> {
@@ -64,4 +74,18 @@ export class ModelAlertsView extends AbstractWebview<
6474
assertNever(msg);
6575
}
6676
}
77+
78+
public async focusView(): Promise<void> {
79+
this.panel?.reveal();
80+
}
81+
82+
private registerToModelingEvents() {
83+
this.push(
84+
this.modelingEvents.onFocusModelAlertsView(async (event) => {
85+
if (event.dbUri === this.dbItem.databaseUri.toString()) {
86+
await this.focusView();
87+
}
88+
}),
89+
);
90+
}
6791
}

extensions/ql-vscode/src/model-editor/model-evaluator.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,21 @@ export class ModelEvaluator extends DisposableObject {
107107
}
108108

109109
public async openModelAlertsView() {
110-
const view = new ModelAlertsView(this.app);
111-
await view.showView();
110+
if (this.modelingStore.isModelAlertsViewOpen(this.dbItem)) {
111+
this.modelingEvents.fireFocusModelAlertsViewEvent(
112+
this.dbItem.databaseUri.toString(),
113+
);
114+
return;
115+
} else {
116+
this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, true);
117+
const view = new ModelAlertsView(
118+
this.app,
119+
this.modelingEvents,
120+
this.modelingStore,
121+
this.dbItem,
122+
);
123+
await view.showView();
124+
}
112125
}
113126

114127
private registerToModelingEvents() {

extensions/ql-vscode/src/model-editor/modeling-events.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ interface FocusModelEditorEvent {
6565
dbUri: string;
6666
}
6767

68+
interface FocusModelAlertsViewEvent {
69+
dbUri: string;
70+
}
71+
6872
export class ModelingEvents extends DisposableObject {
6973
public readonly onActiveDbChanged: AppEvent<void>;
7074
public readonly onDbOpened: AppEvent<DatabaseItem>;
@@ -79,6 +83,7 @@ export class ModelingEvents extends DisposableObject {
7983
public readonly onModelEvaluationRunChanged: AppEvent<ModelEvaluationRunChangedEvent>;
8084
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
8185
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
86+
public readonly onFocusModelAlertsView: AppEvent<FocusModelAlertsViewEvent>;
8287

8388
private readonly onActiveDbChangedEventEmitter: AppEventEmitter<void>;
8489
private readonly onDbOpenedEventEmitter: AppEventEmitter<DatabaseItem>;
@@ -93,6 +98,7 @@ export class ModelingEvents extends DisposableObject {
9398
private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter<ModelEvaluationRunChangedEvent>;
9499
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
95100
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
101+
private readonly onFocusModelAlertsViewEventEmitter: AppEventEmitter<FocusModelAlertsViewEvent>;
96102

97103
constructor(app: App) {
98104
super();
@@ -165,6 +171,11 @@ export class ModelingEvents extends DisposableObject {
165171
app.createEventEmitter<FocusModelEditorEvent>(),
166172
);
167173
this.onFocusModelEditor = this.onFocusModelEditorEventEmitter.event;
174+
175+
this.onFocusModelAlertsViewEventEmitter = this.push(
176+
app.createEventEmitter<FocusModelAlertsViewEvent>(),
177+
);
178+
this.onFocusModelAlertsView = this.onFocusModelAlertsViewEventEmitter.event;
168179
}
169180

170181
public fireActiveDbChangedEvent() {
@@ -286,4 +297,8 @@ export class ModelingEvents extends DisposableObject {
286297
dbUri,
287298
});
288299
}
300+
301+
public fireFocusModelAlertsViewEvent(dbUri: string) {
302+
this.onFocusModelAlertsViewEventEmitter.fire({ dbUri });
303+
}
289304
}

extensions/ql-vscode/src/model-editor/modeling-store.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface InternalDbModelingState {
2020
selectedMethod: Method | undefined;
2121
selectedUsage: Usage | undefined;
2222
modelEvaluationRun: ModelEvaluationRun | undefined;
23+
isModelAlertsViewOpen: boolean;
2324
}
2425

2526
export interface DbModelingState {
@@ -34,6 +35,7 @@ export interface DbModelingState {
3435
readonly selectedMethod: Method | undefined;
3536
readonly selectedUsage: Usage | undefined;
3637
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
38+
readonly isModelAlertsViewOpen: boolean;
3739
}
3840

3941
export interface SelectedMethodDetails {
@@ -71,6 +73,7 @@ export class ModelingStore extends DisposableObject {
7173
selectedUsage: undefined,
7274
inProgressMethods: new Set(),
7375
modelEvaluationRun: undefined,
76+
isModelAlertsViewOpen: false,
7477
});
7578

7679
this.modelingEvents.fireDbOpenedEvent(databaseItem);
@@ -498,4 +501,23 @@ export class ModelingStore extends DisposableObject {
498501
state.modelEvaluationRun,
499502
);
500503
}
504+
505+
public isModelAlertsViewOpen(dbItem: DatabaseItem): boolean {
506+
return this.getState(dbItem).isModelAlertsViewOpen ?? false;
507+
}
508+
509+
private changeIsModelAlertsViewOpen(
510+
dbItem: DatabaseItem,
511+
updateState: (state: InternalDbModelingState) => void,
512+
) {
513+
const state = this.getState(dbItem);
514+
515+
updateState(state);
516+
}
517+
518+
public updateIsModelAlertsViewOpen(dbItem: DatabaseItem, isOpen: boolean) {
519+
this.changeIsModelAlertsViewOpen(dbItem, (state) => {
520+
state.isModelAlertsViewOpen = isOpen;
521+
});
522+
}
501523
}

extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ export const ModelEvaluation = ({
3434

3535
const shouldShowStopButton = !shouldShowEvaluateButton;
3636

37-
const shouldShowEvaluationRunLink = !!evaluationRun;
37+
const shouldShowEvaluationRunLink =
38+
!!evaluationRun && evaluationRun.variantAnalysis;
3839

3940
const customModelsExist = Object.values(modeledMethods).some(
4041
(methods) => methods.filter((m) => m.type !== "none").length > 0,

extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe(ModelEvaluation.name, () => {
9898
expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument();
9999
});
100100

101-
it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation", () => {
101+
it("renders 'Stop evaluation' button when there is an in progress evaluation, but no variant analysis yet", () => {
102102
render({
103103
evaluationRun: {
104104
isPreparing: true,
@@ -112,6 +112,27 @@ describe(ModelEvaluation.name, () => {
112112
stopEvaluationButton?.getElementsByTagName("input")[0],
113113
).toBeEnabled();
114114

115+
expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument();
116+
117+
expect(screen.queryByText("Evaluate")).not.toBeInTheDocument();
118+
});
119+
120+
it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation with variant analysis", () => {
121+
render({
122+
evaluationRun: {
123+
isPreparing: false,
124+
variantAnalysis: createMockVariantAnalysis({
125+
status: VariantAnalysisStatus.InProgress,
126+
}),
127+
},
128+
});
129+
130+
const stopEvaluationButton = screen.queryByText("Stop evaluation");
131+
expect(stopEvaluationButton).toBeInTheDocument();
132+
expect(
133+
stopEvaluationButton?.getElementsByTagName("input")[0],
134+
).toBeEnabled();
135+
115136
expect(screen.queryByText("Evaluation run")).toBeInTheDocument();
116137

117138
expect(screen.queryByText("Evaluate")).not.toBeInTheDocument();

0 commit comments

Comments
 (0)