Skip to content

Commit ef7ee9e

Browse files
committed
Load existing data extension YAML in editor
This loads in the existing data extension YAML file for the selected database. It only supports the filename we save it to, and will not load it from any other data extension YAML files.
1 parent 6d133f8 commit ef7ee9e

File tree

5 files changed

+202
-6
lines changed

5 files changed

+202
-6
lines changed

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ 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, load } from "js-yaml";
2020
import { getOnDiskWorkspaceFolders } from "../helpers";
2121
import { DatabaseItem } from "../local-databases";
2222
import { CodeQLCliServer } from "../cli";
@@ -78,7 +78,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
7878
protected async onWebViewLoaded() {
7979
super.onWebViewLoaded();
8080

81-
await this.loadExternalApiUsages();
81+
await Promise.all([this.loadExternalApiUsages(), this.readExistingYaml()]);
8282
}
8383

8484
protected async saveYaml(yaml: string): Promise<void> {
@@ -92,6 +92,28 @@ export class DataExtensionsEditorView extends AbstractWebview<
9292
void extLogger.log(`Saved data extension YAML to ${modelFilename}`);
9393
}
9494

95+
protected async readExistingYaml(): Promise<void> {
96+
const modelFilename = this.modelFileName;
97+
if (!modelFilename) {
98+
return;
99+
}
100+
101+
try {
102+
const yaml = await readFile(modelFilename, "utf8");
103+
104+
const data = load(yaml, {
105+
filename: modelFilename,
106+
});
107+
108+
await this.postMessage({
109+
t: "setExistingYamlData",
110+
data,
111+
});
112+
} catch (e: unknown) {
113+
void extLogger.log(`Unable to read data extension YAML: ${e}`);
114+
}
115+
}
116+
95117
protected async loadExternalApiUsages(): Promise<void> {
96118
const queryResult = await this.runQuery();
97119
if (!queryResult) {

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ type ExternalApiUsageByType = {
1212
type DataExtensionDefinition = {
1313
extensible: string;
1414
generateMethodDefinition: (method: ExternalApiUsageByType) => any[];
15+
readModeledMethod: (row: any[]) => [string, ModeledMethod] | undefined;
1516
};
1617

18+
function readRowToMethod(row: any[]): string {
19+
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
20+
}
21+
1722
const definitions: Record<
1823
Exclude<ModeledMethodType, "none">,
1924
DataExtensionDefinition
@@ -35,6 +40,15 @@ const definitions: Record<
3540
method.modeledMethod.kind,
3641
"manual",
3742
],
43+
readModeledMethod: (row) => [
44+
readRowToMethod(row),
45+
{
46+
type: "source",
47+
input: "",
48+
output: row[6],
49+
kind: row[7],
50+
},
51+
],
3852
},
3953
sink: {
4054
extensible: "sinkModel",
@@ -53,6 +67,15 @@ const definitions: Record<
5367
method.modeledMethod.kind,
5468
"manual",
5569
],
70+
readModeledMethod: (row) => [
71+
readRowToMethod(row),
72+
{
73+
type: "sink",
74+
input: row[6],
75+
output: "",
76+
kind: row[7],
77+
},
78+
],
5679
},
5780
summary: {
5881
extensible: "summaryModel",
@@ -72,6 +95,15 @@ const definitions: Record<
7295
method.modeledMethod.kind,
7396
"manual",
7497
],
98+
readModeledMethod: (row) => [
99+
readRowToMethod(row),
100+
{
101+
type: "summary",
102+
input: row[6],
103+
output: row[7],
104+
kind: row[8],
105+
},
106+
],
75107
},
76108
neutral: {
77109
extensible: "neutralModel",
@@ -85,6 +117,15 @@ const definitions: Record<
85117
method.method.methodParameters,
86118
"manual",
87119
],
120+
readModeledMethod: (row) => [
121+
`${row[0]}.${row[1]}#${row[2]}${row[3]}`,
122+
{
123+
type: "neutral",
124+
input: "",
125+
output: "",
126+
kind: "",
127+
},
128+
],
88129
},
89130
};
90131

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

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,14 +492,20 @@ export interface ShowProgressMessage {
492492
message: string;
493493
}
494494

495+
export interface SetExistingYamlDataMessage {
496+
t: "setExistingYamlData";
497+
data: any;
498+
}
499+
495500
export interface ApplyDataExtensionYamlMessage {
496501
t: "applyDataExtensionYaml";
497502
yaml: string;
498503
}
499504

500505
export type ToDataExtensionsEditorMessage =
501506
| SetExternalApiResultsMessage
502-
| ShowProgressMessage;
507+
| ShowProgressMessage
508+
| SetExistingYamlDataMessage;
503509

504510
export type FromDataExtensionsEditorMessage =
505511
| ViewLoadedMsg

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import {
2020
import { MethodRow } from "./MethodRow";
2121
import { assertNever } from "../../pure/helpers-pure";
2222
import { vscode } from "../vscode-api";
23-
import { createDataExtensionYaml } from "../../data-extensions-editor/yaml";
23+
import {
24+
createDataExtensionYaml,
25+
loadDataExtensionYaml,
26+
} from "../../data-extensions-editor/yaml";
2427

2528
export const DataExtensionsEditorContainer = styled.div`
2629
margin-top: 1rem;
@@ -60,6 +63,17 @@ export function DataExtensionsEditor(): JSX.Element {
6063
break;
6164
case "showProgress":
6265
setProgress(msg);
66+
break;
67+
case "setExistingYamlData":
68+
setModeledMethods((oldModeledMethods) => {
69+
const existingModeledMethods = loadDataExtensionYaml(msg.data);
70+
71+
return {
72+
...existingModeledMethods,
73+
...oldModeledMethods,
74+
};
75+
});
76+
6377
break;
6478
default:
6579
assertNever(msg);

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)