@wictorwilen/cocogen 1.0.17 → 1.0.19
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 +14 -1
- package/README.md +3 -0
- package/dist/init/init.d.ts.map +1 -1
- package/dist/init/init.js +273 -34
- package/dist/init/init.js.map +1 -1
- package/dist/init/templates/dotnet/Core/ConnectorCore.cs.ejs +27 -4
- package/dist/init/templates/dotnet/Core/ItemId.cs.ejs +39 -0
- package/dist/init/templates/dotnet/Core/Validation.cs.ejs +108 -0
- package/dist/init/templates/dotnet/Datasource/CsvItemSource.cs.ejs +1 -1
- package/dist/init/templates/dotnet/Datasource/IItemSource.cs.ejs +1 -1
- package/dist/init/templates/dotnet/Generated/CsvParser.cs.ejs +0 -179
- package/dist/init/templates/dotnet/Generated/FromCsvRow.cs.ejs +0 -21
- package/dist/init/templates/dotnet/Generated/FromRow.cs.ejs +23 -0
- package/dist/init/templates/dotnet/Generated/ItemPayload.cs.ejs +2 -26
- package/dist/init/templates/dotnet/Generated/Model.cs.ejs +5 -1
- package/dist/init/templates/dotnet/Generated/PropertyTransformBase.cs.ejs +10 -6
- package/dist/init/templates/dotnet/Generated/RowParser.cs.ejs +184 -0
- package/dist/init/templates/dotnet/Program.commandline.cs.ejs +6 -3
- package/dist/init/templates/dotnet/PropertyTransform.cs.ejs +1 -1
- package/dist/init/templates/dotnet/README.md.ejs +2 -1
- package/dist/init/templates/ts/README.md.ejs +2 -1
- package/dist/init/templates/ts/src/cli.ts.ejs +5 -1
- package/dist/init/templates/ts/src/core/connectorCore.ts.ejs +21 -2
- package/dist/init/templates/ts/src/core/itemId.ts.ejs +34 -0
- package/dist/init/templates/ts/src/core/validation.ts.ejs +89 -0
- package/dist/init/templates/ts/src/datasource/csvItemSource.ts.ejs +2 -2
- package/dist/init/templates/ts/src/datasource/itemSource.ts.ejs +1 -1
- package/dist/init/templates/ts/src/generated/csv.ts.ejs +0 -53
- package/dist/init/templates/ts/src/generated/fromCsvRow.ts.ejs +0 -19
- package/dist/init/templates/ts/src/generated/fromRow.ts.ejs +20 -0
- package/dist/init/templates/ts/src/generated/index.ts.ejs +1 -1
- package/dist/init/templates/ts/src/generated/itemPayload.ts.ejs +3 -28
- package/dist/init/templates/ts/src/generated/model.ts.ejs +7 -1
- package/dist/init/templates/ts/src/generated/propertyTransformBase.ts.ejs +9 -3
- package/dist/init/templates/ts/src/generated/row.ts.ejs +54 -0
- package/dist/init/templates/ts/src/propertyTransform.ts.ejs +1 -1
- package/dist/ir.d.ts +12 -0
- package/dist/ir.d.ts.map +1 -1
- package/dist/tsp/loader.d.ts.map +1 -1
- package/dist/tsp/loader.js +63 -3
- package/dist/tsp/loader.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [1.0.
|
|
10
|
+
## [1.0.19] - 2026-01-21
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Clarified error messaging for unsupported scalar types (with float64 hint).
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Moved item payload ID encoding helpers into shared core helpers for TS and .NET.
|
|
17
|
+
|
|
18
|
+
## [1.0.18] - 2026-01-21
|
|
11
19
|
|
|
12
20
|
### Breaking Changes
|
|
21
|
+
- Removed legacy CSV-row helpers (`fromCsvRow`, `CsvParser`, `csv.ts`) in favor of row-based helpers.
|
|
13
22
|
- Renamed CLI commands: `init-tsp` → `init`, `init` → `generate`.
|
|
14
23
|
|
|
15
24
|
### Added
|
|
25
|
+
- Added agent-facing schema guidance document (docs/schema-assistant.md).
|
|
16
26
|
- Managed identity authentication as the preferred credential for generated TS and .NET projects (client secret fallback).
|
|
17
27
|
- .NET user-secrets support for configuration.
|
|
28
|
+
- TypeSpec metadata support for `@doc`, `@example`, `@minLength`, `@maxLength`, `@minValue`, `@maxValue`, `@pattern`, `@format`, and `#deprecated`.
|
|
18
29
|
|
|
19
30
|
## [1.0.16] - 2026-01-20
|
|
20
31
|
|
|
@@ -37,5 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
37
48
|
- Collection values no longer split on commas; use semicolons instead.
|
|
38
49
|
|
|
39
50
|
[Unreleased]: https://github.com/wictorwilen/cocogen/compare/v1.0.16...HEAD
|
|
51
|
+
[1.0.19]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.19
|
|
52
|
+
[1.0.18]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.18
|
|
40
53
|
[1.0.17]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.17
|
|
41
54
|
[1.0.16]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.16
|
package/README.md
CHANGED
|
@@ -32,6 +32,9 @@
|
|
|
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:
|
|
36
|
+
- [docs/schema-assistant.md](https://github.com/wictorwilen/cocogen/blob/main/docs/schema-assistant.md)
|
|
37
|
+
|
|
35
38
|
## TypeSpec format
|
|
36
39
|
`cocogen` expects a single “item model” decorated with `@coco.item()` and a single ID property decorated with `@coco.id`.
|
|
37
40
|
|
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;AAk1CF,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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { access, copyFile, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, copyFile, mkdir, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -7,6 +7,16 @@ import { loadIrFromTypeSpec } from "../tsp/loader.js";
|
|
|
7
7
|
import { validateIr } from "../validate/validator.js";
|
|
8
8
|
import { renderTemplate } from "./template.js";
|
|
9
9
|
const COCOGEN_CONFIG_FILE = "cocogen.json";
|
|
10
|
+
async function removeIfExists(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
await unlink(filePath);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
const err = error;
|
|
16
|
+
if (err.code !== "ENOENT")
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
10
20
|
function toTsType(type) {
|
|
11
21
|
switch (type) {
|
|
12
22
|
case "string":
|
|
@@ -202,11 +212,15 @@ async function loadProjectConfig(outDir) {
|
|
|
202
212
|
};
|
|
203
213
|
}
|
|
204
214
|
async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
215
|
+
await mkdir(path.join(outDir, "src", "datasource"), { recursive: true });
|
|
205
216
|
await mkdir(path.join(outDir, "src", schemaFolderName), { recursive: true });
|
|
206
217
|
await mkdir(path.join(outDir, "src", "core"), { recursive: true });
|
|
218
|
+
await removeIfExists(path.join(outDir, "src", schemaFolderName, "fromCsvRow.ts"));
|
|
219
|
+
await removeIfExists(path.join(outDir, "src", "datasource", "csv.ts"));
|
|
207
220
|
const modelProperties = ir.properties.map((p) => ({
|
|
208
221
|
name: p.name,
|
|
209
222
|
tsType: toTsType(p.type),
|
|
223
|
+
docComment: p.doc ? formatDocComment(p.doc, " ") : undefined,
|
|
210
224
|
}));
|
|
211
225
|
const transformProperties = ir.properties.map((p) => {
|
|
212
226
|
const parser = (() => {
|
|
@@ -230,16 +244,28 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
230
244
|
return "parseString";
|
|
231
245
|
}
|
|
232
246
|
})();
|
|
247
|
+
const nameLiteral = JSON.stringify(p.name);
|
|
248
|
+
const stringConstraints = buildTsStringConstraintsLiteral(p);
|
|
233
249
|
const personEntity = p.personEntity
|
|
234
250
|
? (p.type === "stringCollection"
|
|
235
251
|
? buildTsPersonEntityCollectionExpression(p.personEntity.fields.map((field) => ({
|
|
236
252
|
path: field.path,
|
|
237
253
|
source: field.source,
|
|
238
|
-
})))
|
|
254
|
+
})), (headersLiteral) => {
|
|
255
|
+
const base = `parseStringCollection(readSourceValue(row, ${headersLiteral}))`;
|
|
256
|
+
return stringConstraints
|
|
257
|
+
? `validateStringCollection(${nameLiteral}, ${base}, ${stringConstraints})`
|
|
258
|
+
: base;
|
|
259
|
+
})
|
|
239
260
|
: buildTsPersonEntityExpression(p.personEntity.fields.map((field) => ({
|
|
240
261
|
path: field.path,
|
|
241
262
|
source: field.source,
|
|
242
|
-
})))
|
|
263
|
+
})), (headersLiteral) => {
|
|
264
|
+
const base = `parseString(readSourceValue(row, ${headersLiteral}))`;
|
|
265
|
+
return stringConstraints
|
|
266
|
+
? `validateString(${nameLiteral}, ${base}, ${stringConstraints})`
|
|
267
|
+
: base;
|
|
268
|
+
}))
|
|
243
269
|
: null;
|
|
244
270
|
const isPeopleLabel = p.labels.some((label) => label.startsWith("person"));
|
|
245
271
|
const needsManualEntity = isPeopleLabel && !p.personEntity;
|
|
@@ -251,10 +277,23 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
251
277
|
: noSource
|
|
252
278
|
? `undefined as unknown as ${toTsType(p.type)}`
|
|
253
279
|
: `${parser}(readSourceValue(row, ${JSON.stringify(p.source.csvHeaders)}))`;
|
|
280
|
+
const validationMetadata = {
|
|
281
|
+
name: p.name,
|
|
282
|
+
type: p.type,
|
|
283
|
+
...(p.minLength !== undefined ? { minLength: p.minLength } : {}),
|
|
284
|
+
...(p.maxLength !== undefined ? { maxLength: p.maxLength } : {}),
|
|
285
|
+
...(p.pattern ? { pattern: p.pattern } : {}),
|
|
286
|
+
...(p.format ? { format: p.format } : {}),
|
|
287
|
+
...(p.minValue !== undefined ? { minValue: p.minValue } : {}),
|
|
288
|
+
...(p.maxValue !== undefined ? { maxValue: p.maxValue } : {}),
|
|
289
|
+
};
|
|
290
|
+
const validatedExpression = needsManualEntity || noSource || personEntity
|
|
291
|
+
? expression
|
|
292
|
+
: applyTsValidationExpression(validationMetadata, expression);
|
|
254
293
|
return {
|
|
255
294
|
name: p.name,
|
|
256
295
|
parser,
|
|
257
|
-
expression,
|
|
296
|
+
expression: validatedExpression,
|
|
258
297
|
isCollection: p.type === "stringCollection",
|
|
259
298
|
transformName: toTsIdentifier(p.name),
|
|
260
299
|
tsType: toTsType(p.type),
|
|
@@ -268,7 +307,9 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
268
307
|
await writeFile(path.join(outDir, "src", schemaFolderName, "model.ts"), await renderTemplate("ts/src/generated/model.ts.ejs", {
|
|
269
308
|
itemTypeName: ir.item.typeName,
|
|
270
309
|
properties: modelProperties,
|
|
310
|
+
itemDocComment: ir.item.doc ? formatDocComment(ir.item.doc) : undefined,
|
|
271
311
|
}), "utf8");
|
|
312
|
+
await writeFile(path.join(outDir, "src", "datasource", "row.ts"), await renderTemplate("ts/src/generated/row.ts.ejs", {}), "utf8");
|
|
272
313
|
await writeFile(path.join(outDir, "src", schemaFolderName, "constants.ts"), await renderTemplate("ts/src/generated/constants.ts.ejs", {
|
|
273
314
|
graphApiVersion: ir.connection.graphApiVersion,
|
|
274
315
|
contentCategory: ir.connection.contentCategory ?? null,
|
|
@@ -285,7 +326,6 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
285
326
|
await writeFile(path.join(outDir, "src", schemaFolderName, "schemaPayload.ts"), await renderTemplate("ts/src/generated/schemaPayload.ts.ejs", {
|
|
286
327
|
schemaPayloadJson: JSON.stringify(schemaPayload(ir), null, 2),
|
|
287
328
|
}), "utf8");
|
|
288
|
-
await writeFile(path.join(outDir, "src", "datasource", "csv.ts"), await renderTemplate("ts/src/generated/csv.ts.ejs", {}), "utf8");
|
|
289
329
|
await writeFile(path.join(outDir, "src", schemaFolderName, "propertyTransformBase.ts"), await renderTemplate("ts/src/generated/propertyTransformBase.ts.ejs", {
|
|
290
330
|
properties: transformProperties,
|
|
291
331
|
}), "utf8");
|
|
@@ -296,7 +336,7 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
296
336
|
catch {
|
|
297
337
|
await writeFile(transformOverridesPath, await renderTemplate("ts/src/propertyTransform.ts.ejs", {}), "utf8");
|
|
298
338
|
}
|
|
299
|
-
await writeFile(path.join(outDir, "src", schemaFolderName, "
|
|
339
|
+
await writeFile(path.join(outDir, "src", schemaFolderName, "fromRow.ts"), await renderTemplate("ts/src/generated/fromRow.ts.ejs", {
|
|
300
340
|
properties: transformProperties,
|
|
301
341
|
itemTypeName: ir.item.typeName,
|
|
302
342
|
idRawExpression,
|
|
@@ -328,6 +368,8 @@ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
|
|
|
328
368
|
itemTypeName: ir.item.typeName,
|
|
329
369
|
isPeopleConnector: ir.connection.contentCategory === "people",
|
|
330
370
|
}), "utf8");
|
|
371
|
+
await writeFile(path.join(outDir, "src", "core", "validation.ts"), await renderTemplate("ts/src/core/validation.ts.ejs", {}), "utf8");
|
|
372
|
+
await writeFile(path.join(outDir, "src", "core", "itemId.ts"), await renderTemplate("ts/src/core/itemId.ts.ejs", {}), "utf8");
|
|
331
373
|
}
|
|
332
374
|
function toGraphPropertyTypeEnumName(type) {
|
|
333
375
|
switch (type) {
|
|
@@ -372,25 +414,25 @@ function toOdataCollectionType(type) {
|
|
|
372
414
|
function toCsParseFunction(type) {
|
|
373
415
|
switch (type) {
|
|
374
416
|
case "stringCollection":
|
|
375
|
-
return "
|
|
417
|
+
return "RowParser.ParseStringCollection";
|
|
376
418
|
case "int64Collection":
|
|
377
|
-
return "
|
|
419
|
+
return "RowParser.ParseInt64Collection";
|
|
378
420
|
case "doubleCollection":
|
|
379
|
-
return "
|
|
421
|
+
return "RowParser.ParseDoubleCollection";
|
|
380
422
|
case "dateTimeCollection":
|
|
381
|
-
return "
|
|
423
|
+
return "RowParser.ParseDateTimeCollection";
|
|
382
424
|
case "boolean":
|
|
383
|
-
return "
|
|
425
|
+
return "RowParser.ParseBoolean";
|
|
384
426
|
case "int64":
|
|
385
|
-
return "
|
|
427
|
+
return "RowParser.ParseInt64";
|
|
386
428
|
case "double":
|
|
387
|
-
return "
|
|
429
|
+
return "RowParser.ParseDouble";
|
|
388
430
|
case "dateTime":
|
|
389
|
-
return "
|
|
431
|
+
return "RowParser.ParseDateTime";
|
|
390
432
|
case "principal":
|
|
391
433
|
case "string":
|
|
392
434
|
default:
|
|
393
|
-
return "
|
|
435
|
+
return "RowParser.ParseString";
|
|
394
436
|
}
|
|
395
437
|
}
|
|
396
438
|
function toCsPropertyValueExpression(type, csPropertyName) {
|
|
@@ -403,6 +445,14 @@ function toCsPropertyValueExpression(type, csPropertyName) {
|
|
|
403
445
|
return `item.${csPropertyName}`;
|
|
404
446
|
}
|
|
405
447
|
}
|
|
448
|
+
function formatDocComment(doc, indent = "") {
|
|
449
|
+
const lines = doc.split(/\r?\n/).map((line) => `${indent} * ${line}`);
|
|
450
|
+
return `${indent}/**\n${lines.join("\n")}\n${indent} */`;
|
|
451
|
+
}
|
|
452
|
+
function formatCsDocSummary(doc) {
|
|
453
|
+
const lines = doc.split(/\r?\n/).map((line) => `/// ${line}`);
|
|
454
|
+
return ["/// <summary>", ...lines, "/// </summary>"];
|
|
455
|
+
}
|
|
406
456
|
function buildObjectTree(fields) {
|
|
407
457
|
const root = {};
|
|
408
458
|
for (const field of fields) {
|
|
@@ -429,7 +479,7 @@ function buildObjectTree(fields) {
|
|
|
429
479
|
}
|
|
430
480
|
return root;
|
|
431
481
|
}
|
|
432
|
-
function buildTsPersonEntityExpression(fields) {
|
|
482
|
+
function buildTsPersonEntityExpression(fields, valueExpressionBuilder = (headersLiteral) => `parseString(readSourceValue(row, ${headersLiteral}))`) {
|
|
433
483
|
const tree = buildObjectTree(fields);
|
|
434
484
|
const indentUnit = " ";
|
|
435
485
|
const renderNode = (node, level) => {
|
|
@@ -439,7 +489,7 @@ function buildTsPersonEntityExpression(fields) {
|
|
|
439
489
|
if (typeof value === "object" && value && "path" in value) {
|
|
440
490
|
const field = value;
|
|
441
491
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
442
|
-
return `${childIndent}${JSON.stringify(key)}:
|
|
492
|
+
return `${childIndent}${JSON.stringify(key)}: ${valueExpressionBuilder(headers)}`;
|
|
443
493
|
}
|
|
444
494
|
return `${childIndent}${JSON.stringify(key)}: ${renderNode(value, level + 1)}`;
|
|
445
495
|
});
|
|
@@ -450,7 +500,7 @@ ${indent}}`;
|
|
|
450
500
|
const rendered = renderNode(tree, 2);
|
|
451
501
|
return `JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
|
|
452
502
|
}
|
|
453
|
-
function buildTsPersonEntityCollectionExpression(fields) {
|
|
503
|
+
function buildTsPersonEntityCollectionExpression(fields, collectionExpressionBuilder = (headersLiteral) => `parseStringCollection(readSourceValue(row, ${headersLiteral}))`) {
|
|
454
504
|
const indentUnit = " ";
|
|
455
505
|
const renderNode = (node, level, valueVar) => {
|
|
456
506
|
const indent = indentUnit.repeat(level);
|
|
@@ -470,7 +520,7 @@ ${indent}}`;
|
|
|
470
520
|
const field = fields[0];
|
|
471
521
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
472
522
|
const rendered = renderNode(tree, 2, "value");
|
|
473
|
-
return
|
|
523
|
+
return `${collectionExpressionBuilder(headers)}
|
|
474
524
|
.map((value) => JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)}))`;
|
|
475
525
|
}
|
|
476
526
|
const tree = buildObjectTree(fields);
|
|
@@ -479,7 +529,7 @@ ${indent}}`;
|
|
|
479
529
|
const varName = `field${index}`;
|
|
480
530
|
fieldVarByPath.set(field.path, varName);
|
|
481
531
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
482
|
-
return ` const ${varName} =
|
|
532
|
+
return ` const ${varName} = ${collectionExpressionBuilder(headers)};`;
|
|
483
533
|
});
|
|
484
534
|
const renderNodeMany = (node) => {
|
|
485
535
|
const entries = Object.entries(node).map(([key, value]) => {
|
|
@@ -500,7 +550,7 @@ ${indentUnit}}`;
|
|
|
500
550
|
: "const lengths = [0];";
|
|
501
551
|
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})()`;
|
|
502
552
|
}
|
|
503
|
-
function buildCsPersonEntityExpression(fields) {
|
|
553
|
+
function buildCsPersonEntityExpression(fields, valueExpressionBuilder = (headersLiteral) => `RowParser.ParseString(row, ${headersLiteral})`) {
|
|
504
554
|
const tree = buildObjectTree(fields);
|
|
505
555
|
const indentUnit = " ";
|
|
506
556
|
const renderNode = (node, level) => {
|
|
@@ -510,7 +560,7 @@ function buildCsPersonEntityExpression(fields) {
|
|
|
510
560
|
if (typeof value === "object" && value && "path" in value) {
|
|
511
561
|
const field = value;
|
|
512
562
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
513
|
-
return `${childIndent}[${JSON.stringify(key)}] =
|
|
563
|
+
return `${childIndent}[${JSON.stringify(key)}] = ${valueExpressionBuilder(headers)}`;
|
|
514
564
|
}
|
|
515
565
|
return `${childIndent}[${JSON.stringify(key)}] = ${renderNode(value, level + 1)}`;
|
|
516
566
|
});
|
|
@@ -519,7 +569,7 @@ function buildCsPersonEntityExpression(fields) {
|
|
|
519
569
|
const rendered = renderNode(tree, 2);
|
|
520
570
|
return `JsonSerializer.Serialize(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
|
|
521
571
|
}
|
|
522
|
-
function buildCsPersonEntityCollectionExpression(fields) {
|
|
572
|
+
function buildCsPersonEntityCollectionExpression(fields, collectionExpressionBuilder = (headersLiteral) => `RowParser.ParseStringCollection(row, ${headersLiteral})`) {
|
|
523
573
|
const indentUnit = " ";
|
|
524
574
|
const renderNode = (node, level, valueVar) => {
|
|
525
575
|
const indent = indentUnit.repeat(level);
|
|
@@ -537,7 +587,7 @@ function buildCsPersonEntityCollectionExpression(fields) {
|
|
|
537
587
|
const field = fields[0];
|
|
538
588
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
539
589
|
const rendered = renderNode(tree, 3, "value");
|
|
540
|
-
return
|
|
590
|
+
return `${collectionExpressionBuilder(headers)}
|
|
541
591
|
.Select(value => JsonSerializer.Serialize(\n${indentUnit.repeat(3)}${rendered}\n${indentUnit.repeat(3)}))
|
|
542
592
|
.ToList()`;
|
|
543
593
|
}
|
|
@@ -547,7 +597,7 @@ function buildCsPersonEntityCollectionExpression(fields) {
|
|
|
547
597
|
const varName = `field${index}`;
|
|
548
598
|
fieldVarByPath.set(field.path, varName);
|
|
549
599
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
550
|
-
return ` var ${varName} =
|
|
600
|
+
return ` var ${varName} = ${collectionExpressionBuilder(headers)};`;
|
|
551
601
|
});
|
|
552
602
|
const renderNodeMany = (node) => {
|
|
553
603
|
const entries = Object.entries(node).map(([key, value]) => {
|
|
@@ -597,6 +647,125 @@ function sampleValueForType(type) {
|
|
|
597
647
|
return "sample";
|
|
598
648
|
}
|
|
599
649
|
}
|
|
650
|
+
function buildTsStringConstraintsLiteral(prop) {
|
|
651
|
+
const parts = [];
|
|
652
|
+
if (prop.minLength !== undefined)
|
|
653
|
+
parts.push(`minLength: ${prop.minLength}`);
|
|
654
|
+
if (prop.maxLength !== undefined)
|
|
655
|
+
parts.push(`maxLength: ${prop.maxLength}`);
|
|
656
|
+
if (prop.pattern?.regex)
|
|
657
|
+
parts.push(`pattern: ${JSON.stringify(prop.pattern.regex)}`);
|
|
658
|
+
if (prop.format)
|
|
659
|
+
parts.push(`format: ${JSON.stringify(prop.format)}`);
|
|
660
|
+
return parts.length > 0 ? `{ ${parts.join(", ")} }` : undefined;
|
|
661
|
+
}
|
|
662
|
+
function buildTsNumberConstraintsLiteral(prop) {
|
|
663
|
+
const parts = [];
|
|
664
|
+
if (prop.minValue !== undefined)
|
|
665
|
+
parts.push(`minValue: ${prop.minValue}`);
|
|
666
|
+
if (prop.maxValue !== undefined)
|
|
667
|
+
parts.push(`maxValue: ${prop.maxValue}`);
|
|
668
|
+
return parts.length > 0 ? `{ ${parts.join(", ")} }` : undefined;
|
|
669
|
+
}
|
|
670
|
+
function applyTsValidationExpression(prop, expression) {
|
|
671
|
+
const stringConstraints = buildTsStringConstraintsLiteral(prop);
|
|
672
|
+
const numberConstraints = buildTsNumberConstraintsLiteral(prop);
|
|
673
|
+
const nameLiteral = JSON.stringify(prop.name);
|
|
674
|
+
switch (prop.type) {
|
|
675
|
+
case "string":
|
|
676
|
+
case "principal":
|
|
677
|
+
case "dateTime":
|
|
678
|
+
return stringConstraints ? `validateString(${nameLiteral}, ${expression}, ${stringConstraints})` : expression;
|
|
679
|
+
case "stringCollection":
|
|
680
|
+
case "dateTimeCollection":
|
|
681
|
+
return stringConstraints
|
|
682
|
+
? `validateStringCollection(${nameLiteral}, ${expression}, ${stringConstraints})`
|
|
683
|
+
: expression;
|
|
684
|
+
case "int64":
|
|
685
|
+
case "double":
|
|
686
|
+
return numberConstraints ? `validateNumber(${nameLiteral}, ${expression}, ${numberConstraints})` : expression;
|
|
687
|
+
case "int64Collection":
|
|
688
|
+
case "doubleCollection":
|
|
689
|
+
return numberConstraints
|
|
690
|
+
? `validateNumberCollection(${nameLiteral}, ${expression}, ${numberConstraints})`
|
|
691
|
+
: expression;
|
|
692
|
+
default:
|
|
693
|
+
return expression;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function buildCsStringConstraintsLiteral(prop) {
|
|
697
|
+
const minLength = prop.minLength !== undefined ? prop.minLength.toString() : "null";
|
|
698
|
+
const maxLength = prop.maxLength !== undefined ? prop.maxLength.toString() : "null";
|
|
699
|
+
const pattern = prop.pattern?.regex ? JSON.stringify(prop.pattern.regex) : "null";
|
|
700
|
+
const format = prop.format ? JSON.stringify(prop.format) : "null";
|
|
701
|
+
const hasAny = prop.minLength !== undefined || prop.maxLength !== undefined || Boolean(prop.pattern?.regex) || Boolean(prop.format);
|
|
702
|
+
return { minLength, maxLength, pattern, format, hasAny };
|
|
703
|
+
}
|
|
704
|
+
function buildCsNumberConstraintsLiteral(prop) {
|
|
705
|
+
const minValue = prop.minValue !== undefined ? prop.minValue.toString() : "null";
|
|
706
|
+
const maxValue = prop.maxValue !== undefined ? prop.maxValue.toString() : "null";
|
|
707
|
+
const hasAny = prop.minValue !== undefined || prop.maxValue !== undefined;
|
|
708
|
+
return { minValue, maxValue, hasAny };
|
|
709
|
+
}
|
|
710
|
+
function applyCsValidationExpression(prop, expression, csvHeadersLiteral) {
|
|
711
|
+
const stringConstraints = buildCsStringConstraintsLiteral(prop);
|
|
712
|
+
const numberConstraints = buildCsNumberConstraintsLiteral(prop);
|
|
713
|
+
const nameLiteral = JSON.stringify(prop.name);
|
|
714
|
+
switch (prop.type) {
|
|
715
|
+
case "string":
|
|
716
|
+
case "principal":
|
|
717
|
+
return stringConstraints.hasAny
|
|
718
|
+
? `Validation.ValidateString(${nameLiteral}, ${expression}, ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`
|
|
719
|
+
: expression;
|
|
720
|
+
case "dateTime":
|
|
721
|
+
if (!stringConstraints.hasAny)
|
|
722
|
+
return expression;
|
|
723
|
+
return `RowParser.ParseDateTime(Validation.ValidateString(${nameLiteral}, RowParser.ReadValue(row, ${csvHeadersLiteral}), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format}))`;
|
|
724
|
+
case "stringCollection":
|
|
725
|
+
return stringConstraints.hasAny
|
|
726
|
+
? `Validation.ValidateStringCollection(${nameLiteral}, ${expression}, ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`
|
|
727
|
+
: expression;
|
|
728
|
+
case "dateTimeCollection":
|
|
729
|
+
if (!stringConstraints.hasAny)
|
|
730
|
+
return expression;
|
|
731
|
+
return `Validation.ValidateStringCollection(${nameLiteral}, RowParser.ParseStringCollection(RowParser.ReadValue(row, ${csvHeadersLiteral})), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format}).Select(value => RowParser.ParseDateTime(value)).ToList()`;
|
|
732
|
+
case "int64":
|
|
733
|
+
return numberConstraints.hasAny
|
|
734
|
+
? `Validation.ValidateInt64(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
735
|
+
: expression;
|
|
736
|
+
case "double":
|
|
737
|
+
return numberConstraints.hasAny
|
|
738
|
+
? `Validation.ValidateDouble(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
739
|
+
: expression;
|
|
740
|
+
case "int64Collection":
|
|
741
|
+
return numberConstraints.hasAny
|
|
742
|
+
? `Validation.ValidateInt64Collection(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
743
|
+
: expression;
|
|
744
|
+
case "doubleCollection":
|
|
745
|
+
return numberConstraints.hasAny
|
|
746
|
+
? `Validation.ValidateDoubleCollection(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
747
|
+
: expression;
|
|
748
|
+
default:
|
|
749
|
+
return expression;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function exampleValueForType(example, type) {
|
|
753
|
+
if (example === undefined || example === null)
|
|
754
|
+
return undefined;
|
|
755
|
+
if (type.endsWith("Collection")) {
|
|
756
|
+
if (Array.isArray(example)) {
|
|
757
|
+
return example.map((value) => (value === undefined || value === null ? "" : String(value))).join(";");
|
|
758
|
+
}
|
|
759
|
+
if (typeof example === "string")
|
|
760
|
+
return example;
|
|
761
|
+
return JSON.stringify(example);
|
|
762
|
+
}
|
|
763
|
+
if (typeof example === "string")
|
|
764
|
+
return example;
|
|
765
|
+
if (typeof example === "number" || typeof example === "boolean")
|
|
766
|
+
return String(example);
|
|
767
|
+
return JSON.stringify(example);
|
|
768
|
+
}
|
|
600
769
|
function sampleValueForHeader(header, type) {
|
|
601
770
|
const lower = header.toLowerCase();
|
|
602
771
|
if (lower.includes("job title"))
|
|
@@ -650,6 +819,14 @@ function buildSampleCsv(ir) {
|
|
|
650
819
|
const valueByHeader = new Map();
|
|
651
820
|
for (const prop of ir.properties) {
|
|
652
821
|
if (prop.personEntity) {
|
|
822
|
+
const exampleValue = exampleValueForType(prop.example, prop.type);
|
|
823
|
+
if (exampleValue && prop.personEntity.fields.length === 1) {
|
|
824
|
+
const headers = prop.personEntity.fields[0]?.source.csvHeaders ?? [];
|
|
825
|
+
for (const header of headers) {
|
|
826
|
+
if (!valueByHeader.has(header))
|
|
827
|
+
valueByHeader.set(header, exampleValue);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
653
830
|
for (const field of prop.personEntity.fields) {
|
|
654
831
|
for (const header of field.source.csvHeaders) {
|
|
655
832
|
if (!valueByHeader.has(header))
|
|
@@ -658,9 +835,11 @@ function buildSampleCsv(ir) {
|
|
|
658
835
|
}
|
|
659
836
|
continue;
|
|
660
837
|
}
|
|
838
|
+
const exampleValue = exampleValueForType(prop.example, prop.type);
|
|
661
839
|
for (const header of prop.source.csvHeaders) {
|
|
662
|
-
if (!valueByHeader.has(header))
|
|
663
|
-
valueByHeader.set(header, sampleValueForHeader(header, prop.type));
|
|
840
|
+
if (!valueByHeader.has(header)) {
|
|
841
|
+
valueByHeader.set(header, exampleValue ?? sampleValueForHeader(header, prop.type));
|
|
842
|
+
}
|
|
664
843
|
}
|
|
665
844
|
}
|
|
666
845
|
const headerLine = headers.map(csvEscape).join(",");
|
|
@@ -669,13 +848,18 @@ function buildSampleCsv(ir) {
|
|
|
669
848
|
}
|
|
670
849
|
async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName, schemaNamespace) {
|
|
671
850
|
await mkdir(path.join(outDir, schemaFolderName), { recursive: true });
|
|
851
|
+
await mkdir(path.join(outDir, "Datasource"), { recursive: true });
|
|
672
852
|
await mkdir(path.join(outDir, "Core"), { recursive: true });
|
|
853
|
+
await removeIfExists(path.join(outDir, schemaFolderName, "FromCsvRow.cs"));
|
|
854
|
+
await removeIfExists(path.join(outDir, "Datasource", "CsvParser.cs"));
|
|
673
855
|
const usedPropertyNames = new Set();
|
|
674
856
|
const itemTypeName = toCsIdentifier(ir.item.typeName);
|
|
675
857
|
const properties = ir.properties.map((p) => {
|
|
676
858
|
const parseFn = toCsParseFunction(p.type);
|
|
677
859
|
const csvHeadersLiteral = `new[] { ${p.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
678
860
|
const isCollection = p.type === "stringCollection";
|
|
861
|
+
const nameLiteral = JSON.stringify(p.name);
|
|
862
|
+
const csStringConstraints = buildCsStringConstraintsLiteral(p);
|
|
679
863
|
const personEntity = p.personEntity
|
|
680
864
|
? {
|
|
681
865
|
entity: p.personEntity.entity,
|
|
@@ -692,11 +876,34 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
692
876
|
? `throw new NotImplementedException("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement in PropertyTransform.cs.")`
|
|
693
877
|
: personEntity
|
|
694
878
|
? isCollection
|
|
695
|
-
? buildCsPersonEntityCollectionExpression(personEntity.fields)
|
|
696
|
-
|
|
879
|
+
? buildCsPersonEntityCollectionExpression(personEntity.fields, (headersLiteral) => {
|
|
880
|
+
const base = `RowParser.ParseStringCollection(row, ${headersLiteral})`;
|
|
881
|
+
return csStringConstraints.hasAny
|
|
882
|
+
? `Validation.ValidateStringCollection(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
|
|
883
|
+
: base;
|
|
884
|
+
})
|
|
885
|
+
: buildCsPersonEntityExpression(personEntity.fields, (headersLiteral) => {
|
|
886
|
+
const base = `RowParser.ParseString(row, ${headersLiteral})`;
|
|
887
|
+
return csStringConstraints.hasAny
|
|
888
|
+
? `Validation.ValidateString(${nameLiteral}, ${base}, ${csStringConstraints.minLength}, ${csStringConstraints.maxLength}, ${csStringConstraints.pattern}, ${csStringConstraints.format})`
|
|
889
|
+
: base;
|
|
890
|
+
})
|
|
697
891
|
: noSource
|
|
698
892
|
? "default!"
|
|
699
893
|
: `${parseFn}(row, ${csvHeadersLiteral})`;
|
|
894
|
+
const validationMetadata = {
|
|
895
|
+
name: p.name,
|
|
896
|
+
type: p.type,
|
|
897
|
+
...(p.minLength !== undefined ? { minLength: p.minLength } : {}),
|
|
898
|
+
...(p.maxLength !== undefined ? { maxLength: p.maxLength } : {}),
|
|
899
|
+
...(p.pattern ? { pattern: p.pattern } : {}),
|
|
900
|
+
...(p.format ? { format: p.format } : {}),
|
|
901
|
+
...(p.minValue !== undefined ? { minValue: p.minValue } : {}),
|
|
902
|
+
...(p.maxValue !== undefined ? { maxValue: p.maxValue } : {}),
|
|
903
|
+
};
|
|
904
|
+
const validatedExpression = needsManualEntity || noSource || personEntity
|
|
905
|
+
? transformExpression
|
|
906
|
+
: applyCsValidationExpression(validationMetadata, transformExpression, csvHeadersLiteral);
|
|
700
907
|
return {
|
|
701
908
|
name: p.name,
|
|
702
909
|
csName: toCsPropertyName(p.name, itemTypeName, usedPropertyNames),
|
|
@@ -706,16 +913,35 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
706
913
|
isCollection,
|
|
707
914
|
personEntity,
|
|
708
915
|
parseFn,
|
|
709
|
-
transformExpression,
|
|
916
|
+
transformExpression: validatedExpression,
|
|
710
917
|
transformThrows: needsManualEntity,
|
|
711
918
|
graphTypeEnumName: toGraphPropertyTypeEnumName(p.type),
|
|
712
919
|
description: p.description,
|
|
920
|
+
doc: p.doc,
|
|
713
921
|
labels: p.labels,
|
|
714
922
|
aliases: p.aliases,
|
|
715
923
|
search: p.search,
|
|
716
924
|
type: p.type,
|
|
925
|
+
format: p.format,
|
|
926
|
+
pattern: p.pattern,
|
|
927
|
+
minLength: p.minLength,
|
|
928
|
+
maxLength: p.maxLength,
|
|
929
|
+
minValue: p.minValue,
|
|
930
|
+
maxValue: p.maxValue,
|
|
717
931
|
};
|
|
718
932
|
});
|
|
933
|
+
const recordDocLines = [];
|
|
934
|
+
if (ir.item.doc) {
|
|
935
|
+
recordDocLines.push(...formatCsDocSummary(ir.item.doc));
|
|
936
|
+
}
|
|
937
|
+
for (const prop of properties) {
|
|
938
|
+
if (!prop.doc)
|
|
939
|
+
continue;
|
|
940
|
+
const docLines = prop.doc.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
941
|
+
if (docLines.length === 0)
|
|
942
|
+
continue;
|
|
943
|
+
recordDocLines.push(`/// <param name=\"${prop.csName}\">${docLines.join(" ")}</param>`);
|
|
944
|
+
}
|
|
719
945
|
const schemaPropertyLines = properties
|
|
720
946
|
.filter((p) => p.name !== ir.item.contentPropertyName)
|
|
721
947
|
.map((p) => {
|
|
@@ -765,7 +991,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
765
991
|
const idRawHeadersDotnet = itemIdProperty?.personEntity?.fields[0]?.source.csvHeaders ?? itemIdProperty?.csvHeaders ?? [];
|
|
766
992
|
const idRawHeadersLiteral = `new[] { ${idRawHeadersDotnet.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
767
993
|
const idRawExpressionDotnet = idRawHeadersDotnet.length
|
|
768
|
-
? `
|
|
994
|
+
? `RowParser.ParseString(row, ${idRawHeadersLiteral})`
|
|
769
995
|
: "string.Empty";
|
|
770
996
|
const constructorArgs = [
|
|
771
997
|
...properties.map((p) => `(${p.csType})transforms.TransformProperty(${JSON.stringify(p.name)}, row)`),
|
|
@@ -790,7 +1016,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
790
1016
|
})
|
|
791
1017
|
.join("\n");
|
|
792
1018
|
const itemIdExpression = itemIdProperty
|
|
793
|
-
? `!string.IsNullOrEmpty(item.
|
|
1019
|
+
? `!string.IsNullOrEmpty(item.InternalId) ? item.InternalId : (item.${itemIdProperty.csName} ?? string.Empty)`
|
|
794
1020
|
: "\"\"";
|
|
795
1021
|
const contentValueExpression = ir.item.contentPropertyName
|
|
796
1022
|
? `Convert.ToString(item.${toCsIdentifier(ir.item.contentPropertyName)}) ?? string.Empty`
|
|
@@ -806,6 +1032,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
806
1032
|
schemaNamespace,
|
|
807
1033
|
itemTypeName: ir.item.typeName,
|
|
808
1034
|
properties: properties.map((p) => ({ csName: p.csName, csType: p.csType })),
|
|
1035
|
+
recordDocLines,
|
|
809
1036
|
}), "utf8");
|
|
810
1037
|
await writeFile(path.join(outDir, schemaFolderName, "Constants.cs"), await renderTemplate("dotnet/Generated/Constants.cs.ejs", {
|
|
811
1038
|
schemaNamespace,
|
|
@@ -826,7 +1053,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
826
1053
|
schemaPropertyLines,
|
|
827
1054
|
graphApiVersion: ir.connection.graphApiVersion,
|
|
828
1055
|
}), "utf8");
|
|
829
|
-
await writeFile(path.join(outDir, "Datasource", "
|
|
1056
|
+
await writeFile(path.join(outDir, "Datasource", "RowParser.cs"), await renderTemplate("dotnet/Generated/RowParser.cs.ejs", {
|
|
830
1057
|
namespaceName,
|
|
831
1058
|
}), "utf8");
|
|
832
1059
|
await writeFile(path.join(outDir, schemaFolderName, "PropertyTransformBase.cs"), await renderTemplate("dotnet/Generated/PropertyTransformBase.cs.ejs", {
|
|
@@ -834,6 +1061,11 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
834
1061
|
schemaNamespace,
|
|
835
1062
|
properties,
|
|
836
1063
|
usesPersonEntity: properties.some((p) => p.personEntity),
|
|
1064
|
+
usesLinq: properties.some((p) => p.type === "dateTimeCollection" &&
|
|
1065
|
+
(p.minLength !== undefined ||
|
|
1066
|
+
p.maxLength !== undefined ||
|
|
1067
|
+
Boolean(p.pattern?.regex) ||
|
|
1068
|
+
Boolean(p.format))),
|
|
837
1069
|
}), "utf8");
|
|
838
1070
|
const transformOverridesPath = path.join(outDir, schemaFolderName, "PropertyTransform.cs");
|
|
839
1071
|
try {
|
|
@@ -844,13 +1076,14 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
844
1076
|
schemaNamespace,
|
|
845
1077
|
}), "utf8");
|
|
846
1078
|
}
|
|
847
|
-
await writeFile(path.join(outDir, schemaFolderName, "
|
|
1079
|
+
await writeFile(path.join(outDir, schemaFolderName, "FromRow.cs"), await renderTemplate("dotnet/Generated/FromRow.cs.ejs", {
|
|
848
1080
|
namespaceName,
|
|
849
1081
|
schemaNamespace,
|
|
850
1082
|
itemTypeName: ir.item.typeName,
|
|
851
1083
|
constructorArgLines,
|
|
852
1084
|
}), "utf8");
|
|
853
1085
|
await writeFile(path.join(outDir, schemaFolderName, "ItemPayload.cs"), await renderTemplate("dotnet/Generated/ItemPayload.cs.ejs", {
|
|
1086
|
+
namespaceName,
|
|
854
1087
|
schemaNamespace,
|
|
855
1088
|
itemTypeName: ir.item.typeName,
|
|
856
1089
|
itemIdExpression,
|
|
@@ -866,6 +1099,12 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
866
1099
|
isPeopleConnector: ir.connection.contentCategory === "people",
|
|
867
1100
|
graphApiVersion: ir.connection.graphApiVersion,
|
|
868
1101
|
}), "utf8");
|
|
1102
|
+
await writeFile(path.join(outDir, "Core", "Validation.cs"), await renderTemplate("dotnet/Core/Validation.cs.ejs", {
|
|
1103
|
+
namespaceName,
|
|
1104
|
+
}), "utf8");
|
|
1105
|
+
await writeFile(path.join(outDir, "Core", "ItemId.cs"), await renderTemplate("dotnet/Core/ItemId.cs.ejs", {
|
|
1106
|
+
namespaceName,
|
|
1107
|
+
}), "utf8");
|
|
869
1108
|
}
|
|
870
1109
|
function formatValidationErrors(ir) {
|
|
871
1110
|
const issues = validateIr(ir);
|