Skip to content

Commit b3ce642

Browse files
committed
Merge remote-tracking branch 'origin/main' into koesie10/data-extension-editor-jump-to-usage
2 parents 2da21a4 + 20e9370 commit b3ce642

File tree

5 files changed

+230
-20
lines changed

5 files changed

+230
-20
lines changed

extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import { extLogger, TeeLogger } from "../common";
1616
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
1717
import { qlpackOfDatabase } from "../contextual/queryResolver";
1818
import { file } from "tmp-promise";
19-
import { writeFile } from "fs-extra";
20-
import { dump } from "js-yaml";
19+
import { readFile, writeFile } from "fs-extra";
20+
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
2121
import {
2222
getOnDiskWorkspaceFolders,
2323
showAndLogExceptionWithTelemetry,
24+
showAndLogWarningMessage,
2425
} from "../helpers";
2526
import { DatabaseItem } from "../local-databases";
2627
import { CodeQLCliServer } from "../cli";
@@ -29,6 +30,9 @@ import { ResolvableLocationValue } from "../pure/bqrs-cli-types";
2930
import { showResolvableLocation } from "../interface-utils";
3031
import { decodeBqrsToExternalApiUsages } from "./bqrs";
3132
import { redactableError } from "../pure/errors";
33+
import { createDataExtensionYaml, loadDataExtensionYaml } from "./yaml";
34+
import { ExternalApiUsage } from "./external-api-usage";
35+
import { ModeledMethod } from "./modeled-method";
3236

3337
export class DataExtensionsEditorView extends AbstractWebview<
3438
ToDataExtensionsEditorMessage,
@@ -77,8 +81,11 @@ export class DataExtensionsEditorView extends AbstractWebview<
7781
await this.jumpToUsage(msg.location);
7882

7983
break;
80-
case "applyDataExtensionYaml":
81-
await this.saveYaml(msg.yaml);
84+
case "saveModeledMethods":
85+
await this.saveModeledMethods(
86+
msg.externalApiUsages,
87+
msg.modeledMethods,
88+
);
8289
await this.loadExternalApiUsages();
8390

8491
break;
@@ -90,7 +97,10 @@ export class DataExtensionsEditorView extends AbstractWebview<
9097
protected async onWebViewLoaded() {
9198
super.onWebViewLoaded();
9299

93-
await this.loadExternalApiUsages();
100+
await Promise.all([
101+
this.loadExternalApiUsages(),
102+
this.loadExistingModeledMethods(),
103+
]);
94104
}
95105

96106
protected async jumpToUsage(
@@ -113,17 +123,51 @@ export class DataExtensionsEditorView extends AbstractWebview<
113123
}
114124
}
115125

116-
protected async saveYaml(yaml: string): Promise<void> {
126+
protected async saveModeledMethods(
127+
externalApiUsages: ExternalApiUsage[],
128+
modeledMethods: Record<string, ModeledMethod>,
129+
): Promise<void> {
117130
const modelFilename = this.calculateModelFilename();
118131
if (!modelFilename) {
119132
return;
120133
}
121134

135+
const yaml = createDataExtensionYaml(externalApiUsages, modeledMethods);
136+
122137
await writeFile(modelFilename, yaml);
123138

124139
void extLogger.log(`Saved data extension YAML to ${modelFilename}`);
125140
}
126141

142+
protected async loadExistingModeledMethods(): Promise<void> {
143+
const modelFilename = this.calculateModelFilename();
144+
if (!modelFilename) {
145+
return;
146+
}
147+
148+
try {
149+
const yaml = await readFile(modelFilename, "utf8");
150+
151+
const data = loadYaml(yaml, {
152+
filename: modelFilename,
153+
});
154+
155+
const existingModeledMethods = loadDataExtensionYaml(data);
156+
157+
if (!existingModeledMethods) {
158+
void showAndLogWarningMessage("Failed to parse data extension YAML.");
159+
return;
160+
}
161+
162+
await this.postMessage({
163+
t: "setExistingModeledMethods",
164+
existingModeledMethods,
165+
});
166+
} catch (e: unknown) {
167+
void extLogger.log(`Unable to read data extension YAML: ${e}`);
168+
}
169+
}
170+
127171
protected async loadExternalApiUsages(): Promise<void> {
128172
try {
129173
const queryResult = await this.runQuery();
@@ -192,7 +236,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
192236
},
193237
});
194238
}
195-
await writeFile(suiteFile, dump(suiteYaml), "utf8");
239+
await writeFile(suiteFile, dumpYaml(suiteYaml), "utf8");
196240

197241
const queries = await this.cliServer.resolveQueriesInSuite(
198242
suiteFile,

extensions/ql-vscode/src/data-extensions-editor/yaml.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ type ExternalApiUsageByType = {
99
type DataExtensionDefinition = {
1010
extensible: string;
1111
generateMethodDefinition: (method: ExternalApiUsageByType) => any[];
12+
readModeledMethod: (row: any[]) => [string, ModeledMethod] | undefined;
1213
};
1314

15+
function readRowToMethod(row: any[]): string {
16+
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
17+
}
18+
1419
const definitions: Record<
1520
Exclude<ModeledMethodType, "none">,
1621
DataExtensionDefinition
@@ -32,6 +37,15 @@ const definitions: Record<
3237
method.modeledMethod.kind,
3338
"manual",
3439
],
40+
readModeledMethod: (row) => [
41+
readRowToMethod(row),
42+
{
43+
type: "source",
44+
input: "",
45+
output: row[6],
46+
kind: row[7],
47+
},
48+
],
3549
},
3650
sink: {
3751
extensible: "sinkModel",
@@ -50,6 +64,15 @@ const definitions: Record<
5064
method.modeledMethod.kind,
5165
"manual",
5266
],
67+
readModeledMethod: (row) => [
68+
readRowToMethod(row),
69+
{
70+
type: "sink",
71+
input: row[6],
72+
output: "",
73+
kind: row[7],
74+
},
75+
],
5376
},
5477
summary: {
5578
extensible: "summaryModel",
@@ -69,6 +92,15 @@ const definitions: Record<
6992
method.modeledMethod.kind,
7093
"manual",
7194
],
95+
readModeledMethod: (row) => [
96+
readRowToMethod(row),
97+
{
98+
type: "summary",
99+
input: row[6],
100+
output: row[7],
101+
kind: row[8],
102+
},
103+
],
72104
},
73105
neutral: {
74106
extensible: "neutralModel",
@@ -82,6 +114,15 @@ const definitions: Record<
82114
method.externalApiUsage.methodParameters,
83115
"manual",
84116
],
117+
readModeledMethod: (row) => [
118+
`${row[0]}.${row[1]}#${row[2]}${row[3]}`,
119+
{
120+
type: "neutral",
121+
input: "",
122+
output: "",
123+
kind: "",
124+
},
125+
],
85126
},
86127
};
87128

@@ -142,3 +183,55 @@ export function createDataExtensionYaml(
142183
return `extensions:
143184
${extensions.join("\n")}`;
144185
}
186+
187+
export function loadDataExtensionYaml(
188+
data: any,
189+
): Record<string, ModeledMethod> | undefined {
190+
if (typeof data !== "object") {
191+
return undefined;
192+
}
193+
194+
const extensions = data.extensions;
195+
if (!Array.isArray(extensions)) {
196+
return undefined;
197+
}
198+
199+
const modeledMethods: Record<string, ModeledMethod> = {};
200+
201+
for (const extension of extensions) {
202+
const addsTo = extension.addsTo;
203+
if (typeof addsTo !== "object") {
204+
continue;
205+
}
206+
207+
const extensible = addsTo.extensible;
208+
if (typeof extensible !== "string") {
209+
continue;
210+
}
211+
212+
const data = extension.data;
213+
if (!Array.isArray(data)) {
214+
continue;
215+
}
216+
217+
const definition = Object.values(definitions).find(
218+
(definition) => definition.extensible === extensible,
219+
);
220+
if (!definition) {
221+
continue;
222+
}
223+
224+
for (const row of data) {
225+
const result = definition.readModeledMethod(row);
226+
if (!result) {
227+
continue;
228+
}
229+
230+
const [apiInfo, modeledMethod] = result;
231+
232+
modeledMethods[apiInfo] = modeledMethod;
233+
}
234+
}
235+
236+
return modeledMethods;
237+
}

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-so
1515
import { ErrorLike } from "./errors";
1616
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
1717
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
18+
import { ModeledMethod } from "../data-extensions-editor/modeled-method";
1819

1920
/**
2021
* This module contains types and code that are shared between
@@ -497,16 +498,23 @@ export interface JumpToUsageMessage {
497498
location: ResolvableLocationValue;
498499
}
499500

500-
export interface ApplyDataExtensionYamlMessage {
501-
t: "applyDataExtensionYaml";
502-
yaml: string;
501+
export interface SetExistingModeledMethods {
502+
t: "setExistingModeledMethods";
503+
existingModeledMethods: Record<string, ModeledMethod>;
504+
}
505+
506+
export interface SaveModeledMethods {
507+
t: "saveModeledMethods";
508+
externalApiUsages: ExternalApiUsage[];
509+
modeledMethods: Record<string, ModeledMethod>;
503510
}
504511

505512
export type ToDataExtensionsEditorMessage =
506513
| SetExternalApiUsagesMessage
507-
| ShowProgressMessage;
514+
| ShowProgressMessage
515+
| SetExistingModeledMethods;
508516

509517
export type FromDataExtensionsEditorMessage =
510518
| ViewLoadedMsg
511519
| JumpToUsageMessage
512-
| ApplyDataExtensionYamlMessage;
520+
| SaveModeledMethods;

extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
1616
import { MethodRow } from "./MethodRow";
1717
import { assertNever } from "../../pure/helpers-pure";
1818
import { vscode } from "../vscode-api";
19-
import { createDataExtensionYaml } from "../../data-extensions-editor/yaml";
2019
import { calculateSupportedPercentage } from "./supported";
2120

2221
export const DataExtensionsEditorContainer = styled.div`
@@ -57,6 +56,15 @@ export function DataExtensionsEditor(): JSX.Element {
5756
break;
5857
case "showProgress":
5958
setProgress(msg);
59+
break;
60+
case "setExistingModeledMethods":
61+
setModeledMethods((oldModeledMethods) => {
62+
return {
63+
...msg.existingModeledMethods,
64+
...oldModeledMethods,
65+
};
66+
});
67+
6068
break;
6169
default:
6270
assertNever(msg);
@@ -92,14 +100,10 @@ export function DataExtensionsEditor(): JSX.Element {
92100
);
93101

94102
const onApplyClick = useCallback(() => {
95-
const yamlString = createDataExtensionYaml(
103+
vscode.postMessage({
104+
t: "saveModeledMethods",
96105
externalApiUsages,
97106
modeledMethods,
98-
);
99-
100-
vscode.postMessage({
101-
t: "applyDataExtensionYaml",
102-
yaml: yamlString,
103107
});
104108
}, [externalApiUsages, modeledMethods]);
105109

extensions/ql-vscode/test/unit-tests/data-extensions-editor/yaml.test.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { createDataExtensionYaml } from "../../../src/data-extensions-editor/yaml";
1+
import {
2+
createDataExtensionYaml,
3+
loadDataExtensionYaml,
4+
} from "../../../src/data-extensions-editor/yaml";
25

36
describe("createDataExtensionYaml", () => {
47
it("creates the correct YAML file", () => {
@@ -99,3 +102,61 @@ describe("createDataExtensionYaml", () => {
99102
`);
100103
});
101104
});
105+
106+
describe("loadDataExtensionYaml", () => {
107+
it("loads the YAML file", () => {
108+
const data = loadDataExtensionYaml({
109+
extensions: [
110+
{
111+
addsTo: { pack: "codeql/java-all", extensible: "sourceModel" },
112+
data: [],
113+
},
114+
{
115+
addsTo: { pack: "codeql/java-all", extensible: "sinkModel" },
116+
data: [
117+
[
118+
"org.sql2o",
119+
"Connection",
120+
true,
121+
"createQuery",
122+
"(String)",
123+
"",
124+
"Argument[0]",
125+
"sql",
126+
"manual",
127+
],
128+
],
129+
},
130+
{
131+
addsTo: { pack: "codeql/java-all", extensible: "summaryModel" },
132+
data: [],
133+
},
134+
{
135+
addsTo: { pack: "codeql/java-all", extensible: "neutralModel" },
136+
data: [],
137+
},
138+
],
139+
});
140+
141+
expect(data).toEqual({
142+
"org.sql2o.Connection#createQuery(String)": {
143+
input: "Argument[0]",
144+
kind: "sql",
145+
output: "",
146+
type: "sink",
147+
},
148+
});
149+
});
150+
151+
it("returns undefined if given a string", () => {
152+
const data = loadDataExtensionYaml(`extensions:
153+
- addsTo:
154+
pack: codeql/java-all
155+
extensible: sinkModel
156+
data:
157+
- ["org.sql2o","Connection",true,"createQuery","(String)","","Argument[0]","sql","manual"]
158+
`);
159+
160+
expect(data).toBeUndefined();
161+
});
162+
});

0 commit comments

Comments
 (0)