Skip to content

Commit 9002313

Browse files
authored
Merge pull request #1562 from github/koesie10/set-variant-analysis-message
Implement `setVariantAnalysis` message
2 parents ab9cf46 + 6941584 commit 9002313

File tree

10 files changed

+172
-42
lines changed

10 files changed

+172
-42
lines changed

extensions/ql-vscode/src/extension.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ import { LogScannerService } from './log-insights/log-scanner-service';
104104
import { createInitialQueryInfo } from './run-queries-shared';
105105
import { LegacyQueryRunner } from './legacy-query-server/legacyRunner';
106106
import { QueryRunner } from './queryRunner';
107-
import { VariantAnalysisView } from './remote-queries/variant-analysis-view';
108107
import { VariantAnalysis } from './remote-queries/shared/variant-analysis';
109108
import {
110109
VariantAnalysis as VariantAnalysisApiResponse,
@@ -935,16 +934,14 @@ async function activateWithInstalledDistribution(
935934

936935
ctx.subscriptions.push(
937936
commandRunner('codeQL.mockVariantAnalysisView', async () => {
938-
const variantAnalysisView = new VariantAnalysisView(ctx, 1);
939-
variantAnalysisView.openView();
937+
await variantAnalysisManager.showView(1);
940938
})
941939
);
942940

943941
// The "openVariantAnalysisView" command is internal-only.
944942
ctx.subscriptions.push(
945943
commandRunner('codeQL.openVariantAnalysisView', async (variantAnalysisId: number) => {
946-
const variantAnalysisView = new VariantAnalysisView(ctx, variantAnalysisId);
947-
variantAnalysisView.openView();
944+
await variantAnalysisManager.showView(variantAnalysisId);
948945
})
949946
);
950947

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as sarif from 'sarif';
22
import { AnalysisResults } from '../remote-queries/shared/analysis-result';
33
import { AnalysisSummary, RemoteQueryResult } from '../remote-queries/shared/remote-query-result';
44
import { RawResultSet, ResultRow, ResultSetSchema, Column, ResolvableLocationValue } from './bqrs-cli-types';
5+
import { VariantAnalysis } from '../remote-queries/shared/variant-analysis';
56

67
/**
78
* This module contains types and code that are shared between
@@ -429,3 +430,14 @@ export interface CopyRepoListMessage {
429430
t: 'copyRepoList';
430431
queryId: string;
431432
}
433+
434+
export interface SetVariantAnalysisMessage {
435+
t: 'setVariantAnalysis';
436+
variantAnalysis: VariantAnalysis;
437+
}
438+
439+
export type ToVariantAnalysisMessage =
440+
| SetVariantAnalysisMessage;
441+
442+
export type FromVariantAnalysisMessage =
443+
| ViewLoadedMsg;

extensions/ql-vscode/src/remote-queries/gh-api/variant-analysis.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export interface VariantAnalysisRepoTask {
7676
}
7777

7878
export interface VariantAnalysisSkippedRepositories {
79-
access_mismatch_repos: VariantAnalysisSkippedRepositoryGroup,
80-
not_found_repo_nwos: VariantAnalysisNotFoundRepositoryGroup,
81-
no_codeql_db_repos: VariantAnalysisSkippedRepositoryGroup,
82-
over_limit_repos: VariantAnalysisSkippedRepositoryGroup
79+
access_mismatch_repos?: VariantAnalysisSkippedRepositoryGroup,
80+
not_found_repo_nwos?: VariantAnalysisNotFoundRepositoryGroup,
81+
no_codeql_db_repos?: VariantAnalysisSkippedRepositoryGroup,
82+
over_limit_repos?: VariantAnalysisSkippedRepositoryGroup
8383
}

extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,56 @@ import {
1313
} from './gh-api/variant-analysis';
1414
import { VariantAnalysis } from './shared/variant-analysis';
1515
import { getErrorMessage } from '../pure/helpers-pure';
16+
import { VariantAnalysisView } from './variant-analysis-view';
17+
import { VariantAnalysisViewManager } from './variant-analysis-view-manager';
1618

17-
export class VariantAnalysisManager extends DisposableObject {
19+
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
1820
private readonly variantAnalysisMonitor: VariantAnalysisMonitor;
21+
private readonly views = new Map<number, VariantAnalysisView>();
1922

2023
constructor(
2124
private readonly ctx: ExtensionContext,
2225
logger: Logger,
2326
) {
2427
super();
25-
this.variantAnalysisMonitor = new VariantAnalysisMonitor(ctx, logger);
28+
this.variantAnalysisMonitor = this.push(new VariantAnalysisMonitor(ctx, logger));
29+
this.variantAnalysisMonitor.onVariantAnalysisChange(this.onVariantAnalysisUpdated.bind(this));
30+
}
31+
32+
public async showView(variantAnalysisId: number): Promise<void> {
33+
if (!this.views.has(variantAnalysisId)) {
34+
// The view will register itself with the manager, so we don't need to do anything here.
35+
this.push(new VariantAnalysisView(this.ctx, variantAnalysisId, this));
36+
}
37+
38+
const variantAnalysisView = this.views.get(variantAnalysisId)!;
39+
await variantAnalysisView.openView();
40+
return;
41+
}
42+
43+
public registerView(view: VariantAnalysisView): void {
44+
if (this.views.has(view.variantAnalysisId)) {
45+
throw new Error(`View for variant analysis with id: ${view.variantAnalysisId} already exists`);
46+
}
47+
48+
this.views.set(view.variantAnalysisId, view);
49+
}
50+
51+
public unregisterView(view: VariantAnalysisView): void {
52+
this.views.delete(view.variantAnalysisId);
53+
}
54+
55+
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
56+
if (!variantAnalysis) {
57+
return;
58+
}
59+
60+
const view = this.views.get(variantAnalysis.id);
61+
if (!view) {
62+
return;
63+
}
64+
65+
await view.updateView(variantAnalysis);
2666
}
2767

2868
public async monitorVariantAnalysis(

extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ExtensionContext, CancellationToken, commands } from 'vscode';
1+
import { ExtensionContext, CancellationToken, commands, EventEmitter } from 'vscode';
22
import { Credentials } from '../authentication';
33
import { Logger } from '../logging';
44
import * as ghApiClient from './gh-api/gh-api-client';
@@ -8,18 +8,23 @@ import {
88
VariantAnalysis as VariantAnalysisApiResponse
99
} from './gh-api/variant-analysis';
1010
import { VariantAnalysisMonitorResult } from './shared/variant-analysis-monitor-result';
11-
import { processFailureReason } from './variant-analysis-processor';
11+
import { processFailureReason, processUpdatedVariantAnalysis } from './variant-analysis-processor';
12+
import { DisposableObject } from '../pure/disposable-object';
1213

13-
export class VariantAnalysisMonitor {
14+
export class VariantAnalysisMonitor extends DisposableObject {
1415
// With a sleep of 5 seconds, the maximum number of attempts takes
1516
// us to just over 2 days worth of monitoring.
1617
public static maxAttemptCount = 17280;
1718
public static sleepTime = 5000;
1819

20+
private readonly _onVariantAnalysisChange = this.push(new EventEmitter<VariantAnalysis | undefined>());
21+
readonly onVariantAnalysisChange = this._onVariantAnalysisChange.event;
22+
1923
constructor(
2024
private readonly extensionContext: ExtensionContext,
2125
private readonly logger: Logger
2226
) {
27+
super();
2328
}
2429

2530
public async monitorVariantAnalysis(
@@ -59,6 +64,10 @@ export class VariantAnalysisMonitor {
5964
};
6065
}
6166

67+
variantAnalysis = processUpdatedVariantAnalysis(variantAnalysis, variantAnalysisSummary);
68+
69+
this._onVariantAnalysisChange.fire(variantAnalysis);
70+
6271
void this.logger.log('****** Retrieved variant analysis' + JSON.stringify(variantAnalysisSummary));
6372

6473
if (variantAnalysisSummary.scanned_repositories) {

extensions/ql-vscode/src/remote-queries/variant-analysis-processor.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,20 @@ export function processVariantAnalysis(
2323
submission: VariantAnalysisSubmission,
2424
response: ApiVariantAnalysis
2525
): VariantAnalysis {
26+
return processUpdatedVariantAnalysis({
27+
query: {
28+
name: submission.query.name,
29+
filePath: submission.query.filePath,
30+
language: submission.query.language
31+
},
32+
databases: submission.databases,
33+
}, response);
34+
}
2635

36+
export function processUpdatedVariantAnalysis(
37+
previousVariantAnalysis: Pick<VariantAnalysis, 'query' | 'databases'>,
38+
response: ApiVariantAnalysis
39+
): VariantAnalysis {
2740
let scannedRepos: VariantAnalysisScannedRepository[] = [];
2841
let skippedRepos: VariantAnalysisSkippedRepositories = {};
2942

@@ -39,11 +52,11 @@ export function processVariantAnalysis(
3952
id: response.id,
4053
controllerRepoId: response.controller_repo.id,
4154
query: {
42-
name: submission.query.name,
43-
filePath: submission.query.filePath,
44-
language: submission.query.language
55+
name: previousVariantAnalysis.query.name,
56+
filePath: previousVariantAnalysis.query.filePath,
57+
language: previousVariantAnalysis.query.language
4558
},
46-
databases: submission.databases,
59+
databases: previousVariantAnalysis.databases,
4760
status: processApiStatus(response.status),
4861
actionsWorkflowRunId: response.actions_workflow_run_id,
4962
scannedRepos: scannedRepos,
@@ -87,7 +100,11 @@ function processSkippedRepositories(
87100
};
88101
}
89102

90-
function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup): VariantAnalysisSkippedRepositoryGroup {
103+
function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup | undefined): VariantAnalysisSkippedRepositoryGroup | undefined {
104+
if (!repoGroup) {
105+
return undefined;
106+
}
107+
91108
const repos = repoGroup.repositories.map(repo => {
92109
return {
93110
id: repo.id,
@@ -101,7 +118,11 @@ function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup):
101118
};
102119
}
103120

104-
function processNotFoundRepoGroup(repoGroup: ApiVariantAnalysisNotFoundRepositoryGroup): VariantAnalysisSkippedRepositoryGroup {
121+
function processNotFoundRepoGroup(repoGroup: ApiVariantAnalysisNotFoundRepositoryGroup | undefined): VariantAnalysisSkippedRepositoryGroup | undefined {
122+
if (!repoGroup) {
123+
return undefined;
124+
}
125+
105126
const repo_full_names = repoGroup.repository_full_names.map(nwo => {
106127
return {
107128
fullName: nwo
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface VariantAnalysisViewInterface {
2+
variantAnalysisId: number;
3+
openView(): Promise<void>;
4+
}
5+
6+
export interface VariantAnalysisViewManager<T extends VariantAnalysisViewInterface> {
7+
registerView(view: T): void;
8+
unregisterView(view: T): void;
9+
}

extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,36 @@ import { ExtensionContext, ViewColumn } from 'vscode';
22
import { AbstractWebview, WebviewPanelConfig } from '../abstract-webview';
33
import { WebviewMessage } from '../interface-utils';
44
import { logger } from '../logging';
5+
import { VariantAnalysisViewInterface, VariantAnalysisViewManager } from './variant-analysis-view-manager';
6+
import { VariantAnalysis } from './shared/variant-analysis';
7+
import { FromVariantAnalysisMessage, ToVariantAnalysisMessage } from '../pure/interface-types';
58

6-
export class VariantAnalysisView extends AbstractWebview<WebviewMessage, WebviewMessage> {
9+
export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage> implements VariantAnalysisViewInterface {
710
public constructor(
811
ctx: ExtensionContext,
9-
private readonly variantAnalysisId: number,
12+
public readonly variantAnalysisId: number,
13+
private readonly manager: VariantAnalysisViewManager<VariantAnalysisView>,
1014
) {
1115
super(ctx);
16+
17+
manager.registerView(this);
1218
}
1319

14-
public openView() {
20+
public async openView() {
1521
this.getPanel().reveal(undefined, true);
1622
}
1723

24+
public async updateView(variantAnalysis: VariantAnalysis): Promise<void> {
25+
if (!this.isShowingPanel) {
26+
return;
27+
}
28+
29+
await this.postMessage({
30+
t: 'setVariantAnalysis',
31+
variantAnalysis,
32+
});
33+
}
34+
1835
protected getPanelConfig(): WebviewPanelConfig {
1936
return {
2037
viewId: 'variantAnalysisView',
@@ -26,7 +43,7 @@ export class VariantAnalysisView extends AbstractWebview<WebviewMessage, Webview
2643
}
2744

2845
protected onPanelDispose(): void {
29-
// Nothing to dispose currently.
46+
this.manager.unregisterView(this);
3047
}
3148

3249
protected async onMessage(msg: WebviewMessage): Promise<void> {

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as React from 'react';
2+
import { useEffect, useState } from 'react';
23

4+
import { ToVariantAnalysisMessage } from '../../pure/interface-types';
35
import {
46
VariantAnalysis as VariantAnalysisDomainModel,
57
VariantAnalysisQueryLanguage,
@@ -222,7 +224,30 @@ function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
222224
);
223225
}
224226

225-
export function VariantAnalysis(): JSX.Element {
227+
type Props = {
228+
variantAnalysis?: VariantAnalysisDomainModel;
229+
}
230+
231+
export function VariantAnalysis({
232+
variantAnalysis: initialVariantAnalysis = variantAnalysis,
233+
}: Props): JSX.Element {
234+
const [variantAnalysis, setVariantAnalysis] = useState<VariantAnalysisDomainModel>(initialVariantAnalysis);
235+
236+
useEffect(() => {
237+
window.addEventListener('message', (evt: MessageEvent) => {
238+
if (evt.origin === window.origin) {
239+
const msg: ToVariantAnalysisMessage = evt.data;
240+
if (msg.t === 'setVariantAnalysis') {
241+
setVariantAnalysis(msg.variantAnalysis);
242+
}
243+
} else {
244+
// sanitize origin
245+
const origin = evt.origin.replace(/\n|\r/g, '');
246+
console.error(`Invalid event origin ${origin}`);
247+
}
248+
});
249+
});
250+
226251
return (
227252
<VariantAnalysisContainer>
228253
{getContainerContents(variantAnalysis)}

extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-processor.test.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,52 +46,52 @@ describe('Variant Analysis processor', function() {
4646
'accessMismatchRepos': {
4747
'repositories': [
4848
{
49-
'fullName': access_mismatch_repos.repositories[0].full_name,
50-
'id': access_mismatch_repos.repositories[0].id
49+
'fullName': access_mismatch_repos?.repositories[0].full_name,
50+
'id': access_mismatch_repos?.repositories[0].id
5151
},
5252
{
53-
'fullName': access_mismatch_repos.repositories[1].full_name,
54-
'id': access_mismatch_repos.repositories[1].id
53+
'fullName': access_mismatch_repos?.repositories[1].full_name,
54+
'id': access_mismatch_repos?.repositories[1].id
5555
}
5656
],
57-
'repositoryCount': access_mismatch_repos.repository_count
57+
'repositoryCount': access_mismatch_repos?.repository_count
5858
},
5959
'noCodeqlDbRepos': {
6060
'repositories': [
6161
{
62-
'fullName': no_codeql_db_repos.repositories[0].full_name,
63-
'id': no_codeql_db_repos.repositories[0].id
62+
'fullName': no_codeql_db_repos?.repositories[0].full_name,
63+
'id': no_codeql_db_repos?.repositories[0].id
6464
},
6565
{
66-
'fullName': no_codeql_db_repos.repositories[1].full_name,
67-
'id': no_codeql_db_repos.repositories[1].id,
66+
'fullName': no_codeql_db_repos?.repositories[1].full_name,
67+
'id': no_codeql_db_repos?.repositories[1].id,
6868
}
6969
],
7070
'repositoryCount': 2
7171
},
7272
'notFoundRepos': {
7373
'repositories': [
7474
{
75-
'fullName': not_found_repo_nwos.repository_full_names[0]
75+
'fullName': not_found_repo_nwos?.repository_full_names[0]
7676
},
7777
{
78-
'fullName': not_found_repo_nwos.repository_full_names[1]
78+
'fullName': not_found_repo_nwos?.repository_full_names[1]
7979
}
8080
],
81-
'repositoryCount': not_found_repo_nwos.repository_count
81+
'repositoryCount': not_found_repo_nwos?.repository_count
8282
},
8383
'overLimitRepos': {
8484
'repositories': [
8585
{
86-
'fullName': over_limit_repos.repositories[0].full_name,
87-
'id': over_limit_repos.repositories[0].id
86+
'fullName': over_limit_repos?.repositories[0].full_name,
87+
'id': over_limit_repos?.repositories[0].id
8888
},
8989
{
90-
'fullName': over_limit_repos.repositories[1].full_name,
91-
'id': over_limit_repos.repositories[1].id
90+
'fullName': over_limit_repos?.repositories[1].full_name,
91+
'id': over_limit_repos?.repositories[1].id
9292
}
9393
],
94-
'repositoryCount': over_limit_repos.repository_count
94+
'repositoryCount': over_limit_repos?.repository_count
9595
}
9696
}
9797
});

0 commit comments

Comments
 (0)