Skip to content

Commit 82f17c4

Browse files
authored
Merge pull request #3148 from github/koesie10/model-editor-capitalization
Fix incorrect model files on case-insensitive file systems
2 parents 790503b + 12ea1b9 commit 82f17c4

File tree

2 files changed

+166
-11
lines changed

2 files changed

+166
-11
lines changed

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

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,25 +181,40 @@ function createDataExtensionYamlsByGrouping(
181181
>,
182182
createFilename: (method: Method) => string,
183183
): Record<string, string> {
184-
const methodsByFilename: Record<string, Record<string, ModeledMethod[]>> = {};
184+
const actualFilenameByCanonicalFilename: Record<string, string> = {};
185+
186+
const methodsByCanonicalFilename: Record<
187+
string,
188+
Record<string, ModeledMethod[]>
189+
> = {};
185190

186191
// We only want to generate a yaml file when it's a known external API usage
187192
// and there are new modeled methods for it. This avoids us overwriting other
188193
// files that may contain data we don't know about.
189194
for (const method of methods) {
190195
if (method.signature in newModeledMethods) {
191-
methodsByFilename[createFilename(method)] = {};
196+
const filename = createFilename(method);
197+
const canonicalFilename = canonicalizeFilename(filename);
198+
199+
methodsByCanonicalFilename[canonicalFilename] = {};
200+
actualFilenameByCanonicalFilename[canonicalFilename] = filename;
192201
}
193202
}
194203

195204
// First populate methodsByFilename with any existing modeled methods.
196205
for (const [filename, methodsBySignature] of Object.entries(
197206
existingModeledMethods,
198207
)) {
199-
if (filename in methodsByFilename) {
208+
const canonicalFilename = canonicalizeFilename(filename);
209+
210+
if (canonicalFilename in methodsByCanonicalFilename) {
200211
for (const [signature, methods] of Object.entries(methodsBySignature)) {
201-
methodsByFilename[filename][signature] = [...methods];
212+
methodsByCanonicalFilename[canonicalFilename][signature] = [...methods];
202213
}
214+
215+
// Ensure that if a file exists on disk, we use the same capitalization
216+
// as the original file.
217+
actualFilenameByCanonicalFilename[canonicalFilename] = filename;
203218
}
204219
}
205220

@@ -209,19 +224,25 @@ function createDataExtensionYamlsByGrouping(
209224
const newMethods = newModeledMethods[method.signature];
210225
if (newMethods) {
211226
const filename = createFilename(method);
227+
const canonicalFilename = canonicalizeFilename(filename);
212228

213229
// Override any existing modeled methods with the new ones.
214-
methodsByFilename[filename][method.signature] = [...newMethods];
230+
methodsByCanonicalFilename[canonicalFilename][method.signature] = [
231+
...newMethods,
232+
];
215233
}
216234
}
217235

218236
const result: Record<string, string> = {};
219237

220-
for (const [filename, methods] of Object.entries(methodsByFilename)) {
221-
result[filename] = createDataExtensionYaml(
222-
language,
223-
Object.values(methods).flatMap((methods) => methods),
224-
);
238+
for (const [canonicalFilename, methods] of Object.entries(
239+
methodsByCanonicalFilename,
240+
)) {
241+
result[actualFilenameByCanonicalFilename[canonicalFilename]] =
242+
createDataExtensionYaml(
243+
language,
244+
Object.values(methods).flatMap((methods) => methods),
245+
);
225246
}
226247

227248
return result;
@@ -299,6 +320,13 @@ export function createFilenameForPackage(
299320
return `${prefix}${packageName}${suffix}.yml`;
300321
}
301322

323+
function canonicalizeFilename(filename: string) {
324+
// We want to canonicalize filenames so that they are always in the same format
325+
// for comparison purposes. This is important because we want to avoid overwriting
326+
// data extension YAML files on case-insensitive file systems.
327+
return filename.toLowerCase();
328+
}
329+
302330
function validateModelExtensionFile(data: unknown): data is ModelExtensionFile {
303331
modelExtensionFileSchemaValidate(data);
304332

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

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
createFilenameForPackage,
77
loadDataExtensionYaml,
88
} from "../../../src/model-editor/yaml";
9-
import { CallClassification } from "../../../src/model-editor/method";
9+
import { CallClassification, Method } from "../../../src/model-editor/method";
1010
import { QueryLanguage } from "../../../src/common/query-language";
11+
import { ModeledMethod } from "../../../src/model-editor/modeled-method";
1112

1213
describe("createDataExtensionYaml", () => {
1314
it("creates the correct YAML file", () => {
@@ -980,6 +981,132 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
980981
`,
981982
});
982983
});
984+
985+
describe("with same package names but different capitalizations", () => {
986+
const methods: Method[] = [
987+
{
988+
library: "HostTestAppDbContext",
989+
signature:
990+
"Volo.Abp.TestApp.MongoDb.HostTestAppDbContext#get_FifthDbContextDummyEntity()",
991+
packageName: "Volo.Abp.TestApp.MongoDb",
992+
typeName: "HostTestAppDbContext",
993+
methodName: "get_FifthDbContextDummyEntity",
994+
methodParameters: "()",
995+
supported: false,
996+
supportedType: "none",
997+
usages: [],
998+
},
999+
{
1000+
library: "CityRepository",
1001+
signature:
1002+
"Volo.Abp.TestApp.MongoDB.CityRepository#FindByNameAsync(System.String)",
1003+
packageName: "Volo.Abp.TestApp.MongoDB",
1004+
typeName: "CityRepository",
1005+
methodName: "FindByNameAsync",
1006+
methodParameters: "(System.String)",
1007+
supported: false,
1008+
supportedType: "none",
1009+
usages: [],
1010+
},
1011+
];
1012+
const newModeledMethods: Record<string, ModeledMethod[]> = {
1013+
"Volo.Abp.TestApp.MongoDb.HostTestAppDbContext#get_FifthDbContextDummyEntity()":
1014+
[
1015+
{
1016+
type: "sink",
1017+
input: "Argument[0]",
1018+
kind: "sql",
1019+
provenance: "df-generated",
1020+
signature:
1021+
"Volo.Abp.TestApp.MongoDb.HostTestAppDbContext#get_FifthDbContextDummyEntity()",
1022+
packageName: "Volo.Abp.TestApp.MongoDb",
1023+
typeName: "HostTestAppDbContext",
1024+
methodName: "get_FifthDbContextDummyEntity",
1025+
methodParameters: "()",
1026+
},
1027+
],
1028+
"Volo.Abp.TestApp.MongoDB.CityRepository#FindByNameAsync(System.String)":
1029+
[
1030+
{
1031+
type: "neutral",
1032+
kind: "summary",
1033+
provenance: "df-generated",
1034+
signature:
1035+
"Volo.Abp.TestApp.MongoDB.CityRepository#FindByNameAsync(System.String)",
1036+
packageName: "Volo.Abp.TestApp.MongoDB",
1037+
typeName: "CityRepository",
1038+
methodName: "FindByNameAsync",
1039+
methodParameters: "(System.String)",
1040+
},
1041+
],
1042+
};
1043+
const modelYaml = `extensions:
1044+
- addsTo:
1045+
pack: codeql/csharp-all
1046+
extensible: sourceModel
1047+
data: []
1048+
1049+
- addsTo:
1050+
pack: codeql/csharp-all
1051+
extensible: sinkModel
1052+
data:
1053+
- ["Volo.Abp.TestApp.MongoDb","HostTestAppDbContext",true,"get_FifthDbContextDummyEntity","()","","Argument[0]","sql","df-generated"]
1054+
1055+
- addsTo:
1056+
pack: codeql/csharp-all
1057+
extensible: summaryModel
1058+
data: []
1059+
1060+
- addsTo:
1061+
pack: codeql/csharp-all
1062+
extensible: neutralModel
1063+
data:
1064+
- ["Volo.Abp.TestApp.MongoDB","CityRepository","FindByNameAsync","(System.String)","summary","df-generated"]
1065+
`;
1066+
1067+
it("creates the correct YAML files when there are existing modeled methods", () => {
1068+
const yaml = createDataExtensionYamlsForFrameworkMode(
1069+
QueryLanguage.CSharp,
1070+
methods,
1071+
newModeledMethods,
1072+
{},
1073+
);
1074+
1075+
expect(yaml).toEqual({
1076+
"models/Volo.Abp.TestApp.MongoDB.model.yml": modelYaml,
1077+
});
1078+
});
1079+
1080+
it("creates the correct YAML files when there are existing modeled methods", () => {
1081+
const yaml = createDataExtensionYamlsForFrameworkMode(
1082+
QueryLanguage.CSharp,
1083+
methods,
1084+
newModeledMethods,
1085+
{
1086+
"models/Volo.Abp.TestApp.mongodb.model.yml": {
1087+
"Volo.Abp.TestApp.MongoDB.CityRepository#FindByNameAsync(System.String)":
1088+
[
1089+
{
1090+
type: "neutral",
1091+
kind: "summary",
1092+
provenance: "manual",
1093+
signature:
1094+
"Volo.Abp.TestApp.MongoDB.CityRepository#FindByNameAsync(System.String)",
1095+
packageName: "Volo.Abp.TestApp.MongoDB",
1096+
typeName: "CityRepository",
1097+
methodName: "FindByNameAsync",
1098+
methodParameters: "(System.String)",
1099+
},
1100+
],
1101+
},
1102+
},
1103+
);
1104+
1105+
expect(yaml).toEqual({
1106+
"models/Volo.Abp.TestApp.mongodb.model.yml": modelYaml,
1107+
});
1108+
});
1109+
});
9831110
});
9841111

9851112
describe("loadDataExtensionYaml", () => {

0 commit comments

Comments
 (0)