Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
})
);

Expand Down
12 changes: 12 additions & 0 deletions extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Comment thread
elenatanasoiu marked this conversation as resolved.
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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};

Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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> {
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,52 +46,52 @@ 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
},
'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
}
}
});
Expand Down