Skip to content

Commit 20e9370

Browse files
authored
Merge pull request #2265 from github/koesie10/data-extension-editor-yaml-load
Load existing data extension YAML in editor
2 parents d60bcf3 + c0818d8 commit 20e9370

File tree

5 files changed

+231
-21
lines changed

5 files changed

+231
-21
lines changed

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

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@ import { extLogger, TeeLogger } from "../common";
1515
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
1616
import { qlpackOfDatabase } from "../contextual/queryResolver";
1717
import { file } from "tmp-promise";
18-
import { writeFile } from "fs-extra";
19-
import { dump } from "js-yaml";
18+
import { readFile, writeFile } from "fs-extra";
19+
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
2020
import {
2121
getOnDiskWorkspaceFolders,
2222
showAndLogExceptionWithTelemetry,
23+
showAndLogWarningMessage,
2324
} from "../helpers";
2425
import { DatabaseItem } from "../local-databases";
2526
import { CodeQLCliServer } from "../cli";
26-
import { assertNever, asError, getErrorMessage } from "../pure/helpers-pure";
27+
import { asError, assertNever, getErrorMessage } from "../pure/helpers-pure";
2728
import { decodeBqrsToExternalApiUsages } from "./bqrs";
2829
import { redactableError } from "../pure/errors";
30+
import { createDataExtensionYaml, loadDataExtensionYaml } from "./yaml";
31+
import { ExternalApiUsage } from "./external-api-usage";
32+
import { ModeledMethod } from "./modeled-method";
2933

3034
export class DataExtensionsEditorView extends AbstractWebview<
3135
ToDataExtensionsEditorMessage,
@@ -70,8 +74,11 @@ export class DataExtensionsEditorView extends AbstractWebview<
7074
await this.onWebViewLoaded();
7175

7276
break;
73-
case "applyDataExtensionYaml":
74-
await this.saveYaml(msg.yaml);
77+
case "saveModeledMethods":
78+
await this.saveModeledMethods(
79+
msg.externalApiUsages,
80+
msg.modeledMethods,
81+
);
7582
await this.loadExternalApiUsages();
7683

7784
break;
@@ -83,20 +90,57 @@ export class DataExtensionsEditorView extends AbstractWebview<
8390
protected async onWebViewLoaded() {
8491
super.onWebViewLoaded();
8592

86-
await this.loadExternalApiUsages();
93+
await Promise.all([
94+
this.loadExternalApiUsages(),
95+
this.loadExistingModeledMethods(),
96+
]);
8797
}
8898

89-
protected async saveYaml(yaml: string): Promise<void> {
99+
protected async saveModeledMethods(
100+
externalApiUsages: ExternalApiUsage[],
101+
modeledMethods: Record<string, ModeledMethod>,
102+
): Promise<void> {
90103
const modelFilename = this.calculateModelFilename();
91104
if (!modelFilename) {
92105
return;
93106
}
94107

108+
const yaml = createDataExtensionYaml(externalApiUsages, modeledMethods);
109+
95110
await writeFile(modelFilename, yaml);
96111

97112
void extLogger.log(`Saved data extension YAML to ${modelFilename}`);
98113
}
99114

115+
protected async loadExistingModeledMethods(): Promise<void> {
116+
const modelFilename = this.calculateModelFilename();
117+
if (!modelFilename) {
118+
return;
119+
}
120+
121+
try {
122+
const yaml = await readFile(modelFilename, "utf8");
123+
124+
const data = loadYaml(yaml, {
125+
filename: modelFilename,
126+
});
127+
128+
const existingModeledMethods = loadDataExtensionYaml(data);
129+
130+
if (!existingModeledMethods) {
131+
void showAndLogWarningMessage("Failed to parse data extension YAML.");
132+
return;
133+
}
134+
135+
await this.postMessage({
136+
t: "setExistingModeledMethods",
137+
existingModeledMethods,
138+
});
139+
} catch (e: unknown) {
140+
void extLogger.log(`Unable to read data extension YAML: ${e}`);
141+
}
142+
}
143+
100144
protected async loadExternalApiUsages(): Promise<void> {
101145
try {
102146
const queryResult = await this.runQuery();
@@ -165,7 +209,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
165209
},
166210
});
167211
}
168-
await writeFile(suiteFile, dump(suiteYaml), "utf8");
212+
await writeFile(suiteFile, dumpYaml(suiteYaml), "utf8");
169213

170214
const queries = await this.cliServer.resolveQueriesInSuite(
171215
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
@@ -492,15 +493,22 @@ export interface ShowProgressMessage {
492493
message: string;
493494
}
494495

495-
export interface ApplyDataExtensionYamlMessage {
496-
t: "applyDataExtensionYaml";
497-
yaml: string;
496+
export interface SetExistingModeledMethods {
497+
t: "setExistingModeledMethods";
498+
existingModeledMethods: Record<string, ModeledMethod>;
499+
}
500+
501+
export interface SaveModeledMethods {
502+
t: "saveModeledMethods";
503+
externalApiUsages: ExternalApiUsage[];
504+
modeledMethods: Record<string, ModeledMethod>;
498505
}
499506

500507
export type ToDataExtensionsEditorMessage =
501508
| SetExternalApiUsagesMessage
502-
| ShowProgressMessage;
509+
| ShowProgressMessage
510+
| SetExistingModeledMethods;
503511

504512
export type FromDataExtensionsEditorMessage =
505513
| ViewLoadedMsg
506-
| ApplyDataExtensionYamlMessage;
514+
| 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)