@typespec/http-server-csharp 0.58.0-alpha.6-dev.1 → 0.58.0-alpha.6-dev.3
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/attributes.d.ts +1 -1
- package/dist/src/attributes.d.ts.map +1 -1
- package/dist/src/attributes.js +48 -4
- package/dist/src/attributes.js.map +1 -1
- package/dist/src/interfaces.d.ts +2 -0
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/interfaces.js +2 -0
- package/dist/src/interfaces.js.map +1 -1
- package/dist/src/lib.d.ts +19 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +12 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/service.d.ts.map +1 -1
- package/dist/src/service.js +290 -44
- package/dist/src/service.js.map +1 -1
- package/dist/src/utils.d.ts +3 -3
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +65 -48
- package/dist/src/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/src/service.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getDoc, getNamespaceFullName, getService, isErrorModel, isNeverType, isNullType, isVoidType, } from "@typespec/compiler";
|
|
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
|
+
import { createRekeyableMap } from "@typespec/compiler/utils";
|
|
3
4
|
import { Visibility, createMetadataInfo, getHttpOperation, isStatusCode, } from "@typespec/http";
|
|
4
5
|
import { getResourceOperation } from "@typespec/rest";
|
|
5
6
|
import { execFile } from "child_process";
|
|
@@ -7,7 +8,7 @@ import { getSerializationSourceFiles } from "./boilerplate.js";
|
|
|
7
8
|
import { CSharpSourceType, CSharpType, NameCasingType, } from "./interfaces.js";
|
|
8
9
|
import { reportDiagnostic } from "./lib.js";
|
|
9
10
|
import { getRecordType, isKnownReferenceType } from "./type-helpers.js";
|
|
10
|
-
import { HttpMetadata, UnknownType, coalesceTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForScalar, getModelAttributes, getModelInstantiationName, getOperationVerbDecorator, isValueType, } from "./utils.js";
|
|
11
|
+
import { HttpMetadata, UnknownType, coalesceTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForIntrinsic, getCSharpTypeForScalar, getModelAttributes, getModelInstantiationName, getOperationVerbDecorator, isValueType, } from "./utils.js";
|
|
11
12
|
export async function $onEmit(context) {
|
|
12
13
|
let _unionCounter = 0;
|
|
13
14
|
const controllers = new Map();
|
|
@@ -40,8 +41,17 @@ export async function $onEmit(context) {
|
|
|
40
41
|
declarationName(declarationType) {
|
|
41
42
|
switch (declarationType.kind) {
|
|
42
43
|
case "Enum":
|
|
44
|
+
if (!declarationType.name)
|
|
45
|
+
return `Enum${_unionCounter++}`;
|
|
46
|
+
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
43
47
|
case "Interface":
|
|
48
|
+
if (!declarationType.name)
|
|
49
|
+
return `Interface${_unionCounter++}`;
|
|
50
|
+
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
44
51
|
case "Model":
|
|
52
|
+
if (!declarationType.name)
|
|
53
|
+
return `Model${_unionCounter++}`;
|
|
54
|
+
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
45
55
|
case "Operation":
|
|
46
56
|
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
47
57
|
case "Union":
|
|
@@ -108,13 +118,15 @@ export async function $onEmit(context) {
|
|
|
108
118
|
return this.emitter.result.rawCode(code `System.Text.Json.Nodes.JsonNode`);
|
|
109
119
|
case "ErrorType":
|
|
110
120
|
case "never":
|
|
111
|
-
case "void":
|
|
112
121
|
reportDiagnostic(this.emitter.getProgram(), {
|
|
113
122
|
code: "invalid-intrinsic",
|
|
114
123
|
target: intrinsic,
|
|
115
124
|
format: { typeName: intrinsic.name },
|
|
116
125
|
});
|
|
117
126
|
return "";
|
|
127
|
+
case "void":
|
|
128
|
+
const type = getCSharpTypeForIntrinsic(this.emitter.getProgram(), intrinsic);
|
|
129
|
+
return this.emitter.result.rawCode(`${type?.type.getTypeReference()}`);
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
#emitUsings(file) {
|
|
@@ -150,14 +162,14 @@ export async function $onEmit(context) {
|
|
|
150
162
|
const modelFile = this.emitter.createSourceFile(`models/${modelName}.cs`);
|
|
151
163
|
modelFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
152
164
|
const modelNamespace = `${this.#getOrSetBaseNamespace(model)}.Models`;
|
|
153
|
-
return this.#createModelContext(modelNamespace, modelFile);
|
|
165
|
+
return this.#createModelContext(modelNamespace, modelFile, modelName);
|
|
154
166
|
}
|
|
155
167
|
modelInstantiationContext(model) {
|
|
156
168
|
const modelName = getModelInstantiationName(this.emitter.getProgram(), model, model.name);
|
|
157
169
|
const sourceFile = this.emitter.createSourceFile(`models/${modelName}.cs`);
|
|
158
170
|
sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
159
171
|
const modelNamespace = `${this.#getOrSetBaseNamespace(model)}.Models`;
|
|
160
|
-
const context = this.#createModelContext(modelNamespace, sourceFile);
|
|
172
|
+
const context = this.#createModelContext(modelNamespace, sourceFile, model.name);
|
|
161
173
|
context.instantiationName = modelName;
|
|
162
174
|
return context;
|
|
163
175
|
}
|
|
@@ -177,48 +189,178 @@ export async function $onEmit(context) {
|
|
|
177
189
|
if (!isVoidType(prop.type) &&
|
|
178
190
|
!isNeverType(prop.type) &&
|
|
179
191
|
!isNullType(prop.type) &&
|
|
180
|
-
!isErrorModel(this.emitter.getProgram(), prop.type))
|
|
192
|
+
!isErrorModel(this.emitter.getProgram(), prop.type)) {
|
|
181
193
|
result.push(code `${this.emitter.emitModelProperty(prop)}`);
|
|
194
|
+
}
|
|
182
195
|
}
|
|
183
196
|
return result.reduce();
|
|
184
197
|
}
|
|
198
|
+
modelLiteralContext(model) {
|
|
199
|
+
const name = this.emitter.emitDeclarationName(model) || "";
|
|
200
|
+
return this.modelDeclarationContext(model, name);
|
|
201
|
+
}
|
|
202
|
+
modelLiteral(model) {
|
|
203
|
+
const modelName = this.emitter.getContext().name;
|
|
204
|
+
reportDiagnostic(this.emitter.getProgram(), {
|
|
205
|
+
code: "anonymous-model",
|
|
206
|
+
target: model,
|
|
207
|
+
format: { emittedName: modelName },
|
|
208
|
+
});
|
|
209
|
+
return this.modelDeclaration(model, modelName);
|
|
210
|
+
}
|
|
185
211
|
#isRecord(type) {
|
|
186
212
|
return type.kind === "Model" && type.name === "Record" && type.indexer !== undefined;
|
|
187
213
|
}
|
|
214
|
+
#isInheritedProperty(property) {
|
|
215
|
+
const visited = [];
|
|
216
|
+
function isInherited(model, propertyName) {
|
|
217
|
+
if (visited.includes(model))
|
|
218
|
+
return false;
|
|
219
|
+
visited.push(model);
|
|
220
|
+
if (model.properties.has(propertyName))
|
|
221
|
+
return true;
|
|
222
|
+
if (model.baseModel === undefined)
|
|
223
|
+
return false;
|
|
224
|
+
return isInherited(model.baseModel, propertyName);
|
|
225
|
+
}
|
|
226
|
+
const model = property.model;
|
|
227
|
+
if (model === undefined || model.baseModel === undefined)
|
|
228
|
+
return false;
|
|
229
|
+
return isInherited(model.baseModel, property.name);
|
|
230
|
+
}
|
|
188
231
|
modelPropertyLiteral(property) {
|
|
189
232
|
if (isStatusCode(this.emitter.getProgram(), property))
|
|
190
233
|
return "";
|
|
191
234
|
const propertyName = ensureCSharpIdentifier(this.emitter.getProgram(), property, property.name);
|
|
192
|
-
const [typeName, typeDefault] = this.#findPropertyType(property);
|
|
235
|
+
const [typeName, typeDefault, nullable] = this.#findPropertyType(property);
|
|
193
236
|
const doc = getDoc(this.emitter.getProgram(), property);
|
|
194
|
-
const attributes = getModelAttributes(this.emitter.getProgram(), property);
|
|
237
|
+
const attributes = getModelAttributes(this.emitter.getProgram(), property, propertyName);
|
|
195
238
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
196
239
|
const defaultValue = property.default
|
|
197
240
|
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
198
241
|
code `${this.emitter.emitType(property.default)}`
|
|
199
242
|
: typeDefault;
|
|
200
243
|
return this.emitter.result
|
|
201
|
-
.rawCode(code `${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public ${
|
|
244
|
+
.rawCode(code `${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public ${this.#isInheritedProperty(property) ? "new " : ""}${typeName}${isValueType(this.emitter.getProgram(), property.type) && (property.optional || nullable)
|
|
245
|
+
? "?"
|
|
246
|
+
: ""} ${propertyName} { get; ${typeDefault ? "}" : "set; }"}${defaultValue ? ` = ${defaultValue};\n` : "\n"}
|
|
202
247
|
`);
|
|
203
248
|
}
|
|
204
249
|
#findPropertyType(property) {
|
|
205
|
-
|
|
250
|
+
return this.#getTypeInfoForTsType(property.type);
|
|
251
|
+
}
|
|
252
|
+
#getTypeInfoForUnion(union) {
|
|
253
|
+
const propResult = this.#getNonNullableTsType(union);
|
|
254
|
+
if (propResult === undefined) {
|
|
255
|
+
return [
|
|
256
|
+
code `${emitter.emitTypeReference(union)}`,
|
|
257
|
+
undefined,
|
|
258
|
+
[...union.variants.values()].filter((v) => isNullType(v.type)).length > 0,
|
|
259
|
+
];
|
|
260
|
+
}
|
|
261
|
+
const [typeName, typeDefault, _] = this.#getTypeInfoForTsType(propResult.type);
|
|
262
|
+
return [typeName, typeDefault, propResult.nullable];
|
|
263
|
+
}
|
|
264
|
+
#getTypeInfoForTsType(tsType) {
|
|
265
|
+
function extractStringValue(type, span) {
|
|
266
|
+
switch (type.kind) {
|
|
267
|
+
case "String":
|
|
268
|
+
return type.value;
|
|
269
|
+
case "Boolean":
|
|
270
|
+
return `${type.value}`;
|
|
271
|
+
case "Number":
|
|
272
|
+
return type.valueAsString;
|
|
273
|
+
case "StringTemplateSpan":
|
|
274
|
+
if (type.isInterpolated) {
|
|
275
|
+
return extractStringValue(type.type, span);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
return type.type.value;
|
|
279
|
+
}
|
|
280
|
+
case "ModelProperty":
|
|
281
|
+
return extractStringValue(type.type, span);
|
|
282
|
+
case "EnumMember":
|
|
283
|
+
if (type.value === undefined)
|
|
284
|
+
return type.name;
|
|
285
|
+
if (typeof type.value === "string")
|
|
286
|
+
return type.value;
|
|
287
|
+
if (typeof type.value === "number")
|
|
288
|
+
return `${type.value}`;
|
|
289
|
+
}
|
|
290
|
+
reportDiagnostic(emitter.getProgram(), {
|
|
291
|
+
code: "invalid-interpolation",
|
|
292
|
+
target: span,
|
|
293
|
+
format: {},
|
|
294
|
+
});
|
|
295
|
+
return "";
|
|
296
|
+
}
|
|
297
|
+
switch (tsType.kind) {
|
|
206
298
|
case "String":
|
|
207
|
-
return [code `string`, `"${
|
|
299
|
+
return [code `string`, `"${tsType.value}"`, false];
|
|
300
|
+
case "StringTemplate":
|
|
301
|
+
const template = tsType;
|
|
302
|
+
if (template.stringValue !== undefined)
|
|
303
|
+
return [code `string`, `"${template.stringValue}"`, false];
|
|
304
|
+
const spanResults = [];
|
|
305
|
+
for (const span of template.spans) {
|
|
306
|
+
spanResults.push(extractStringValue(span, span));
|
|
307
|
+
}
|
|
308
|
+
return [code `string`, `"${spanResults.join("")}"`, false];
|
|
208
309
|
case "Boolean":
|
|
209
|
-
return [code `bool`, `${
|
|
310
|
+
return [code `bool`, `${tsType.value === true ? true : false}`, false];
|
|
210
311
|
case "Number":
|
|
312
|
+
const [type, value] = this.#findNumericType(tsType);
|
|
313
|
+
return [code `${type}`, `${value}`, false];
|
|
314
|
+
case "Tuple":
|
|
315
|
+
const defaults = [];
|
|
316
|
+
const [csharpType, isObject] = this.#coalesceTypes(tsType.values);
|
|
317
|
+
if (isObject)
|
|
318
|
+
return ["object[]", undefined, false];
|
|
319
|
+
for (const value of tsType.values) {
|
|
320
|
+
const [_, itemDefault] = this.#getTypeInfoForTsType(value);
|
|
321
|
+
defaults.push(itemDefault);
|
|
322
|
+
}
|
|
323
|
+
return [
|
|
324
|
+
code `${csharpType.getTypeReference()}[]`,
|
|
325
|
+
`[${defaults.join(", ")}]`,
|
|
326
|
+
csharpType.isNullable,
|
|
327
|
+
];
|
|
211
328
|
case "Object":
|
|
212
|
-
return [code `object`, undefined];
|
|
329
|
+
return [code `object`, undefined, false];
|
|
213
330
|
case "Model":
|
|
214
|
-
if (this.#isRecord(
|
|
215
|
-
return [code `JsonObject`, undefined];
|
|
331
|
+
if (this.#isRecord(tsType)) {
|
|
332
|
+
return [code `JsonObject`, undefined, false];
|
|
333
|
+
}
|
|
334
|
+
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
335
|
+
case "ModelProperty":
|
|
336
|
+
return this.#getTypeInfoForTsType(tsType.type);
|
|
337
|
+
case "Enum":
|
|
338
|
+
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
339
|
+
case "EnumMember":
|
|
340
|
+
if (typeof tsType.value === "number") {
|
|
341
|
+
const stringValue = tsType.value.toString();
|
|
342
|
+
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
343
|
+
return ["double", stringValue, false];
|
|
344
|
+
return ["int", stringValue, false];
|
|
216
345
|
}
|
|
217
|
-
|
|
346
|
+
if (typeof tsType.value === "string") {
|
|
347
|
+
return ["string", tsType.value, false];
|
|
348
|
+
}
|
|
349
|
+
return [code `object`, undefined, false];
|
|
350
|
+
case "Union":
|
|
351
|
+
return this.#getTypeInfoForUnion(tsType);
|
|
352
|
+
case "UnionVariant":
|
|
353
|
+
return this.#getTypeInfoForTsType(tsType.type);
|
|
218
354
|
default:
|
|
219
|
-
return [code `${
|
|
355
|
+
return [code `${emitter.emitTypeReference(tsType)}`, undefined, false];
|
|
220
356
|
}
|
|
221
357
|
}
|
|
358
|
+
#findNumericType(type) {
|
|
359
|
+
const stringValue = type.valueAsString;
|
|
360
|
+
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
361
|
+
return ["double", stringValue];
|
|
362
|
+
return ["int", stringValue];
|
|
363
|
+
}
|
|
222
364
|
modelPropertyReference(property) {
|
|
223
365
|
return code `${this.emitter.emitTypeReference(property.type)}`;
|
|
224
366
|
}
|
|
@@ -252,7 +394,7 @@ export async function $onEmit(context) {
|
|
|
252
394
|
sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Interface;
|
|
253
395
|
const ifaceNamespace = this.#getOrSetBaseNamespace(iface);
|
|
254
396
|
const modelNamespace = `${ifaceNamespace}.Models`;
|
|
255
|
-
const context = this.#createModelContext(ifaceNamespace, sourceFile);
|
|
397
|
+
const context = this.#createModelContext(ifaceNamespace, sourceFile, ifaceName);
|
|
256
398
|
context.file.imports.set("System", ["System"]);
|
|
257
399
|
context.file.imports.set("System.Net", ["System.Net"]);
|
|
258
400
|
context.file.imports.set("System.Text.Json", ["System.Text.Json"]);
|
|
@@ -369,6 +511,9 @@ export async function $onEmit(context) {
|
|
|
369
511
|
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
370
512
|
return this.#emitOperationResponses(httpOperation);
|
|
371
513
|
}
|
|
514
|
+
stringTemplate(stringTemplate) {
|
|
515
|
+
return this.emitter.result.rawCode(stringTemplate.stringValue || "");
|
|
516
|
+
}
|
|
372
517
|
#getOperationResponse(operation) {
|
|
373
518
|
const validResponses = operation.responses.filter((r) => !isErrorModel(this.emitter.getProgram(), r.type) &&
|
|
374
519
|
getCSharpStatusCode(r.statusCodes) !== undefined);
|
|
@@ -397,7 +542,8 @@ export async function $onEmit(context) {
|
|
|
397
542
|
}
|
|
398
543
|
}
|
|
399
544
|
for (const response of operation.responses) {
|
|
400
|
-
this.emitter.
|
|
545
|
+
if (!isEmptyResponseModel(this.emitter.getProgram(), response.type))
|
|
546
|
+
this.emitter.emitType(response.type);
|
|
401
547
|
}
|
|
402
548
|
return builder.reduce();
|
|
403
549
|
}
|
|
@@ -425,10 +571,12 @@ export async function $onEmit(context) {
|
|
|
425
571
|
}
|
|
426
572
|
let i = 1;
|
|
427
573
|
for (const requiredParam of requiredParams) {
|
|
428
|
-
|
|
574
|
+
const [paramType, _, __] = this.#findPropertyType(requiredParam);
|
|
575
|
+
signature.push(code `${paramType} ${ensureCSharpIdentifier(this.emitter.getProgram(), requiredParam, requiredParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
429
576
|
}
|
|
430
577
|
for (const optionalParam of optionalParams) {
|
|
431
|
-
|
|
578
|
+
const [paramType, _, __] = this.#findPropertyType(optionalParam);
|
|
579
|
+
signature.push(code `${paramType}? ${ensureCSharpIdentifier(this.emitter.getProgram(), optionalParam, optionalParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
432
580
|
}
|
|
433
581
|
return signature.reduce();
|
|
434
582
|
}
|
|
@@ -521,7 +669,9 @@ export async function $onEmit(context) {
|
|
|
521
669
|
const name = httpParam.param.name;
|
|
522
670
|
const parameter = httpParam.param;
|
|
523
671
|
const emittedName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, name, NameCasingType.Parameter);
|
|
524
|
-
|
|
672
|
+
let [emittedType, emittedDefault, _] = this.#findPropertyType(parameter);
|
|
673
|
+
if (emittedType.toString().endsWith("[]"))
|
|
674
|
+
emittedDefault = undefined;
|
|
525
675
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
526
676
|
const defaultValue = parameter.default
|
|
527
677
|
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
@@ -529,21 +679,41 @@ export async function $onEmit(context) {
|
|
|
529
679
|
: emittedDefault;
|
|
530
680
|
return this.emitter.result.rawCode(code `${httpParam.type !== "path" ? this.#emitParameterAttribute(httpParam) : ""}${emittedType} ${emittedName}${defaultValue === undefined ? "" : ` = ${defaultValue}`}`);
|
|
531
681
|
}
|
|
682
|
+
#getBodyParameters(operation) {
|
|
683
|
+
const bodyParam = operation.parameters.body;
|
|
684
|
+
if (bodyParam === undefined)
|
|
685
|
+
return undefined;
|
|
686
|
+
if (bodyParam.property !== undefined)
|
|
687
|
+
return [bodyParam.property];
|
|
688
|
+
if (bodyParam.type.kind !== "Model" || bodyParam.type.properties.size < 1)
|
|
689
|
+
return undefined;
|
|
690
|
+
return [...bodyParam.type.properties.values()];
|
|
691
|
+
}
|
|
532
692
|
#emitOperationCallParameters(operation) {
|
|
533
693
|
const signature = new StringBuilder();
|
|
534
|
-
const bodyParam = operation.parameters.body;
|
|
535
694
|
let i = 0;
|
|
695
|
+
const bodyParameters = this.#getBodyParameters(operation);
|
|
536
696
|
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
|
|
537
697
|
for (const parameter of operation.parameters.parameters) {
|
|
538
698
|
i++;
|
|
539
699
|
if (!isNeverType(parameter.param.type) &&
|
|
540
700
|
!isNullType(parameter.param.type) &&
|
|
541
701
|
!isVoidType(parameter.param.type)) {
|
|
542
|
-
signature.push(code `${this.#emitOperationCallParameter(operation, parameter)}${i < operation.parameters.parameters.length ||
|
|
702
|
+
signature.push(code `${this.#emitOperationCallParameter(operation, parameter)}${i < operation.parameters.parameters.length || bodyParameters !== undefined ? ", " : ""}`);
|
|
543
703
|
}
|
|
544
704
|
}
|
|
545
|
-
if (
|
|
546
|
-
|
|
705
|
+
if (bodyParameters !== undefined) {
|
|
706
|
+
if (bodyParameters.length === 1) {
|
|
707
|
+
signature.push(code `body`);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
let j = 0;
|
|
711
|
+
for (const parameter of bodyParameters) {
|
|
712
|
+
j++;
|
|
713
|
+
const propertyName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, parameter.name, NameCasingType.Property);
|
|
714
|
+
signature.push(code `body?.${propertyName}${j < bodyParameters.length ? ", " : ""}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
547
717
|
}
|
|
548
718
|
return signature.reduce();
|
|
549
719
|
}
|
|
@@ -563,9 +733,10 @@ export async function $onEmit(context) {
|
|
|
563
733
|
return "";
|
|
564
734
|
}
|
|
565
735
|
}
|
|
566
|
-
#createModelContext(namespace, file) {
|
|
736
|
+
#createModelContext(namespace, file, name) {
|
|
567
737
|
const context = {
|
|
568
738
|
namespace: namespace,
|
|
739
|
+
name: name,
|
|
569
740
|
file: file,
|
|
570
741
|
scope: file.globalScope,
|
|
571
742
|
};
|
|
@@ -716,35 +887,76 @@ export async function $onEmit(context) {
|
|
|
716
887
|
return "";
|
|
717
888
|
}
|
|
718
889
|
#coalesceUnionTypes(union) {
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
890
|
+
const [result, _] = this.#coalesceTypes([...union.variants.values()].flatMap((v) => v.type));
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
#getNonNullableTsType(union) {
|
|
894
|
+
const types = [...union.variants.values()];
|
|
895
|
+
const nulls = types.flatMap((v) => v.type).filter((t) => isNullType(t));
|
|
896
|
+
const nonNulls = types.flatMap((v) => v.type).filter((t) => !isNullType(t));
|
|
897
|
+
if (nonNulls.length === 1)
|
|
898
|
+
return { type: nonNulls[0], nullable: nulls.length > 0 };
|
|
899
|
+
return undefined;
|
|
900
|
+
}
|
|
901
|
+
#coalesceTypes(types) {
|
|
902
|
+
const defaultValue = [
|
|
903
|
+
new CSharpType({
|
|
904
|
+
name: "object",
|
|
905
|
+
namespace: "System",
|
|
906
|
+
isValueType: false,
|
|
907
|
+
}),
|
|
908
|
+
true,
|
|
909
|
+
];
|
|
724
910
|
let current = undefined;
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
911
|
+
let nullable = false;
|
|
912
|
+
for (const type of types) {
|
|
913
|
+
let candidate = undefined;
|
|
914
|
+
switch (type.kind) {
|
|
728
915
|
case "Boolean":
|
|
729
916
|
candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
|
|
730
917
|
break;
|
|
918
|
+
case "StringTemplate":
|
|
731
919
|
case "String":
|
|
732
920
|
candidate = new CSharpType({ name: "string", namespace: "System", isValueType: false });
|
|
733
921
|
break;
|
|
922
|
+
case "Number":
|
|
923
|
+
const stringValue = type.valueAsString;
|
|
924
|
+
if (stringValue.includes(".") || stringValue.includes("e")) {
|
|
925
|
+
candidate = new CSharpType({
|
|
926
|
+
name: "double",
|
|
927
|
+
namespace: "System",
|
|
928
|
+
isValueType: true,
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
candidate = new CSharpType({ name: "int", namespace: "System", isValueType: true });
|
|
933
|
+
}
|
|
934
|
+
break;
|
|
734
935
|
case "Union":
|
|
735
|
-
candidate = this.#coalesceUnionTypes(
|
|
936
|
+
candidate = this.#coalesceUnionTypes(type);
|
|
736
937
|
break;
|
|
737
938
|
case "Scalar":
|
|
738
|
-
candidate = getCSharpTypeForScalar(this.emitter.getProgram(),
|
|
939
|
+
candidate = getCSharpTypeForScalar(this.emitter.getProgram(), type);
|
|
940
|
+
break;
|
|
941
|
+
case "Intrinsic":
|
|
942
|
+
if (isNullType(type)) {
|
|
943
|
+
nullable = true;
|
|
944
|
+
candidate = current;
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
return defaultValue;
|
|
948
|
+
}
|
|
739
949
|
break;
|
|
740
950
|
default:
|
|
741
951
|
return defaultValue;
|
|
742
952
|
}
|
|
743
953
|
current = current ?? candidate;
|
|
744
|
-
if (current === undefined || !candidate.equals(current))
|
|
954
|
+
if (current === undefined || (candidate !== undefined && !candidate.equals(current)))
|
|
745
955
|
return defaultValue;
|
|
746
956
|
}
|
|
747
|
-
|
|
957
|
+
if (current !== undefined && nullable)
|
|
958
|
+
current.isNullable = true;
|
|
959
|
+
return current === undefined ? defaultValue : [current, false];
|
|
748
960
|
}
|
|
749
961
|
writeOutput(sourceFiles) {
|
|
750
962
|
for (const source of this.#libraryFiles) {
|
|
@@ -786,11 +998,19 @@ export async function $onEmit(context) {
|
|
|
786
998
|
return this.#baseNamespace;
|
|
787
999
|
}
|
|
788
1000
|
}
|
|
789
|
-
function
|
|
790
|
-
|
|
1001
|
+
function isEmptyResponseModel(program, model) {
|
|
1002
|
+
if (model.kind !== "Model")
|
|
1003
|
+
return false;
|
|
1004
|
+
return model.properties.size === 1 && isStatusCode(program, [...model.properties.values()][0]);
|
|
1005
|
+
}
|
|
1006
|
+
function processNameSpace(program, target, service) {
|
|
1007
|
+
if (!service)
|
|
1008
|
+
service = getService(program, target);
|
|
791
1009
|
if (service) {
|
|
792
1010
|
for (const [_, model] of target.models) {
|
|
793
|
-
|
|
1011
|
+
if (!isTemplateDeclaration(model) && !isEmptyResponseModel(program, model)) {
|
|
1012
|
+
emitter.emitType(model);
|
|
1013
|
+
}
|
|
794
1014
|
}
|
|
795
1015
|
for (const [_, en] of target.enums) {
|
|
796
1016
|
emitter.emitType(en);
|
|
@@ -799,10 +1019,36 @@ export async function $onEmit(context) {
|
|
|
799
1019
|
emitter.emitType(sc);
|
|
800
1020
|
}
|
|
801
1021
|
for (const [_, iface] of target.interfaces) {
|
|
1022
|
+
if (!isTemplateDeclaration(iface)) {
|
|
1023
|
+
emitter.emitType(iface);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (target.operations.size > 0) {
|
|
1027
|
+
// Collect interface operations for a business logic interface and controller
|
|
1028
|
+
const nsOps = [];
|
|
1029
|
+
for (const [name, op] of target.operations) {
|
|
1030
|
+
if (!isTemplateDeclaration(op)) {
|
|
1031
|
+
nsOps.push([name, op]);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
const iface = program.checker.createAndFinishType({
|
|
1035
|
+
node: undefined,
|
|
1036
|
+
sourceInterfaces: [],
|
|
1037
|
+
decorators: [],
|
|
1038
|
+
operations: createRekeyableMap(nsOps),
|
|
1039
|
+
kind: "Interface",
|
|
1040
|
+
name: `${target.name}Operations`,
|
|
1041
|
+
namespace: target,
|
|
1042
|
+
entityKind: "Type",
|
|
1043
|
+
isFinished: true,
|
|
1044
|
+
});
|
|
1045
|
+
for (const [_, op] of nsOps) {
|
|
1046
|
+
op.interface = iface;
|
|
1047
|
+
}
|
|
802
1048
|
emitter.emitType(iface);
|
|
803
1049
|
}
|
|
804
|
-
for (const [_,
|
|
805
|
-
|
|
1050
|
+
for (const [_, sub] of target.namespaces) {
|
|
1051
|
+
processNameSpace(program, sub, service);
|
|
806
1052
|
}
|
|
807
1053
|
}
|
|
808
1054
|
else {
|