@wictorwilen/cocogen 1.0.17 → 1.0.18
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 +5 -1
- package/README.md +2 -0
- package/dist/init/init.d.ts.map +1 -1
- package/dist/init/init.js +263 -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/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/Model.cs.ejs +5 -1
- package/dist/init/templates/dotnet/Generated/PropertyTransformBase.cs.ejs +19 -5
- 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/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 +1 -1
- 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 +59 -2
- package/dist/tsp/loader.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,17 @@ 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.18] - 2026-01-21
|
|
11
11
|
|
|
12
12
|
### Breaking Changes
|
|
13
|
+
- Removed legacy CSV-row helpers (`fromCsvRow`, `CsvParser`, `csv.ts`) in favor of row-based helpers.
|
|
13
14
|
- Renamed CLI commands: `init-tsp` → `init`, `init` → `generate`.
|
|
14
15
|
|
|
15
16
|
### Added
|
|
17
|
+
- Added agent-facing schema guidance document (docs/schema-assistant.md).
|
|
16
18
|
- Managed identity authentication as the preferred credential for generated TS and .NET projects (client secret fallback).
|
|
17
19
|
- .NET user-secrets support for configuration.
|
|
20
|
+
- TypeSpec metadata support for `@doc`, `@example`, `@minLength`, `@maxLength`, `@minValue`, `@maxValue`, `@pattern`, `@format`, and `#deprecated`.
|
|
18
21
|
|
|
19
22
|
## [1.0.16] - 2026-01-20
|
|
20
23
|
|
|
@@ -37,5 +40,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
37
40
|
- Collection values no longer split on commas; use semicolons instead.
|
|
38
41
|
|
|
39
42
|
[Unreleased]: https://github.com/wictorwilen/cocogen/compare/v1.0.16...HEAD
|
|
43
|
+
[1.0.18]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.18
|
|
40
44
|
[1.0.17]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.17
|
|
41
45
|
[1.0.16]: https://github.com/wictorwilen/cocogen/compare/main...v1.0.16
|
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@
|
|
|
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
|
+
Agent schema guidance:
|
|
35
|
+
- [docs/schema-assistant.md](https://github.com/wictorwilen/cocogen/blob/main/docs/schema-assistant.md)
|
|
34
36
|
|
|
35
37
|
## TypeSpec format
|
|
36
38
|
`cocogen` expects a single “item model” decorated with `@coco.item()` and a single ID property decorated with `@coco.id`.
|
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;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"}
|
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,7 @@ 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");
|
|
331
372
|
}
|
|
332
373
|
function toGraphPropertyTypeEnumName(type) {
|
|
333
374
|
switch (type) {
|
|
@@ -372,25 +413,25 @@ function toOdataCollectionType(type) {
|
|
|
372
413
|
function toCsParseFunction(type) {
|
|
373
414
|
switch (type) {
|
|
374
415
|
case "stringCollection":
|
|
375
|
-
return "
|
|
416
|
+
return "RowParser.ParseStringCollection";
|
|
376
417
|
case "int64Collection":
|
|
377
|
-
return "
|
|
418
|
+
return "RowParser.ParseInt64Collection";
|
|
378
419
|
case "doubleCollection":
|
|
379
|
-
return "
|
|
420
|
+
return "RowParser.ParseDoubleCollection";
|
|
380
421
|
case "dateTimeCollection":
|
|
381
|
-
return "
|
|
422
|
+
return "RowParser.ParseDateTimeCollection";
|
|
382
423
|
case "boolean":
|
|
383
|
-
return "
|
|
424
|
+
return "RowParser.ParseBoolean";
|
|
384
425
|
case "int64":
|
|
385
|
-
return "
|
|
426
|
+
return "RowParser.ParseInt64";
|
|
386
427
|
case "double":
|
|
387
|
-
return "
|
|
428
|
+
return "RowParser.ParseDouble";
|
|
388
429
|
case "dateTime":
|
|
389
|
-
return "
|
|
430
|
+
return "RowParser.ParseDateTime";
|
|
390
431
|
case "principal":
|
|
391
432
|
case "string":
|
|
392
433
|
default:
|
|
393
|
-
return "
|
|
434
|
+
return "RowParser.ParseString";
|
|
394
435
|
}
|
|
395
436
|
}
|
|
396
437
|
function toCsPropertyValueExpression(type, csPropertyName) {
|
|
@@ -403,6 +444,14 @@ function toCsPropertyValueExpression(type, csPropertyName) {
|
|
|
403
444
|
return `item.${csPropertyName}`;
|
|
404
445
|
}
|
|
405
446
|
}
|
|
447
|
+
function formatDocComment(doc, indent = "") {
|
|
448
|
+
const lines = doc.split(/\r?\n/).map((line) => `${indent} * ${line}`);
|
|
449
|
+
return `${indent}/**\n${lines.join("\n")}\n${indent} */`;
|
|
450
|
+
}
|
|
451
|
+
function formatCsDocSummary(doc) {
|
|
452
|
+
const lines = doc.split(/\r?\n/).map((line) => `/// ${line}`);
|
|
453
|
+
return ["/// <summary>", ...lines, "/// </summary>"];
|
|
454
|
+
}
|
|
406
455
|
function buildObjectTree(fields) {
|
|
407
456
|
const root = {};
|
|
408
457
|
for (const field of fields) {
|
|
@@ -429,7 +478,7 @@ function buildObjectTree(fields) {
|
|
|
429
478
|
}
|
|
430
479
|
return root;
|
|
431
480
|
}
|
|
432
|
-
function buildTsPersonEntityExpression(fields) {
|
|
481
|
+
function buildTsPersonEntityExpression(fields, valueExpressionBuilder = (headersLiteral) => `parseString(readSourceValue(row, ${headersLiteral}))`) {
|
|
433
482
|
const tree = buildObjectTree(fields);
|
|
434
483
|
const indentUnit = " ";
|
|
435
484
|
const renderNode = (node, level) => {
|
|
@@ -439,7 +488,7 @@ function buildTsPersonEntityExpression(fields) {
|
|
|
439
488
|
if (typeof value === "object" && value && "path" in value) {
|
|
440
489
|
const field = value;
|
|
441
490
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
442
|
-
return `${childIndent}${JSON.stringify(key)}:
|
|
491
|
+
return `${childIndent}${JSON.stringify(key)}: ${valueExpressionBuilder(headers)}`;
|
|
443
492
|
}
|
|
444
493
|
return `${childIndent}${JSON.stringify(key)}: ${renderNode(value, level + 1)}`;
|
|
445
494
|
});
|
|
@@ -450,7 +499,7 @@ ${indent}}`;
|
|
|
450
499
|
const rendered = renderNode(tree, 2);
|
|
451
500
|
return `JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
|
|
452
501
|
}
|
|
453
|
-
function buildTsPersonEntityCollectionExpression(fields) {
|
|
502
|
+
function buildTsPersonEntityCollectionExpression(fields, collectionExpressionBuilder = (headersLiteral) => `parseStringCollection(readSourceValue(row, ${headersLiteral}))`) {
|
|
454
503
|
const indentUnit = " ";
|
|
455
504
|
const renderNode = (node, level, valueVar) => {
|
|
456
505
|
const indent = indentUnit.repeat(level);
|
|
@@ -470,7 +519,7 @@ ${indent}}`;
|
|
|
470
519
|
const field = fields[0];
|
|
471
520
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
472
521
|
const rendered = renderNode(tree, 2, "value");
|
|
473
|
-
return
|
|
522
|
+
return `${collectionExpressionBuilder(headers)}
|
|
474
523
|
.map((value) => JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)}))`;
|
|
475
524
|
}
|
|
476
525
|
const tree = buildObjectTree(fields);
|
|
@@ -479,7 +528,7 @@ ${indent}}`;
|
|
|
479
528
|
const varName = `field${index}`;
|
|
480
529
|
fieldVarByPath.set(field.path, varName);
|
|
481
530
|
const headers = JSON.stringify(field.source.csvHeaders);
|
|
482
|
-
return ` const ${varName} =
|
|
531
|
+
return ` const ${varName} = ${collectionExpressionBuilder(headers)};`;
|
|
483
532
|
});
|
|
484
533
|
const renderNodeMany = (node) => {
|
|
485
534
|
const entries = Object.entries(node).map(([key, value]) => {
|
|
@@ -500,7 +549,7 @@ ${indentUnit}}`;
|
|
|
500
549
|
: "const lengths = [0];";
|
|
501
550
|
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
551
|
}
|
|
503
|
-
function buildCsPersonEntityExpression(fields) {
|
|
552
|
+
function buildCsPersonEntityExpression(fields, valueExpressionBuilder = (headersLiteral) => `RowParser.ParseString(row, ${headersLiteral})`) {
|
|
504
553
|
const tree = buildObjectTree(fields);
|
|
505
554
|
const indentUnit = " ";
|
|
506
555
|
const renderNode = (node, level) => {
|
|
@@ -510,7 +559,7 @@ function buildCsPersonEntityExpression(fields) {
|
|
|
510
559
|
if (typeof value === "object" && value && "path" in value) {
|
|
511
560
|
const field = value;
|
|
512
561
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
513
|
-
return `${childIndent}[${JSON.stringify(key)}] =
|
|
562
|
+
return `${childIndent}[${JSON.stringify(key)}] = ${valueExpressionBuilder(headers)}`;
|
|
514
563
|
}
|
|
515
564
|
return `${childIndent}[${JSON.stringify(key)}] = ${renderNode(value, level + 1)}`;
|
|
516
565
|
});
|
|
@@ -519,7 +568,7 @@ function buildCsPersonEntityExpression(fields) {
|
|
|
519
568
|
const rendered = renderNode(tree, 2);
|
|
520
569
|
return `JsonSerializer.Serialize(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
|
|
521
570
|
}
|
|
522
|
-
function buildCsPersonEntityCollectionExpression(fields) {
|
|
571
|
+
function buildCsPersonEntityCollectionExpression(fields, collectionExpressionBuilder = (headersLiteral) => `RowParser.ParseStringCollection(row, ${headersLiteral})`) {
|
|
523
572
|
const indentUnit = " ";
|
|
524
573
|
const renderNode = (node, level, valueVar) => {
|
|
525
574
|
const indent = indentUnit.repeat(level);
|
|
@@ -537,7 +586,7 @@ function buildCsPersonEntityCollectionExpression(fields) {
|
|
|
537
586
|
const field = fields[0];
|
|
538
587
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
539
588
|
const rendered = renderNode(tree, 3, "value");
|
|
540
|
-
return
|
|
589
|
+
return `${collectionExpressionBuilder(headers)}
|
|
541
590
|
.Select(value => JsonSerializer.Serialize(\n${indentUnit.repeat(3)}${rendered}\n${indentUnit.repeat(3)}))
|
|
542
591
|
.ToList()`;
|
|
543
592
|
}
|
|
@@ -547,7 +596,7 @@ function buildCsPersonEntityCollectionExpression(fields) {
|
|
|
547
596
|
const varName = `field${index}`;
|
|
548
597
|
fieldVarByPath.set(field.path, varName);
|
|
549
598
|
const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
550
|
-
return ` var ${varName} =
|
|
599
|
+
return ` var ${varName} = ${collectionExpressionBuilder(headers)};`;
|
|
551
600
|
});
|
|
552
601
|
const renderNodeMany = (node) => {
|
|
553
602
|
const entries = Object.entries(node).map(([key, value]) => {
|
|
@@ -597,6 +646,125 @@ function sampleValueForType(type) {
|
|
|
597
646
|
return "sample";
|
|
598
647
|
}
|
|
599
648
|
}
|
|
649
|
+
function buildTsStringConstraintsLiteral(prop) {
|
|
650
|
+
const parts = [];
|
|
651
|
+
if (prop.minLength !== undefined)
|
|
652
|
+
parts.push(`minLength: ${prop.minLength}`);
|
|
653
|
+
if (prop.maxLength !== undefined)
|
|
654
|
+
parts.push(`maxLength: ${prop.maxLength}`);
|
|
655
|
+
if (prop.pattern?.regex)
|
|
656
|
+
parts.push(`pattern: ${JSON.stringify(prop.pattern.regex)}`);
|
|
657
|
+
if (prop.format)
|
|
658
|
+
parts.push(`format: ${JSON.stringify(prop.format)}`);
|
|
659
|
+
return parts.length > 0 ? `{ ${parts.join(", ")} }` : undefined;
|
|
660
|
+
}
|
|
661
|
+
function buildTsNumberConstraintsLiteral(prop) {
|
|
662
|
+
const parts = [];
|
|
663
|
+
if (prop.minValue !== undefined)
|
|
664
|
+
parts.push(`minValue: ${prop.minValue}`);
|
|
665
|
+
if (prop.maxValue !== undefined)
|
|
666
|
+
parts.push(`maxValue: ${prop.maxValue}`);
|
|
667
|
+
return parts.length > 0 ? `{ ${parts.join(", ")} }` : undefined;
|
|
668
|
+
}
|
|
669
|
+
function applyTsValidationExpression(prop, expression) {
|
|
670
|
+
const stringConstraints = buildTsStringConstraintsLiteral(prop);
|
|
671
|
+
const numberConstraints = buildTsNumberConstraintsLiteral(prop);
|
|
672
|
+
const nameLiteral = JSON.stringify(prop.name);
|
|
673
|
+
switch (prop.type) {
|
|
674
|
+
case "string":
|
|
675
|
+
case "principal":
|
|
676
|
+
case "dateTime":
|
|
677
|
+
return stringConstraints ? `validateString(${nameLiteral}, ${expression}, ${stringConstraints})` : expression;
|
|
678
|
+
case "stringCollection":
|
|
679
|
+
case "dateTimeCollection":
|
|
680
|
+
return stringConstraints
|
|
681
|
+
? `validateStringCollection(${nameLiteral}, ${expression}, ${stringConstraints})`
|
|
682
|
+
: expression;
|
|
683
|
+
case "int64":
|
|
684
|
+
case "double":
|
|
685
|
+
return numberConstraints ? `validateNumber(${nameLiteral}, ${expression}, ${numberConstraints})` : expression;
|
|
686
|
+
case "int64Collection":
|
|
687
|
+
case "doubleCollection":
|
|
688
|
+
return numberConstraints
|
|
689
|
+
? `validateNumberCollection(${nameLiteral}, ${expression}, ${numberConstraints})`
|
|
690
|
+
: expression;
|
|
691
|
+
default:
|
|
692
|
+
return expression;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function buildCsStringConstraintsLiteral(prop) {
|
|
696
|
+
const minLength = prop.minLength !== undefined ? prop.minLength.toString() : "null";
|
|
697
|
+
const maxLength = prop.maxLength !== undefined ? prop.maxLength.toString() : "null";
|
|
698
|
+
const pattern = prop.pattern?.regex ? JSON.stringify(prop.pattern.regex) : "null";
|
|
699
|
+
const format = prop.format ? JSON.stringify(prop.format) : "null";
|
|
700
|
+
const hasAny = prop.minLength !== undefined || prop.maxLength !== undefined || Boolean(prop.pattern?.regex) || Boolean(prop.format);
|
|
701
|
+
return { minLength, maxLength, pattern, format, hasAny };
|
|
702
|
+
}
|
|
703
|
+
function buildCsNumberConstraintsLiteral(prop) {
|
|
704
|
+
const minValue = prop.minValue !== undefined ? prop.minValue.toString() : "null";
|
|
705
|
+
const maxValue = prop.maxValue !== undefined ? prop.maxValue.toString() : "null";
|
|
706
|
+
const hasAny = prop.minValue !== undefined || prop.maxValue !== undefined;
|
|
707
|
+
return { minValue, maxValue, hasAny };
|
|
708
|
+
}
|
|
709
|
+
function applyCsValidationExpression(prop, expression, csvHeadersLiteral) {
|
|
710
|
+
const stringConstraints = buildCsStringConstraintsLiteral(prop);
|
|
711
|
+
const numberConstraints = buildCsNumberConstraintsLiteral(prop);
|
|
712
|
+
const nameLiteral = JSON.stringify(prop.name);
|
|
713
|
+
switch (prop.type) {
|
|
714
|
+
case "string":
|
|
715
|
+
case "principal":
|
|
716
|
+
return stringConstraints.hasAny
|
|
717
|
+
? `Validation.ValidateString(${nameLiteral}, ${expression}, ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`
|
|
718
|
+
: expression;
|
|
719
|
+
case "dateTime":
|
|
720
|
+
if (!stringConstraints.hasAny)
|
|
721
|
+
return expression;
|
|
722
|
+
return `RowParser.ParseDateTime(Validation.ValidateString(${nameLiteral}, RowParser.ReadValue(row, ${csvHeadersLiteral}), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format}))`;
|
|
723
|
+
case "stringCollection":
|
|
724
|
+
return stringConstraints.hasAny
|
|
725
|
+
? `Validation.ValidateStringCollection(${nameLiteral}, ${expression}, ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`
|
|
726
|
+
: expression;
|
|
727
|
+
case "dateTimeCollection":
|
|
728
|
+
if (!stringConstraints.hasAny)
|
|
729
|
+
return expression;
|
|
730
|
+
return `ValidateDateTimeCollection(${nameLiteral}, RowParser.ReadValue(row, ${csvHeadersLiteral}), ${stringConstraints.minLength}, ${stringConstraints.maxLength}, ${stringConstraints.pattern}, ${stringConstraints.format})`;
|
|
731
|
+
case "int64":
|
|
732
|
+
return numberConstraints.hasAny
|
|
733
|
+
? `Validation.ValidateInt64(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
734
|
+
: expression;
|
|
735
|
+
case "double":
|
|
736
|
+
return numberConstraints.hasAny
|
|
737
|
+
? `Validation.ValidateDouble(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
738
|
+
: expression;
|
|
739
|
+
case "int64Collection":
|
|
740
|
+
return numberConstraints.hasAny
|
|
741
|
+
? `Validation.ValidateInt64Collection(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
742
|
+
: expression;
|
|
743
|
+
case "doubleCollection":
|
|
744
|
+
return numberConstraints.hasAny
|
|
745
|
+
? `Validation.ValidateDoubleCollection(${nameLiteral}, ${expression}, ${numberConstraints.minValue}, ${numberConstraints.maxValue})`
|
|
746
|
+
: expression;
|
|
747
|
+
default:
|
|
748
|
+
return expression;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function exampleValueForType(example, type) {
|
|
752
|
+
if (example === undefined || example === null)
|
|
753
|
+
return undefined;
|
|
754
|
+
if (type.endsWith("Collection")) {
|
|
755
|
+
if (Array.isArray(example)) {
|
|
756
|
+
return example.map((value) => (value === undefined || value === null ? "" : String(value))).join(";");
|
|
757
|
+
}
|
|
758
|
+
if (typeof example === "string")
|
|
759
|
+
return example;
|
|
760
|
+
return JSON.stringify(example);
|
|
761
|
+
}
|
|
762
|
+
if (typeof example === "string")
|
|
763
|
+
return example;
|
|
764
|
+
if (typeof example === "number" || typeof example === "boolean")
|
|
765
|
+
return String(example);
|
|
766
|
+
return JSON.stringify(example);
|
|
767
|
+
}
|
|
600
768
|
function sampleValueForHeader(header, type) {
|
|
601
769
|
const lower = header.toLowerCase();
|
|
602
770
|
if (lower.includes("job title"))
|
|
@@ -650,6 +818,14 @@ function buildSampleCsv(ir) {
|
|
|
650
818
|
const valueByHeader = new Map();
|
|
651
819
|
for (const prop of ir.properties) {
|
|
652
820
|
if (prop.personEntity) {
|
|
821
|
+
const exampleValue = exampleValueForType(prop.example, prop.type);
|
|
822
|
+
if (exampleValue && prop.personEntity.fields.length === 1) {
|
|
823
|
+
const headers = prop.personEntity.fields[0]?.source.csvHeaders ?? [];
|
|
824
|
+
for (const header of headers) {
|
|
825
|
+
if (!valueByHeader.has(header))
|
|
826
|
+
valueByHeader.set(header, exampleValue);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
653
829
|
for (const field of prop.personEntity.fields) {
|
|
654
830
|
for (const header of field.source.csvHeaders) {
|
|
655
831
|
if (!valueByHeader.has(header))
|
|
@@ -658,9 +834,11 @@ function buildSampleCsv(ir) {
|
|
|
658
834
|
}
|
|
659
835
|
continue;
|
|
660
836
|
}
|
|
837
|
+
const exampleValue = exampleValueForType(prop.example, prop.type);
|
|
661
838
|
for (const header of prop.source.csvHeaders) {
|
|
662
|
-
if (!valueByHeader.has(header))
|
|
663
|
-
valueByHeader.set(header, sampleValueForHeader(header, prop.type));
|
|
839
|
+
if (!valueByHeader.has(header)) {
|
|
840
|
+
valueByHeader.set(header, exampleValue ?? sampleValueForHeader(header, prop.type));
|
|
841
|
+
}
|
|
664
842
|
}
|
|
665
843
|
}
|
|
666
844
|
const headerLine = headers.map(csvEscape).join(",");
|
|
@@ -669,13 +847,18 @@ function buildSampleCsv(ir) {
|
|
|
669
847
|
}
|
|
670
848
|
async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName, schemaNamespace) {
|
|
671
849
|
await mkdir(path.join(outDir, schemaFolderName), { recursive: true });
|
|
850
|
+
await mkdir(path.join(outDir, "Datasource"), { recursive: true });
|
|
672
851
|
await mkdir(path.join(outDir, "Core"), { recursive: true });
|
|
852
|
+
await removeIfExists(path.join(outDir, schemaFolderName, "FromCsvRow.cs"));
|
|
853
|
+
await removeIfExists(path.join(outDir, "Datasource", "CsvParser.cs"));
|
|
673
854
|
const usedPropertyNames = new Set();
|
|
674
855
|
const itemTypeName = toCsIdentifier(ir.item.typeName);
|
|
675
856
|
const properties = ir.properties.map((p) => {
|
|
676
857
|
const parseFn = toCsParseFunction(p.type);
|
|
677
858
|
const csvHeadersLiteral = `new[] { ${p.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
678
859
|
const isCollection = p.type === "stringCollection";
|
|
860
|
+
const nameLiteral = JSON.stringify(p.name);
|
|
861
|
+
const csStringConstraints = buildCsStringConstraintsLiteral(p);
|
|
679
862
|
const personEntity = p.personEntity
|
|
680
863
|
? {
|
|
681
864
|
entity: p.personEntity.entity,
|
|
@@ -692,11 +875,34 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
692
875
|
? `throw new NotImplementedException("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement in PropertyTransform.cs.")`
|
|
693
876
|
: personEntity
|
|
694
877
|
? isCollection
|
|
695
|
-
? buildCsPersonEntityCollectionExpression(personEntity.fields)
|
|
696
|
-
|
|
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
|
+
})
|
|
697
890
|
: noSource
|
|
698
891
|
? "default!"
|
|
699
892
|
: `${parseFn}(row, ${csvHeadersLiteral})`;
|
|
893
|
+
const validationMetadata = {
|
|
894
|
+
name: p.name,
|
|
895
|
+
type: p.type,
|
|
896
|
+
...(p.minLength !== undefined ? { minLength: p.minLength } : {}),
|
|
897
|
+
...(p.maxLength !== undefined ? { maxLength: p.maxLength } : {}),
|
|
898
|
+
...(p.pattern ? { pattern: p.pattern } : {}),
|
|
899
|
+
...(p.format ? { format: p.format } : {}),
|
|
900
|
+
...(p.minValue !== undefined ? { minValue: p.minValue } : {}),
|
|
901
|
+
...(p.maxValue !== undefined ? { maxValue: p.maxValue } : {}),
|
|
902
|
+
};
|
|
903
|
+
const validatedExpression = needsManualEntity || noSource || personEntity
|
|
904
|
+
? transformExpression
|
|
905
|
+
: applyCsValidationExpression(validationMetadata, transformExpression, csvHeadersLiteral);
|
|
700
906
|
return {
|
|
701
907
|
name: p.name,
|
|
702
908
|
csName: toCsPropertyName(p.name, itemTypeName, usedPropertyNames),
|
|
@@ -706,16 +912,35 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
706
912
|
isCollection,
|
|
707
913
|
personEntity,
|
|
708
914
|
parseFn,
|
|
709
|
-
transformExpression,
|
|
915
|
+
transformExpression: validatedExpression,
|
|
710
916
|
transformThrows: needsManualEntity,
|
|
711
917
|
graphTypeEnumName: toGraphPropertyTypeEnumName(p.type),
|
|
712
918
|
description: p.description,
|
|
919
|
+
doc: p.doc,
|
|
713
920
|
labels: p.labels,
|
|
714
921
|
aliases: p.aliases,
|
|
715
922
|
search: p.search,
|
|
716
923
|
type: p.type,
|
|
924
|
+
format: p.format,
|
|
925
|
+
pattern: p.pattern,
|
|
926
|
+
minLength: p.minLength,
|
|
927
|
+
maxLength: p.maxLength,
|
|
928
|
+
minValue: p.minValue,
|
|
929
|
+
maxValue: p.maxValue,
|
|
717
930
|
};
|
|
718
931
|
});
|
|
932
|
+
const recordDocLines = [];
|
|
933
|
+
if (ir.item.doc) {
|
|
934
|
+
recordDocLines.push(...formatCsDocSummary(ir.item.doc));
|
|
935
|
+
}
|
|
936
|
+
for (const prop of properties) {
|
|
937
|
+
if (!prop.doc)
|
|
938
|
+
continue;
|
|
939
|
+
const docLines = prop.doc.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
940
|
+
if (docLines.length === 0)
|
|
941
|
+
continue;
|
|
942
|
+
recordDocLines.push(`/// <param name=\"${prop.csName}\">${docLines.join(" ")}</param>`);
|
|
943
|
+
}
|
|
719
944
|
const schemaPropertyLines = properties
|
|
720
945
|
.filter((p) => p.name !== ir.item.contentPropertyName)
|
|
721
946
|
.map((p) => {
|
|
@@ -765,7 +990,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
765
990
|
const idRawHeadersDotnet = itemIdProperty?.personEntity?.fields[0]?.source.csvHeaders ?? itemIdProperty?.csvHeaders ?? [];
|
|
766
991
|
const idRawHeadersLiteral = `new[] { ${idRawHeadersDotnet.map((h) => JSON.stringify(h)).join(", ")} }`;
|
|
767
992
|
const idRawExpressionDotnet = idRawHeadersDotnet.length
|
|
768
|
-
? `
|
|
993
|
+
? `RowParser.ParseString(row, ${idRawHeadersLiteral})`
|
|
769
994
|
: "string.Empty";
|
|
770
995
|
const constructorArgs = [
|
|
771
996
|
...properties.map((p) => `(${p.csType})transforms.TransformProperty(${JSON.stringify(p.name)}, row)`),
|
|
@@ -790,7 +1015,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
790
1015
|
})
|
|
791
1016
|
.join("\n");
|
|
792
1017
|
const itemIdExpression = itemIdProperty
|
|
793
|
-
? `!string.IsNullOrEmpty(item.
|
|
1018
|
+
? `!string.IsNullOrEmpty(item.InternalId) ? item.InternalId : (item.${itemIdProperty.csName} ?? string.Empty)`
|
|
794
1019
|
: "\"\"";
|
|
795
1020
|
const contentValueExpression = ir.item.contentPropertyName
|
|
796
1021
|
? `Convert.ToString(item.${toCsIdentifier(ir.item.contentPropertyName)}) ?? string.Empty`
|
|
@@ -806,6 +1031,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
806
1031
|
schemaNamespace,
|
|
807
1032
|
itemTypeName: ir.item.typeName,
|
|
808
1033
|
properties: properties.map((p) => ({ csName: p.csName, csType: p.csType })),
|
|
1034
|
+
recordDocLines,
|
|
809
1035
|
}), "utf8");
|
|
810
1036
|
await writeFile(path.join(outDir, schemaFolderName, "Constants.cs"), await renderTemplate("dotnet/Generated/Constants.cs.ejs", {
|
|
811
1037
|
schemaNamespace,
|
|
@@ -826,7 +1052,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
826
1052
|
schemaPropertyLines,
|
|
827
1053
|
graphApiVersion: ir.connection.graphApiVersion,
|
|
828
1054
|
}), "utf8");
|
|
829
|
-
await writeFile(path.join(outDir, "Datasource", "
|
|
1055
|
+
await writeFile(path.join(outDir, "Datasource", "RowParser.cs"), await renderTemplate("dotnet/Generated/RowParser.cs.ejs", {
|
|
830
1056
|
namespaceName,
|
|
831
1057
|
}), "utf8");
|
|
832
1058
|
await writeFile(path.join(outDir, schemaFolderName, "PropertyTransformBase.cs"), await renderTemplate("dotnet/Generated/PropertyTransformBase.cs.ejs", {
|
|
@@ -844,7 +1070,7 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
844
1070
|
schemaNamespace,
|
|
845
1071
|
}), "utf8");
|
|
846
1072
|
}
|
|
847
|
-
await writeFile(path.join(outDir, schemaFolderName, "
|
|
1073
|
+
await writeFile(path.join(outDir, schemaFolderName, "FromRow.cs"), await renderTemplate("dotnet/Generated/FromRow.cs.ejs", {
|
|
848
1074
|
namespaceName,
|
|
849
1075
|
schemaNamespace,
|
|
850
1076
|
itemTypeName: ir.item.typeName,
|
|
@@ -866,6 +1092,9 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName,
|
|
|
866
1092
|
isPeopleConnector: ir.connection.contentCategory === "people",
|
|
867
1093
|
graphApiVersion: ir.connection.graphApiVersion,
|
|
868
1094
|
}), "utf8");
|
|
1095
|
+
await writeFile(path.join(outDir, "Core", "Validation.cs"), await renderTemplate("dotnet/Core/Validation.cs.ejs", {
|
|
1096
|
+
namespaceName,
|
|
1097
|
+
}), "utf8");
|
|
869
1098
|
}
|
|
870
1099
|
function formatValidationErrors(ir) {
|
|
871
1100
|
const issues = validateIr(ir);
|