@wictorwilen/cocogen 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/dist/init/init.d.ts.map +1 -1
- package/dist/init/init.js +231 -28
- package/dist/init/init.js.map +1 -1
- package/dist/init/templates/dotnet/Core/ItemId.cs.ejs +39 -0
- package/dist/init/templates/dotnet/Core/Principal.cs.ejs +30 -0
- package/dist/init/templates/dotnet/Generated/FromRow.cs.ejs +3 -0
- package/dist/init/templates/dotnet/Generated/ItemPayload.cs.ejs +2 -26
- package/dist/init/templates/dotnet/Generated/Model.cs.ejs +4 -0
- package/dist/init/templates/dotnet/Generated/PropertyTransformBase.cs.ejs +3 -13
- package/dist/init/templates/dotnet/Program.commandline.cs.ejs +5 -8
- package/dist/init/templates/ts/src/cli.ts.ejs +6 -10
- package/dist/init/templates/ts/src/core/itemId.ts.ejs +34 -0
- package/dist/init/templates/ts/src/core/principal.ts.ejs +12 -0
- package/dist/init/templates/ts/src/generated/fromRow.ts.ejs +3 -0
- package/dist/init/templates/ts/src/generated/itemPayload.ts.ejs +3 -28
- package/dist/init/templates/ts/src/generated/model.ts.ejs +5 -0
- package/dist/init/templates/ts/src/generated/propertyTransformBase.ts.ejs +3 -0
- package/dist/ir.d.ts +1 -1
- package/dist/ir.d.ts.map +1 -1
- package/dist/tsp/loader.d.ts.map +1 -1
- package/dist/tsp/loader.js +7 -5
- package/dist/tsp/loader.js.map +1 -1
- package/dist/validate/validator.js +2 -2
- package/dist/validate/validator.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.20] - 2026-01-21
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Principal properties now emit a typed principal object with `@odata.type`, `userPrincipalName`, and `tenantId` mappings.
|
|
14
|
+
- Added support for `coco.Principal[]` mapping to Graph `principalCollection` (preview).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- .NET CLI now uses client secret credentials directly when configured (avoids managed identity failures).
|
|
18
|
+
- TS CLI now uses client secret credentials directly when configured (avoids managed identity failures).
|
|
19
|
+
|
|
20
|
+
## [1.0.19] - 2026-01-21
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Clarified error messaging for unsupported scalar types (with float64 hint).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Moved item payload ID encoding helpers into shared core helpers for TS and .NET.
|
|
27
|
+
|
|
10
28
|
## [1.0.18] - 2026-01-21
|
|
11
29
|
|
|
12
30
|
### Breaking Changes
|
|
@@ -40,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
40
58
|
- Collection values no longer split on commas; use semicolons instead.
|
|
41
59
|
|
|
42
60
|
[Unreleased]: https://github.com/wictorwilen/cocogen/compare/v1.0.16...HEAD
|
|
61
|
+
[1.0.20]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.20
|
|
62
|
+
[1.0.19]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.19
|
|
43
63
|
[1.0.18]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.18
|
|
44
64
|
[1.0.17]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.17
|
|
45
65
|
[1.0.16]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.16
|
package/README.md
CHANGED
package/dist/init/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/init/init.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAC;AAK1D,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/init/init.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAC;AAK1D,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AA2kDF,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC,CAyB1G;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC,CA2B9C;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC,CAOxG;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC,CAsHtG;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC,CAoH9C"}
|
package/dist/init/init.js
CHANGED
|
@@ -36,7 +36,9 @@ function toTsType(type) {
|
|
|
36
36
|
case "dateTimeCollection":
|
|
37
37
|
return "string[]";
|
|
38
38
|
case "principal":
|
|
39
|
-
return "
|
|
39
|
+
return "Principal";
|
|
40
|
+
case "principalCollection":
|
|
41
|
+
return "Principal[]";
|
|
40
42
|
default:
|
|
41
43
|
return "unknown";
|
|
42
44
|
}
|
|
@@ -44,8 +46,11 @@ function toTsType(type) {
|
|
|
44
46
|
function toCsType(type) {
|
|
45
47
|
switch (type) {
|
|
46
48
|
case "string":
|
|
47
|
-
case "principal":
|
|
48
49
|
return "string";
|
|
50
|
+
case "principal":
|
|
51
|
+
return "Principal";
|
|
52
|
+
case "principalCollection":
|
|
53
|
+
return "List<Principal>";
|
|
49
54
|
case "boolean":
|
|
50
55
|
return "bool";
|
|
51
56
|
case "int64":
|
|
@@ -232,6 +237,8 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
232
237
|
return "parseNumberCollection";
|
|
233
238
|
case "dateTimeCollection":
|
|
234
239
|
return "parseStringCollection";
|
|
240
|
+
case "principalCollection":
|
|
241
|
+
return "parseStringCollection";
|
|
235
242
|
case "boolean":
|
|
236
243
|
return "parseBoolean";
|
|
237
244
|
case "int64":
|
|
@@ -267,16 +274,23 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
267
274
|
: base;
|
|
268
275
|
}))
|
|
269
276
|
: null;
|
|
277
|
+
const principalExpression = p.type === "principal"
|
|
278
|
+
? buildTsPrincipalExpression(p.personEntity?.fields ?? null, p.source.csvHeaders)
|
|
279
|
+
: p.type === "principalCollection"
|
|
280
|
+
? buildTsPrincipalCollectionExpression(p.personEntity?.fields ?? null, p.source.csvHeaders)
|
|
281
|
+
: null;
|
|
270
282
|
const isPeopleLabel = p.labels.some((label) => label.startsWith("person"));
|
|
271
283
|
const needsManualEntity = isPeopleLabel && !p.personEntity;
|
|
272
284
|
const noSource = Boolean(p.source.noSource);
|
|
273
285
|
const expression = needsManualEntity
|
|
274
286
|
? `(() => { throw new Error("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement transform in propertyTransform.ts."); })()`
|
|
275
|
-
:
|
|
276
|
-
?
|
|
277
|
-
:
|
|
278
|
-
?
|
|
279
|
-
:
|
|
287
|
+
: noSource
|
|
288
|
+
? `undefined as unknown as ${toTsType(p.type)}`
|
|
289
|
+
: (p.type === "principal" || p.type === "principalCollection") && principalExpression
|
|
290
|
+
? principalExpression
|
|
291
|
+
: personEntity
|
|
292
|
+
? personEntity
|
|
293
|
+
: `${parser}(readSourceValue(row, ${JSON.stringify(p.source.csvHeaders)}))`;
|
|
280
294
|
const validationMetadata = {
|
|
281
295
|
name: p.name,
|
|
282
296
|
type: p.type,
|
|
@@ -287,7 +301,7 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
287
301
|
...(p.minValue !== undefined ? { minValue: p.minValue } : {}),
|
|
288
302
|
...(p.maxValue !== undefined ? { maxValue: p.maxValue } : {}),
|
|
289
303
|
};
|
|
290
|
-
const validatedExpression = needsManualEntity || noSource || personEntity
|
|
304
|
+
const validatedExpression = needsManualEntity || noSource || personEntity || p.type === "principal" || p.type === "principalCollection"
|
|
291
305
|
? expression
|
|
292
306
|
: applyTsValidationExpression(validationMetadata, expression);
|
|
293
307
|
return {
|
|
@@ -304,10 +318,12 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
304
318
|
const idRawExpression = idRawHeaders.length
|
|
305
319
|
? `parseString(readSourceValue(row, ${JSON.stringify(idRawHeaders)}))`
|
|
306
320
|
: '""';
|
|
321
|
+
const usesPrincipal = ir.properties.some((p) => p.type === "principal" || p.type === "principalCollection");
|
|
307
322
|
await writeFile(path.join(outDir, "src", schemaFolderName, "model.ts"), await renderTemplate("ts/src/generated/model.ts.ejs", {
|
|
308
323
|
itemTypeName: ir.item.typeName,
|
|
309
324
|
properties: modelProperties,
|
|
310
325
|
itemDocComment: ir.item.doc ? formatDocComment(ir.item.doc) : undefined,
|
|
326
|
+
usesPrincipal,
|
|
311
327
|
}), "utf8");
|
|
312
328
|
await writeFile(path.join(outDir, "src", "datasource", "row.ts"), await renderTemplate("ts/src/generated/row.ts.ejs", {}), "utf8");
|
|
313
329
|
await writeFile(path.join(outDir, "src", schemaFolderName, "constants.ts"), await renderTemplate("ts/src/generated/constants.ts.ejs", {
|
|
@@ -328,6 +344,7 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
328
344
|
}), "utf8");
|
|
329
345
|
await writeFile(path.join(outDir, "src", schemaFolderName, "propertyTransformBase.ts"), await renderTemplate("ts/src/generated/propertyTransformBase.ts.ejs", {
|
|
330
346
|
properties: transformProperties,
|
|
347
|
+
usesPrincipal,
|
|
331
348
|
}), "utf8");
|
|
332
349
|
const transformOverridesPath = path.join(outDir, "src", schemaFolderName, "propertyTransform.ts");
|
|
333
350
|
try {
|
|
@@ -340,6 +357,7 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
340
357
|
properties: transformProperties,
|
|
341
358
|
itemTypeName: ir.item.typeName,
|
|
342
359
|
idRawExpression,
|
|
360
|
+
usesPrincipal,
|
|
343
361
|
}), "utf8");
|
|
344
362
|
const propertiesObjectLines = ir.properties
|
|
345
363
|
.filter((p) => p.name !== ir.item.contentPropertyName)
|
|
@@ -369,6 +387,10 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
369
387
|
isPeopleConnector: ir.connection.contentCategory === "people",
|
|
370
388
|
}), "utf8");
|
|
371
389
|
await writeFile(path.join(outDir, "src", "core", "validation.ts"), await renderTemplate("ts/src/core/validation.ts.ejs", {}), "utf8");
|
|
390
|
+
await writeFile(path.join(outDir, "src", "core", "itemId.ts"), await renderTemplate("ts/src/core/itemId.ts.ejs", {}), "utf8");
|
|
391
|
+
if (usesPrincipal) {
|
|
392
|
+
await writeFile(path.join(outDir, "src", "core", "principal.ts"), await renderTemplate("ts/src/core/principal.ts.ejs", {}), "utf8");
|
|
393
|
+
}
|
|
372
394
|
}
|
|
373
395
|
function toGraphPropertyTypeEnumName(type) {
|
|
374
396
|
switch (type) {
|
|
@@ -392,6 +414,8 @@ function toGraphPropertyTypeEnumName(type) {
|
|
|
392
414
|
return "DateTimeCollection";
|
|
393
415
|
case "principal":
|
|
394
416
|
return "Principal";
|
|
417
|
+
case "principalCollection":
|
|
418
|
+
return "PrincipalCollection";
|
|
395
419
|
default:
|
|
396
420
|
return "String";
|
|
397
421
|
}
|
|
@@ -429,6 +453,7 @@ function toCsParseFunction(type) {
|
|
|
429
453
|
case "dateTime":
|
|
430
454
|
return "RowParser.ParseDateTime";
|
|
431
455
|
case "principal":
|
|
456
|
+
case "principalCollection":
|
|
432
457
|
case "string":
|
|
433
458
|
default:
|
|
434
459
|
return "RowParser.ParseString";
|
|
@@ -549,6 +574,151 @@ ${indentUnit}}`;
|
|
|
549
574
|
: "const lengths = [0];";
|
|
550
575
|
return `(() => {\n${fieldLines.join("\n")}\n${lengthVars}\n const maxLen = Math.max(0, ...lengths);\n const getValue = (values: string[], index: number): string => {\n if (values.length === 0) return \"\";\n if (values.length === 1) return values[0] ?? \"\";\n return values[index] ?? \"\";\n };\n const results: string[] = [];\n for (let index = 0; index < maxLen; index++) {\n results.push(JSON.stringify(\n ${renderNodeMany(tree)}\n ));\n }\n return results;\n})()`;
|
|
551
576
|
}
|
|
577
|
+
function buildPrincipalFieldEntries(fields, fallbackHeaders) {
|
|
578
|
+
if (fields && fields.length > 0) {
|
|
579
|
+
return fields
|
|
580
|
+
.map((field) => {
|
|
581
|
+
const key = field.path.split(".").pop() ?? field.path;
|
|
582
|
+
return {
|
|
583
|
+
key,
|
|
584
|
+
headersLiteral: JSON.stringify(field.source.csvHeaders),
|
|
585
|
+
};
|
|
586
|
+
})
|
|
587
|
+
.filter((entry) => entry.headersLiteral.length > 2 && entry.key.length > 0);
|
|
588
|
+
}
|
|
589
|
+
if (fallbackHeaders.length > 0) {
|
|
590
|
+
return [
|
|
591
|
+
{
|
|
592
|
+
key: "userPrincipalName",
|
|
593
|
+
headersLiteral: JSON.stringify([fallbackHeaders[0]]),
|
|
594
|
+
},
|
|
595
|
+
];
|
|
596
|
+
}
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
function buildTsPrincipalExpression(fields, fallbackHeaders) {
|
|
600
|
+
const entries = buildPrincipalFieldEntries(fields, fallbackHeaders).map((entry) => ` ${JSON.stringify(entry.key)}: parseString(readSourceValue(row, ${entry.headersLiteral}))`);
|
|
601
|
+
return `({\n "@odata.type": "#microsoft.graph.externalConnectors.principal"${entries.length ? ",\n" + entries.join(",\n") : ""}\n})`;
|
|
602
|
+
}
|
|
603
|
+
function buildCsPrincipalExpression(fields, fallbackHeaders) {
|
|
604
|
+
const entries = buildPrincipalFieldEntries(fields, fallbackHeaders);
|
|
605
|
+
const knownMap = new Map([
|
|
606
|
+
["userPrincipalName", "UserPrincipalName"],
|
|
607
|
+
["tenantId", "TenantId"],
|
|
608
|
+
["id", "Id"],
|
|
609
|
+
["type", "Type"],
|
|
610
|
+
["displayName", "DisplayName"],
|
|
611
|
+
]);
|
|
612
|
+
const knownAssignments = [];
|
|
613
|
+
const additionalDataEntries = [];
|
|
614
|
+
for (const entry of entries) {
|
|
615
|
+
const headers = `new[] { ${JSON.parse(entry.headersLiteral).map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
616
|
+
const propertyName = knownMap.get(entry.key);
|
|
617
|
+
if (propertyName) {
|
|
618
|
+
knownAssignments.push(` ${propertyName} = RowParser.ParseString(row, ${headers}),`);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
additionalDataEntries.push(` [${JSON.stringify(entry.key)}] = RowParser.ParseString(row, ${headers}),`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
const additionalDataBlock = additionalDataEntries.length
|
|
625
|
+
? [
|
|
626
|
+
" AdditionalData = new Dictionary<string, object?>",
|
|
627
|
+
" {",
|
|
628
|
+
...additionalDataEntries,
|
|
629
|
+
" },",
|
|
630
|
+
]
|
|
631
|
+
: [];
|
|
632
|
+
return [
|
|
633
|
+
"new Principal",
|
|
634
|
+
"{",
|
|
635
|
+
" OdataType = \"#microsoft.graph.externalConnectors.principal\",",
|
|
636
|
+
...knownAssignments,
|
|
637
|
+
...additionalDataBlock,
|
|
638
|
+
"}"
|
|
639
|
+
].join("\n");
|
|
640
|
+
}
|
|
641
|
+
function buildTsPrincipalCollectionExpression(fields, fallbackHeaders) {
|
|
642
|
+
const entries = buildPrincipalFieldEntries(fields, fallbackHeaders);
|
|
643
|
+
if (entries.length === 0)
|
|
644
|
+
return "[]";
|
|
645
|
+
const fieldLines = entries.map((entry, index) => ` const field${index} = parseStringCollection(readSourceValue(row, ${entry.headersLiteral}));`);
|
|
646
|
+
const lengthVars = entries.length
|
|
647
|
+
? ` const lengths = [${entries.map((_, index) => `field${index}.length`).join(", ")}];`
|
|
648
|
+
: " const lengths = [0];";
|
|
649
|
+
const fieldsBlock = entries
|
|
650
|
+
.map((entry, index) => ` ${JSON.stringify(entry.key)}: getValue(field${index}, index)`)
|
|
651
|
+
.join(",\n");
|
|
652
|
+
return `(() => {\n${fieldLines.join("\n")}\n${lengthVars}\n const maxLen = Math.max(0, ...lengths);\n const getValue = (values: string[], index: number): string => {\n if (values.length === 0) return "";\n if (values.length === 1) return values[0] ?? "";\n return values[index] ?? "";\n };\n const results: Principal[] = [];\n for (let index = 0; index < maxLen; index++) {\n results.push({\n "@odata.type": "#microsoft.graph.externalConnectors.principal",\n${fieldsBlock}\n });\n }\n return results;\n})()`;
|
|
653
|
+
}
|
|
654
|
+
function buildCsPrincipalCollectionExpression(fields, fallbackHeaders) {
|
|
655
|
+
const entries = buildPrincipalFieldEntries(fields, fallbackHeaders);
|
|
656
|
+
if (entries.length === 0)
|
|
657
|
+
return "new List<Principal>()";
|
|
658
|
+
const fieldLines = entries.map((entry, index) => {
|
|
659
|
+
const headers = `new[] { ${JSON.parse(entry.headersLiteral).map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
660
|
+
return ` var field${index} = RowParser.ParseStringCollection(row, ${headers});`;
|
|
661
|
+
});
|
|
662
|
+
const knownMap = new Map([
|
|
663
|
+
["userPrincipalName", "UserPrincipalName"],
|
|
664
|
+
["tenantId", "TenantId"],
|
|
665
|
+
["id", "Id"],
|
|
666
|
+
["type", "Type"],
|
|
667
|
+
["displayName", "DisplayName"],
|
|
668
|
+
]);
|
|
669
|
+
const knownAssignments = entries
|
|
670
|
+
.map((entry, index) => {
|
|
671
|
+
const propertyName = knownMap.get(entry.key);
|
|
672
|
+
return propertyName ? ` ${propertyName} = GetValue(field${index}, index),` : null;
|
|
673
|
+
})
|
|
674
|
+
.filter((line) => Boolean(line));
|
|
675
|
+
const additionalDataEntries = entries
|
|
676
|
+
.map((entry, index) => {
|
|
677
|
+
if (knownMap.has(entry.key))
|
|
678
|
+
return null;
|
|
679
|
+
return ` [${JSON.stringify(entry.key)}] = GetValue(field${index}, index),`;
|
|
680
|
+
})
|
|
681
|
+
.filter((line) => Boolean(line));
|
|
682
|
+
const additionalDataBlock = additionalDataEntries.length
|
|
683
|
+
? [
|
|
684
|
+
" AdditionalData = new Dictionary<string, object?>",
|
|
685
|
+
" {",
|
|
686
|
+
...additionalDataEntries,
|
|
687
|
+
" },",
|
|
688
|
+
]
|
|
689
|
+
: [];
|
|
690
|
+
const lengthsLine = entries.map((_, index) => `field${index}.Count`).join(", ");
|
|
691
|
+
return [
|
|
692
|
+
"new Func<List<Principal>>(() =>",
|
|
693
|
+
"{",
|
|
694
|
+
...fieldLines,
|
|
695
|
+
` var lengths = new[] { ${lengthsLine} };`,
|
|
696
|
+
" var maxLen = 0;",
|
|
697
|
+
" foreach (var len in lengths)",
|
|
698
|
+
" {",
|
|
699
|
+
" if (len > maxLen) maxLen = len;",
|
|
700
|
+
" }",
|
|
701
|
+
" string GetValue(IReadOnlyList<string> values, int index)",
|
|
702
|
+
" {",
|
|
703
|
+
" if (values.Count == 0) return \"\";",
|
|
704
|
+
" if (values.Count == 1) return values[0] ?? \"\";",
|
|
705
|
+
" return index < values.Count ? (values[index] ?? \"\") : \"\";",
|
|
706
|
+
" }",
|
|
707
|
+
" var results = new List<Principal>();",
|
|
708
|
+
" for (var index = 0; index < maxLen; index++)",
|
|
709
|
+
" {",
|
|
710
|
+
" var principal = new Principal",
|
|
711
|
+
" {",
|
|
712
|
+
" OdataType = \"#microsoft.graph.externalConnectors.principal\",",
|
|
713
|
+
...knownAssignments,
|
|
714
|
+
...additionalDataBlock,
|
|
715
|
+
" };",
|
|
716
|
+
" results.Add(principal);",
|
|
717
|
+
" }",
|
|
718
|
+
" return results;",
|
|
719
|
+
"})()",
|
|
720
|
+
].join("\n");
|
|
721
|
+
}
|
|
552
722
|
function buildCsPersonEntityExpression(fields, valueExpressionBuilder = (headersLiteral) => `RowParser.ParseString(row, ${headersLiteral})`) {
|
|
553
723
|
const tree = buildObjectTree(fields);
|
|
554
724
|
const indentUnit = " ";
|
|
@@ -641,6 +811,8 @@ function sampleValueForType(type) {
|
|
|
641
811
|
return "2024-01-01T00:00:00Z;2024-01-02T00:00:00Z";
|
|
642
812
|
case "principal":
|
|
643
813
|
return 'alice@contoso.com';
|
|
814
|
+
case "principalCollection":
|
|
815
|
+
return "alice@contoso.com;bob@contoso.com";
|
|
644
816
|
case "string":
|
|
645
817
|
default:
|
|
646
818
|
return "sample";
|
|
@@ -727,7 +899,7 @@ function applyCsValidationExpression(prop, expression, csvHeadersLiteral) {
|
|
|
727
899
|
case "dateTimeCollection":
|
|
728
900
|
if (!stringConstraints.hasAny)
|
|
729
901
|
return expression;
|
|
730
|
-
return `
|
|
902
|
+
return `Validation.ValidateStringCollection(${nameLiteral}, RowParser.ParseStringCollection(RowParser.ReadValue(row, ${csvHeadersLiteral})), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format}).Select(value => RowParser.ParseDateTime(value)).ToList()`;
|
|
731
903
|
case "int64":
|
|
732
904
|
return numberConstraints.hasAny
|
|
733
905
|
? `Validation.ValidateInt64(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
@@ -871,25 +1043,32 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
871
1043
|
const isPeopleLabel = p.labels.some((label) => label.startsWith("person"));
|
|
872
1044
|
const needsManualEntity = isPeopleLabel && !p.personEntity;
|
|
873
1045
|
const noSource = Boolean(p.source.noSource);
|
|
1046
|
+
const principalExpression = p.type === "principal"
|
|
1047
|
+
? buildCsPrincipalExpression(p.personEntity?.fields ?? null, p.source.csvHeaders)
|
|
1048
|
+
: p.type === "principalCollection"
|
|
1049
|
+
? buildCsPrincipalCollectionExpression(p.personEntity?.fields ?? null, p.source.csvHeaders)
|
|
1050
|
+
: null;
|
|
874
1051
|
const transformExpression = needsManualEntity
|
|
875
1052
|
? `throw new NotImplementedException("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement in PropertyTransform.cs.")`
|
|
876
|
-
:
|
|
877
|
-
?
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
:
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1053
|
+
: noSource
|
|
1054
|
+
? "default!"
|
|
1055
|
+
: (p.type === "principal" || p.type === "principalCollection") && principalExpression
|
|
1056
|
+
? principalExpression
|
|
1057
|
+
: personEntity
|
|
1058
|
+
? isCollection
|
|
1059
|
+
? buildCsPersonEntityCollectionExpression(personEntity.fields, (headersLiteral) => {
|
|
1060
|
+
const base = `RowParser.ParseStringCollection(row, ${headersLiteral})`;
|
|
1061
|
+
return csStringConstraints.hasAny
|
|
1062
|
+
? `Validation.ValidateStringCollection(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
|
|
1063
|
+
: base;
|
|
1064
|
+
})
|
|
1065
|
+
: buildCsPersonEntityExpression(personEntity.fields, (headersLiteral) => {
|
|
1066
|
+
const base = `RowParser.ParseString(row, ${headersLiteral})`;
|
|
1067
|
+
return csStringConstraints.hasAny
|
|
1068
|
+
? `Validation.ValidateString(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
|
|
1069
|
+
: base;
|
|
1070
|
+
})
|
|
1071
|
+
: `${parseFn}(row, ${csvHeadersLiteral})`;
|
|
893
1072
|
const validationMetadata = {
|
|
894
1073
|
name: p.name,
|
|
895
1074
|
type: p.type,
|
|
@@ -900,7 +1079,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
900
1079
|
...(p.minValue !== undefined ? { minValue: p.minValue } : {}),
|
|
901
1080
|
...(p.maxValue !== undefined ? { maxValue: p.maxValue } : {}),
|
|
902
1081
|
};
|
|
903
|
-
const validatedExpression = needsManualEntity || noSource || personEntity
|
|
1082
|
+
const validatedExpression = needsManualEntity || noSource || personEntity || p.type === "principal" || p.type === "principalCollection"
|
|
904
1083
|
? transformExpression
|
|
905
1084
|
: applyCsValidationExpression(validationMetadata, transformExpression, csvHeadersLiteral);
|
|
906
1085
|
return {
|
|
@@ -944,6 +1123,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
944
1123
|
const schemaPropertyLines = properties
|
|
945
1124
|
.filter((p) => p.name !== ir.item.contentPropertyName)
|
|
946
1125
|
.map((p) => {
|
|
1126
|
+
const isPrincipalCollection = ir.connection.graphApiVersion === "beta" && p.type === "principalCollection";
|
|
947
1127
|
const labels = p.labels.length > 0
|
|
948
1128
|
? `new List<string> { ${p.labels.map((l) => JSON.stringify(l)).join(", ")} }`
|
|
949
1129
|
: null;
|
|
@@ -955,6 +1135,8 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
955
1135
|
additionalDataEntries.push(` ["description"] = ${JSON.stringify(p.description)},`);
|
|
956
1136
|
if (labels)
|
|
957
1137
|
additionalDataEntries.push(` ["labels"] = ${labels},`);
|
|
1138
|
+
if (isPrincipalCollection)
|
|
1139
|
+
additionalDataEntries.push(` ["type"] = "principalCollection",`);
|
|
958
1140
|
const additionalDataBlock = additionalDataEntries.length > 0
|
|
959
1141
|
? [
|
|
960
1142
|
" AdditionalData = new Dictionary<string, object>",
|
|
@@ -967,7 +1149,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
967
1149
|
" new Property",
|
|
968
1150
|
" {",
|
|
969
1151
|
` Name = ${JSON.stringify(p.name)},`,
|
|
970
|
-
` Type = PropertyType.${p.graphTypeEnumName}
|
|
1152
|
+
...(isPrincipalCollection ? [] : [` Type = PropertyType.${p.graphTypeEnumName},`]),
|
|
971
1153
|
];
|
|
972
1154
|
if (p.search.searchable !== undefined)
|
|
973
1155
|
lines.push(` IsSearchable = ${p.search.searchable ? "true" : "false"},`);
|
|
@@ -1027,11 +1209,15 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
1027
1209
|
` Value = ${contentValueExpression},`,
|
|
1028
1210
|
" };",
|
|
1029
1211
|
].join("\n");
|
|
1212
|
+
const usesPrincipal = properties.some((p) => p.type === "principal" || p.type === "principalCollection");
|
|
1030
1213
|
await writeFile(path.join(outDir, schemaFolderName, "Model.cs"), await renderTemplate("dotnet/Generated/Model.cs.ejs", {
|
|
1214
|
+
namespaceName,
|
|
1031
1215
|
schemaNamespace,
|
|
1032
1216
|
itemTypeName: ir.item.typeName,
|
|
1033
1217
|
properties: properties.map((p) => ({ csName: p.csName, csType: p.csType })),
|
|
1034
1218
|
recordDocLines,
|
|
1219
|
+
graphApiVersion: ir.connection.graphApiVersion,
|
|
1220
|
+
usesPrincipal,
|
|
1035
1221
|
}), "utf8");
|
|
1036
1222
|
await writeFile(path.join(outDir, schemaFolderName, "Constants.cs"), await renderTemplate("dotnet/Generated/Constants.cs.ejs", {
|
|
1037
1223
|
schemaNamespace,
|
|
@@ -1060,6 +1246,11 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
1060
1246
|
schemaNamespace,
|
|
1061
1247
|
properties,
|
|
1062
1248
|
usesPersonEntity: properties.some((p) => p.personEntity),
|
|
1249
|
+
usesLinq: properties.some((p) => p.type === "dateTimeCollection" &&
|
|
1250
|
+
(p.minLength !== undefined ||
|
|
1251
|
+
p.maxLength !== undefined ||
|
|
1252
|
+
Boolean(p.pattern?.regex) ||
|
|
1253
|
+
Boolean(p.format))),
|
|
1063
1254
|
}), "utf8");
|
|
1064
1255
|
const transformOverridesPath = path.join(outDir, schemaFolderName, "PropertyTransform.cs");
|
|
1065
1256
|
try {
|
|
@@ -1075,8 +1266,11 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
1075
1266
|
schemaNamespace,
|
|
1076
1267
|
itemTypeName: ir.item.typeName,
|
|
1077
1268
|
constructorArgLines,
|
|
1269
|
+
usesPrincipal,
|
|
1270
|
+
graphApiVersion: ir.connection.graphApiVersion,
|
|
1078
1271
|
}), "utf8");
|
|
1079
1272
|
await writeFile(path.join(outDir, schemaFolderName, "ItemPayload.cs"), await renderTemplate("dotnet/Generated/ItemPayload.cs.ejs", {
|
|
1273
|
+
namespaceName,
|
|
1080
1274
|
schemaNamespace,
|
|
1081
1275
|
itemTypeName: ir.item.typeName,
|
|
1082
1276
|
itemIdExpression,
|
|
@@ -1095,6 +1289,15 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
1095
1289
|
await writeFile(path.join(outDir, "Core", "Validation.cs"), await renderTemplate("dotnet/Core/Validation.cs.ejs", {
|
|
1096
1290
|
namespaceName,
|
|
1097
1291
|
}), "utf8");
|
|
1292
|
+
await writeFile(path.join(outDir, "Core", "ItemId.cs"), await renderTemplate("dotnet/Core/ItemId.cs.ejs", {
|
|
1293
|
+
namespaceName,
|
|
1294
|
+
}), "utf8");
|
|
1295
|
+
if (usesPrincipal && ir.connection.graphApiVersion === "beta") {
|
|
1296
|
+
await writeFile(path.join(outDir, "Core", "Principal.cs"), await renderTemplate("dotnet/Core/Principal.cs.ejs", {
|
|
1297
|
+
namespaceName,
|
|
1298
|
+
graphApiVersion: ir.connection.graphApiVersion,
|
|
1299
|
+
}), "utf8");
|
|
1300
|
+
}
|
|
1098
1301
|
}
|
|
1099
1302
|
function formatValidationErrors(ir) {
|
|
1100
1303
|
const issues = validateIr(ir);
|