Skip to content

Commit 5b170d0

Browse files
Avoiding overwriting any existing modeled methods from the files
1 parent db4dc89 commit 5b170d0

4 files changed

Lines changed: 549 additions & 30 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
123123
msg.externalApiUsages,
124124
msg.modeledMethods,
125125
this.mode,
126+
this.cliServer,
126127
this.app.logger,
127128
);
128129
await Promise.all([this.setViewState(), this.loadExternalApiUsages()]);

extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import { Mode } from "./shared/mode";
55
import { createDataExtensionYamls, loadDataExtensionYaml } from "./yaml";
66
import { join, relative } from "path";
77
import { ExtensionPack } from "./shared/extension-pack";
8-
import {
9-
Logger,
10-
NotificationLogger,
11-
showAndLogErrorMessage,
12-
} from "../common/logging";
8+
import { NotificationLogger, showAndLogErrorMessage } from "../common/logging";
139
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
1410
import { load as loadYaml } from "js-yaml";
1511
import { CodeQLCliServer } from "../codeql-cli/cli";
@@ -22,13 +18,21 @@ export async function saveModeledMethods(
2218
externalApiUsages: ExternalApiUsage[],
2319
modeledMethods: Record<string, ModeledMethod>,
2420
mode: Mode,
25-
logger: Logger,
21+
cliServer: CodeQLCliServer,
22+
logger: NotificationLogger,
2623
): Promise<void> {
24+
const existingModeledMethods = await loadModeledMethodFiles(
25+
extensionPack,
26+
cliServer,
27+
logger,
28+
);
29+
2730
const yamls = createDataExtensionYamls(
2831
databaseName,
2932
language,
3033
externalApiUsages,
3134
modeledMethods,
35+
existingModeledMethods,
3236
mode,
3337
);
3438

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

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,25 @@ export function createDataExtensionYamls(
7272
databaseName: string,
7373
language: string,
7474
externalApiUsages: ExternalApiUsage[],
75-
modeledMethods: Record<string, ModeledMethod>,
75+
newModeledMethods: Record<string, ModeledMethod>,
76+
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
7677
mode: Mode,
7778
) {
7879
switch (mode) {
7980
case Mode.Application:
8081
return createDataExtensionYamlsForApplicationMode(
8182
language,
8283
externalApiUsages,
83-
modeledMethods,
84+
newModeledMethods,
85+
existingModeledMethods,
8486
);
8587
case Mode.Framework:
8688
return createDataExtensionYamlsForFrameworkMode(
8789
databaseName,
8890
language,
8991
externalApiUsages,
90-
modeledMethods,
92+
newModeledMethods,
93+
existingModeledMethods,
9194
);
9295
default:
9396
assertNever(mode);
@@ -97,27 +100,51 @@ export function createDataExtensionYamls(
97100
export function createDataExtensionYamlsForApplicationMode(
98101
language: string,
99102
externalApiUsages: ExternalApiUsage[],
100-
modeledMethods: Record<string, ModeledMethod>,
103+
newModeledMethods: Record<string, ModeledMethod>,
104+
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
101105
): Record<string, string> {
102-
const methodsByLibraryFilename: Record<string, ModeledMethod[]> = {};
103-
106+
const methodsByLibraryFilename: Record<
107+
string,
108+
Record<string, ModeledMethod>
109+
> = {};
110+
111+
// We only want to generate a yaml file when it's a known external API usage
112+
// and there are new modeled methods for it. This avoids us overwriting other
113+
// files that may contain data we don't know about.
104114
for (const externalApiUsage of externalApiUsages) {
105-
const modeledMethod = modeledMethods[externalApiUsage.signature];
106-
if (!modeledMethod) {
107-
continue;
115+
if (externalApiUsage.signature in newModeledMethods) {
116+
methodsByLibraryFilename[
117+
createFilenameForLibrary(externalApiUsage.library)
118+
] = {};
108119
}
120+
}
109121

110-
const filename = createFilenameForLibrary(externalApiUsage.library);
122+
// First populate methodsByLibraryFilename with any existing modeled methods.
123+
for (const [filename, methods] of Object.entries(existingModeledMethods)) {
124+
if (filename in methodsByLibraryFilename) {
125+
for (const [signature, method] of Object.entries(methods)) {
126+
methodsByLibraryFilename[filename][signature] = method;
127+
}
128+
}
129+
}
111130

112-
methodsByLibraryFilename[filename] =
113-
methodsByLibraryFilename[filename] || [];
114-
methodsByLibraryFilename[filename].push(modeledMethod);
131+
// Add the new modeled methods, potentially overwriting existing modeled methods
132+
// but not removing existing modeled methods that are not in the new set.
133+
for (const externalApiUsage of externalApiUsages) {
134+
const method = newModeledMethods[externalApiUsage.signature];
135+
if (method) {
136+
const filename = createFilenameForLibrary(externalApiUsage.library);
137+
methodsByLibraryFilename[filename][method.signature] = method;
138+
}
115139
}
116140

117141
const result: Record<string, string> = {};
118142

119143
for (const [filename, methods] of Object.entries(methodsByLibraryFilename)) {
120-
result[filename] = createDataExtensionYaml(language, methods);
144+
result[filename] = createDataExtensionYaml(
145+
language,
146+
Object.values(methods),
147+
);
121148
}
122149

123150
return result;
@@ -127,7 +154,8 @@ export function createDataExtensionYamlsForFrameworkMode(
127154
databaseName: string,
128155
language: string,
129156
externalApiUsages: ExternalApiUsage[],
130-
modeledMethods: Record<string, ModeledMethod>,
157+
newModeledMethods: Record<string, ModeledMethod>,
158+
existingModeledMethods: Record<string, Record<string, ModeledMethod>>,
131159
prefix = "models/",
132160
suffix = ".model",
133161
): Record<string, string> {
@@ -136,16 +164,25 @@ export function createDataExtensionYamlsForFrameworkMode(
136164
.slice(1)
137165
.map((part) => sanitizeExtensionPackName(part))
138166
.join("-");
167+
const filename = `${prefix}${libraryName}${suffix}.yml`;
168+
169+
const methods: Record<string, ModeledMethod> = {};
170+
171+
for (const [signature, method] of Object.entries(
172+
existingModeledMethods[filename] || {},
173+
)) {
174+
methods[signature] = method;
175+
}
139176

140-
const methods = externalApiUsages
141-
.map((externalApiUsage) => modeledMethods[externalApiUsage.signature])
142-
.filter((modeledMethod) => modeledMethod !== undefined);
177+
for (const externalApiUsage of externalApiUsages) {
178+
const modeledMethod = newModeledMethods[externalApiUsage.signature];
179+
if (modeledMethod) {
180+
methods[modeledMethod.signature] = modeledMethod;
181+
}
182+
}
143183

144184
return {
145-
[`${prefix}${libraryName}${suffix}.yml`]: createDataExtensionYaml(
146-
language,
147-
methods,
148-
),
185+
[filename]: createDataExtensionYaml(language, Object.values(methods)),
149186
};
150187
}
151188

0 commit comments

Comments
 (0)