@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 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
@@ -31,6 +31,7 @@
31
31
 
32
32
  End-user guide:
33
33
  - [docs/end-user.md](https://github.com/wictorwilen/cocogen/blob/main/docs/end-user.md)
34
+
34
35
  Agent schema guidance:
35
36
  - [docs/schema-assistant.md](https://github.com/wictorwilen/cocogen/blob/main/docs/schema-assistant.md)
36
37
 
@@ -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;AA2zCF,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"}
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 "string";
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
- : personEntity
276
- ? personEntity
277
- : noSource
278
- ? `undefined as unknown as ${toTsType(p.type)}`
279
- : `${parser}(readSourceValue(row, ${JSON.stringify(p.source.csvHeaders)}))`;
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 `ValidateDateTimeCollection(${nameLiteral}, RowParser.ReadValue(row, ${csvHeadersLiteral}), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`;
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
- : personEntity
877
- ? isCollection
878
- ? buildCsPersonEntityCollectionExpression(personEntity.fields, (headersLiteral) => {
879
- const base = `RowParser.ParseStringCollection(row, ${headersLiteral})`;
880
- return csStringConstraints.hasAny
881
- ? `Validation.ValidateStringCollection(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
882
- : base;
883
- })
884
- : buildCsPersonEntityExpression(personEntity.fields, (headersLiteral) => {
885
- const base = `RowParser.ParseString(row, ${headersLiteral})`;
886
- return csStringConstraints.hasAny
887
- ? `Validation.ValidateString(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
888
- : base;
889
- })
890
- : noSource
891
- ? "default!"
892
- : `${parseFn}(row, ${csvHeadersLiteral})`;
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);