@typespec/http-server-csharp 0.58.0-alpha.10-dev.0 → 0.58.0-alpha.10-dev.2
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/dist/src/lib/attributes.js +4 -4
- package/dist/src/lib/attributes.js.map +1 -1
- package/dist/src/lib/boilerplate.d.ts.map +1 -1
- package/dist/src/lib/boilerplate.js +5 -5
- package/dist/src/lib/boilerplate.js.map +1 -1
- package/dist/src/lib/interfaces.d.ts +17 -0
- package/dist/src/lib/interfaces.d.ts.map +1 -1
- package/dist/src/lib/interfaces.js +4 -0
- package/dist/src/lib/interfaces.js.map +1 -1
- package/dist/src/lib/lib.d.ts +10 -1
- package/dist/src/lib/lib.d.ts.map +1 -1
- package/dist/src/lib/lib.js +6 -0
- package/dist/src/lib/lib.js.map +1 -1
- package/dist/src/lib/scaffolding.d.ts +5 -4
- package/dist/src/lib/scaffolding.d.ts.map +1 -1
- package/dist/src/lib/scaffolding.js +70 -26
- package/dist/src/lib/scaffolding.js.map +1 -1
- package/dist/src/lib/service.d.ts.map +1 -1
- package/dist/src/lib/service.js +64 -421
- package/dist/src/lib/service.js.map +1 -1
- package/dist/src/lib/utils.d.ts +43 -3
- package/dist/src/lib/utils.d.ts.map +1 -1
- package/dist/src/lib/utils.js +545 -4
- package/dist/src/lib/utils.js.map +1 -1
- package/package.json +3 -3
package/dist/src/lib/service.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { getDoc, getNamespaceFullName, getService, isErrorModel, isNeverType, isNullType, isTemplateDeclaration, isVoidType, } from "@typespec/compiler";
|
|
2
2
|
import { CodeTypeEmitter, StringBuilder, code, createAssetEmitter, } from "@typespec/compiler/emitter-framework";
|
|
3
3
|
import { createRekeyableMap } from "@typespec/compiler/utils";
|
|
4
|
-
import {
|
|
4
|
+
import { getHttpOperation, getHttpPart, isStatusCode, } from "@typespec/http";
|
|
5
5
|
import { getResourceOperation } from "@typespec/rest";
|
|
6
6
|
import { execFile } from "child_process";
|
|
7
|
+
import { getEncodedNameAttribute } from "./attributes.js";
|
|
7
8
|
import { GeneratedFileHeader, GeneratedFileHeaderWithNullable, getSerializationSourceFiles, } from "./boilerplate.js";
|
|
8
9
|
import { CSharpSourceType, CSharpType, NameCasingType, } from "./interfaces.js";
|
|
9
10
|
import { reportDiagnostic } from "./lib.js";
|
|
10
|
-
import { getBusinessLogicImplementations,
|
|
11
|
+
import { getBusinessLogicImplementations, } from "./scaffolding.js";
|
|
11
12
|
import { getRecordType, isKnownReferenceType } from "./type-helpers.js";
|
|
12
|
-
import { HttpMetadata, UnknownType, coalesceTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForIntrinsic, getCSharpTypeForScalar, getModelAttributes, getModelInstantiationName, getOperationVerbDecorator, isValueType, } from "./utils.js";
|
|
13
|
+
import { CSharpOperationHelpers, HttpMetadata, UnknownType, coalesceTypes, coalesceUnionTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getBusinessLogicCallParameters, getBusinessLogicDeclParameters, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForIntrinsic, getCSharpTypeForScalar, getHttpDeclParameters, getModelAttributes, getModelDeclarationName, getModelInstantiationName, getOperationVerbDecorator, isEmptyResponseModel, isValueType, } from "./utils.js";
|
|
13
14
|
export async function $onEmit(context) {
|
|
14
15
|
let _unionCounter = 0;
|
|
15
16
|
const controllers = new Map();
|
|
@@ -27,14 +28,8 @@ export async function $onEmit(context) {
|
|
|
27
28
|
#useSwagger = context.options["use-swaggerui"] || false;
|
|
28
29
|
#openapiPath = context.options["openapi-path"] || "openapi/openapi.yaml";
|
|
29
30
|
#mockRegistrations = new Map();
|
|
30
|
-
#mockHelpers = this.#emitMocks === "all"
|
|
31
|
-
? getScaffoldingHelpers(this.emitter, this.#useSwagger, this.#openapiPath)
|
|
32
|
-
: [];
|
|
33
31
|
#mockFiles = [];
|
|
34
|
-
#
|
|
35
|
-
canonicalVisibility: Visibility.Read,
|
|
36
|
-
canShareProperty: (p) => true,
|
|
37
|
-
});
|
|
32
|
+
#opHelpers = new CSharpOperationHelpers(this.emitter);
|
|
38
33
|
arrayDeclaration(array, name, elementType) {
|
|
39
34
|
return this.emitter.result.declaration(ensureCSharpIdentifier(this.emitter.getProgram(), array, name), code `${this.emitter.emitTypeReference(elementType)}[]`);
|
|
40
35
|
}
|
|
@@ -45,8 +40,8 @@ export async function $onEmit(context) {
|
|
|
45
40
|
return this.emitter.result.rawCode(code `${boolean.value === true ? "true" : "false"}`);
|
|
46
41
|
}
|
|
47
42
|
unionLiteral(union) {
|
|
48
|
-
const csType =
|
|
49
|
-
return this.emitter.result.rawCode(csType
|
|
43
|
+
const csType = coalesceUnionTypes(this.emitter.getProgram(), union);
|
|
44
|
+
return this.emitter.result.rawCode(csType ? csType.getTypeReference(this.emitter.getContext()?.scope) : "object");
|
|
50
45
|
}
|
|
51
46
|
declarationName(declarationType) {
|
|
52
47
|
switch (declarationType.kind) {
|
|
@@ -60,7 +55,7 @@ export async function $onEmit(context) {
|
|
|
60
55
|
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
61
56
|
case "Model":
|
|
62
57
|
if (!declarationType.name)
|
|
63
|
-
return
|
|
58
|
+
return getModelDeclarationName(this.emitter.getProgram(), declarationType, `${_unionCounter++}`);
|
|
64
59
|
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
65
60
|
case "Operation":
|
|
66
61
|
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
@@ -91,7 +86,7 @@ export async function $onEmit(context) {
|
|
|
91
86
|
${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}
|
|
92
87
|
public enum ${enumName}
|
|
93
88
|
{
|
|
94
|
-
${this.emitter.emitEnumMembers(en)}
|
|
89
|
+
${this.emitter.emitEnumMembers(en)}
|
|
95
90
|
}
|
|
96
91
|
} `);
|
|
97
92
|
}
|
|
@@ -100,11 +95,7 @@ export async function $onEmit(context) {
|
|
|
100
95
|
const enumFile = this.emitter.createSourceFile(`models/${enumName}.cs`);
|
|
101
96
|
enumFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
102
97
|
const enumNamespace = `${this.#getOrSetBaseNamespace(en)}.Models`;
|
|
103
|
-
return
|
|
104
|
-
namespace: enumNamespace,
|
|
105
|
-
file: enumFile,
|
|
106
|
-
scope: enumFile.globalScope,
|
|
107
|
-
};
|
|
98
|
+
return this.#createEnumContext(enumNamespace, enumFile, enumName);
|
|
108
99
|
}
|
|
109
100
|
enumMembers(en) {
|
|
110
101
|
const result = new StringBuilder();
|
|
@@ -113,9 +104,11 @@ export async function $onEmit(context) {
|
|
|
113
104
|
i++;
|
|
114
105
|
const memberName = ensureCSharpIdentifier(this.emitter.getProgram(), member, name);
|
|
115
106
|
this.#metadateMap.set(member, { name: memberName });
|
|
116
|
-
result.push(code
|
|
107
|
+
result.push(code `
|
|
108
|
+
[JsonStringEnumMemberName("${member.value ? member.value : name}")]
|
|
109
|
+
${ensureCSharpIdentifier(this.emitter.getProgram(), member, name)}`);
|
|
117
110
|
if (i < en.members.size)
|
|
118
|
-
result.pushLiteralSegment("
|
|
111
|
+
result.pushLiteralSegment(",\n");
|
|
119
112
|
}
|
|
120
113
|
return this.emitter.result.rawCode(result.reduce());
|
|
121
114
|
}
|
|
@@ -243,9 +236,6 @@ export async function $onEmit(context) {
|
|
|
243
236
|
});
|
|
244
237
|
return this.modelDeclaration(model, modelName);
|
|
245
238
|
}
|
|
246
|
-
#isRecord(type) {
|
|
247
|
-
return type.kind === "Model" && type.name === "Record" && type.indexer !== undefined;
|
|
248
|
-
}
|
|
249
239
|
#isInheritedProperty(property) {
|
|
250
240
|
const visited = [];
|
|
251
241
|
function isInherited(model, propertyName) {
|
|
@@ -266,10 +256,15 @@ export async function $onEmit(context) {
|
|
|
266
256
|
modelPropertyLiteral(property) {
|
|
267
257
|
if (isStatusCode(this.emitter.getProgram(), property))
|
|
268
258
|
return "";
|
|
269
|
-
|
|
270
|
-
const
|
|
259
|
+
let propertyName = ensureCSharpIdentifier(this.emitter.getProgram(), property, property.name);
|
|
260
|
+
const { typeReference: typeName, defaultValue: typeDefault, nullableType: nullable, } = this.#findPropertyType(property);
|
|
271
261
|
const doc = getDoc(this.emitter.getProgram(), property);
|
|
272
262
|
const attributes = getModelAttributes(this.emitter.getProgram(), property, propertyName);
|
|
263
|
+
const modelName = this.emitter.getContext()["name"];
|
|
264
|
+
if (modelName === propertyName) {
|
|
265
|
+
propertyName = `${propertyName}Prop`;
|
|
266
|
+
attributes.push(getEncodedNameAttribute(this.emitter.getProgram(), property, propertyName));
|
|
267
|
+
}
|
|
273
268
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
274
269
|
const defaultValue = property.default
|
|
275
270
|
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
@@ -282,7 +277,7 @@ export async function $onEmit(context) {
|
|
|
282
277
|
`);
|
|
283
278
|
}
|
|
284
279
|
#findPropertyType(property) {
|
|
285
|
-
return this.#
|
|
280
|
+
return this.#opHelpers.getTypeInfo(this.emitter.getProgram(), property.type);
|
|
286
281
|
}
|
|
287
282
|
#isMultipartRequest(operation) {
|
|
288
283
|
const body = operation.parameters.body;
|
|
@@ -298,118 +293,6 @@ export async function $onEmit(context) {
|
|
|
298
293
|
}
|
|
299
294
|
return false;
|
|
300
295
|
}
|
|
301
|
-
#getTypeInfoForUnion(union) {
|
|
302
|
-
const propResult = this.#getNonNullableTsType(union);
|
|
303
|
-
if (propResult === undefined) {
|
|
304
|
-
return [
|
|
305
|
-
code `${emitter.emitTypeReference(union)}`,
|
|
306
|
-
undefined,
|
|
307
|
-
[...union.variants.values()].filter((v) => isNullType(v.type)).length > 0,
|
|
308
|
-
];
|
|
309
|
-
}
|
|
310
|
-
const [typeName, typeDefault, _] = this.#getTypeInfoForTsType(propResult.type);
|
|
311
|
-
return [typeName, typeDefault, propResult.nullable];
|
|
312
|
-
}
|
|
313
|
-
#getTypeInfoForTsType(tsType) {
|
|
314
|
-
function extractStringValue(type, span) {
|
|
315
|
-
switch (type.kind) {
|
|
316
|
-
case "String":
|
|
317
|
-
return type.value;
|
|
318
|
-
case "Boolean":
|
|
319
|
-
return `${type.value}`;
|
|
320
|
-
case "Number":
|
|
321
|
-
return type.valueAsString;
|
|
322
|
-
case "StringTemplateSpan":
|
|
323
|
-
if (type.isInterpolated) {
|
|
324
|
-
return extractStringValue(type.type, span);
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
return type.type.value;
|
|
328
|
-
}
|
|
329
|
-
case "ModelProperty":
|
|
330
|
-
return extractStringValue(type.type, span);
|
|
331
|
-
case "EnumMember":
|
|
332
|
-
if (type.value === undefined)
|
|
333
|
-
return type.name;
|
|
334
|
-
if (typeof type.value === "string")
|
|
335
|
-
return type.value;
|
|
336
|
-
if (typeof type.value === "number")
|
|
337
|
-
return `${type.value}`;
|
|
338
|
-
}
|
|
339
|
-
reportDiagnostic(emitter.getProgram(), {
|
|
340
|
-
code: "invalid-interpolation",
|
|
341
|
-
target: span,
|
|
342
|
-
format: {},
|
|
343
|
-
});
|
|
344
|
-
return "";
|
|
345
|
-
}
|
|
346
|
-
switch (tsType.kind) {
|
|
347
|
-
case "String":
|
|
348
|
-
return [code `string`, `"${tsType.value}"`, false];
|
|
349
|
-
case "StringTemplate":
|
|
350
|
-
const template = tsType;
|
|
351
|
-
if (template.stringValue !== undefined)
|
|
352
|
-
return [code `string`, `"${template.stringValue}"`, false];
|
|
353
|
-
const spanResults = [];
|
|
354
|
-
for (const span of template.spans) {
|
|
355
|
-
spanResults.push(extractStringValue(span, span));
|
|
356
|
-
}
|
|
357
|
-
return [code `string`, `"${spanResults.join("")}"`, false];
|
|
358
|
-
case "Boolean":
|
|
359
|
-
return [code `bool`, `${tsType.value === true ? true : false}`, false];
|
|
360
|
-
case "Number":
|
|
361
|
-
const [type, value] = this.#findNumericType(tsType);
|
|
362
|
-
return [code `${type}`, `${value}`, false];
|
|
363
|
-
case "Tuple":
|
|
364
|
-
const defaults = [];
|
|
365
|
-
const [csharpType, isObject] = this.#coalesceTypes(tsType.values);
|
|
366
|
-
if (isObject)
|
|
367
|
-
return ["object[]", undefined, false];
|
|
368
|
-
for (const value of tsType.values) {
|
|
369
|
-
const [_, itemDefault] = this.#getTypeInfoForTsType(value);
|
|
370
|
-
defaults.push(itemDefault);
|
|
371
|
-
}
|
|
372
|
-
return [
|
|
373
|
-
code `${csharpType.getTypeReference()}[]`,
|
|
374
|
-
`[${defaults.join(", ")}]`,
|
|
375
|
-
csharpType.isNullable,
|
|
376
|
-
];
|
|
377
|
-
case "Object":
|
|
378
|
-
return [code `object`, undefined, false];
|
|
379
|
-
case "Model":
|
|
380
|
-
if (this.#isRecord(tsType)) {
|
|
381
|
-
return [code `JsonObject`, undefined, false];
|
|
382
|
-
}
|
|
383
|
-
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
384
|
-
case "ModelProperty":
|
|
385
|
-
return this.#getTypeInfoForTsType(tsType.type);
|
|
386
|
-
case "Enum":
|
|
387
|
-
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
388
|
-
case "EnumMember":
|
|
389
|
-
if (typeof tsType.value === "number") {
|
|
390
|
-
const stringValue = tsType.value.toString();
|
|
391
|
-
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
392
|
-
return ["double", stringValue, false];
|
|
393
|
-
return ["int", stringValue, false];
|
|
394
|
-
}
|
|
395
|
-
if (typeof tsType.value === "string") {
|
|
396
|
-
return ["string", tsType.value, false];
|
|
397
|
-
}
|
|
398
|
-
return [code `object`, undefined, false];
|
|
399
|
-
case "Union":
|
|
400
|
-
return this.#getTypeInfoForUnion(tsType);
|
|
401
|
-
case "UnionVariant":
|
|
402
|
-
return this.#getTypeInfoForTsType(tsType.type);
|
|
403
|
-
default:
|
|
404
|
-
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
#findNumericType(type) {
|
|
408
|
-
const stringValue = type.valueAsString;
|
|
409
|
-
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
410
|
-
return ["double", stringValue];
|
|
411
|
-
return ["int", stringValue];
|
|
412
|
-
}
|
|
413
296
|
modelPropertyReference(property) {
|
|
414
297
|
return code `${this.emitter.emitTypeReference(property.type)}`;
|
|
415
298
|
}
|
|
@@ -484,30 +367,17 @@ export async function $onEmit(context) {
|
|
|
484
367
|
const returnInfo = coalesceTypes(this.emitter.getProgram(), returnTypes, context.namespace);
|
|
485
368
|
const returnType = returnInfo?.type || UnknownType;
|
|
486
369
|
const opName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${opImpl.returnType} ${opImpl.methodName}( ${opImpl.methodParams});`);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
opImpl = {
|
|
502
|
-
methodName: `${opName}Async`,
|
|
503
|
-
methodParams: `${this.#emitInterfaceOperationParameters(operation)}`,
|
|
504
|
-
returnType: `${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`}`,
|
|
505
|
-
instantiatedReturnType: returnType.name === "void"
|
|
506
|
-
? undefined
|
|
507
|
-
: `${returnType.getTypeReference(context.scope)}`,
|
|
508
|
-
};
|
|
509
|
-
opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${opImpl.returnType} ${opImpl.methodName}( ${opImpl.methodParams});`);
|
|
510
|
-
}
|
|
370
|
+
const parameters = this.#opHelpers.getParameters(this.emitter.getProgram(), httpOp);
|
|
371
|
+
const opImpl = {
|
|
372
|
+
methodName: `${opName}Async`,
|
|
373
|
+
methodParams: `${getBusinessLogicDeclParameters(parameters)}`,
|
|
374
|
+
returnType: returnType,
|
|
375
|
+
returnTypeName: `${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`}`,
|
|
376
|
+
instantiatedReturnType: returnType.name === "void"
|
|
377
|
+
? undefined
|
|
378
|
+
: `${returnType.getTypeReference(context.scope)}`,
|
|
379
|
+
};
|
|
380
|
+
const opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${opImpl.returnTypeName} ${opImpl.methodName}( ${opImpl.methodParams});`);
|
|
511
381
|
mock.methods.push(opImpl);
|
|
512
382
|
builder.push(code `${opDecl.value}\n`);
|
|
513
383
|
this.emitter.emitInterfaceOperation(operation);
|
|
@@ -529,9 +399,8 @@ export async function $onEmit(context) {
|
|
|
529
399
|
const doc = getDoc(this.emitter.getProgram(), operation);
|
|
530
400
|
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
531
401
|
const multipart = this.#isMultipartRequest(httpOperation);
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
: this.#emitHttpOperationParameters(httpOperation, true);
|
|
402
|
+
const parameters = this.#opHelpers.getParameters(this.emitter.getProgram(), httpOperation);
|
|
403
|
+
const declParams = getHttpDeclParameters(parameters);
|
|
535
404
|
if (multipart) {
|
|
536
405
|
const context = this.emitter.getContext();
|
|
537
406
|
context.file.imports.set("Microsoft.AspNetCore.WebUtilities", [
|
|
@@ -561,9 +430,9 @@ export async function $onEmit(context) {
|
|
|
561
430
|
public virtual async Task<IActionResult> ${operationName}(${declParams})
|
|
562
431
|
{
|
|
563
432
|
${hasResponseValue
|
|
564
|
-
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
433
|
+
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
565
434
|
return ${resultString}(result);`
|
|
566
|
-
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
435
|
+
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
567
436
|
return ${resultString}();`}
|
|
568
437
|
}`);
|
|
569
438
|
}
|
|
@@ -585,9 +454,9 @@ export async function $onEmit(context) {
|
|
|
585
454
|
|
|
586
455
|
var reader = new MultipartReader(boundary, Request.Body);
|
|
587
456
|
${hasResponseValue
|
|
588
|
-
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
457
|
+
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
589
458
|
return ${resultString}(result);`
|
|
590
|
-
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
459
|
+
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
591
460
|
return ${resultString}();`}
|
|
592
461
|
}`);
|
|
593
462
|
}
|
|
@@ -603,7 +472,8 @@ export async function $onEmit(context) {
|
|
|
603
472
|
const operationName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
|
|
604
473
|
const doc = getDoc(this.emitter.getProgram(), operation);
|
|
605
474
|
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
606
|
-
const
|
|
475
|
+
const parameters = this.#opHelpers.getParameters(this.emitter.getProgram(), httpOperation);
|
|
476
|
+
const declParams = getHttpDeclParameters(parameters);
|
|
607
477
|
const responseInfo = this.#getOperationResponse(httpOperation);
|
|
608
478
|
const status = responseInfo?.statusCode ?? 200;
|
|
609
479
|
const response = responseInfo?.resultType ??
|
|
@@ -623,9 +493,9 @@ export async function $onEmit(context) {
|
|
|
623
493
|
public virtual async Task<IActionResult> ${operationName}(${declParams})
|
|
624
494
|
{
|
|
625
495
|
${hasResponseValue
|
|
626
|
-
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
496
|
+
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
627
497
|
return ${resultString}(result);`
|
|
628
|
-
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${
|
|
498
|
+
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${getBusinessLogicCallParameters(parameters)});
|
|
629
499
|
return ${resultString}();`}
|
|
630
500
|
}`);
|
|
631
501
|
}
|
|
@@ -683,68 +553,8 @@ export async function $onEmit(context) {
|
|
|
683
553
|
const resultType = result?.type || UnknownType;
|
|
684
554
|
return resultType.getTypeReference(context.scope);
|
|
685
555
|
}
|
|
686
|
-
#emitInterfaceOperationParameters(operation, bodyParam) {
|
|
687
|
-
const signature = new StringBuilder();
|
|
688
|
-
const requiredParams = [];
|
|
689
|
-
const optionalParams = [];
|
|
690
|
-
let totalParams = 0;
|
|
691
|
-
if (bodyParam !== undefined)
|
|
692
|
-
totalParams++;
|
|
693
|
-
const validParams = [...operation.parameters.properties.entries()].filter(([_, p]) => isValidParameter(this.emitter.getProgram(), p));
|
|
694
|
-
for (const [_, parameter] of validParams) {
|
|
695
|
-
if (!isContentTypeHeader(this.emitter.getProgram(), parameter) &&
|
|
696
|
-
(bodyParam === undefined || isHttpMetadata(this.emitter.getProgram(), parameter))) {
|
|
697
|
-
if (parameter.optional || parameter.defaultValue)
|
|
698
|
-
optionalParams.push(parameter);
|
|
699
|
-
else
|
|
700
|
-
requiredParams.push(parameter);
|
|
701
|
-
totalParams++;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
let i = 1;
|
|
705
|
-
for (const requiredParam of requiredParams) {
|
|
706
|
-
const [paramType, _, __] = this.#findPropertyType(requiredParam);
|
|
707
|
-
signature.push(code `${paramType} ${ensureCSharpIdentifier(this.emitter.getProgram(), requiredParam, requiredParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
708
|
-
}
|
|
709
|
-
if (bodyParam) {
|
|
710
|
-
signature.push(bodyParam);
|
|
711
|
-
if (i++ < totalParams)
|
|
712
|
-
signature.push(", ");
|
|
713
|
-
}
|
|
714
|
-
for (const optionalParam of optionalParams) {
|
|
715
|
-
const [paramType, _, __] = this.#findPropertyType(optionalParam);
|
|
716
|
-
signature.push(code `${paramType}? ${ensureCSharpIdentifier(this.emitter.getProgram(), optionalParam, optionalParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
717
|
-
}
|
|
718
|
-
return signature.reduce();
|
|
719
|
-
}
|
|
720
|
-
#emitHttpOperationParameters(operation, bodyParameter) {
|
|
721
|
-
const signature = new StringBuilder();
|
|
722
|
-
const bodyParam = operation.parameters.body;
|
|
723
|
-
let i = 0;
|
|
724
|
-
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
|
|
725
|
-
const validParams = operation.parameters.parameters.filter((p) => isValidParameter(this.emitter.getProgram(), p.param));
|
|
726
|
-
const requiredParams = validParams.filter((p) => p.type === "path" || (!p.param.optional && p.param.defaultValue === undefined));
|
|
727
|
-
const optionalParams = validParams.filter((p) => p.type !== "path" && (p.param.optional || p.param.defaultValue !== undefined));
|
|
728
|
-
for (const parameter of requiredParams) {
|
|
729
|
-
signature.push(code `${this.#emitOperationSignatureParameter(operation, parameter)}${++i < requiredParams.length ? ", " : ""}`);
|
|
730
|
-
}
|
|
731
|
-
if (requiredParams.length > 0 &&
|
|
732
|
-
(optionalParams.length > 0 || (bodyParameter === undefined && bodyParam !== undefined))) {
|
|
733
|
-
signature.push(code `, `);
|
|
734
|
-
}
|
|
735
|
-
if (bodyParameter === undefined) {
|
|
736
|
-
if (bodyParam !== undefined) {
|
|
737
|
-
signature.push(code `${this.emitter.emitTypeReference(this.#metaInfo.getEffectivePayloadType(bodyParam.type, Visibility.Create || Visibility.Update))} body${requiredParams.length > 0 && optionalParams.length > 0 ? ", " : ""}`);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
i = 0;
|
|
741
|
-
for (const parameter of optionalParams) {
|
|
742
|
-
signature.push(code `${this.#emitOperationSignatureParameter(operation, parameter)}${++i < optionalParams.length ? ", " : ""}`);
|
|
743
|
-
}
|
|
744
|
-
return signature.reduce();
|
|
745
|
-
}
|
|
746
556
|
unionDeclaration(union, name) {
|
|
747
|
-
const baseType =
|
|
557
|
+
const baseType = coalesceUnionTypes(this.emitter.getProgram(), union);
|
|
748
558
|
if (baseType.isBuiltIn && baseType.name === "string") {
|
|
749
559
|
const program = this.emitter.getProgram();
|
|
750
560
|
const unionName = ensureCSharpIdentifier(program, union, name);
|
|
@@ -770,7 +580,7 @@ export async function $onEmit(context) {
|
|
|
770
580
|
return this.emitter.result.rawCode(code `${baseType.getTypeReference()}`);
|
|
771
581
|
}
|
|
772
582
|
unionDeclarationContext(union) {
|
|
773
|
-
const baseType =
|
|
583
|
+
const baseType = coalesceUnionTypes(this.emitter.getProgram(), union);
|
|
774
584
|
if (baseType.isBuiltIn && baseType.name === "string") {
|
|
775
585
|
const unionName = ensureCSharpIdentifier(this.emitter.getProgram(), union, union.name || "Union");
|
|
776
586
|
const unionFile = this.emitter.createSourceFile(`models/${unionName}.cs`);
|
|
@@ -811,89 +621,6 @@ export async function $onEmit(context) {
|
|
|
811
621
|
unionVariantContext(union) {
|
|
812
622
|
return super.unionVariantContext(union);
|
|
813
623
|
}
|
|
814
|
-
#emitOperationSignatureParameter(operation, httpParam) {
|
|
815
|
-
const name = httpParam.param.name;
|
|
816
|
-
const parameter = httpParam.param;
|
|
817
|
-
const emittedName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, name, NameCasingType.Parameter);
|
|
818
|
-
let [emittedType, emittedDefault, _] = this.#findPropertyType(parameter);
|
|
819
|
-
if (emittedType.toString().endsWith("[]"))
|
|
820
|
-
emittedDefault = undefined;
|
|
821
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
822
|
-
const defaultValue = parameter.default
|
|
823
|
-
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
824
|
-
code `${this.emitter.emitType(parameter.default)}`
|
|
825
|
-
: emittedDefault;
|
|
826
|
-
return this.emitter.result.rawCode(code `${httpParam.type !== "path" ? this.#emitParameterAttribute(httpParam) : ""}${emittedType} ${emittedName}${defaultValue === undefined ? "" : ` = ${defaultValue}`}`);
|
|
827
|
-
}
|
|
828
|
-
#getBodyParameters(operation) {
|
|
829
|
-
const bodyParam = operation.parameters.body;
|
|
830
|
-
if (bodyParam === undefined)
|
|
831
|
-
return undefined;
|
|
832
|
-
if (bodyParam.property !== undefined)
|
|
833
|
-
return [bodyParam.property];
|
|
834
|
-
if (bodyParam.type.kind !== "Model" || bodyParam.type.properties.size < 1)
|
|
835
|
-
return undefined;
|
|
836
|
-
return [...bodyParam.type.properties.values()];
|
|
837
|
-
}
|
|
838
|
-
#emitOperationCallParameters(operation, bodyParameter = "body") {
|
|
839
|
-
const signature = new StringBuilder();
|
|
840
|
-
let i = 0;
|
|
841
|
-
const bodyParameters = this.#getBodyParameters(operation);
|
|
842
|
-
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
|
|
843
|
-
const valid = operation.parameters.parameters.filter((p) => isValidParameter(this.emitter.getProgram(), p.param));
|
|
844
|
-
const required = valid.filter((p) => p.type === "path" || (!p.param.optional && p.param.defaultValue === undefined));
|
|
845
|
-
const optional = valid.filter((p) => p.type !== "path" && (p.param.optional || p.param.defaultValue !== undefined));
|
|
846
|
-
for (const parameter of required) {
|
|
847
|
-
const contentType = isContentTypeHeader(this.emitter.getProgram(), parameter.param);
|
|
848
|
-
i++;
|
|
849
|
-
if (!isNeverType(parameter.param.type) &&
|
|
850
|
-
!isNullType(parameter.param.type) &&
|
|
851
|
-
!isVoidType(parameter.param.type) &&
|
|
852
|
-
!contentType) {
|
|
853
|
-
signature.push(code `${this.#emitOperationCallParameter(operation, parameter)}${i < valid.length || bodyParameters !== undefined ? ", " : ""}`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
if (bodyParameters !== undefined) {
|
|
857
|
-
if (bodyParameters.length === 1) {
|
|
858
|
-
signature.push(code `${bodyParameter}`);
|
|
859
|
-
}
|
|
860
|
-
else {
|
|
861
|
-
let j = 0;
|
|
862
|
-
for (const parameter of bodyParameters) {
|
|
863
|
-
j++;
|
|
864
|
-
const propertyName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, parameter.name, NameCasingType.Property);
|
|
865
|
-
signature.push(code `${bodyParameter}?.${propertyName}${j < bodyParameters.length || i < valid.length ? ", " : ""}`);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
for (const parameter of optional) {
|
|
870
|
-
const contentType = isContentTypeHeader(this.emitter.getProgram(), parameter.param);
|
|
871
|
-
i++;
|
|
872
|
-
if (!isNeverType(parameter.param.type) &&
|
|
873
|
-
!isNullType(parameter.param.type) &&
|
|
874
|
-
!isVoidType(parameter.param.type) &&
|
|
875
|
-
!contentType) {
|
|
876
|
-
signature.push(code `${this.#emitOperationCallParameter(operation, parameter)}${i < valid.length ? ", " : ""}`);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
return signature.reduce();
|
|
880
|
-
}
|
|
881
|
-
#emitOperationCallParameter(operation, httpParam) {
|
|
882
|
-
const name = httpParam.param.name;
|
|
883
|
-
const parameter = httpParam.param;
|
|
884
|
-
const emittedName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, name, NameCasingType.Parameter);
|
|
885
|
-
return this.emitter.result.rawCode(code `${emittedName}`);
|
|
886
|
-
}
|
|
887
|
-
#emitParameterAttribute(parameter) {
|
|
888
|
-
switch (parameter.type) {
|
|
889
|
-
case "header":
|
|
890
|
-
return code `[FromHeader(Name="${parameter.name}")] `;
|
|
891
|
-
case "query":
|
|
892
|
-
return code `[FromQuery(Name="${parameter.name}")] `;
|
|
893
|
-
default:
|
|
894
|
-
return "";
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
624
|
#createModelContext(namespace, file, name) {
|
|
898
625
|
const context = {
|
|
899
626
|
namespace: namespace,
|
|
@@ -906,9 +633,26 @@ export async function $onEmit(context) {
|
|
|
906
633
|
context.file.imports.set("System.Text.Json.Serialization", [
|
|
907
634
|
"System.Text.Json.Serialization",
|
|
908
635
|
]);
|
|
636
|
+
context.file.imports.set("TypeSpec.Helpers.JsonConverters", [
|
|
637
|
+
"TypeSpec.Helpers.JsonConverters",
|
|
638
|
+
]);
|
|
639
|
+
return context;
|
|
640
|
+
}
|
|
641
|
+
#createEnumContext(namespace, file, name) {
|
|
642
|
+
const context = {
|
|
643
|
+
namespace: namespace,
|
|
644
|
+
name: name,
|
|
645
|
+
file: file,
|
|
646
|
+
scope: file.globalScope,
|
|
647
|
+
};
|
|
648
|
+
context.file.imports.set("System.Text.Json", ["System.Text.Json"]);
|
|
649
|
+
context.file.imports.set("System.Text.Json.Serialization", [
|
|
650
|
+
"System.Text.Json.Serialization",
|
|
651
|
+
]);
|
|
909
652
|
return context;
|
|
910
653
|
}
|
|
911
654
|
#createOrGetResourceContext(name, operation, resource) {
|
|
655
|
+
name = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Class);
|
|
912
656
|
let context = controllers.get(name);
|
|
913
657
|
if (context !== undefined)
|
|
914
658
|
return context;
|
|
@@ -962,12 +706,6 @@ export async function $onEmit(context) {
|
|
|
962
706
|
if (sourceFile === libFile.source)
|
|
963
707
|
return libFile.emitted;
|
|
964
708
|
}
|
|
965
|
-
if (this.#emitMocks === "all") {
|
|
966
|
-
for (const helper of this.#mockHelpers) {
|
|
967
|
-
if (sourceFile === helper.source)
|
|
968
|
-
return helper.emitted;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
709
|
if (this.#mockFiles.length > 0) {
|
|
972
710
|
for (const mock of this.#mockFiles) {
|
|
973
711
|
if (sourceFile === mock.source)
|
|
@@ -998,7 +736,7 @@ export async function $onEmit(context) {
|
|
|
998
736
|
#emitControllerContents(file) {
|
|
999
737
|
const namespace = file.meta.namespace;
|
|
1000
738
|
const contents = new StringBuilder();
|
|
1001
|
-
contents.push(`${this.#
|
|
739
|
+
contents.push(`${this.#generatedFileHeaderWithNullable}\n\n`);
|
|
1002
740
|
contents.push(code `${this.#emitUsings(file)}\n`);
|
|
1003
741
|
contents.push("\n");
|
|
1004
742
|
contents.push(`namespace ${namespace}.Controllers\n`);
|
|
@@ -1063,88 +801,13 @@ export async function $onEmit(context) {
|
|
|
1063
801
|
return params.reduce();
|
|
1064
802
|
return "";
|
|
1065
803
|
}
|
|
1066
|
-
#coalesceUnionTypes(union) {
|
|
1067
|
-
const [result, _] = this.#coalesceTypes([...union.variants.values()].flatMap((v) => v.type));
|
|
1068
|
-
return result;
|
|
1069
|
-
}
|
|
1070
|
-
#getNonNullableTsType(union) {
|
|
1071
|
-
const types = [...union.variants.values()];
|
|
1072
|
-
const nulls = types.flatMap((v) => v.type).filter((t) => isNullType(t));
|
|
1073
|
-
const nonNulls = types.flatMap((v) => v.type).filter((t) => !isNullType(t));
|
|
1074
|
-
if (nonNulls.length === 1)
|
|
1075
|
-
return { type: nonNulls[0], nullable: nulls.length > 0 };
|
|
1076
|
-
return undefined;
|
|
1077
|
-
}
|
|
1078
|
-
#coalesceTypes(types) {
|
|
1079
|
-
const defaultValue = [
|
|
1080
|
-
new CSharpType({
|
|
1081
|
-
name: "object",
|
|
1082
|
-
namespace: "System",
|
|
1083
|
-
isValueType: false,
|
|
1084
|
-
}),
|
|
1085
|
-
true,
|
|
1086
|
-
];
|
|
1087
|
-
let current = undefined;
|
|
1088
|
-
let nullable = false;
|
|
1089
|
-
for (const type of types) {
|
|
1090
|
-
let candidate = undefined;
|
|
1091
|
-
switch (type.kind) {
|
|
1092
|
-
case "Boolean":
|
|
1093
|
-
candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
|
|
1094
|
-
break;
|
|
1095
|
-
case "StringTemplate":
|
|
1096
|
-
case "String":
|
|
1097
|
-
candidate = new CSharpType({ name: "string", namespace: "System", isValueType: false });
|
|
1098
|
-
break;
|
|
1099
|
-
case "Number":
|
|
1100
|
-
const stringValue = type.valueAsString;
|
|
1101
|
-
if (stringValue.includes(".") || stringValue.includes("e")) {
|
|
1102
|
-
candidate = new CSharpType({
|
|
1103
|
-
name: "double",
|
|
1104
|
-
namespace: "System",
|
|
1105
|
-
isValueType: true,
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
else {
|
|
1109
|
-
candidate = new CSharpType({ name: "int", namespace: "System", isValueType: true });
|
|
1110
|
-
}
|
|
1111
|
-
break;
|
|
1112
|
-
case "Union":
|
|
1113
|
-
candidate = this.#coalesceUnionTypes(type);
|
|
1114
|
-
break;
|
|
1115
|
-
case "Scalar":
|
|
1116
|
-
candidate = getCSharpTypeForScalar(this.emitter.getProgram(), type);
|
|
1117
|
-
break;
|
|
1118
|
-
case "Intrinsic":
|
|
1119
|
-
if (isNullType(type)) {
|
|
1120
|
-
nullable = true;
|
|
1121
|
-
candidate = current;
|
|
1122
|
-
}
|
|
1123
|
-
else {
|
|
1124
|
-
return defaultValue;
|
|
1125
|
-
}
|
|
1126
|
-
break;
|
|
1127
|
-
default:
|
|
1128
|
-
return defaultValue;
|
|
1129
|
-
}
|
|
1130
|
-
current = current ?? candidate;
|
|
1131
|
-
if (current === undefined || (candidate !== undefined && !candidate.equals(current)))
|
|
1132
|
-
return defaultValue;
|
|
1133
|
-
}
|
|
1134
|
-
if (current !== undefined && nullable)
|
|
1135
|
-
current.isNullable = true;
|
|
1136
|
-
return current === undefined ? defaultValue : [current, false];
|
|
1137
|
-
}
|
|
1138
804
|
writeOutput(sourceFiles) {
|
|
1139
805
|
for (const source of this.#libraryFiles) {
|
|
1140
806
|
sourceFiles.push(source.source);
|
|
1141
807
|
}
|
|
1142
808
|
if (this.#emitMocks === "all") {
|
|
1143
|
-
for (const helper of this.#mockHelpers) {
|
|
1144
|
-
sourceFiles.push(helper.source);
|
|
1145
|
-
}
|
|
1146
809
|
if (this.#mockRegistrations.size > 0) {
|
|
1147
|
-
const mocks = getBusinessLogicImplementations(this.emitter, this.#mockRegistrations);
|
|
810
|
+
const mocks = getBusinessLogicImplementations(this.emitter, this.#mockRegistrations, this.#useSwagger, this.#openapiPath);
|
|
1148
811
|
this.#mockFiles.push(...mocks);
|
|
1149
812
|
sourceFiles.push(...mocks.flatMap((l) => l.source));
|
|
1150
813
|
}
|
|
@@ -1185,26 +848,6 @@ export async function $onEmit(context) {
|
|
|
1185
848
|
return this.#baseNamespace;
|
|
1186
849
|
}
|
|
1187
850
|
}
|
|
1188
|
-
function isEmptyResponseModel(program, model) {
|
|
1189
|
-
if (model.kind !== "Model")
|
|
1190
|
-
return false;
|
|
1191
|
-
return model.properties.size === 1 && isStatusCode(program, [...model.properties.values()][0]);
|
|
1192
|
-
}
|
|
1193
|
-
function isContentTypeHeader(program, parameter) {
|
|
1194
|
-
return (isHeader(program, parameter) &&
|
|
1195
|
-
(parameter.name === "contentType" ||
|
|
1196
|
-
getHeaderFieldName(program, parameter) === "Content-type"));
|
|
1197
|
-
}
|
|
1198
|
-
function isValidParameter(program, parameter) {
|
|
1199
|
-
return (!isContentTypeHeader(program, parameter) &&
|
|
1200
|
-
(parameter.type.kind !== "Intrinsic" || parameter.type.name !== "never"));
|
|
1201
|
-
}
|
|
1202
|
-
/** Determine whether the given parameter is http metadata */
|
|
1203
|
-
function isHttpMetadata(program, property) {
|
|
1204
|
-
return (isPathParam(program, property) ||
|
|
1205
|
-
isHeader(program, property) ||
|
|
1206
|
-
isQueryParam(program, property));
|
|
1207
|
-
}
|
|
1208
851
|
function processNameSpace(program, target, service) {
|
|
1209
852
|
if (!service)
|
|
1210
853
|
service = getService(program, target);
|