@typespec/http-server-csharp 0.58.0-alpha.8-dev.1 → 0.58.0-alpha.8-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.
Files changed (64) hide show
  1. package/cmd/hscs.js +2 -0
  2. package/dist/src/cli/cli.d.ts +2 -0
  3. package/dist/src/cli/cli.d.ts.map +1 -0
  4. package/dist/src/cli/cli.js +124 -0
  5. package/dist/src/cli/cli.js.map +1 -0
  6. package/dist/src/lib/attributes.d.ts.map +1 -0
  7. package/dist/src/lib/attributes.js.map +1 -0
  8. package/dist/src/{boilerplate.d.ts → lib/boilerplate.d.ts} +2 -0
  9. package/dist/src/lib/boilerplate.d.ts.map +1 -0
  10. package/dist/src/{boilerplate.js → lib/boilerplate.js} +101 -36
  11. package/dist/src/lib/boilerplate.js.map +1 -0
  12. package/dist/src/lib/index.d.ts.map +1 -0
  13. package/dist/src/lib/index.js.map +1 -0
  14. package/dist/src/{interfaces.d.ts → lib/interfaces.d.ts} +2 -0
  15. package/dist/src/lib/interfaces.d.ts.map +1 -0
  16. package/dist/src/{interfaces.js → lib/interfaces.js} +3 -1
  17. package/dist/src/lib/interfaces.js.map +1 -0
  18. package/dist/src/{lib.d.ts → lib/lib.d.ts} +6 -0
  19. package/dist/src/lib/lib.d.ts.map +1 -0
  20. package/dist/src/{lib.js → lib/lib.js} +19 -0
  21. package/dist/src/lib/lib.js.map +1 -0
  22. package/dist/src/lib/scaffolding.d.ts +20 -0
  23. package/dist/src/lib/scaffolding.d.ts.map +1 -0
  24. package/dist/src/lib/scaffolding.js +388 -0
  25. package/dist/src/lib/scaffolding.js.map +1 -0
  26. package/dist/src/lib/service.d.ts.map +1 -0
  27. package/dist/src/{service.js → lib/service.js} +78 -18
  28. package/dist/src/lib/service.js.map +1 -0
  29. package/dist/src/lib/testing/index.d.ts.map +1 -0
  30. package/dist/src/{testing → lib/testing}/index.js +1 -0
  31. package/dist/src/lib/testing/index.js.map +1 -0
  32. package/dist/src/lib/type-helpers.d.ts.map +1 -0
  33. package/dist/src/lib/type-helpers.js.map +1 -0
  34. package/dist/src/lib/utils.d.ts.map +1 -0
  35. package/dist/src/lib/utils.js.map +1 -0
  36. package/package.json +23 -14
  37. package/dist/src/attributes.d.ts.map +0 -1
  38. package/dist/src/attributes.js.map +0 -1
  39. package/dist/src/boilerplate.d.ts.map +0 -1
  40. package/dist/src/boilerplate.js.map +0 -1
  41. package/dist/src/index.d.ts.map +0 -1
  42. package/dist/src/index.js.map +0 -1
  43. package/dist/src/interfaces.d.ts.map +0 -1
  44. package/dist/src/interfaces.js.map +0 -1
  45. package/dist/src/lib.d.ts.map +0 -1
  46. package/dist/src/lib.js.map +0 -1
  47. package/dist/src/service.d.ts.map +0 -1
  48. package/dist/src/service.js.map +0 -1
  49. package/dist/src/testing/index.d.ts.map +0 -1
  50. package/dist/src/testing/index.js.map +0 -1
  51. package/dist/src/type-helpers.d.ts.map +0 -1
  52. package/dist/src/type-helpers.js.map +0 -1
  53. package/dist/src/utils.d.ts.map +0 -1
  54. package/dist/src/utils.js.map +0 -1
  55. /package/dist/src/{attributes.d.ts → lib/attributes.d.ts} +0 -0
  56. /package/dist/src/{attributes.js → lib/attributes.js} +0 -0
  57. /package/dist/src/{index.d.ts → lib/index.d.ts} +0 -0
  58. /package/dist/src/{index.js → lib/index.js} +0 -0
  59. /package/dist/src/{service.d.ts → lib/service.d.ts} +0 -0
  60. /package/dist/src/{testing → lib/testing}/index.d.ts +0 -0
  61. /package/dist/src/{type-helpers.d.ts → lib/type-helpers.d.ts} +0 -0
  62. /package/dist/src/{type-helpers.js → lib/type-helpers.js} +0 -0
  63. /package/dist/src/{utils.d.ts → lib/utils.d.ts} +0 -0
  64. /package/dist/src/{utils.js → lib/utils.js} +0 -0
@@ -0,0 +1,388 @@
1
+ import { code } from "@typespec/compiler/emitter-framework";
2
+ import { GeneratedFileHeaderWithNullable } from "./boilerplate.js";
3
+ import { LibrarySourceFile } from "./interfaces.js";
4
+ export function getScaffoldingHelpers(emitter, useSwagger, openApiPath) {
5
+ const sourceFiles = [];
6
+ sourceFiles.push(new LibrarySourceFile({
7
+ filename: "IInitializer.cs",
8
+ emitter: emitter,
9
+ getContents: getInitializerInterface,
10
+ }), new LibrarySourceFile({
11
+ filename: "Initializer.cs",
12
+ emitter: emitter,
13
+ getContents: getInitializerImplementation,
14
+ }), new LibrarySourceFile({
15
+ filename: "Program.cs",
16
+ emitter: emitter,
17
+ getContents: () => getProjectStartup(useSwagger, openApiPath),
18
+ path: "../",
19
+ }));
20
+ return sourceFiles;
21
+ }
22
+ export function getBusinessLogicImplementations(emitter, registrations) {
23
+ const sourceFiles = [];
24
+ const mocks = [];
25
+ for (const [name, impl] of registrations) {
26
+ sourceFiles.push(new LibrarySourceFile({
27
+ filename: `${impl.className}.cs`,
28
+ emitter: emitter,
29
+ getContents: () => getBusinessLogicImplementation(name, impl),
30
+ path: "../mocks/",
31
+ }));
32
+ mocks.push(impl);
33
+ }
34
+ if (mocks.length > 0) {
35
+ sourceFiles.push(new LibrarySourceFile({
36
+ filename: "MockRegistration.cs",
37
+ emitter: emitter,
38
+ getContents: () => getMockRegistration(mocks),
39
+ path: "../mocks/",
40
+ }));
41
+ }
42
+ return sourceFiles;
43
+ }
44
+ function getBusinessLogicImplementation(iface, mock) {
45
+ const methods = [];
46
+ for (const method of mock.methods) {
47
+ const methodCode = method.instantiatedReturnType !== undefined
48
+ ? `return Task.FromResult(_initializer.Initialize<${method.instantiatedReturnType}>());`
49
+ : "return Task.CompletedTask;";
50
+ methods.push(` public ${method.returnType} ${method.methodName}( ${method.methodParams})
51
+ {
52
+ ${methodCode}
53
+ }`);
54
+ }
55
+ return `${GeneratedFileHeaderWithNullable}
56
+
57
+ using System;
58
+ using System.Net;
59
+ using System.Text.Json;
60
+ using System.Text.Json.Serialization;
61
+ using System.Threading.Tasks;
62
+ using Microsoft.AspNetCore.Mvc;${mock.methods.some((m) => m.methodParams.includes("MultipartReader")) ? `\nusing Microsoft.AspNetCore.WebUtilities;` : ""}
63
+ using ${mock.namespace}.Models;
64
+ using TypeSpec.Helpers;
65
+
66
+ namespace ${mock.namespace}
67
+ {
68
+ /// <summary>
69
+ /// This is a mock implementation of the business logic interface for
70
+ /// demonstration and early development. Feel free to overwrite this file.
71
+ /// Or replace it with another implementation, and register that implementation
72
+ /// in the dependency injection container
73
+ /// </summary>
74
+ /// <param name="initializer">The initializer class, registered with dependency injection</param>
75
+ public class ${mock.className} : ${mock.interfaceName}
76
+ {
77
+ public ${mock.className}(IInitializer initializer)
78
+ {
79
+ _initializer = initializer;
80
+ }
81
+
82
+ private IInitializer _initializer;
83
+
84
+ ${methods.join("\n\n")}
85
+ }
86
+ }
87
+ `;
88
+ }
89
+ function getMockRegistration(mocks) {
90
+ if (mocks.length < 1)
91
+ return "";
92
+ const cache = new Map();
93
+ return `${GeneratedFileHeaderWithNullable}
94
+
95
+ using Microsoft.AspNetCore.Http.Features;
96
+ ${mocks
97
+ .flatMap((m) => m.usings)
98
+ .filter((t) => {
99
+ const result = !cache.has(t);
100
+ cache.set(t, t);
101
+ return result;
102
+ })
103
+ .flatMap((e) => `using ${e};`)
104
+ .join("\n")}
105
+ using ${mocks[0].namespace};
106
+
107
+ namespace TypeSpec.Helpers
108
+ {
109
+ /// <summary>
110
+ /// Register Business Logic implementations. Replace with actual implementations when available.
111
+ /// </summary>
112
+ public static class MockRegistration
113
+ {
114
+ public static void Register(WebApplicationBuilder builder)
115
+ {
116
+ builder.Services.AddScoped<IJsonSerializationProvider, JsonSerializationProvider>();
117
+ // Used for mock implementation only. Remove once business logic interfaces are implemented.
118
+ builder.Services.AddSingleton<IDictionary<Type, object?>>(new Dictionary<Type, object?>());
119
+ builder.Services.AddScoped<IInitializer, Initializer>();
120
+ // Mock business logic implementations
121
+ ${mocks.flatMap((m) => ` builder.Services.AddScoped<${m.interfaceName}, ${m.className}>();`).join("\n")}
122
+ // Included for multipart/form-data support
123
+ builder.Services.Configure<FormOptions>(options =>
124
+ {
125
+ options.MemoryBufferThreshold = int.MaxValue;
126
+ options.MultipartBodyLengthLimit = int.MaxValue;
127
+ });
128
+ }
129
+ }
130
+ }`;
131
+ }
132
+ function getProjectStartup(useSwagger, openApiPath) {
133
+ return `${GeneratedFileHeaderWithNullable}
134
+
135
+ using TypeSpec.Helpers;
136
+
137
+ var builder = WebApplication.CreateBuilder(args);
138
+
139
+ // Add services to the container.
140
+ builder.Services.AddControllersWithViews();
141
+ builder.Services.AddEndpointsApiExplorer();
142
+ ${useSwagger ? "builder.Services.AddSwaggerGen();" : ""}
143
+ MockRegistration.Register(builder);
144
+
145
+ var app = builder.Build();
146
+
147
+ // Configure the HTTP request pipeline.
148
+ if (!app.Environment.IsDevelopment())
149
+ {
150
+ app.UseExceptionHandler("/Home/Error");
151
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
152
+ app.UseHsts();
153
+ }
154
+ ${useSwagger
155
+ ? code `else
156
+ {
157
+ app.UseSwagger();
158
+ app.UseSwaggerUI( c=> {
159
+ c.DocumentTitle = "TypeSpec Generated OpenAPI Viewer";
160
+ c.SwaggerEndpoint("/openapi.yaml", "TypeSpec Generated OpenAPI Docs");
161
+ c.RoutePrefix = "swagger";
162
+ });
163
+ }\n`
164
+ : ""}
165
+
166
+ app.UseHttpsRedirection();
167
+ app.UseStaticFiles();
168
+ app.Use(async (context, next) =>
169
+ {
170
+ context.Request.EnableBuffering();
171
+ await next();
172
+ });
173
+ ${useSwagger
174
+ ? code `
175
+ app.MapGet("/openapi.yaml", async (HttpContext context) =>
176
+ {
177
+ var externalFilePath = "${openApiPath}"; // Full path to the file outside the project
178
+ if (!File.Exists(externalFilePath))
179
+ {
180
+ context.Response.StatusCode = StatusCodes.Status404NotFound;
181
+ await context.Response.WriteAsync("OpenAPI spec not found.");
182
+ return;
183
+ }
184
+ context.Response.ContentType = "application/json";
185
+ await context.Response.SendFileAsync(externalFilePath);
186
+ });\n`
187
+ : ""}
188
+
189
+ app.UseRouting();
190
+
191
+ app.UseAuthorization();
192
+
193
+
194
+ app.MapControllerRoute(
195
+ name: "default",
196
+ pattern: "{controller=Home}/{action=Index}/{id?}");
197
+
198
+
199
+ app.Run();`;
200
+ }
201
+ function getInitializerInterface() {
202
+ return `${GeneratedFileHeaderWithNullable}
203
+
204
+ namespace TypeSpec.Helpers
205
+ {
206
+ /// <summary>
207
+ /// Interface for object initialization in mocks
208
+ /// </summary>
209
+ public interface IInitializer
210
+ {
211
+ /// <summary>
212
+ /// Initialize an object fo the given type
213
+ /// </summary>
214
+ /// <param name="type"> The type to initialize</param>
215
+ /// <returns>An instance of the given type. Or null if initialization was impossible.</returns>
216
+ object? Initialize(System.Type type);
217
+
218
+ /// <summary>
219
+ /// Initialize an object of the given type
220
+ /// </summary>
221
+ /// <typeparam name="T">The type to initialize</typeparam>
222
+ /// <returns>An instance of the given type</returns>
223
+ T Initialize<T>() where T : class, new();
224
+ }
225
+ }
226
+ `;
227
+ }
228
+ function getInitializerImplementation() {
229
+ return `${GeneratedFileHeaderWithNullable}
230
+
231
+ namespace TypeSpec.Helpers
232
+ {
233
+ /// <summary>
234
+ /// Default initializer for mock implementations of business logic interfaces
235
+ /// </summary>
236
+ public class Initializer : IInitializer
237
+ {
238
+ /// <summary>
239
+ /// Instantiate the initializer. The cache *should* be instantiated using ASP.Net Core's dependency injection
240
+ /// </summary>
241
+ /// <param name="cache"></param>
242
+ public Initializer(IDictionary<Type, object?> cache)
243
+ {
244
+ Cache = cache;
245
+ }
246
+
247
+ internal virtual IDictionary<Type, object?> Cache { get; }
248
+
249
+ internal object? CacheAndReturn(Type type, object? instance)
250
+ {
251
+ Cache[type] = instance;
252
+ return instance;
253
+ }
254
+
255
+ /// <summary>
256
+ /// Initialize an object fo the given type
257
+ /// </summary>
258
+ /// <param name="type"> The type to initialize</param>
259
+ /// <returns>An instance of the given type. Or null if initialization was impossible.</returns>
260
+ public object? Initialize (Type type)
261
+ {
262
+ if (Cache.ContainsKey(type))
263
+ {
264
+ return Cache[type];
265
+ }
266
+ if (type == typeof(string))
267
+ {
268
+ return CacheAndReturn(type, string.Empty);
269
+ }
270
+ if (type == typeof(int))
271
+ {
272
+ return CacheAndReturn(type, 0);
273
+ }
274
+ if (type == typeof(long))
275
+ {
276
+ return CacheAndReturn(type, 0L);
277
+ }
278
+ if (type == typeof(float))
279
+ {
280
+ return CacheAndReturn(type, 0.0f);
281
+ }
282
+ if (type == typeof(double))
283
+ {
284
+ return CacheAndReturn(type, 0.0);
285
+ }
286
+ if (type == typeof(decimal))
287
+ {
288
+ return CacheAndReturn(type, 0.0m);
289
+ }
290
+ if (type == typeof(bool))
291
+ {
292
+ return CacheAndReturn(type, false);
293
+ }
294
+ if (type == typeof(byte))
295
+ {
296
+ return CacheAndReturn(type, (byte)0);
297
+ }
298
+ if (type == typeof(char))
299
+ {
300
+ return CacheAndReturn(type, (char)0);
301
+ }
302
+ if (type == typeof(short))
303
+ {
304
+ return CacheAndReturn(type, (short)0);
305
+ }
306
+ if (type == typeof(uint))
307
+ {
308
+ return CacheAndReturn(type, (uint)0);
309
+ }
310
+ if (type == typeof(ulong))
311
+ {
312
+ return CacheAndReturn(type, (ulong)0);
313
+ }
314
+ if (type == typeof(ushort))
315
+ {
316
+ return CacheAndReturn(type, (ushort)0);
317
+ }
318
+ if (type == typeof(sbyte))
319
+ {
320
+ return CacheAndReturn(type, (sbyte)0);
321
+ }
322
+ if (type == typeof(DateTime))
323
+ {
324
+ return CacheAndReturn(type, DateTime.UtcNow);
325
+ }
326
+ if (type == typeof(DateTimeOffset))
327
+ {
328
+ return CacheAndReturn(type, DateTimeOffset.UtcNow);
329
+ }
330
+ if ( type == typeof(TimeSpan))
331
+ {
332
+ return CacheAndReturn(type, TimeSpan.Zero);
333
+ }
334
+ if (type.IsArray)
335
+ {
336
+ var element = type.GetElementType();
337
+ if (element == null) return null;
338
+ return CacheAndReturn(type, Array.CreateInstance(element, 0));
339
+ }
340
+ if (type.IsClass)
341
+ {
342
+ return InitializeClass(type);
343
+ }
344
+ var genericType = Nullable.GetUnderlyingType(type);
345
+ if ( (genericType != null))
346
+ {
347
+ return Initialize(genericType);
348
+ }
349
+ return new object();
350
+ }
351
+
352
+ /// <summary>
353
+ /// Initialize an object of the given type
354
+ /// </summary>
355
+ /// <typeparam name="T">The type to initialize</typeparam>
356
+ /// <returns>An instance of the given type</returns>
357
+ public T Initialize<T>() where T: class, new()
358
+ {
359
+ var result = new T();
360
+ var initialized = InitializeClass(typeof(T), result);
361
+ return initialized as T ?? result;
362
+ }
363
+
364
+ private object? InitializeClass(Type type, object? instance = null)
365
+ {
366
+ if (Cache.ContainsKey(type))
367
+ {
368
+ instance = Cache[type];
369
+ return instance;
370
+ }
371
+
372
+ var result = instance == null ? Activator.CreateInstance(type) : instance;
373
+ foreach (var property in type.GetProperties())
374
+ {
375
+ if (property.CanWrite)
376
+ {
377
+ var propertyType = property.PropertyType;
378
+ property.SetValue(result, Initialize(propertyType));
379
+ }
380
+ }
381
+
382
+ return CacheAndReturn(type, result);
383
+ }
384
+ }
385
+ }
386
+ `;
387
+ }
388
+ //# sourceMappingURL=scaffolding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffolding.js","sourceRoot":"","sources":["../../../src/lib/scaffolding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/lib/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EA0BZ,MAAM,oBAAoB,CAAC;AA+C5B,OAAO,EAAE,2BAA2B,EAAoB,MAAM,UAAU,CAAC;AA2BzE,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,2BAA2B,CAAC,iBAgjD9E"}
@@ -4,9 +4,10 @@ import { createRekeyableMap } from "@typespec/compiler/utils";
4
4
  import { Visibility, createMetadataInfo, getHeaderFieldName, getHttpOperation, getHttpPart, isHeader, isPathParam, isQueryParam, isStatusCode, } from "@typespec/http";
5
5
  import { getResourceOperation } from "@typespec/rest";
6
6
  import { execFile } from "child_process";
7
- import { getSerializationSourceFiles } from "./boilerplate.js";
7
+ import { GeneratedFileHeader, GeneratedFileHeaderWithNullable, getSerializationSourceFiles, } from "./boilerplate.js";
8
8
  import { CSharpSourceType, CSharpType, NameCasingType, } from "./interfaces.js";
9
9
  import { reportDiagnostic } from "./lib.js";
10
+ import { getBusinessLogicImplementations, getScaffoldingHelpers, } from "./scaffolding.js";
10
11
  import { getRecordType, isKnownReferenceType } from "./type-helpers.js";
11
12
  import { HttpMetadata, UnknownType, coalesceTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForIntrinsic, getCSharpTypeForScalar, getModelAttributes, getModelInstantiationName, getOperationVerbDecorator, isValueType, } from "./utils.js";
12
13
  export async function $onEmit(context) {
@@ -16,12 +17,20 @@ export async function $onEmit(context) {
16
17
  const doNotEmit = context.program.compilerOptions.noEmit || false;
17
18
  class CSharpCodeEmitter extends CodeTypeEmitter {
18
19
  #metadateMap = new Map();
19
- #licenseHeader = `// Copyright (c) Microsoft Corporation. All rights reserved.
20
- // Licensed under the MIT License.`;
20
+ #generatedFileHeaderWithNullable = GeneratedFileHeaderWithNullable;
21
+ #generatedFileHeader = GeneratedFileHeader;
21
22
  #sourceTypeKey = "sourceType";
22
23
  #libraryFiles = getSerializationSourceFiles(this.emitter);
23
24
  #baseNamespace = undefined;
24
25
  #emitterOutputType = context.options["output-type"];
26
+ #emitMocks = context.options["emit-mocks"];
27
+ #useSwagger = context.options["use-swaggerui"] || false;
28
+ #openapiPath = context.options["openapi-path"] || "openapi/openapi.yaml";
29
+ #mockRegistrations = new Map();
30
+ #mockHelpers = this.#emitMocks === "all"
31
+ ? getScaffoldingHelpers(this.emitter, this.#useSwagger, this.#openapiPath)
32
+ : [];
33
+ #mockFiles = [];
25
34
  #metaInfo = createMetadataInfo(this.emitter.getProgram(), {
26
35
  canonicalVisibility: Visibility.Read,
27
36
  canShareProperty: (p) => true,
@@ -71,8 +80,7 @@ export async function $onEmit(context) {
71
80
  const doc = getDoc(this.emitter.getProgram(), en);
72
81
  const attributes = getModelAttributes(program, en, enumName);
73
82
  this.#metadateMap.set(en, new CSharpType({ name: enumName, namespace: namespace }));
74
- return this.emitter.result.declaration(enumName, code `${this.#licenseHeader}
75
- // <auto-generated />
83
+ return this.emitter.result.declaration(enumName, code `${this.#generatedFileHeader}
76
84
 
77
85
  ${this.#emitUsings()}
78
86
 
@@ -150,8 +158,7 @@ export async function $onEmit(context) {
150
158
  const doc = getDoc(this.emitter.getProgram(), model);
151
159
  const attributes = getModelAttributes(this.emitter.getProgram(), model, className);
152
160
  this.#metadateMap.set(model, new CSharpType({ name: className, namespace: namespace }));
153
- const decl = this.emitter.result.declaration(className, code `${this.#licenseHeader}
154
- // <auto-generated />
161
+ const decl = this.emitter.result.declaration(className, code `${this.#generatedFileHeader}
155
162
 
156
163
  ${this.#emitUsings()}
157
164
 
@@ -416,8 +423,7 @@ export async function $onEmit(context) {
416
423
  const doc = getDoc(this.emitter.getProgram(), iface);
417
424
  const attributes = getModelAttributes(this.emitter.getProgram(), iface, ifaceName);
418
425
  this.#metadateMap.set(iface, new CSharpType({ name: ifaceName, namespace: namespace }));
419
- const decl = this.emitter.result.declaration(ifaceName, code `${this.#licenseHeader}
420
- // <auto-generated />
426
+ const decl = this.emitter.result.declaration(ifaceName, code `${this.#generatedFileHeaderWithNullable}
421
427
 
422
428
  ${this.#emitUsings()}
423
429
 
@@ -458,6 +464,16 @@ export async function $onEmit(context) {
458
464
  const builder = new StringBuilder();
459
465
  const metadata = new HttpMetadata();
460
466
  const context = this.emitter.getContext();
467
+ const name = `${ensureCSharpIdentifier(this.emitter.getProgram(), iface, iface.name, NameCasingType.Class)}`;
468
+ const ifaceNamespace = this.#getOrSetBaseNamespace(iface);
469
+ const namespace = `${ifaceNamespace}`;
470
+ const mock = {
471
+ className: name,
472
+ interfaceName: `I${name}`,
473
+ methods: [],
474
+ namespace: namespace,
475
+ usings: [`${ifaceNamespace}.Models`],
476
+ };
461
477
  for (const [name, operation] of iface.operations) {
462
478
  const doc = getDoc(this.emitter.getProgram(), operation);
463
479
  const returnTypes = [];
@@ -469,15 +485,34 @@ export async function $onEmit(context) {
469
485
  const returnType = returnInfo?.type || UnknownType;
470
486
  const opName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
471
487
  let opDecl;
488
+ let opImpl;
472
489
  if (this.#isMultipartRequest(httpOp)) {
473
- opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`} ${opName}Async( ${this.#emitInterfaceOperationParameters(operation, "MultipartReader reader")});`);
490
+ opImpl = {
491
+ methodName: `${opName}Async`,
492
+ methodParams: `${this.#emitInterfaceOperationParameters(operation, "MultipartReader reader")}`,
493
+ returnType: `${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`}`,
494
+ instantiatedReturnType: returnType.name === "void"
495
+ ? undefined
496
+ : `${returnType.getTypeReference(context.scope)}`,
497
+ };
498
+ opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${opImpl.returnType} ${opImpl.methodName}( ${opImpl.methodParams});`);
474
499
  }
475
500
  else {
476
- opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`} ${opName}Async( ${this.#emitInterfaceOperationParameters(operation)});`);
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});`);
477
510
  }
511
+ mock.methods.push(opImpl);
478
512
  builder.push(code `${opDecl.value}\n`);
479
513
  this.emitter.emitInterfaceOperation(operation);
480
514
  }
515
+ this.#mockRegistrations.set(mock.interfaceName, mock);
481
516
  return builder.reduce();
482
517
  }
483
518
  interfaceOperationDeclarationContext(operation) {
@@ -717,8 +752,7 @@ export async function $onEmit(context) {
717
752
  const doc = getDoc(this.emitter.getProgram(), union);
718
753
  const attributes = getModelAttributes(program, union, unionName);
719
754
  this.#metadateMap.set(union, new CSharpType({ name: unionName, namespace: namespace }));
720
- return this.emitter.result.declaration(unionName, code `${this.#licenseHeader}
721
- // <auto-generated />
755
+ return this.emitter.result.declaration(unionName, code `${this.#generatedFileHeader}
722
756
 
723
757
  ${this.#emitUsings()}
724
758
 
@@ -878,7 +912,7 @@ export async function $onEmit(context) {
878
912
  let context = controllers.get(name);
879
913
  if (context !== undefined)
880
914
  return context;
881
- const sourceFile = this.emitter.createSourceFile(`controllers/${name}ControllerBase.cs`);
915
+ const sourceFile = this.emitter.createSourceFile(`controllers/${name}Controller.cs`);
882
916
  const namespace = this.#getOrSetBaseNamespace(operation);
883
917
  const modelNamespace = `${namespace}.Models`;
884
918
  sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Controller;
@@ -928,6 +962,18 @@ export async function $onEmit(context) {
928
962
  if (sourceFile === libFile.source)
929
963
  return libFile.emitted;
930
964
  }
965
+ if (this.#emitMocks === "all") {
966
+ for (const helper of this.#mockHelpers) {
967
+ if (sourceFile === helper.source)
968
+ return helper.emitted;
969
+ }
970
+ }
971
+ if (this.#mockFiles.length > 0) {
972
+ for (const mock of this.#mockFiles) {
973
+ if (sourceFile === mock.source)
974
+ return mock.emitted;
975
+ }
976
+ }
931
977
  const emittedSourceFile = {
932
978
  path: sourceFile.path,
933
979
  contents: "",
@@ -952,17 +998,21 @@ export async function $onEmit(context) {
952
998
  #emitControllerContents(file) {
953
999
  const namespace = file.meta.namespace;
954
1000
  const contents = new StringBuilder();
955
- contents.push(`${this.#licenseHeader}\n`);
956
- contents.push("// <auto-generated />\n\n");
1001
+ contents.push(`${this.#generatedFileHeader}\n\n`);
957
1002
  contents.push(code `${this.#emitUsings(file)}\n`);
958
1003
  contents.push("\n");
959
1004
  contents.push(`namespace ${namespace}.Controllers\n`);
960
1005
  contents.push("{\n");
961
1006
  contents.push("[ApiController]\n");
962
- contents.push(`public abstract partial class ${file.meta["resource"]}Base: ControllerBase\n`);
1007
+ contents.push(`public partial class ${file.meta["resource"]}: ControllerBase\n`);
1008
+ contents.push("{\n");
1009
+ contents.push("\n");
1010
+ contents.push(`public ${file.meta["resource"]}(I${file.meta.resourceName} operations)\n`);
963
1011
  contents.push("{\n");
1012
+ contents.push(` ${file.meta.resourceName}Impl = operations;\n`);
1013
+ contents.push("}");
964
1014
  contents.push("\n");
965
- contents.push(code `internal abstract I${file.meta.resourceName} ${file.meta.resourceName}Impl { get;}\n`);
1015
+ contents.push(code `internal virtual I${file.meta.resourceName} ${file.meta.resourceName}Impl { get;}\n`);
966
1016
  for (const decl of file.globalScope.declarations) {
967
1017
  contents.push(decl.value + "\n");
968
1018
  }
@@ -1089,6 +1139,16 @@ export async function $onEmit(context) {
1089
1139
  for (const source of this.#libraryFiles) {
1090
1140
  sourceFiles.push(source.source);
1091
1141
  }
1142
+ if (this.#emitMocks === "all") {
1143
+ for (const helper of this.#mockHelpers) {
1144
+ sourceFiles.push(helper.source);
1145
+ }
1146
+ if (this.#mockRegistrations.size > 0) {
1147
+ const mocks = getBusinessLogicImplementations(this.emitter, this.#mockRegistrations);
1148
+ this.#mockFiles.push(...mocks);
1149
+ sourceFiles.push(...mocks.flatMap((l) => l.source));
1150
+ }
1151
+ }
1092
1152
  const emittedSourceFiles = [];
1093
1153
  for (const source of sourceFiles) {
1094
1154
  switch (this.#emitterOutputType) {