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.
This commit is contained in:
Koen Vlaswinkel
2023-04-04 13:37:34 +02:00
parent 6d133f800f
commit ef7ee9ef3d
5 changed files with 202 additions and 6 deletions

View File

@@ -15,8 +15,8 @@ import { extLogger, TeeLogger } from "../common";
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
import { qlpackOfDatabase } from "../contextual/queryResolver";
import { file } from "tmp-promise";
import { writeFile } from "fs-extra";
import { dump } from "js-yaml";
import { readFile, writeFile } from "fs-extra";
import { dump, load } from "js-yaml";
import { getOnDiskWorkspaceFolders } from "../helpers";
import { DatabaseItem } from "../local-databases";
import { CodeQLCliServer } from "../cli";
@@ -78,7 +78,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
protected async onWebViewLoaded() {
super.onWebViewLoaded();
await this.loadExternalApiUsages();
await Promise.all([this.loadExternalApiUsages(), this.readExistingYaml()]);
}
protected async saveYaml(yaml: string): Promise<void> {
@@ -92,6 +92,28 @@ export class DataExtensionsEditorView extends AbstractWebview<
void extLogger.log(`Saved data extension YAML to ${modelFilename}`);
}
protected async readExistingYaml(): Promise<void> {
const modelFilename = this.modelFileName;
if (!modelFilename) {
return;
}
try {
const yaml = await readFile(modelFilename, "utf8");
const data = load(yaml, {
filename: modelFilename,
});
await this.postMessage({
t: "setExistingYamlData",
data,
});
} catch (e: unknown) {
void extLogger.log(`Unable to read data extension YAML: ${e}`);
}
}
protected async loadExternalApiUsages(): Promise<void> {
const queryResult = await this.runQuery();
if (!queryResult) {

View File

@@ -12,8 +12,13 @@ type ExternalApiUsageByType = {
type DataExtensionDefinition = {
extensible: string;
generateMethodDefinition: (method: ExternalApiUsageByType) => any[];
readModeledMethod: (row: any[]) => [string, ModeledMethod] | undefined;
};
function readRowToMethod(row: any[]): string {
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
}
const definitions: Record<
Exclude<ModeledMethodType, "none">,
DataExtensionDefinition
@@ -35,6 +40,15 @@ const definitions: Record<
method.modeledMethod.kind,
"manual",
],
readModeledMethod: (row) => [
readRowToMethod(row),
{
type: "source",
input: "",
output: row[6],
kind: row[7],
},
],
},
sink: {
extensible: "sinkModel",
@@ -53,6 +67,15 @@ const definitions: Record<
method.modeledMethod.kind,
"manual",
],
readModeledMethod: (row) => [
readRowToMethod(row),
{
type: "sink",
input: row[6],
output: "",
kind: row[7],
},
],
},
summary: {
extensible: "summaryModel",
@@ -72,6 +95,15 @@ const definitions: Record<
method.modeledMethod.kind,
"manual",
],
readModeledMethod: (row) => [
readRowToMethod(row),
{
type: "summary",
input: row[6],
output: row[7],
kind: row[8],
},
],
},
neutral: {
extensible: "neutralModel",
@@ -85,6 +117,15 @@ const definitions: Record<
method.method.methodParameters,
"manual",
],
readModeledMethod: (row) => [
`${row[0]}.${row[1]}#${row[2]}${row[3]}`,
{
type: "neutral",
input: "",
output: "",
kind: "",
},
],
},
};
@@ -145,3 +186,55 @@ export function createDataExtensionYaml(
return `extensions:
${extensions.join("\n")}`;
}
export function loadDataExtensionYaml(
data: any,
): Record<string, ModeledMethod> | undefined {
if (typeof data !== "object") {
return undefined;
}
const extensions = data.extensions;
if (!Array.isArray(extensions)) {
return undefined;
}
const modeledMethods: Record<string, ModeledMethod> = {};
for (const extension of extensions) {
const addsTo = extension.addsTo;
if (typeof addsTo !== "object") {
continue;
}
const extensible = addsTo.extensible;
if (typeof extensible !== "string") {
continue;
}
const data = extension.data;
if (!Array.isArray(data)) {
continue;
}
const definition = Object.values(definitions).find(
(definition) => definition.extensible === extensible,
);
if (!definition) {
continue;
}
for (const row of data) {
const result = definition.readModeledMethod(row);
if (!result) {
continue;
}
const [apiInfo, modeledMethod] = result;
modeledMethods[apiInfo] = modeledMethod;
}
}
return modeledMethods;
}

View File

@@ -492,6 +492,11 @@ export interface ShowProgressMessage {
message: string;
}
export interface SetExistingYamlDataMessage {
t: "setExistingYamlData";
data: any;
}
export interface ApplyDataExtensionYamlMessage {
t: "applyDataExtensionYaml";
yaml: string;
@@ -499,7 +504,8 @@ export interface ApplyDataExtensionYamlMessage {
export type ToDataExtensionsEditorMessage =
| SetExternalApiResultsMessage
| ShowProgressMessage;
| ShowProgressMessage
| SetExistingYamlDataMessage;
export type FromDataExtensionsEditorMessage =
| ViewLoadedMsg

View File

@@ -20,7 +20,10 @@ import {
import { MethodRow } from "./MethodRow";
import { assertNever } from "../../pure/helpers-pure";
import { vscode } from "../vscode-api";
import { createDataExtensionYaml } from "../../data-extensions-editor/yaml";
import {
createDataExtensionYaml,
loadDataExtensionYaml,
} from "../../data-extensions-editor/yaml";
export const DataExtensionsEditorContainer = styled.div`
margin-top: 1rem;
@@ -60,6 +63,17 @@ export function DataExtensionsEditor(): JSX.Element {
break;
case "showProgress":
setProgress(msg);
break;
case "setExistingYamlData":
setModeledMethods((oldModeledMethods) => {
const existingModeledMethods = loadDataExtensionYaml(msg.data);
return {
...existingModeledMethods,
...oldModeledMethods,
};
});
break;
default:
assertNever(msg);

View File

@@ -1,4 +1,7 @@
import { createDataExtensionYaml } from "../../../src/data-extensions-editor/yaml";
import {
createDataExtensionYaml,
loadDataExtensionYaml,
} from "../../../src/data-extensions-editor/yaml";
describe("createDataExtensionYaml", () => {
it("creates the correct YAML file", () => {
@@ -99,3 +102,61 @@ describe("createDataExtensionYaml", () => {
`);
});
});
describe("loadDataExtensionYaml", () => {
it("loads the YAML file", () => {
const data = loadDataExtensionYaml({
extensions: [
{
addsTo: { pack: "codeql/java-all", extensible: "sourceModel" },
data: [],
},
{
addsTo: { pack: "codeql/java-all", extensible: "sinkModel" },
data: [
[
"org.sql2o",
"Connection",
true,
"createQuery",
"(String)",
"",
"Argument[0]",
"sql",
"manual",
],
],
},
{
addsTo: { pack: "codeql/java-all", extensible: "summaryModel" },
data: [],
},
{
addsTo: { pack: "codeql/java-all", extensible: "neutralModel" },
data: [],
},
],
});
expect(data).toEqual({
"org.sql2o.Connection#createQuery(String)": {
input: "Argument[0]",
kind: "sql",
output: "",
type: "sink",
},
});
});
it("returns undefined if given a string", () => {
const data = loadDataExtensionYaml(`extensions:
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["org.sql2o","Connection",true,"createQuery","(String)","","Argument[0]","sql","manual"]
`);
expect(data).toBeUndefined();
});
});