Skip to content

Commit 8a10a49

Browse files
authored
Merge pull request #1639 from github/shati-patel/repo-count
Implement query history label for variant analysis items
2 parents 40d281a + 8737cfd commit 8a10a49

File tree

8 files changed

+125
-30
lines changed

8 files changed

+125
-30
lines changed

extensions/ql-vscode/src/helpers.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -584,11 +584,3 @@ export async function* walkDirectory(dir: string): AsyncIterableIterator<string>
584584
}
585585
}
586586
}
587-
588-
/**
589-
* Pluralizes a word.
590-
* Example: Returns "N repository" if N is one, "N repositories" otherwise.
591-
*/
592-
export function pluralize(numItems: number | undefined, singular: string, plural: string): string {
593-
return numItems ? `${numItems} ${numItems === 1 ? singular : plural}` : '';
594-
}

extensions/ql-vscode/src/history-item-label-provider.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { env } from 'vscode';
22
import * as path from 'path';
33
import { QueryHistoryConfig } from './config';
44
import { LocalQueryInfo } from './query-results';
5-
import { getRawQueryName, QueryHistoryInfo } from './query-history-info';
5+
import { buildRepoLabel, getRawQueryName, QueryHistoryInfo } from './query-history-info';
66
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
7-
import { pluralize } from './helpers';
87
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
98
import { assertNever } from './pure/helpers-pure';
9+
import { pluralize } from './pure/word';
1010

1111
interface InterpolateReplacements {
1212
t: string; // Start time
@@ -79,23 +79,12 @@ export class HistoryItemLabelProvider {
7979
};
8080
}
8181

82-
// Return the number of repositories queried if available. Otherwise, use the controller repository name.
83-
private buildRepoLabel(item: RemoteQueryHistoryItem): string {
84-
const repositoryCount = item.remoteQuery.repositoryCount;
85-
86-
if (repositoryCount) {
87-
return pluralize(repositoryCount, 'repository', 'repositories');
88-
}
89-
90-
return `${item.remoteQuery.controllerRepository.owner}/${item.remoteQuery.controllerRepository.name}`;
91-
}
92-
9382
private getRemoteInterpolateReplacements(item: RemoteQueryHistoryItem): InterpolateReplacements {
9483
const resultCount = item.resultCount ? `(${pluralize(item.resultCount, 'result', 'results')})` : '';
9584
return {
9685
t: new Date(item.remoteQuery.executionStartTime).toLocaleString(env.language),
9786
q: `${item.remoteQuery.queryName} (${item.remoteQuery.language})`,
98-
d: this.buildRepoLabel(item),
87+
d: buildRepoLabel(item),
9988
r: resultCount,
10089
s: item.status,
10190
f: path.basename(item.remoteQuery.queryFilePath),
@@ -108,7 +97,7 @@ export class HistoryItemLabelProvider {
10897
return {
10998
t: new Date(item.variantAnalysis.executionStartTime).toLocaleString(env.language),
11099
q: `${item.variantAnalysis.query.name} (${item.variantAnalysis.query.language})`,
111-
d: 'TODO',
100+
d: buildRepoLabel(item),
112101
r: resultCount,
113102
s: item.status,
114103
f: path.basename(item.variantAnalysis.query.filePath),
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Pluralizes a word.
3+
* Example: Returns "N repository" if N is one, "N repositories" otherwise.
4+
*/
5+
6+
export function pluralize(numItems: number | undefined, singular: string, plural: string): string {
7+
return numItems !== undefined ? `${numItems} ${numItems === 1 ? singular : plural}` : '';
8+
}

extensions/ql-vscode/src/query-history-info.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-it
22
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
33
import { LocalQueryInfo } from './query-results';
44
import { assertNever } from './pure/helpers-pure';
5+
import { pluralize } from './pure/word';
6+
import { hasRepoScanCompleted } from './remote-queries/shared/variant-analysis';
57

68
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem | VariantAnalysisHistoryItem;
79

@@ -43,3 +45,22 @@ export function getQueryText(item: QueryHistoryInfo): string {
4345
assertNever(item);
4446
}
4547
}
48+
49+
export function buildRepoLabel(item: RemoteQueryHistoryItem | VariantAnalysisHistoryItem): string {
50+
if (item.t === 'remote') {
51+
// Return the number of repositories queried if available. Otherwise, use the controller repository name.
52+
const repositoryCount = item.remoteQuery.repositoryCount;
53+
54+
if (repositoryCount) {
55+
return pluralize(repositoryCount, 'repository', 'repositories');
56+
}
57+
return `${item.remoteQuery.controllerRepository.owner}/${item.remoteQuery.controllerRepository.name}`;
58+
} else if (item.t === 'variant-analysis') {
59+
const totalScannedRepositoryCount = item.variantAnalysis.scannedRepos?.length ?? 0;
60+
const completedRepositoryCount = item.variantAnalysis.scannedRepos?.filter(repo => hasRepoScanCompleted(repo)).length ?? 0;
61+
62+
return `${completedRepositoryCount}/${pluralize(totalScannedRepositoryCount, 'repository', 'repositories')}`; // e.g. "2/3 repositories"
63+
} else {
64+
assertNever(item);
65+
}
66+
}

extensions/ql-vscode/src/remote-queries/export-results.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import * as fs from 'fs-extra';
44
import { window, commands, Uri, ExtensionContext, QuickPickItem, workspace, ViewColumn } from 'vscode';
55
import { Credentials } from '../authentication';
66
import { UserCancellationException } from '../commandRunner';
7-
import {
8-
showInformationMessageWithAction,
9-
pluralize
10-
} from '../helpers';
7+
import { showInformationMessageWithAction } from '../helpers';
118
import { logger } from '../logging';
129
import { QueryHistoryManager } from '../query-history';
1310
import { createGist } from './gh-api/gh-actions-api-client';
@@ -16,6 +13,7 @@ import { generateMarkdown } from './remote-queries-markdown-generation';
1613
import { RemoteQuery } from './remote-query';
1714
import { AnalysisResults, sumAnalysesResults } from './shared/analysis-result';
1815
import { RemoteQueryHistoryItem } from './remote-query-history-item';
16+
import { pluralize } from '../pure/word';
1917

2018
/**
2119
* Exports the results of the given or currently-selected remote query.

extensions/ql-vscode/src/remote-queries/run-remote-query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
showAndLogErrorMessage,
1212
showAndLogInformationMessage,
1313
tryGetQueryMetadata,
14-
pluralize,
1514
tmpDir,
1615
} from '../helpers';
1716
import { Credentials } from '../authentication';
@@ -24,6 +23,7 @@ import { RemoteQuery } from './remote-query';
2423
import { RemoteQuerySubmissionResult } from './remote-query-submission-result';
2524
import { QueryMetadata } from '../pure/interface-types';
2625
import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure';
26+
import { pluralize } from '../pure/word';
2727
import * as ghApiClient from './gh-api/gh-api-client';
2828
import { getRepositorySelection, isValidSelection, RepositorySelection } from './repository-selection';
2929
import { parseVariantAnalysisQueryLanguage, VariantAnalysisSubmission } from './shared/variant-analysis';

extensions/ql-vscode/test/pure-tests/query-history-info.test.ts

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { expect } from 'chai';
22

33
import { QueryStatus } from '../../src/query-status';
4-
import { getQueryHistoryItemId, getQueryText, getRawQueryName } from '../../src/query-history-info';
4+
import { buildRepoLabel, getQueryHistoryItemId, getQueryText, getRawQueryName } from '../../src/query-history-info';
55
import { VariantAnalysisHistoryItem } from '../../src/remote-queries/variant-analysis-history-item';
66
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
7+
import { createMockScannedRepos } from '../../src/vscode-tests/factories/remote-queries/shared/scanned-repositories';
78
import { createMockLocalQueryInfo } from '../../src/vscode-tests/factories/local-queries/local-query-history-item';
89
import { createMockRemoteQueryHistoryItem } from '../../src/vscode-tests/factories/remote-queries/remote-query-history-item';
10+
import { VariantAnalysisRepoStatus, VariantAnalysisStatus } from '../../src/remote-queries/shared/variant-analysis';
911

1012
describe('Query history info', () => {
1113

@@ -18,7 +20,15 @@ describe('Query history info', () => {
1820
status: QueryStatus.InProgress,
1921
completed: false,
2022
historyItemId: 'abc123',
21-
variantAnalysis: createMockVariantAnalysis()
23+
variantAnalysis: createMockVariantAnalysis(
24+
VariantAnalysisStatus.InProgress,
25+
createMockScannedRepos([
26+
VariantAnalysisRepoStatus.Succeeded,
27+
VariantAnalysisRepoStatus.Pending,
28+
VariantAnalysisRepoStatus.InProgress,
29+
VariantAnalysisRepoStatus.Canceled,
30+
])
31+
),
2232
};
2333

2434
describe('getRawQueryName', () => {
@@ -80,4 +90,61 @@ describe('Query history info', () => {
8090
expect(queryText).to.equal(variantAnalysisHistoryItem.variantAnalysis.query.text);
8191
});
8292
});
93+
94+
describe('buildRepoLabel', () => {
95+
describe('repo label for remote query history items', () => {
96+
it('should return controller repo when `repositoryCount` is 0', () => {
97+
const repoLabel = buildRepoLabel(remoteQueryHistoryItem);
98+
const expectedRepoLabel = `${remoteQueryHistoryItem.remoteQuery.controllerRepository.owner}/${remoteQueryHistoryItem.remoteQuery.controllerRepository.name}`;
99+
100+
expect(repoLabel).to.equal(expectedRepoLabel);
101+
});
102+
it('should return number of repositories when `repositoryCount` is non-zero', () => {
103+
const remoteQueryHistoryItem2 = createMockRemoteQueryHistoryItem({repositoryCount: 3});
104+
const repoLabel2 = buildRepoLabel(remoteQueryHistoryItem2);
105+
const expectedRepoLabel2 = '3 repositories';
106+
107+
expect(repoLabel2).to.equal(expectedRepoLabel2);
108+
});
109+
});
110+
describe('repo label for variant analysis history items', () => {
111+
it('should return label when `totalScannedRepositoryCount` is 0', () => {
112+
const variantAnalysisHistoryItem0: VariantAnalysisHistoryItem = {
113+
t: 'variant-analysis',
114+
status: QueryStatus.InProgress,
115+
completed: false,
116+
historyItemId: 'abc123',
117+
variantAnalysis: createMockVariantAnalysis(
118+
VariantAnalysisStatus.InProgress,
119+
createMockScannedRepos([])
120+
),
121+
};
122+
const repoLabel0 = buildRepoLabel(variantAnalysisHistoryItem0);
123+
124+
expect(repoLabel0).to.equal('0/0 repositories');
125+
});
126+
it('should return label when `totalScannedRepositoryCount` is 1', () => {
127+
const variantAnalysisHistoryItem1: VariantAnalysisHistoryItem = {
128+
t: 'variant-analysis',
129+
status: QueryStatus.InProgress,
130+
completed: false,
131+
historyItemId: 'abc123',
132+
variantAnalysis: createMockVariantAnalysis(
133+
VariantAnalysisStatus.InProgress,
134+
createMockScannedRepos([
135+
VariantAnalysisRepoStatus.Pending,
136+
])
137+
),
138+
};
139+
140+
const repoLabel1 = buildRepoLabel(variantAnalysisHistoryItem1);
141+
expect(repoLabel1).to.equal('0/1 repository');
142+
});
143+
it('should return label when `totalScannedRepositoryCount` is greater than 1', () => {
144+
const repoLabel = buildRepoLabel(variantAnalysisHistoryItem);
145+
146+
expect(repoLabel).to.equal('2/4 repositories');
147+
});
148+
});
149+
});
83150
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect } from 'chai';
2+
3+
import { pluralize } from '../../src/pure/word';
4+
5+
describe('word helpers', () => {
6+
describe('pluralize', () => {
7+
it('should return the plural form if the number is 0', () => {
8+
expect(pluralize(0, 'thing', 'things')).to.eq('0 things');
9+
});
10+
it('should return the singular form if the number is 1', () => {
11+
expect(pluralize(1, 'thing', 'things')).to.eq('1 thing');
12+
});
13+
it('should return the plural form if the number is greater than 1', () => {
14+
expect(pluralize(7, 'thing', 'things')).to.eq('7 things');
15+
});
16+
it('should return the empty string if the number is undefined', () => {
17+
expect(pluralize(undefined, 'thing', 'things')).to.eq('');
18+
});
19+
});
20+
});

0 commit comments

Comments
 (0)