Use selected repositories for copying repo lists

This will use the selected repositories to limit which repositories are
included in the copied repo list. If there are both selected
repositories and a search filter (on the full name), the search filter
will be ignored and the selected repositories will be used in full.
This commit is contained in:
Koen Vlaswinkel
2022-11-15 11:23:01 +01:00
parent 73ff057df0
commit 4ed409d91a
6 changed files with 134 additions and 17 deletions

View File

@@ -116,7 +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';
import { RepositoriesFilterSortStateWithIds } from './pure/variant-analysis-filter-sort';
/**
* extension.ts
@@ -946,7 +946,7 @@ async function activateWithInstalledDistribution(
);
ctx.subscriptions.push(
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number, filterSort?: RepositoriesFilterSortState) => {
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number, filterSort?: RepositoriesFilterSortStateWithIds) => {
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysisId, filterSort);
})
);

View File

@@ -7,7 +7,7 @@ import {
VariantAnalysisScannedRepositoryResult,
VariantAnalysisScannedRepositoryState,
} from '../remote-queries/shared/variant-analysis';
import { RepositoriesFilterSortState } from './variant-analysis-filter-sort';
import { RepositoriesFilterSortStateWithIds } from './variant-analysis-filter-sort';
/**
* This module contains types and code that are shared between
@@ -475,7 +475,7 @@ export interface OpenQueryTextMessage {
export interface CopyRepositoryListMessage {
t: 'copyRepositoryList';
filterSort?: RepositoriesFilterSortState;
filterSort?: RepositoriesFilterSortStateWithIds;
}
export interface OpenLogsMessage {

View File

@@ -13,6 +13,10 @@ export type RepositoriesFilterSortState = {
sortKey: SortKey;
}
export type RepositoriesFilterSortStateWithIds = RepositoriesFilterSortState & {
repositoryIds?: number[];
}
export const defaultFilterSortState: RepositoriesFilterSortState = {
searchValue: '',
sortKey: SortKey.Name,
@@ -52,7 +56,7 @@ export function compareRepository(filterSortState: RepositoriesFilterSortState |
}
type SortableResult = {
repository: SortableRepository;
repository: SortableRepository & Pick<Repository, 'id'>;
resultCount?: number;
}
@@ -72,16 +76,30 @@ export function compareWithResults(filterSortState: RepositoriesFilterSortState
};
}
// 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;
function hasRepositoryIds(filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): filterSortState is RepositoriesFilterSortStateWithIds {
if (!filterSortState) {
return false;
}
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | undefined): T[] | undefined {
return 'repositoryIds' in filterSortState;
}
function isFilterOnRepositoryIds(filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): filterSortState is RepositoriesFilterSortStateWithIds & Required<Pick<RepositoriesFilterSortStateWithIds, 'repositoryIds'>> {
return hasRepositoryIds(filterSortState) && filterSortState.repositoryIds !== undefined && filterSortState.repositoryIds.length > 0;
}
// These define the behavior for undefined input values
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[], filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[];
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[] | undefined;
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[] | undefined {
if (!repositories) {
return undefined;
}
return repositories
.filter(repo => matchesFilter(repo.repository, filterSortState))
.sort(compareWithResults(filterSortState));
const filteredRepositories = isFilterOnRepositoryIds(filterSortState) ?
repositories.filter(repo => filterSortState.repositoryIds.includes(repo.repository.id)) :
repositories.filter(repo => matchesFilter(repo.repository, filterSortState));
return filteredRepositories.sort(compareWithResults(filterSortState));
}

View File

@@ -35,7 +35,7 @@ import { CodeQLCliServer } from '../cli';
import {
defaultFilterSortState,
filterAndSortRepositoriesWithResults,
RepositoriesFilterSortState,
RepositoriesFilterSortStateWithIds,
} from '../pure/variant-analysis-filter-sort';
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
@@ -373,7 +373,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
await cancelVariantAnalysis(credentials, variantAnalysis);
}
public async copyRepoListToClipboard(variantAnalysisId: number, filterSort: RepositoriesFilterSortState = defaultFilterSortState) {
public async copyRepoListToClipboard(variantAnalysisId: number, filterSort: RepositoriesFilterSortStateWithIds = defaultFilterSortState) {
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
if (!variantAnalysis) {
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);

View File

@@ -91,9 +91,12 @@ export function VariantAnalysis({
const copyRepositoryList = useCallback(() => {
vscode.postMessage({
t: 'copyRepositoryList',
filterSort: filterSortState,
filterSort: {
...filterSortState,
repositoryIds: selectedRepositoryIds,
},
});
}, [filterSortState]);
}, [filterSortState, selectedRepositoryIds]);
if (variantAnalysis?.actionsWorkflowRunId === undefined) {
return <VariantAnalysisLoading />;

View File

@@ -1,4 +1,11 @@
import { compareRepository, compareWithResults, defaultFilterSortState, matchesFilter, SortKey } from '../../../pure/variant-analysis-filter-sort';
import {
compareRepository,
compareWithResults,
defaultFilterSortState,
filterAndSortRepositoriesWithResults,
matchesFilter,
SortKey,
} from '../../../pure/variant-analysis-filter-sort';
// TODO: Move this file to the "pure" tests once it has been switched to Jest
describe(matchesFilter.name, () => {
@@ -170,11 +177,13 @@ describe(compareWithResults.name, () => {
const left = {
repository: {
id: 10,
fullName: 'github/galaxy',
},
};
const right = {
repository: {
id: 12,
fullName: 'github/world',
},
};
@@ -192,12 +201,14 @@ describe(compareWithResults.name, () => {
const left = {
repository: {
id: 11,
fullName: 'github/galaxy',
stargazersCount: 1,
},
};
const right = {
repository: {
id: 12,
fullName: 'github/world',
stargazersCount: 10,
},
@@ -216,12 +227,14 @@ describe(compareWithResults.name, () => {
const left = {
repository: {
id: 11,
fullName: 'github/galaxy',
updatedAt: '2020-01-01T00:00:00Z',
},
};
const right = {
repository: {
id: 12,
fullName: 'github/world',
updatedAt: '2021-01-01T00:00:00Z',
},
@@ -240,12 +253,14 @@ describe(compareWithResults.name, () => {
const left = {
repository: {
id: 11,
fullName: 'github/galaxy',
},
resultCount: 10,
};
const right = {
repository: {
id: 12,
fullName: 'github/world',
},
resultCount: 100,
@@ -278,3 +293,84 @@ describe(compareWithResults.name, () => {
});
});
});
describe(filterAndSortRepositoriesWithResults.name, () => {
const repositories = [
{
repository: {
id: 10,
fullName: 'github/galaxy',
},
resultCount: 10,
},
{
repository: {
id: 11,
fullName: 'github/world',
},
resultCount: undefined,
},
{
repository: {
id: 13,
fullName: 'github/planet',
},
resultCount: 500,
},
{
repository: {
id: 783532,
fullName: 'github/stars',
},
resultCount: 8000,
}
];
describe('when sort key is given without filter', () => {
it('returns the correct results', () => {
expect(filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
})).toEqual([repositories[3], repositories[2], repositories[0], repositories[1]]);
});
});
describe('when sort key and search filter are given', () => {
it('returns the correct results', () => {
expect(filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
searchValue: 'la',
})).toEqual([repositories[2], repositories[0]]);
});
});
describe('when sort key, search filter, and repository ids are given', () => {
it('returns the correct results', () => {
expect(filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
searchValue: 'la',
repositoryIds: [repositories[1].repository.id, repositories[3].repository.id],
})).toEqual([repositories[3], repositories[1]]);
});
});
describe('when repository ids are given', () => {
it('returns the correct results', () => {
expect(filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
repositoryIds: [repositories[0].repository.id, repositories[3].repository.id],
})).toEqual([repositories[0], repositories[3]]);
});
});
describe('when empty repository ids are given', () => {
it('returns the correct results', () => {
expect(filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
repositoryIds: [],
})).toEqual([repositories[0], repositories[2], repositories[3], repositories[1]]);
});
});
});