Merge remote-tracking branch 'origin/main' into koesie10/restore-mrva-on-restart
This commit is contained in:
14
extensions/ql-vscode/package-lock.json
generated
14
extensions/ql-vscode/package-lock.json
generated
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@octokit/rest": "^19.0.4",
|
||||
"@primer/octicons-react": "^16.3.0",
|
||||
"@primer/octicons-react": "^17.6.0",
|
||||
"@primer/react": "^35.0.0",
|
||||
"@vscode/codicons": "^0.0.31",
|
||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||
@@ -5537,9 +5537,9 @@
|
||||
"integrity": "sha512-Ej2OUc3ZIFaR7WwIUqESO1DTzmpb7wc8xbTVRT9s52jZQDjN7g5iljoK3ocYZm+BIAcKn3MvcwB42hEk4Ga4xQ=="
|
||||
},
|
||||
"node_modules/@primer/octicons-react": {
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-16.3.0.tgz",
|
||||
"integrity": "sha512-nxB6L5IYeR2hPY6q7DAyQGW42MO0kA4PqMBNysN4WaZAHjiKgtTDr9rI+759PqGK7BTTuT43HTuu2cMsg7pH0Q==",
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-17.6.0.tgz",
|
||||
"integrity": "sha512-jn1fWag3eU6BvOltMS2MqLPNh39D45cpegsTO2Qhb8SlJoUsj/ZO1qbJgYd9ibvZo8evDyXx3syh4kDbxJQFsg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
@@ -44167,9 +44167,9 @@
|
||||
"integrity": "sha512-Ej2OUc3ZIFaR7WwIUqESO1DTzmpb7wc8xbTVRT9s52jZQDjN7g5iljoK3ocYZm+BIAcKn3MvcwB42hEk4Ga4xQ=="
|
||||
},
|
||||
"@primer/octicons-react": {
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-16.3.0.tgz",
|
||||
"integrity": "sha512-nxB6L5IYeR2hPY6q7DAyQGW42MO0kA4PqMBNysN4WaZAHjiKgtTDr9rI+759PqGK7BTTuT43HTuu2cMsg7pH0Q=="
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-17.6.0.tgz",
|
||||
"integrity": "sha512-jn1fWag3eU6BvOltMS2MqLPNh39D45cpegsTO2Qhb8SlJoUsj/ZO1qbJgYd9ibvZo8evDyXx3syh4kDbxJQFsg=="
|
||||
},
|
||||
"@primer/primitives": {
|
||||
"version": "7.1.1",
|
||||
|
||||
@@ -1208,7 +1208,7 @@
|
||||
"dependencies": {
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@octokit/rest": "^19.0.4",
|
||||
"@primer/octicons-react": "^16.3.0",
|
||||
"@primer/octicons-react": "^17.6.0",
|
||||
"@primer/react": "^35.0.0",
|
||||
"@vscode/codicons": "^0.0.31",
|
||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||
|
||||
@@ -178,6 +178,7 @@ export interface CodeQLExtensionInterface {
|
||||
readonly distributionManager: DistributionManager;
|
||||
readonly databaseManager: DatabaseManager;
|
||||
readonly databaseUI: DatabaseUI;
|
||||
readonly variantAnalysisManager: VariantAnalysisManager;
|
||||
readonly dispose: () => void;
|
||||
}
|
||||
|
||||
@@ -400,7 +401,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
||||
allowAutoUpdating: !!ctx.globalState.get(shouldUpdateOnNextActivationKey)
|
||||
});
|
||||
|
||||
variantAnalysisViewSerializer.onExtensionLoaded();
|
||||
variantAnalysisViewSerializer.onExtensionLoaded(codeQlExtension.variantAnalysisManager);
|
||||
|
||||
return codeQlExtension;
|
||||
}
|
||||
@@ -946,16 +947,14 @@ async function activateWithInstalledDistribution(
|
||||
// Generate a random variant analysis ID for testing
|
||||
const variantAnalysisId: number = Math.floor(Math.random() * 1000000);
|
||||
|
||||
const variantAnalysisView = new VariantAnalysisView(ctx, variantAnalysisId);
|
||||
void variantAnalysisView.openView();
|
||||
await variantAnalysisManager.showView(variantAnalysisId);
|
||||
})
|
||||
);
|
||||
|
||||
// The "openVariantAnalysisView" command is internal-only.
|
||||
ctx.subscriptions.push(
|
||||
commandRunner('codeQL.openVariantAnalysisView', async (variantAnalysisId: number) => {
|
||||
const variantAnalysisView = new VariantAnalysisView(ctx, variantAnalysisId);
|
||||
void variantAnalysisView.openView();
|
||||
await variantAnalysisManager.showView(variantAnalysisId);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1173,6 +1172,7 @@ async function activateWithInstalledDistribution(
|
||||
distributionManager,
|
||||
databaseManager: dbm,
|
||||
databaseUI,
|
||||
variantAnalysisManager,
|
||||
dispose: () => {
|
||||
ctx.subscriptions.forEach(d => d.dispose());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ 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';
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState,
|
||||
} from '../remote-queries/shared/variant-analysis';
|
||||
|
||||
/**
|
||||
* This module contains types and code that are shared between
|
||||
@@ -436,18 +440,30 @@ export interface SetVariantAnalysisMessage {
|
||||
variantAnalysis: VariantAnalysis;
|
||||
}
|
||||
|
||||
export type ToVariantAnalysisMessage =
|
||||
| SetVariantAnalysisMessage;
|
||||
|
||||
export type StopVariantAnalysisMessage = {
|
||||
t: 'stopVariantAnalysis';
|
||||
variantAnalysisId: number;
|
||||
}
|
||||
|
||||
export type FromVariantAnalysisMessage =
|
||||
| ViewLoadedMsg
|
||||
| StopVariantAnalysisMessage;
|
||||
|
||||
export type VariantAnalysisState = {
|
||||
variantAnalysisId: number;
|
||||
}
|
||||
|
||||
export interface SetRepoResultsMessage {
|
||||
t: 'setRepoResults';
|
||||
repoResults: VariantAnalysisScannedRepositoryResult[];
|
||||
}
|
||||
|
||||
export interface SetRepoStatesMessage {
|
||||
t: 'setRepoStates';
|
||||
repoStates: VariantAnalysisScannedRepositoryState[];
|
||||
}
|
||||
|
||||
export type ToVariantAnalysisMessage =
|
||||
| SetVariantAnalysisMessage
|
||||
| SetRepoResultsMessage
|
||||
| SetRepoStatesMessage;
|
||||
|
||||
export type FromVariantAnalysisMessage =
|
||||
| ViewLoadedMsg
|
||||
| StopVariantAnalysisMessage;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -82,6 +82,18 @@ export interface VariantAnalysisSkippedRepository {
|
||||
private?: boolean,
|
||||
}
|
||||
|
||||
export enum VariantAnalysisScannedRepositoryDownloadStatus {
|
||||
Pending = 'pending',
|
||||
InProgress = 'inProgress',
|
||||
Succeeded = 'succeeded',
|
||||
Failed = 'failed',
|
||||
}
|
||||
|
||||
export interface VariantAnalysisScannedRepositoryState {
|
||||
repositoryId: number;
|
||||
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus;
|
||||
}
|
||||
|
||||
export interface VariantAnalysisScannedRepositoryResult {
|
||||
repositoryId: number;
|
||||
interpretedResults?: AnalysisAlert[];
|
||||
|
||||
@@ -11,18 +11,65 @@ import {
|
||||
VariantAnalysisRepoTask,
|
||||
VariantAnalysisScannedRepository as ApiVariantAnalysisScannedRepository
|
||||
} from './gh-api/variant-analysis';
|
||||
import { VariantAnalysis } from './shared/variant-analysis';
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||
VariantAnalysisScannedRepositoryState
|
||||
} 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);
|
||||
}
|
||||
|
||||
public getView(variantAnalysisId: number): VariantAnalysisView | undefined {
|
||||
return this.views.get(variantAnalysisId);
|
||||
}
|
||||
|
||||
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
|
||||
if (!variantAnalysis) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getView(variantAnalysis.id)?.updateView(variantAnalysis);
|
||||
}
|
||||
|
||||
private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
|
||||
await this.getView(variantAnalysisId)?.updateRepoState(repoState);
|
||||
}
|
||||
|
||||
public async monitorVariantAnalysis(
|
||||
@@ -37,11 +84,19 @@ export class VariantAnalysisManager extends DisposableObject {
|
||||
variantAnalysisSummary: VariantAnalysisApiResponse,
|
||||
cancellationToken: CancellationToken
|
||||
): Promise<void> {
|
||||
const repoState = {
|
||||
repositoryId: scannedRepo.repository.id,
|
||||
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Pending,
|
||||
};
|
||||
|
||||
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
|
||||
|
||||
const credentials = await Credentials.initialize(this.ctx);
|
||||
if (!credentials) { throw Error('Error authenticating with GitHub'); }
|
||||
|
||||
if (cancellationToken && cancellationToken.isCancellationRequested) {
|
||||
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Failed;
|
||||
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,10 +108,16 @@ export class VariantAnalysisManager extends DisposableObject {
|
||||
variantAnalysisSummary.id,
|
||||
scannedRepo.repository.id
|
||||
);
|
||||
} catch (e) {
|
||||
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Failed;
|
||||
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
|
||||
throw new Error(`Could not download the results for variant analysis with id: ${variantAnalysisSummary.id}. Error: ${getErrorMessage(e)}`);
|
||||
}
|
||||
catch (e) { throw new Error(`Could not download the results for variant analysis with id: ${variantAnalysisSummary.id}. Error: ${getErrorMessage(e)}`); }
|
||||
|
||||
if (repoTask.artifact_url) {
|
||||
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.InProgress;
|
||||
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
|
||||
|
||||
const resultDirectory = path.join(
|
||||
this.ctx.globalStorageUri.fsPath,
|
||||
'variant-analyses',
|
||||
@@ -77,5 +138,8 @@ export class VariantAnalysisManager extends DisposableObject {
|
||||
fs.mkdirSync(resultDirectory, { recursive: true });
|
||||
await fs.writeFile(storagePath, JSON.stringify(result, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
|
||||
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
import { ExtensionContext, WebviewPanel, WebviewPanelSerializer } from 'vscode';
|
||||
import { VariantAnalysisView } from './variant-analysis-view';
|
||||
import { VariantAnalysisState } from '../pure/interface-types';
|
||||
import { VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
|
||||
export class VariantAnalysisViewSerializer implements WebviewPanelSerializer {
|
||||
private extensionLoaded = false;
|
||||
private readonly resolvePromises: (() => void)[] = [];
|
||||
private resolvePromises: ((value: VariantAnalysisViewManager<VariantAnalysisView>) => void)[] = [];
|
||||
|
||||
private manager?: VariantAnalysisViewManager<VariantAnalysisView>;
|
||||
|
||||
public constructor(
|
||||
private readonly ctx: ExtensionContext
|
||||
private readonly ctx: ExtensionContext,
|
||||
) { }
|
||||
|
||||
onExtensionLoaded(): void {
|
||||
this.extensionLoaded = true;
|
||||
this.resolvePromises.forEach((resolve) => resolve());
|
||||
onExtensionLoaded(manager: VariantAnalysisViewManager<VariantAnalysisView>): void {
|
||||
this.manager = manager;
|
||||
|
||||
this.resolvePromises.forEach((resolve) => resolve(manager));
|
||||
this.resolvePromises = [];
|
||||
}
|
||||
|
||||
async deserializeWebviewPanel(webviewPanel: WebviewPanel, state: unknown): Promise<void> {
|
||||
@@ -26,18 +30,18 @@ export class VariantAnalysisViewSerializer implements WebviewPanelSerializer {
|
||||
|
||||
const variantAnalysisState: VariantAnalysisState = state as VariantAnalysisState;
|
||||
|
||||
await this.waitForExtensionFullyLoaded();
|
||||
const manager = await this.waitForExtensionFullyLoaded();
|
||||
|
||||
const view = new VariantAnalysisView(this.ctx, variantAnalysisState.variantAnalysisId);
|
||||
const view = new VariantAnalysisView(this.ctx, variantAnalysisState.variantAnalysisId, manager);
|
||||
await view.restoreView(webviewPanel);
|
||||
}
|
||||
|
||||
private waitForExtensionFullyLoaded(): Promise<void> {
|
||||
if (this.extensionLoaded) {
|
||||
return Promise.resolve();
|
||||
private waitForExtensionFullyLoaded(): Promise<VariantAnalysisViewManager<VariantAnalysisView>> {
|
||||
if (this.manager) {
|
||||
return Promise.resolve(this.manager);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
return new Promise<VariantAnalysisViewManager<VariantAnalysisView>>((resolve) => {
|
||||
this.resolvePromises.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,17 +7,22 @@ import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisQueryLanguage,
|
||||
VariantAnalysisRepoStatus,
|
||||
VariantAnalysisScannedRepositoryState,
|
||||
VariantAnalysisStatus
|
||||
} from './shared/variant-analysis';
|
||||
import { VariantAnalysisViewInterface, VariantAnalysisViewManager } from './variant-analysis-view-manager';
|
||||
|
||||
export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage> {
|
||||
export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage> implements VariantAnalysisViewInterface {
|
||||
public static readonly viewType = 'codeQL.variantAnalysis';
|
||||
|
||||
public constructor(
|
||||
ctx: ExtensionContext,
|
||||
private readonly variantAnalysisId: number
|
||||
public readonly variantAnalysisId: number,
|
||||
private readonly manager: VariantAnalysisViewManager<VariantAnalysisView>,
|
||||
) {
|
||||
super(ctx);
|
||||
|
||||
manager.registerView(this);
|
||||
}
|
||||
|
||||
public async openView() {
|
||||
@@ -26,6 +31,28 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
|
||||
await this.waitForPanelLoaded();
|
||||
}
|
||||
|
||||
public async updateView(variantAnalysis: VariantAnalysis): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.postMessage({
|
||||
t: 'setVariantAnalysis',
|
||||
variantAnalysis,
|
||||
});
|
||||
}
|
||||
|
||||
public async updateRepoState(repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
|
||||
if (!this.isShowingPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.postMessage({
|
||||
t: 'setRepoStates',
|
||||
repoStates: [repoState],
|
||||
});
|
||||
}
|
||||
|
||||
protected getPanelConfig(): WebviewPanelConfig {
|
||||
return {
|
||||
viewId: VariantAnalysisView.viewType,
|
||||
@@ -37,7 +64,7 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
|
||||
}
|
||||
|
||||
protected onPanelDispose(): void {
|
||||
// Nothing to dispose currently.
|
||||
this.manager.unregisterView(this);
|
||||
}
|
||||
|
||||
protected async onMessage(msg: FromVariantAnalysisMessage): Promise<void> {
|
||||
|
||||
@@ -3,7 +3,10 @@ import React from 'react';
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
|
||||
import { VariantAnalysisContainer } from '../../view/variant-analysis/VariantAnalysisContainer';
|
||||
import { VariantAnalysisRepoStatus } from '../../remote-queries/shared/variant-analysis';
|
||||
import {
|
||||
VariantAnalysisRepoStatus,
|
||||
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { AnalysisAlert, AnalysisRawResults } from '../../remote-queries/shared/analysis-result';
|
||||
|
||||
import analysesResults from '../remote-queries/data/analysesResultsMessage.json';
|
||||
@@ -62,6 +65,14 @@ Canceled.args = {
|
||||
status: VariantAnalysisRepoStatus.Canceled,
|
||||
};
|
||||
|
||||
export const SucceededDownloading = Template.bind({});
|
||||
SucceededDownloading.args = {
|
||||
...Pending.args,
|
||||
status: VariantAnalysisRepoStatus.Succeeded,
|
||||
resultCount: 198,
|
||||
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.InProgress,
|
||||
};
|
||||
|
||||
export const InterpretedResults = Template.bind({});
|
||||
InterpretedResults.args = {
|
||||
...Pending.args,
|
||||
|
||||
@@ -2,7 +2,11 @@ import * as React from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { VSCodeBadge, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||
import { isCompletedAnalysisRepoStatus, VariantAnalysisRepoStatus } from '../../remote-queries/shared/variant-analysis';
|
||||
import {
|
||||
isCompletedAnalysisRepoStatus,
|
||||
VariantAnalysisRepoStatus,
|
||||
VariantAnalysisScannedRepositoryDownloadStatus
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { formatDecimal } from '../../pure/number';
|
||||
import { Codicon, ErrorIcon, LoadingIcon, SuccessIcon, WarningIcon } from '../common';
|
||||
import { Repository } from '../../remote-queries/shared/repository';
|
||||
@@ -62,6 +66,7 @@ export type RepoRowProps = {
|
||||
// Only fullName is required
|
||||
repository: Partial<Repository> & Pick<Repository, 'fullName'>;
|
||||
status?: VariantAnalysisRepoStatus;
|
||||
downloadStatus?: VariantAnalysisScannedRepositoryDownloadStatus;
|
||||
resultCount?: number;
|
||||
|
||||
interpretedResults?: AnalysisAlert[];
|
||||
@@ -71,6 +76,7 @@ export type RepoRowProps = {
|
||||
export const RepoRow = ({
|
||||
repository,
|
||||
status,
|
||||
downloadStatus,
|
||||
resultCount,
|
||||
interpretedResults,
|
||||
rawResults,
|
||||
@@ -99,6 +105,7 @@ export const RepoRow = ({
|
||||
{status === VariantAnalysisRepoStatus.InProgress && <LoadingIcon label="In progress" />}
|
||||
{!status && <WarningIcon />}
|
||||
</span>
|
||||
{downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.InProgress && <LoadingIcon label="Downloading" />}
|
||||
</TitleContainer>
|
||||
{isExpanded && status &&
|
||||
<AnalyzedRepoItemContent status={status} interpretedResults={interpretedResults} rawResults={rawResults} />}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { useEffect, useState } from 'react';
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisDomainModel,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState,
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { VariantAnalysisContainer } from './VariantAnalysisContainer';
|
||||
import { VariantAnalysisHeader } from './VariantAnalysisHeader';
|
||||
import { VariantAnalysisOutcomePanels } from './VariantAnalysisOutcomePanels';
|
||||
import { VariantAnalysisLoading } from './VariantAnalysisLoading';
|
||||
@@ -48,8 +48,50 @@ const repositoryResults: VariantAnalysisScannedRepositoryResult[] = [
|
||||
}
|
||||
];
|
||||
|
||||
function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
|
||||
if (variantAnalysis.actionsWorkflowRunId === undefined) {
|
||||
type Props = {
|
||||
variantAnalysis?: VariantAnalysisDomainModel;
|
||||
repoStates?: VariantAnalysisScannedRepositoryState[];
|
||||
repoResults?: VariantAnalysisScannedRepositoryResult[];
|
||||
}
|
||||
|
||||
export function VariantAnalysis({
|
||||
variantAnalysis: initialVariantAnalysis,
|
||||
repoStates: initialRepoStates = [],
|
||||
repoResults: initialRepoResults = repositoryResults,
|
||||
}: Props): JSX.Element {
|
||||
const [variantAnalysis, setVariantAnalysis] = useState<VariantAnalysisDomainModel | undefined>();
|
||||
const [repoStates, setRepoStates] = useState<VariantAnalysisScannedRepositoryState[]>(initialRepoStates);
|
||||
const [repoResults, setRepoResults] = useState<VariantAnalysisScannedRepositoryResult[]>(initialRepoResults);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', (evt: MessageEvent) => {
|
||||
if (evt.origin === window.origin) {
|
||||
const msg: ToVariantAnalysisMessage = evt.data;
|
||||
if (msg.t === 'setVariantAnalysis') {
|
||||
setVariantAnalysis(msg.variantAnalysis);
|
||||
vscode.setState({
|
||||
variantAnalysisId: msg.variantAnalysis.id,
|
||||
});
|
||||
} else if (msg.t === 'setRepoResults') {
|
||||
setRepoResults(oldRepoResults => {
|
||||
const newRepoIds = msg.repoResults.map(r => r.repositoryId);
|
||||
return [...oldRepoResults.filter(v => !newRepoIds.includes(v.repositoryId)), ...msg.repoResults];
|
||||
});
|
||||
} else if (msg.t === 'setRepoStates') {
|
||||
setRepoStates(oldRepoStates => {
|
||||
const newRepoIds = msg.repoStates.map(r => r.repositoryId);
|
||||
return [...oldRepoStates.filter(v => !newRepoIds.includes(v.repositoryId)), ...msg.repoStates];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// sanitize origin
|
||||
const origin = evt.origin.replace(/\n|\r/g, '');
|
||||
console.error(`Invalid event origin ${origin}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (variantAnalysis?.actionsWorkflowRunId === undefined) {
|
||||
return <VariantAnalysisLoading />;
|
||||
}
|
||||
|
||||
@@ -66,46 +108,9 @@ function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
|
||||
/>
|
||||
<VariantAnalysisOutcomePanels
|
||||
variantAnalysis={variantAnalysis}
|
||||
repositoryResults={repositoryResults}
|
||||
repositoryStates={repoStates}
|
||||
repositoryResults={repoResults}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type Props = {
|
||||
variantAnalysis?: VariantAnalysisDomainModel;
|
||||
}
|
||||
|
||||
export function VariantAnalysis({
|
||||
variantAnalysis: initialVariantAnalysis,
|
||||
}: Props): JSX.Element {
|
||||
const [variantAnalysis, setVariantAnalysis] = useState<VariantAnalysisDomainModel | undefined>(initialVariantAnalysis);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', (evt: MessageEvent) => {
|
||||
if (evt.origin === window.origin) {
|
||||
const msg: ToVariantAnalysisMessage = evt.data;
|
||||
if (msg.t === 'setVariantAnalysis') {
|
||||
setVariantAnalysis(msg.variantAnalysis);
|
||||
vscode.setState({
|
||||
variantAnalysisId: msg.variantAnalysis.id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// sanitize origin
|
||||
const origin = evt.origin.replace(/\n|\r/g, '');
|
||||
console.error(`Invalid event origin ${origin}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!variantAnalysis) {
|
||||
return <VariantAnalysisLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<VariantAnalysisContainer>
|
||||
{getContainerContents(variantAnalysis)}
|
||||
</VariantAnalysisContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { VariantAnalysis, VariantAnalysisScannedRepositoryResult } from '../../remote-queries/shared/variant-analysis';
|
||||
import { RepoRow } from './RepoRow';
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const Container = styled.div`
|
||||
@@ -12,13 +16,23 @@ const Container = styled.div`
|
||||
|
||||
export type VariantAnalysisAnalyzedReposProps = {
|
||||
variantAnalysis: VariantAnalysis;
|
||||
repositoryStates?: VariantAnalysisScannedRepositoryState[];
|
||||
repositoryResults?: VariantAnalysisScannedRepositoryResult[];
|
||||
}
|
||||
|
||||
export const VariantAnalysisAnalyzedRepos = ({
|
||||
variantAnalysis,
|
||||
repositoryStates,
|
||||
repositoryResults,
|
||||
}: VariantAnalysisAnalyzedReposProps) => {
|
||||
const repositoryStateById = useMemo(() => {
|
||||
const map = new Map<number, VariantAnalysisScannedRepositoryState>();
|
||||
repositoryStates?.forEach((repository) => {
|
||||
map.set(repository.repositoryId, repository);
|
||||
});
|
||||
return map;
|
||||
}, [repositoryStates]);
|
||||
|
||||
const repositoryResultsById = useMemo(() => {
|
||||
const map = new Map<number, VariantAnalysisScannedRepositoryResult>();
|
||||
repositoryResults?.forEach((repository) => {
|
||||
@@ -30,6 +44,7 @@ export const VariantAnalysisAnalyzedRepos = ({
|
||||
return (
|
||||
<Container>
|
||||
{variantAnalysis.scannedRepos?.map(repository => {
|
||||
const state = repositoryStateById.get(repository.repository.id);
|
||||
const results = repositoryResultsById.get(repository.repository.id);
|
||||
|
||||
return (
|
||||
@@ -37,6 +52,7 @@ export const VariantAnalysisAnalyzedRepos = ({
|
||||
key={repository.repository.id}
|
||||
repository={repository.repository}
|
||||
status={repository.analysisStatus}
|
||||
downloadStatus={state?.downloadStatus}
|
||||
resultCount={repository.resultCount}
|
||||
interpretedResults={results?.interpretedResults}
|
||||
rawResults={results?.rawResults}
|
||||
|
||||
@@ -2,13 +2,18 @@ import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { VSCodeBadge, VSCodePanels, VSCodePanelTab, VSCodePanelView } from '@vscode/webview-ui-toolkit/react';
|
||||
import { formatDecimal } from '../../pure/number';
|
||||
import { VariantAnalysis, VariantAnalysisScannedRepositoryResult } from '../../remote-queries/shared/variant-analysis';
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { VariantAnalysisAnalyzedRepos } from './VariantAnalysisAnalyzedRepos';
|
||||
import { Alert } from '../common';
|
||||
import { VariantAnalysisSkippedRepositoriesTab } from './VariantAnalysisSkippedRepositoriesTab';
|
||||
|
||||
export type VariantAnalysisOutcomePanelProps = {
|
||||
variantAnalysis: VariantAnalysis;
|
||||
repositoryStates?: VariantAnalysisScannedRepositoryState[];
|
||||
repositoryResults?: VariantAnalysisScannedRepositoryResult[];
|
||||
};
|
||||
|
||||
@@ -34,6 +39,7 @@ const WarningsContainer = styled.div`
|
||||
|
||||
export const VariantAnalysisOutcomePanels = ({
|
||||
variantAnalysis,
|
||||
repositoryStates,
|
||||
repositoryResults,
|
||||
}: VariantAnalysisOutcomePanelProps) => {
|
||||
const noCodeqlDbRepos = variantAnalysis.skippedRepos?.noCodeqlDbRepos;
|
||||
@@ -64,7 +70,11 @@ export const VariantAnalysisOutcomePanels = ({
|
||||
return (
|
||||
<>
|
||||
{warnings}
|
||||
<VariantAnalysisAnalyzedRepos variantAnalysis={variantAnalysis} repositoryResults={repositoryResults} />
|
||||
<VariantAnalysisAnalyzedRepos
|
||||
variantAnalysis={variantAnalysis}
|
||||
repositoryStates={repositoryStates}
|
||||
repositoryResults={repositoryResults}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -89,7 +99,13 @@ export const VariantAnalysisOutcomePanels = ({
|
||||
<VSCodeBadge appearance="secondary">{formatDecimal(noCodeqlDbRepos.repositoryCount)}</VSCodeBadge>
|
||||
</Tab>
|
||||
)}
|
||||
<VSCodePanelView><VariantAnalysisAnalyzedRepos variantAnalysis={variantAnalysis} repositoryResults={repositoryResults} /></VSCodePanelView>
|
||||
<VSCodePanelView>
|
||||
<VariantAnalysisAnalyzedRepos
|
||||
variantAnalysis={variantAnalysis}
|
||||
repositoryStates={repositoryStates}
|
||||
repositoryResults={repositoryResults}
|
||||
/>
|
||||
</VSCodePanelView>
|
||||
{notFoundRepos?.repositoryCount &&
|
||||
<VSCodePanelView>
|
||||
<VariantAnalysisSkippedRepositoriesTab
|
||||
|
||||
@@ -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