Merge pull request #1562 from github/koesie10/set-variant-analysis-message
Implement `setVariantAnalysis` message
This commit is contained in:
@@ -104,7 +104,6 @@ import { LogScannerService } from './log-insights/log-scanner-service';
|
||||
import { createInitialQueryInfo } from './run-queries-shared';
|
||||
import { LegacyQueryRunner } from './legacy-query-server/legacyRunner';
|
||||
import { QueryRunner } from './queryRunner';
|
||||
import { VariantAnalysisView } from './remote-queries/variant-analysis-view';
|
||||
import { VariantAnalysis } from './remote-queries/shared/variant-analysis';
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisApiResponse,
|
||||
@@ -935,16 +934,14 @@ async function activateWithInstalledDistribution(
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner('codeQL.mockVariantAnalysisView', async () => {
|
||||
const variantAnalysisView = new VariantAnalysisView(ctx, 1);
|
||||
variantAnalysisView.openView();
|
||||
await variantAnalysisManager.showView(1);
|
||||
})
|
||||
);
|
||||
|
||||
// The "openVariantAnalysisView" command is internal-only.
|
||||
ctx.subscriptions.push(
|
||||
commandRunner('codeQL.openVariantAnalysisView', async (variantAnalysisId: number) => {
|
||||
const variantAnalysisView = new VariantAnalysisView(ctx, variantAnalysisId);
|
||||
variantAnalysisView.openView();
|
||||
await variantAnalysisManager.showView(variantAnalysisId);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as sarif from 'sarif';
|
||||
import { AnalysisResults } from '../remote-queries/shared/analysis-result';
|
||||
import { AnalysisSummary, RemoteQueryResult } from '../remote-queries/shared/remote-query-result';
|
||||
import { RawResultSet, ResultRow, ResultSetSchema, Column, ResolvableLocationValue } from './bqrs-cli-types';
|
||||
import { VariantAnalysis } from '../remote-queries/shared/variant-analysis';
|
||||
|
||||
/**
|
||||
* This module contains types and code that are shared between
|
||||
@@ -429,3 +430,14 @@ export interface CopyRepoListMessage {
|
||||
t: 'copyRepoList';
|
||||
queryId: string;
|
||||
}
|
||||
|
||||
export interface SetVariantAnalysisMessage {
|
||||
t: 'setVariantAnalysis';
|
||||
variantAnalysis: VariantAnalysis;
|
||||
}
|
||||
|
||||
export type ToVariantAnalysisMessage =
|
||||
| SetVariantAnalysisMessage;
|
||||
|
||||
export type FromVariantAnalysisMessage =
|
||||
| ViewLoadedMsg;
|
||||
|
||||
@@ -76,8 +76,8 @@ export interface VariantAnalysisRepoTask {
|
||||
}
|
||||
|
||||
export interface VariantAnalysisSkippedRepositories {
|
||||
access_mismatch_repos: VariantAnalysisSkippedRepositoryGroup,
|
||||
not_found_repo_nwos: VariantAnalysisNotFoundRepositoryGroup,
|
||||
no_codeql_db_repos: VariantAnalysisSkippedRepositoryGroup,
|
||||
over_limit_repos: VariantAnalysisSkippedRepositoryGroup
|
||||
access_mismatch_repos?: VariantAnalysisSkippedRepositoryGroup,
|
||||
not_found_repo_nwos?: VariantAnalysisNotFoundRepositoryGroup,
|
||||
no_codeql_db_repos?: VariantAnalysisSkippedRepositoryGroup,
|
||||
over_limit_repos?: VariantAnalysisSkippedRepositoryGroup
|
||||
}
|
||||
|
||||
@@ -13,16 +13,56 @@ import {
|
||||
} from './gh-api/variant-analysis';
|
||||
import { VariantAnalysis } from './shared/variant-analysis';
|
||||
import { getErrorMessage } from '../pure/helpers-pure';
|
||||
import { VariantAnalysisView } from './variant-analysis-view';
|
||||
import { VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
|
||||
export class VariantAnalysisManager extends DisposableObject {
|
||||
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
|
||||
private readonly variantAnalysisMonitor: VariantAnalysisMonitor;
|
||||
private readonly views = new Map<number, VariantAnalysisView>();
|
||||
|
||||
constructor(
|
||||
private readonly ctx: ExtensionContext,
|
||||
logger: Logger,
|
||||
) {
|
||||
super();
|
||||
this.variantAnalysisMonitor = new VariantAnalysisMonitor(ctx, logger);
|
||||
this.variantAnalysisMonitor = this.push(new VariantAnalysisMonitor(ctx, logger));
|
||||
this.variantAnalysisMonitor.onVariantAnalysisChange(this.onVariantAnalysisUpdated.bind(this));
|
||||
}
|
||||
|
||||
public async showView(variantAnalysisId: number): Promise<void> {
|
||||
if (!this.views.has(variantAnalysisId)) {
|
||||
// The view will register itself with the manager, so we don't need to do anything here.
|
||||
this.push(new VariantAnalysisView(this.ctx, variantAnalysisId, this));
|
||||
}
|
||||
|
||||
const variantAnalysisView = this.views.get(variantAnalysisId)!;
|
||||
await variantAnalysisView.openView();
|
||||
return;
|
||||
}
|
||||
|
||||
public registerView(view: VariantAnalysisView): void {
|
||||
if (this.views.has(view.variantAnalysisId)) {
|
||||
throw new Error(`View for variant analysis with id: ${view.variantAnalysisId} already exists`);
|
||||
}
|
||||
|
||||
this.views.set(view.variantAnalysisId, view);
|
||||
}
|
||||
|
||||
public unregisterView(view: VariantAnalysisView): void {
|
||||
this.views.delete(view.variantAnalysisId);
|
||||
}
|
||||
|
||||
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
|
||||
if (!variantAnalysis) {
|
||||
return;
|
||||
}
|
||||
|
||||
const view = this.views.get(variantAnalysis.id);
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
await view.updateView(variantAnalysis);
|
||||
}
|
||||
|
||||
public async monitorVariantAnalysis(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExtensionContext, CancellationToken, commands } from 'vscode';
|
||||
import { ExtensionContext, CancellationToken, commands, EventEmitter } from 'vscode';
|
||||
import { Credentials } from '../authentication';
|
||||
import { Logger } from '../logging';
|
||||
import * as ghApiClient from './gh-api/gh-api-client';
|
||||
@@ -8,18 +8,23 @@ import {
|
||||
VariantAnalysis as VariantAnalysisApiResponse
|
||||
} from './gh-api/variant-analysis';
|
||||
import { VariantAnalysisMonitorResult } from './shared/variant-analysis-monitor-result';
|
||||
import { processFailureReason } from './variant-analysis-processor';
|
||||
import { processFailureReason, processUpdatedVariantAnalysis } from './variant-analysis-processor';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
|
||||
export class VariantAnalysisMonitor {
|
||||
export class VariantAnalysisMonitor extends DisposableObject {
|
||||
// With a sleep of 5 seconds, the maximum number of attempts takes
|
||||
// us to just over 2 days worth of monitoring.
|
||||
public static maxAttemptCount = 17280;
|
||||
public static sleepTime = 5000;
|
||||
|
||||
private readonly _onVariantAnalysisChange = this.push(new EventEmitter<VariantAnalysis | undefined>());
|
||||
readonly onVariantAnalysisChange = this._onVariantAnalysisChange.event;
|
||||
|
||||
constructor(
|
||||
private readonly extensionContext: ExtensionContext,
|
||||
private readonly logger: Logger
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async monitorVariantAnalysis(
|
||||
@@ -59,6 +64,10 @@ export class VariantAnalysisMonitor {
|
||||
};
|
||||
}
|
||||
|
||||
variantAnalysis = processUpdatedVariantAnalysis(variantAnalysis, variantAnalysisSummary);
|
||||
|
||||
this._onVariantAnalysisChange.fire(variantAnalysis);
|
||||
|
||||
void this.logger.log('****** Retrieved variant analysis' + JSON.stringify(variantAnalysisSummary));
|
||||
|
||||
if (variantAnalysisSummary.scanned_repositories) {
|
||||
|
||||
@@ -23,7 +23,20 @@ export function processVariantAnalysis(
|
||||
submission: VariantAnalysisSubmission,
|
||||
response: ApiVariantAnalysis
|
||||
): VariantAnalysis {
|
||||
return processUpdatedVariantAnalysis({
|
||||
query: {
|
||||
name: submission.query.name,
|
||||
filePath: submission.query.filePath,
|
||||
language: submission.query.language
|
||||
},
|
||||
databases: submission.databases,
|
||||
}, response);
|
||||
}
|
||||
|
||||
export function processUpdatedVariantAnalysis(
|
||||
previousVariantAnalysis: Pick<VariantAnalysis, 'query' | 'databases'>,
|
||||
response: ApiVariantAnalysis
|
||||
): VariantAnalysis {
|
||||
let scannedRepos: VariantAnalysisScannedRepository[] = [];
|
||||
let skippedRepos: VariantAnalysisSkippedRepositories = {};
|
||||
|
||||
@@ -39,11 +52,11 @@ export function processVariantAnalysis(
|
||||
id: response.id,
|
||||
controllerRepoId: response.controller_repo.id,
|
||||
query: {
|
||||
name: submission.query.name,
|
||||
filePath: submission.query.filePath,
|
||||
language: submission.query.language
|
||||
name: previousVariantAnalysis.query.name,
|
||||
filePath: previousVariantAnalysis.query.filePath,
|
||||
language: previousVariantAnalysis.query.language
|
||||
},
|
||||
databases: submission.databases,
|
||||
databases: previousVariantAnalysis.databases,
|
||||
status: processApiStatus(response.status),
|
||||
actionsWorkflowRunId: response.actions_workflow_run_id,
|
||||
scannedRepos: scannedRepos,
|
||||
@@ -87,7 +100,11 @@ function processSkippedRepositories(
|
||||
};
|
||||
}
|
||||
|
||||
function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup): VariantAnalysisSkippedRepositoryGroup {
|
||||
function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup | undefined): VariantAnalysisSkippedRepositoryGroup | undefined {
|
||||
if (!repoGroup) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const repos = repoGroup.repositories.map(repo => {
|
||||
return {
|
||||
id: repo.id,
|
||||
@@ -101,7 +118,11 @@ function processRepoGroup(repoGroup: ApiVariantAnalysisSkippedRepositoryGroup):
|
||||
};
|
||||
}
|
||||
|
||||
function processNotFoundRepoGroup(repoGroup: ApiVariantAnalysisNotFoundRepositoryGroup): VariantAnalysisSkippedRepositoryGroup {
|
||||
function processNotFoundRepoGroup(repoGroup: ApiVariantAnalysisNotFoundRepositoryGroup | undefined): VariantAnalysisSkippedRepositoryGroup | undefined {
|
||||
if (!repoGroup) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const repo_full_names = repoGroup.repository_full_names.map(nwo => {
|
||||
return {
|
||||
fullName: nwo
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface VariantAnalysisViewInterface {
|
||||
variantAnalysisId: number;
|
||||
openView(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface VariantAnalysisViewManager<T extends VariantAnalysisViewInterface> {
|
||||
registerView(view: T): void;
|
||||
unregisterView(view: T): void;
|
||||
}
|
||||
@@ -2,19 +2,36 @@ import { ExtensionContext, ViewColumn } from 'vscode';
|
||||
import { AbstractWebview, WebviewPanelConfig } from '../abstract-webview';
|
||||
import { WebviewMessage } from '../interface-utils';
|
||||
import { logger } from '../logging';
|
||||
import { VariantAnalysisViewInterface, VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
import { VariantAnalysis } from './shared/variant-analysis';
|
||||
import { FromVariantAnalysisMessage, ToVariantAnalysisMessage } from '../pure/interface-types';
|
||||
|
||||
export class VariantAnalysisView extends AbstractWebview<WebviewMessage, WebviewMessage> {
|
||||
export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage> implements VariantAnalysisViewInterface {
|
||||
public constructor(
|
||||
ctx: ExtensionContext,
|
||||
private readonly variantAnalysisId: number,
|
||||
public readonly variantAnalysisId: number,
|
||||
private readonly manager: VariantAnalysisViewManager<VariantAnalysisView>,
|
||||
) {
|
||||
super(ctx);
|
||||
|
||||
manager.registerView(this);
|
||||
}
|
||||
|
||||
public openView() {
|
||||
public async openView() {
|
||||
this.getPanel().reveal(undefined, true);
|
||||
}
|
||||
|
||||
public async updateView(variantAnalysis: VariantAnalysis): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.postMessage({
|
||||
t: 'setVariantAnalysis',
|
||||
variantAnalysis,
|
||||
});
|
||||
}
|
||||
|
||||
protected getPanelConfig(): WebviewPanelConfig {
|
||||
return {
|
||||
viewId: 'variantAnalysisView',
|
||||
@@ -26,7 +43,7 @@ export class VariantAnalysisView extends AbstractWebview<WebviewMessage, Webview
|
||||
}
|
||||
|
||||
protected onPanelDispose(): void {
|
||||
// Nothing to dispose currently.
|
||||
this.manager.unregisterView(this);
|
||||
}
|
||||
|
||||
protected async onMessage(msg: WebviewMessage): Promise<void> {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { ToVariantAnalysisMessage } from '../../pure/interface-types';
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisDomainModel,
|
||||
VariantAnalysisQueryLanguage,
|
||||
@@ -222,7 +224,30 @@ function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
|
||||
);
|
||||
}
|
||||
|
||||
export function VariantAnalysis(): JSX.Element {
|
||||
type Props = {
|
||||
variantAnalysis?: VariantAnalysisDomainModel;
|
||||
}
|
||||
|
||||
export function VariantAnalysis({
|
||||
variantAnalysis: initialVariantAnalysis = variantAnalysis,
|
||||
}: Props): JSX.Element {
|
||||
const [variantAnalysis, setVariantAnalysis] = useState<VariantAnalysisDomainModel>(initialVariantAnalysis);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', (evt: MessageEvent) => {
|
||||
if (evt.origin === window.origin) {
|
||||
const msg: ToVariantAnalysisMessage = evt.data;
|
||||
if (msg.t === 'setVariantAnalysis') {
|
||||
setVariantAnalysis(msg.variantAnalysis);
|
||||
}
|
||||
} else {
|
||||
// sanitize origin
|
||||
const origin = evt.origin.replace(/\n|\r/g, '');
|
||||
console.error(`Invalid event origin ${origin}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<VariantAnalysisContainer>
|
||||
{getContainerContents(variantAnalysis)}
|
||||
|
||||
@@ -46,25 +46,25 @@ describe('Variant Analysis processor', function() {
|
||||
'accessMismatchRepos': {
|
||||
'repositories': [
|
||||
{
|
||||
'fullName': access_mismatch_repos.repositories[0].full_name,
|
||||
'id': access_mismatch_repos.repositories[0].id
|
||||
'fullName': access_mismatch_repos?.repositories[0].full_name,
|
||||
'id': access_mismatch_repos?.repositories[0].id
|
||||
},
|
||||
{
|
||||
'fullName': access_mismatch_repos.repositories[1].full_name,
|
||||
'id': access_mismatch_repos.repositories[1].id
|
||||
'fullName': access_mismatch_repos?.repositories[1].full_name,
|
||||
'id': access_mismatch_repos?.repositories[1].id
|
||||
}
|
||||
],
|
||||
'repositoryCount': access_mismatch_repos.repository_count
|
||||
'repositoryCount': access_mismatch_repos?.repository_count
|
||||
},
|
||||
'noCodeqlDbRepos': {
|
||||
'repositories': [
|
||||
{
|
||||
'fullName': no_codeql_db_repos.repositories[0].full_name,
|
||||
'id': no_codeql_db_repos.repositories[0].id
|
||||
'fullName': no_codeql_db_repos?.repositories[0].full_name,
|
||||
'id': no_codeql_db_repos?.repositories[0].id
|
||||
},
|
||||
{
|
||||
'fullName': no_codeql_db_repos.repositories[1].full_name,
|
||||
'id': no_codeql_db_repos.repositories[1].id,
|
||||
'fullName': no_codeql_db_repos?.repositories[1].full_name,
|
||||
'id': no_codeql_db_repos?.repositories[1].id,
|
||||
}
|
||||
],
|
||||
'repositoryCount': 2
|
||||
@@ -72,26 +72,26 @@ describe('Variant Analysis processor', function() {
|
||||
'notFoundRepos': {
|
||||
'repositories': [
|
||||
{
|
||||
'fullName': not_found_repo_nwos.repository_full_names[0]
|
||||
'fullName': not_found_repo_nwos?.repository_full_names[0]
|
||||
},
|
||||
{
|
||||
'fullName': not_found_repo_nwos.repository_full_names[1]
|
||||
'fullName': not_found_repo_nwos?.repository_full_names[1]
|
||||
}
|
||||
],
|
||||
'repositoryCount': not_found_repo_nwos.repository_count
|
||||
'repositoryCount': not_found_repo_nwos?.repository_count
|
||||
},
|
||||
'overLimitRepos': {
|
||||
'repositories': [
|
||||
{
|
||||
'fullName': over_limit_repos.repositories[0].full_name,
|
||||
'id': over_limit_repos.repositories[0].id
|
||||
'fullName': over_limit_repos?.repositories[0].full_name,
|
||||
'id': over_limit_repos?.repositories[0].id
|
||||
},
|
||||
{
|
||||
'fullName': over_limit_repos.repositories[1].full_name,
|
||||
'id': over_limit_repos.repositories[1].id
|
||||
'fullName': over_limit_repos?.repositories[1].full_name,
|
||||
'id': over_limit_repos?.repositories[1].id
|
||||
}
|
||||
],
|
||||
'repositoryCount': over_limit_repos.repository_count
|
||||
'repositoryCount': over_limit_repos?.repository_count
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user