Add filtering and sorting to exported repo list
This will pass the filter and sort parameters in the export repo list message so it can be used by the command to filter and sort the repositories which are placed in the repo list.
This commit is contained in:
@@ -116,6 +116,7 @@ import { createVariantAnalysisContentProvider } from './remote-queries/variant-a
|
||||
import { VSCodeMockGitHubApiServer } from './mocks/vscode-mock-gh-api-server';
|
||||
import { VariantAnalysisResultsManager } from './remote-queries/variant-analysis-results-manager';
|
||||
import { initializeDbModule } from './databases/db-module';
|
||||
import { RepositoriesFilterSortState } from './pure/variant-analysis-filter-sort';
|
||||
|
||||
/**
|
||||
* extension.ts
|
||||
@@ -945,8 +946,8 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number) => {
|
||||
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysisId);
|
||||
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number, filterSort?: RepositoriesFilterSortState) => {
|
||||
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysisId, filterSort);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState,
|
||||
} from '../remote-queries/shared/variant-analysis';
|
||||
import { RepositoriesFilterSortState } from './variant-analysis-filter-sort';
|
||||
|
||||
/**
|
||||
* This module contains types and code that are shared between
|
||||
@@ -474,6 +475,7 @@ export interface OpenQueryTextMessage {
|
||||
|
||||
export interface CopyRepositoryListMessage {
|
||||
t: 'copyRepositoryList';
|
||||
filterSort?: RepositoriesFilterSortState;
|
||||
}
|
||||
|
||||
export interface OpenLogsMessage {
|
||||
|
||||
@@ -71,3 +71,17 @@ export function compareWithResults(filterSortState: RepositoriesFilterSortState
|
||||
return fallbackSort(left.repository, right.repository);
|
||||
};
|
||||
}
|
||||
|
||||
// These define the behavior for undefined input values
|
||||
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[], filterSortState: RepositoriesFilterSortState | undefined): T[];
|
||||
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | undefined): T[] | undefined;
|
||||
|
||||
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | undefined): T[] | undefined {
|
||||
if (!repositories) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return repositories
|
||||
.filter(repo => matchesFilter(repo.repository, filterSortState))
|
||||
.sort(compareWithResults(filterSortState));
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ import * as os from 'os';
|
||||
import { cancelVariantAnalysis } from './gh-api/gh-actions-api-client';
|
||||
import { ProgressCallback, UserCancellationException } from '../commandRunner';
|
||||
import { CodeQLCliServer } from '../cli';
|
||||
import {
|
||||
defaultFilterSortState,
|
||||
filterAndSortRepositoriesWithResults,
|
||||
RepositoriesFilterSortState,
|
||||
} from '../pure/variant-analysis-filter-sort';
|
||||
|
||||
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
|
||||
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
|
||||
@@ -368,13 +373,15 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
||||
await cancelVariantAnalysis(credentials, variantAnalysis);
|
||||
}
|
||||
|
||||
public async copyRepoListToClipboard(variantAnalysisId: number) {
|
||||
public async copyRepoListToClipboard(variantAnalysisId: number, filterSort: RepositoriesFilterSortState = defaultFilterSortState) {
|
||||
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
|
||||
if (!variantAnalysis) {
|
||||
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
||||
}
|
||||
|
||||
const fullNames = variantAnalysis.scannedRepos?.filter(a => a.resultCount && a.resultCount > 0).map(a => a.repository.fullName);
|
||||
const filteredRepositories = filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos ?? [], filterSort);
|
||||
|
||||
const fullNames = filteredRepositories.filter(a => a.resultCount && a.resultCount > 0).map(a => a.repository.fullName);
|
||||
if (!fullNames || fullNames.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
|
||||
await this.openQueryText();
|
||||
break;
|
||||
case 'copyRepositoryList':
|
||||
void commands.executeCommand('codeQL.copyVariantAnalysisRepoList', this.variantAnalysisId);
|
||||
void commands.executeCommand('codeQL.copyVariantAnalysisRepoList', this.variantAnalysisId, msg.filterSort);
|
||||
break;
|
||||
case 'openLogs':
|
||||
await this.openLogs();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
|
||||
@@ -8,6 +8,7 @@ import { VariantAnalysisRepoStatus, VariantAnalysisStatus } from '../../remote-q
|
||||
import { createMockScannedRepo } from '../../vscode-tests/factories/remote-queries/shared/scanned-repositories';
|
||||
import { createMockVariantAnalysis } from '../../vscode-tests/factories/remote-queries/shared/variant-analysis';
|
||||
import { createMockRepositoryWithMetadata } from '../../vscode-tests/factories/remote-queries/shared/repository';
|
||||
import { defaultFilterSortState, RepositoriesFilterSortState } from '../../pure/variant-analysis-filter-sort';
|
||||
|
||||
export default {
|
||||
title: 'Variant Analysis/Variant Analysis Outcome Panels',
|
||||
@@ -21,9 +22,13 @@ export default {
|
||||
],
|
||||
} as ComponentMeta<typeof VariantAnalysisOutcomePanels>;
|
||||
|
||||
const Template: ComponentStory<typeof VariantAnalysisOutcomePanels> = (args) => (
|
||||
<VariantAnalysisOutcomePanels {...args} />
|
||||
);
|
||||
const Template: ComponentStory<typeof VariantAnalysisOutcomePanels> = (args) => {
|
||||
const [filterSortState, setFilterSortState] = useState<RepositoriesFilterSortState>(defaultFilterSortState);
|
||||
|
||||
return (
|
||||
<VariantAnalysisOutcomePanels {...args} filterSortState={filterSortState} setFilterSortState={setFilterSortState} />
|
||||
);
|
||||
};
|
||||
|
||||
export const WithoutSkippedRepos = Template.bind({});
|
||||
WithoutSkippedRepos.args = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
VariantAnalysis as VariantAnalysisDomainModel,
|
||||
@@ -11,6 +11,7 @@ import { VariantAnalysisOutcomePanels } from './VariantAnalysisOutcomePanels';
|
||||
import { VariantAnalysisLoading } from './VariantAnalysisLoading';
|
||||
import { ToVariantAnalysisMessage } from '../../pure/interface-types';
|
||||
import { vscode } from '../vscode-api';
|
||||
import { defaultFilterSortState, RepositoriesFilterSortState } from '../../pure/variant-analysis-filter-sort';
|
||||
|
||||
type Props = {
|
||||
variantAnalysis?: VariantAnalysisDomainModel;
|
||||
@@ -36,12 +37,6 @@ const stopQuery = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const copyRepositoryList = () => {
|
||||
vscode.postMessage({
|
||||
t: 'copyRepositoryList',
|
||||
});
|
||||
};
|
||||
|
||||
const openLogs = () => {
|
||||
vscode.postMessage({
|
||||
t: 'openLogs',
|
||||
@@ -57,6 +52,8 @@ export function VariantAnalysis({
|
||||
const [repoStates, setRepoStates] = useState<VariantAnalysisScannedRepositoryState[]>(initialRepoStates);
|
||||
const [repoResults, setRepoResults] = useState<VariantAnalysisScannedRepositoryResult[]>(initialRepoResults);
|
||||
|
||||
const [filterSortState, setFilterSortState] = useState<RepositoriesFilterSortState>(defaultFilterSortState);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (evt: MessageEvent) => {
|
||||
if (evt.origin === window.origin) {
|
||||
@@ -90,6 +87,13 @@ export function VariantAnalysis({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const copyRepositoryList = useCallback(() => {
|
||||
vscode.postMessage({
|
||||
t: 'copyRepositoryList',
|
||||
filterSort: filterSortState,
|
||||
});
|
||||
}, [filterSortState]);
|
||||
|
||||
if (variantAnalysis?.actionsWorkflowRunId === undefined) {
|
||||
return <VariantAnalysisLoading />;
|
||||
}
|
||||
@@ -109,6 +113,8 @@ export function VariantAnalysis({
|
||||
variantAnalysis={variantAnalysis}
|
||||
repositoryStates={repoStates}
|
||||
repositoryResults={repoResults}
|
||||
filterSortState={filterSortState}
|
||||
setFilterSortState={setFilterSortState}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
VariantAnalysisScannedRepositoryState
|
||||
} from '../../remote-queries/shared/variant-analysis';
|
||||
import { compareWithResults, matchesFilter, RepositoriesFilterSortState } from '../../pure/variant-analysis-filter-sort';
|
||||
import {
|
||||
filterAndSortRepositoriesWithResults,
|
||||
RepositoriesFilterSortState,
|
||||
} from '../../pure/variant-analysis-filter-sort';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -47,9 +50,7 @@ export const VariantAnalysisAnalyzedRepos = ({
|
||||
}, [repositoryResults]);
|
||||
|
||||
const repositories = useMemo(() => {
|
||||
return variantAnalysis.scannedRepos?.filter((repoTask) => {
|
||||
return matchesFilter(repoTask.repository, filterSortState);
|
||||
})?.sort(compareWithResults(filterSortState));
|
||||
return filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos, filterSortState);
|
||||
}, [filterSortState, variantAnalysis.scannedRepos]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { VSCodeBadge, VSCodePanels, VSCodePanelTab, VSCodePanelView } from '@vscode/webview-ui-toolkit/react';
|
||||
import { formatDecimal } from '../../pure/number';
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { VariantAnalysisAnalyzedRepos } from './VariantAnalysisAnalyzedRepos';
|
||||
import { Alert } from '../common';
|
||||
import { VariantAnalysisSkippedRepositoriesTab } from './VariantAnalysisSkippedRepositoriesTab';
|
||||
import { defaultFilterSortState, RepositoriesFilterSortState } from '../../pure/variant-analysis-filter-sort';
|
||||
import { RepositoriesFilterSortState } from '../../pure/variant-analysis-filter-sort';
|
||||
import { RepositoriesSearchSortRow } from './RepositoriesSearchSortRow';
|
||||
import { FailureReasonAlert } from './FailureReasonAlert';
|
||||
|
||||
@@ -20,6 +20,9 @@ export type VariantAnalysisOutcomePanelProps = {
|
||||
variantAnalysis: VariantAnalysis;
|
||||
repositoryStates?: VariantAnalysisScannedRepositoryState[];
|
||||
repositoryResults?: VariantAnalysisScannedRepositoryResult[];
|
||||
|
||||
filterSortState: RepositoriesFilterSortState;
|
||||
setFilterSortState: Dispatch<SetStateAction<RepositoriesFilterSortState>>;
|
||||
};
|
||||
|
||||
const Tab = styled(VSCodePanelTab)`
|
||||
@@ -46,9 +49,9 @@ export const VariantAnalysisOutcomePanels = ({
|
||||
variantAnalysis,
|
||||
repositoryStates,
|
||||
repositoryResults,
|
||||
filterSortState,
|
||||
setFilterSortState,
|
||||
}: VariantAnalysisOutcomePanelProps) => {
|
||||
const [filterSortState, setFilterSortState] = useState<RepositoriesFilterSortState>(defaultFilterSortState);
|
||||
|
||||
const scannedReposCount = variantAnalysis.scannedRepos?.length ?? 0;
|
||||
const noCodeqlDbRepos = variantAnalysis.skippedRepos?.noCodeqlDbRepos;
|
||||
const notFoundRepos = variantAnalysis.skippedRepos?.notFoundRepos;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
createMockScannedRepo,
|
||||
createMockScannedRepos
|
||||
} from '../../../vscode-tests/factories/remote-queries/shared/scanned-repositories';
|
||||
import { defaultFilterSortState } from '../../../pure/variant-analysis-filter-sort';
|
||||
|
||||
describe(VariantAnalysisOutcomePanels.name, () => {
|
||||
const defaultVariantAnalysis = {
|
||||
@@ -81,6 +82,8 @@ describe(VariantAnalysisOutcomePanels.name, () => {
|
||||
...defaultVariantAnalysis,
|
||||
...variantAnalysis,
|
||||
}}
|
||||
filterSortState={defaultFilterSortState}
|
||||
setFilterSortState={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CancellationTokenSource, commands, env, extensions, QuickPickItem, Uri,
|
||||
import { CodeQLExtensionInterface } from '../../../extension';
|
||||
import { logger } from '../../../logging';
|
||||
import * as config from '../../../config';
|
||||
import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../../config';
|
||||
import * as ghApiClient from '../../../remote-queries/gh-api/gh-api-client';
|
||||
import * as ghActionsApiClient from '../../../remote-queries/gh-api/gh-actions-api-client';
|
||||
import { Credentials } from '../../../authentication';
|
||||
@@ -35,7 +36,7 @@ import {
|
||||
import { createMockApiResponse } from '../../factories/remote-queries/gh-api/variant-analysis-api-response';
|
||||
import { UserCancellationException } from '../../../commandRunner';
|
||||
import { Repository } from '../../../remote-queries/gh-api/repository';
|
||||
import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../../config';
|
||||
import { defaultFilterSortState, SortKey } from '../../../pure/variant-analysis-filter-sort';
|
||||
|
||||
describe('Variant Analysis Manager', async function() {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
@@ -766,23 +767,23 @@ describe('Variant Analysis Manager', async function() {
|
||||
describe('when the variant analysis has repositories with results', () => {
|
||||
const scannedRepos = [
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
...createMockScannedRepo('pear'),
|
||||
resultCount: 100,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
...createMockScannedRepo('apple'),
|
||||
resultCount: 0,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
...createMockScannedRepo('citrus'),
|
||||
resultCount: 200,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
...createMockScannedRepo('sky'),
|
||||
resultCount: undefined,
|
||||
},
|
||||
{
|
||||
...createMockScannedRepo(),
|
||||
...createMockScannedRepo('banana'),
|
||||
resultCount: 5,
|
||||
},
|
||||
];
|
||||
@@ -809,8 +810,44 @@ describe('Variant Analysis Manager', async function() {
|
||||
|
||||
expect(parsed).to.deep.eq({
|
||||
'new-repo-list': [
|
||||
scannedRepos[0].repository.fullName,
|
||||
scannedRepos[4].repository.fullName,
|
||||
scannedRepos[2].repository.fullName,
|
||||
scannedRepos[0].repository.fullName,
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the sort key', async () => {
|
||||
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysis.id, {
|
||||
...defaultFilterSortState,
|
||||
sortKey: SortKey.ResultsCount,
|
||||
});
|
||||
|
||||
const text = writeTextStub.getCalls()[0].lastArg;
|
||||
|
||||
const parsed = JSON.parse('{' + text + '}');
|
||||
|
||||
expect(parsed).to.deep.eq({
|
||||
'new-repo-list': [
|
||||
scannedRepos[2].repository.fullName,
|
||||
scannedRepos[0].repository.fullName,
|
||||
scannedRepos[4].repository.fullName,
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the search value', async () => {
|
||||
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysis.id, {
|
||||
...defaultFilterSortState,
|
||||
searchValue: 'ban',
|
||||
});
|
||||
|
||||
const text = writeTextStub.getCalls()[0].lastArg;
|
||||
|
||||
const parsed = JSON.parse('{' + text + '}');
|
||||
|
||||
expect(parsed).to.deep.eq({
|
||||
'new-repo-list': [
|
||||
scannedRepos[4].repository.fullName,
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user