Skip to content

Commit 9c200e0

Browse files
authored
Merge pull request #3102 from github/koesie10/refactor-compare-view
Refactor compare view
2 parents 3020bf7 + b1289a4 commit 9c200e0

File tree

7 files changed

+121
-86
lines changed

7 files changed

+121
-86
lines changed

extensions/ql-vscode/src/common/bqrs-cli-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type ColumnKind =
2121
| typeof ColumnKindCode.DATE
2222
| typeof ColumnKindCode.ENTITY;
2323

24-
export interface Column {
24+
interface Column {
2525
name?: string;
2626
kind: ColumnKind;
2727
}
@@ -112,7 +112,7 @@ export type BqrsKind =
112112
| "Date"
113113
| "Entity";
114114

115-
interface BqrsColumn {
115+
export interface BqrsColumn {
116116
name?: string;
117117
kind: BqrsKind;
118118
}

extensions/ql-vscode/src/common/interface-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
RawResultSet,
44
ResultRow,
55
ResultSetSchema,
6-
Column,
76
ResolvableLocationValue,
7+
BqrsColumn,
88
} from "../common/bqrs-cli-types";
99
import {
1010
VariantAnalysis,
@@ -353,7 +353,7 @@ export interface SetComparisonsMessage {
353353
time: string;
354354
};
355355
};
356-
readonly columns: readonly Column[];
356+
readonly columns: readonly BqrsColumn[];
357357
readonly commonResultSetNames: string[];
358358
readonly currentResultSetName: string;
359359
readonly rows: QueryCompareResult | undefined;

extensions/ql-vscode/src/compare/compare-view.ts

Lines changed: 57 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ import { ViewColumn } from "vscode";
22

33
import {
44
FromCompareViewMessage,
5-
ToCompareViewMessage,
65
QueryCompareResult,
6+
ToCompareViewMessage,
77
} from "../common/interface-types";
88
import { Logger, showAndLogExceptionWithTelemetry } from "../common/logging";
99
import { extLogger } from "../common/logging/vscode";
1010
import { CodeQLCliServer } from "../codeql-cli/cli";
1111
import { DatabaseManager } from "../databases/local-databases";
1212
import { jumpToLocation } from "../databases/local-databases/locations";
13-
import {
14-
transformBqrsResultSet,
15-
RawResultSet,
16-
BQRSInfo,
17-
} from "../common/bqrs-cli-types";
13+
import { BQRSInfo, DecodedBqrsChunk } from "../common/bqrs-cli-types";
1814
import resultsDiff from "./resultsDiff";
1915
import { CompletedLocalQueryInfo } from "../query-results";
2016
import { assertNever, getErrorMessage } from "../common/helpers-pure";
@@ -26,10 +22,13 @@ import {
2622
import { telemetryListener } from "../common/vscode/telemetry";
2723
import { redactableError } from "../common/errors";
2824
import { App } from "../common/app";
25+
import { findResultSetNames } from "./result-set-names";
2926

3027
interface ComparePair {
3128
from: CompletedLocalQueryInfo;
29+
fromSchemas: BQRSInfo;
3230
to: CompletedLocalQueryInfo;
31+
toSchemas: BQRSInfo;
3332
}
3433

3534
export class CompareView extends AbstractWebview<
@@ -56,18 +55,44 @@ export class CompareView extends AbstractWebview<
5655
to: CompletedLocalQueryInfo,
5756
selectedResultSetName?: string,
5857
) {
59-
this.comparePair = { from, to };
58+
const fromSchemas = await this.cliServer.bqrsInfo(
59+
from.completedQuery.query.resultsPaths.resultsPath,
60+
);
61+
const toSchemas = await this.cliServer.bqrsInfo(
62+
to.completedQuery.query.resultsPaths.resultsPath,
63+
);
64+
65+
this.comparePair = {
66+
from,
67+
fromSchemas,
68+
to,
69+
toSchemas,
70+
};
71+
72+
await this.showResultsInternal(selectedResultSetName);
73+
}
74+
75+
private async showResultsInternal(selectedResultSetName?: string) {
76+
if (!this.comparePair) {
77+
return;
78+
}
79+
80+
const { from, to } = this.comparePair;
81+
6082
const panel = await this.getPanel();
6183
panel.reveal(undefined, true);
6284

6385
await this.waitForPanelLoaded();
64-
const [
86+
const {
6587
commonResultSetNames,
66-
currentResultSetName,
88+
currentResultSetDisplayName,
6789
fromResultSet,
6890
toResultSet,
69-
] = await this.findCommonResultSetNames(from, to, selectedResultSetName);
70-
if (currentResultSetName) {
91+
} = await this.findResultSetsToCompare(
92+
this.comparePair,
93+
selectedResultSetName,
94+
);
95+
if (currentResultSetDisplayName) {
7196
let rows: QueryCompareResult | undefined;
7297
let message: string | undefined;
7398
try {
@@ -93,9 +118,9 @@ export class CompareView extends AbstractWebview<
93118
time: to.startTime,
94119
},
95120
},
96-
columns: fromResultSet.schema.columns,
121+
columns: fromResultSet.columns,
97122
commonResultSetNames,
98-
currentResultSetName,
123+
currentResultSetName: currentResultSetDisplayName,
99124
rows,
100125
message,
101126
databaseUri: to.initialInfo.databaseInfo.databaseUri,
@@ -165,92 +190,56 @@ export class CompareView extends AbstractWebview<
165190
}
166191
}
167192

168-
private async findCommonResultSetNames(
169-
from: CompletedLocalQueryInfo,
170-
to: CompletedLocalQueryInfo,
193+
private async findResultSetsToCompare(
194+
{ from, fromSchemas, to, toSchemas }: ComparePair,
171195
selectedResultSetName: string | undefined,
172-
): Promise<[string[], string, RawResultSet, RawResultSet]> {
173-
const fromSchemas = await this.cliServer.bqrsInfo(
174-
from.completedQuery.query.resultsPaths.resultsPath,
175-
);
176-
const toSchemas = await this.cliServer.bqrsInfo(
177-
to.completedQuery.query.resultsPaths.resultsPath,
178-
);
179-
const fromSchemaNames = fromSchemas["result-sets"].map(
180-
(schema) => schema.name,
181-
);
182-
const toSchemaNames = toSchemas["result-sets"].map((schema) => schema.name);
183-
const commonResultSetNames = fromSchemaNames.filter((name) =>
184-
toSchemaNames.includes(name),
185-
);
186-
187-
// Fall back on the default result set names if there are no common ones.
188-
const defaultFromResultSetName = fromSchemaNames.find((name) =>
189-
name.startsWith("#"),
190-
);
191-
const defaultToResultSetName = toSchemaNames.find((name) =>
192-
name.startsWith("#"),
193-
);
194-
195-
if (
196-
commonResultSetNames.length === 0 &&
197-
!(defaultFromResultSetName || defaultToResultSetName)
198-
) {
199-
throw new Error(
200-
"No common result sets found between the two queries. Please check that the queries are compatible.",
201-
);
202-
}
196+
) {
197+
const {
198+
commonResultSetNames,
199+
currentResultSetDisplayName,
200+
fromResultSetName,
201+
toResultSetName,
202+
} = await findResultSetNames(fromSchemas, toSchemas, selectedResultSetName);
203203

204-
const currentResultSetName =
205-
selectedResultSetName || commonResultSetNames[0];
206204
const fromResultSet = await this.getResultSet(
207205
fromSchemas,
208-
currentResultSetName || defaultFromResultSetName!,
206+
fromResultSetName,
209207
from.completedQuery.query.resultsPaths.resultsPath,
210208
);
211209
const toResultSet = await this.getResultSet(
212210
toSchemas,
213-
currentResultSetName || defaultToResultSetName!,
211+
toResultSetName,
214212
to.completedQuery.query.resultsPaths.resultsPath,
215213
);
216-
return [
214+
return {
217215
commonResultSetNames,
218-
currentResultSetName ||
219-
`${defaultFromResultSetName} <-> ${defaultToResultSetName}`,
216+
currentResultSetDisplayName,
220217
fromResultSet,
221218
toResultSet,
222-
];
219+
};
223220
}
224221

225222
private async changeTable(newResultSetName: string) {
226-
if (!this.comparePair?.from || !this.comparePair.to) {
227-
return;
228-
}
229-
await this.showResults(
230-
this.comparePair.from,
231-
this.comparePair.to,
232-
newResultSetName,
233-
);
223+
await this.showResultsInternal(newResultSetName);
234224
}
235225

236226
private async getResultSet(
237227
bqrsInfo: BQRSInfo,
238228
resultSetName: string,
239229
resultsPath: string,
240-
): Promise<RawResultSet> {
230+
): Promise<DecodedBqrsChunk> {
241231
const schema = bqrsInfo["result-sets"].find(
242232
(schema) => schema.name === resultSetName,
243233
);
244234
if (!schema) {
245235
throw new Error(`Schema ${resultSetName} not found.`);
246236
}
247-
const chunk = await this.cliServer.bqrsDecode(resultsPath, resultSetName);
248-
return transformBqrsResultSet(schema, chunk);
237+
return await this.cliServer.bqrsDecode(resultsPath, resultSetName);
249238
}
250239

251240
private compareResults(
252-
fromResults: RawResultSet,
253-
toResults: RawResultSet,
241+
fromResults: DecodedBqrsChunk,
242+
toResults: DecodedBqrsChunk,
254243
): QueryCompareResult {
255244
// Only compare columns that have the same name
256245
return resultsDiff(fromResults, toResults);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { BQRSInfo } from "../common/bqrs-cli-types";
2+
3+
export async function findResultSetNames(
4+
fromSchemas: BQRSInfo,
5+
toSchemas: BQRSInfo,
6+
selectedResultSetName: string | undefined,
7+
) {
8+
const fromSchemaNames = fromSchemas["result-sets"].map(
9+
(schema) => schema.name,
10+
);
11+
const toSchemaNames = toSchemas["result-sets"].map((schema) => schema.name);
12+
const commonResultSetNames = fromSchemaNames.filter((name) =>
13+
toSchemaNames.includes(name),
14+
);
15+
16+
// Fall back on the default result set names if there are no common ones.
17+
const defaultFromResultSetName = fromSchemaNames.find((name) =>
18+
name.startsWith("#"),
19+
);
20+
const defaultToResultSetName = toSchemaNames.find((name) =>
21+
name.startsWith("#"),
22+
);
23+
24+
if (
25+
commonResultSetNames.length === 0 &&
26+
!(defaultFromResultSetName || defaultToResultSetName)
27+
) {
28+
throw new Error(
29+
"No common result sets found between the two queries. Please check that the queries are compatible.",
30+
);
31+
}
32+
33+
const currentResultSetName = selectedResultSetName || commonResultSetNames[0];
34+
const fromResultSetName = currentResultSetName || defaultFromResultSetName!;
35+
const toResultSetName = currentResultSetName || defaultToResultSetName!;
36+
37+
return {
38+
commonResultSetNames,
39+
currentResultSetDisplayName:
40+
currentResultSetName ||
41+
`${defaultFromResultSetName} <-> ${defaultToResultSetName}`,
42+
fromResultSetName,
43+
toResultSetName,
44+
};
45+
}

extensions/ql-vscode/src/compare/resultsDiff.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RawResultSet } from "../common/bqrs-cli-types";
1+
import { DecodedBqrsChunk } from "../common/bqrs-cli-types";
22
import { QueryCompareResult } from "../common/interface-types";
33

44
/**
@@ -20,29 +20,29 @@ import { QueryCompareResult } from "../common/interface-types";
2020
* 3. If the queries are 100% disjoint
2121
*/
2222
export default function resultsDiff(
23-
fromResults: RawResultSet,
24-
toResults: RawResultSet,
23+
fromResults: DecodedBqrsChunk,
24+
toResults: DecodedBqrsChunk,
2525
): QueryCompareResult {
26-
if (fromResults.schema.columns.length !== toResults.schema.columns.length) {
26+
if (fromResults.columns.length !== toResults.columns.length) {
2727
throw new Error("CodeQL Compare: Columns do not match.");
2828
}
2929

30-
if (!fromResults.rows.length) {
30+
if (!fromResults.tuples.length) {
3131
throw new Error("CodeQL Compare: Source query has no results.");
3232
}
3333

34-
if (!toResults.rows.length) {
34+
if (!toResults.tuples.length) {
3535
throw new Error("CodeQL Compare: Target query has no results.");
3636
}
3737

3838
const results = {
39-
from: arrayDiff(fromResults.rows, toResults.rows),
40-
to: arrayDiff(toResults.rows, fromResults.rows),
39+
from: arrayDiff(fromResults.tuples, toResults.tuples),
40+
to: arrayDiff(toResults.tuples, fromResults.tuples),
4141
};
4242

4343
if (
44-
fromResults.rows.length === results.from.length &&
45-
toResults.rows.length === results.to.length
44+
fromResults.tuples.length === results.from.length &&
45+
toResults.tuples.length === results.to.length
4646
) {
4747
throw new Error("CodeQL Compare: No overlap between the selected queries.");
4848
}

extensions/ql-vscode/src/stories/compare/CompareTable.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ CompareTable.args = {
3232
},
3333
},
3434
columns: [
35-
{ name: "a", kind: "e" },
36-
{ name: "b", kind: "e" },
35+
{ name: "a", kind: "Entity" },
36+
{ name: "b", kind: "Entity" },
3737
],
3838
commonResultSetNames: ["edges", "nodes", "subpaths", "#select"],
3939
currentResultSetName: "edges",

extensions/ql-vscode/src/view/results/RawTableHeader.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import {
66
SortDirection,
77
} from "../../common/interface-types";
88
import { nextSortDirection } from "./result-table-utils";
9-
import { Column } from "../../common/bqrs-cli-types";
109

1110
interface Props {
12-
readonly columns: readonly Column[];
11+
readonly columns: ReadonlyArray<{
12+
name?: string;
13+
}>;
1314
readonly schemaName: string;
1415
readonly sortState?: RawResultsSortState;
1516
readonly preventSort?: boolean;

0 commit comments

Comments
 (0)