@team-supercharge/oasg 18.0.2 → 18.2.0

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 (28) hide show
  1. package/README.md +222 -33
  2. package/bin/oasg +2 -0
  3. package/config.schema.yml +37 -0
  4. package/package.json +1 -1
  5. package/targets/dotnet/generate.sh +2 -11
  6. package/targets/dotnet/publish.sh +2 -17
  7. package/targets/dotnet/templates/Project.csproj.mustache +39 -0
  8. package/targets/dotnet-common.sh +48 -0
  9. package/targets/dotnet-system-text-json/generate.sh +6 -0
  10. package/targets/dotnet-system-text-json/generator-config.json +15 -0
  11. package/targets/dotnet-system-text-json/publish.sh +6 -0
  12. package/targets/dotnet-system-text-json/templates/netcore_project.mustache +85 -0
  13. package/targets/dotnet-webapi/generate.sh +2 -11
  14. package/targets/dotnet-webapi/publish.sh +2 -17
  15. package/targets/dotnet-webapi/templates/Project.csproj.mustache +61 -0
  16. package/targets/dotnet-webapi-system-text-json/README.md +241 -0
  17. package/targets/dotnet-webapi-system-text-json/generate.sh +33 -0
  18. package/targets/dotnet-webapi-system-text-json/generator-config.json +29 -0
  19. package/targets/dotnet-webapi-system-text-json/publish.sh +6 -0
  20. package/targets/dotnet-webapi-system-text-json/templates/Project.csproj.mustache +66 -0
  21. package/targets/dotnet-webapi-system-text-json/templates/controller.mustache +7 -0
  22. package/targets/dotnet-webapi-system-text-json/templates/endpointParam.mustache +1 -0
  23. package/targets/dotnet-webapi-system-text-json/templates/enumClass.mustache +24 -0
  24. package/targets/dotnet-webapi-system-text-json/templates/minimalEndpoints.mustache +120 -0
  25. package/targets/dotnet-webapi-system-text-json/templates/model.mustache +205 -0
  26. package/targets/dotnet-webapi-system-text-json/templates/paramType.mustache +1 -0
  27. package/targets/dotnet-webapi-system-text-json/templates/serviceCollectionExtensions.mustache +90 -0
  28. package/targets/dotnet-webapi-system-text-json/templates/typeConverter.mustache +11 -0
@@ -0,0 +1,61 @@
1
+ <Project Sdk="{{projectSdk}}">
2
+ <PropertyGroup>
3
+ <Description>{{packageDescription}}{{^packageDescription}}{{packageName}}{{/packageDescription}}</Description>
4
+ <Copyright>{{packageCopyright}}</Copyright>
5
+ <Authors>{{packageAuthors}}</Authors>
6
+ <TargetFramework>net10.0</TargetFramework>
7
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
8
+ <PreserveCompilationContext>true</PreserveCompilationContext>
9
+ <Version>{{packageVersion}}</Version>
10
+ {{#nullableReferenceTypes}}
11
+ <Nullable>annotations</Nullable>
12
+ {{/nullableReferenceTypes}}
13
+ {{#isLibrary}}
14
+ <OutputType>Library</OutputType>
15
+ {{/isLibrary}}
16
+ <AssemblyName>{{packageName}}</AssemblyName>
17
+ <PackageId>{{packageName}}</PackageId>
18
+ <UserSecretsId>{{userSecretsGuid}}</UserSecretsId>
19
+ <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
20
+ <DockerfileContext>..\..</DockerfileContext>
21
+ {{#centralizedPackageVersionManagement}}
22
+ <ManagePackageVersionsCentrally>{{.}}</ManagePackageVersionsCentrally>
23
+ {{/centralizedPackageVersionManagement}}
24
+ </PropertyGroup>
25
+ <ItemGroup>
26
+ {{#useSeparateModelProject}}
27
+ <ProjectReference Include="../{{modelPackage}}/{{modelPackage}}.csproj"/>
28
+ {{/useSeparateModelProject}}
29
+ {{#useFrameworkReference}}
30
+ {{#isLibrary}}
31
+ <FrameworkReference Include="Microsoft.AspNetCore.App" />
32
+ {{/isLibrary}}
33
+ {{/useFrameworkReference}}
34
+ {{^useFrameworkReference}}
35
+ <PackageReference Include="Microsoft.AspNetCore.App" />
36
+ {{/useFrameworkReference}}
37
+ {{^useSeparateModelProject}}
38
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" {{#usePackageVersions}}Version="{{aspnetCoreVersion}}.0" {{/usePackageVersions}}/>
39
+ {{/useSeparateModelProject}}
40
+ {{#useSwashbuckle}}
41
+ <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" {{#usePackageVersions}}Version="1.10.8" {{/usePackageVersions}}/>
42
+ {{#useNewtonsoft}}
43
+ <PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
44
+ <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
45
+ {{/useNewtonsoft}}
46
+ {{^useNewtonsoft}}
47
+ <PackageReference Include="Swashbuckle.AspNetCore" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
48
+ {{/useNewtonsoft}}
49
+ <PackageReference Include="Swashbuckle.AspNetCore.Annotations" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
50
+ {{/useSwashbuckle}}
51
+ {{^useSwashbuckle}}
52
+ {{#useNewtonsoft}}
53
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" {{#usePackageVersions}}Version="{{newtonsoftVersion}}" {{/usePackageVersions}}/>
54
+ {{/useNewtonsoft}}
55
+ {{/useSwashbuckle}}
56
+ <PackageReference Include="JsonSubTypes" {{#usePackageVersions}}Version="1.8.0" {{/usePackageVersions}}/>
57
+ </ItemGroup>
58
+ <ItemGroup>
59
+ <!--<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="{{aspnetCoreVersion}}.0" />-->
60
+ </ItemGroup>
61
+ </Project>
@@ -0,0 +1,241 @@
1
+ # `dotnet-webapi-system-text-json` — Wolverine implementation
2
+
3
+ This target generates an ASP.NET Core server-stub **library** exposing every
4
+ operation as a **minimal-API endpoint** (Native-AOT compatible) with
5
+ System.Text.Json source-generated models. By default each endpoint delegates to a
6
+ generated `I{ApiName}` interface you implement. When the
7
+ `withWolverineImplementation` flag is set, the endpoints instead invoke
8
+ [Wolverine](https://wolverinefx.net) **directly** via `IMessageBus.InvokeAsync` —
9
+ there are no `I{ApiName}` interfaces in that mode.
10
+
11
+ > The examples below assume the package was generated with
12
+ > `packageName = "MyApi"`. Generated request wrappers and the
13
+ > `EndpointRouteBuilderExtensions` live in the `MyApi` namespace; models live in
14
+ > `MyApi.Models`.
15
+
16
+ ## What gets generated (Wolverine mode)
17
+
18
+ Everything lives in a single `EndpointRouteBuilderExtensions.cs`:
19
+
20
+ - A **request wrapper** record per operation, bundling every parameter:
21
+ `AddPetRequest(Pet pet)`, `GetPetByIdRequest(long petId)`,
22
+ `UploadFileRequest(long petId, string? additionalMetadata, Stream? body)`, …
23
+ A distinct type per operation, so request/response pairs never collide.
24
+ - **`Map{ApiName}Endpoints(this IEndpointRouteBuilder)`** (one per API, e.g.
25
+ `MapPetApiEndpoints`) — maps that API's operations as minimal-API endpoints
26
+ that invoke the request through Wolverine and return the result
27
+ (`Results.Ok(result)`, or `Results.NoContent()` when there is no response body).
28
+ Secured operations get `.RequireAuthorization()`.
29
+ - **`MapAll{PackageName}Endpoints(this IEndpointRouteBuilder)`** (e.g.
30
+ `MapAllMyApiEndpoints`) — calls every `Map{ApiName}Endpoints()`.
31
+ - **`ConfigureWolverineMessaging(this WolverineOptions, string localQueueName = "MyApi")`** —
32
+ routes every request type in the assembly to a local queue.
33
+
34
+ A separate `ServiceCollectionExtensions.cs` holds a source-generated
35
+ `{PackageName}JsonSerializerContext` (covering every model, request/response type
36
+ and inline enum) and an **`Add{PackageName}JsonOptions(this IServiceCollection)`**
37
+ extension that registers it on the minimal-API JSON resolver chain — call it in
38
+ Program.cs (required when publishing the host with Native AOT; harmless otherwise).
39
+
40
+ The `WolverineFx` package reference is added automatically.
41
+
42
+ ---
43
+
44
+ ## Setup
45
+
46
+ ### 1. Enable the option
47
+
48
+ ```json
49
+ {
50
+ "id": "my-api",
51
+ "type": "dotnet-webapi-system-text-json",
52
+ "source": "source-merged",
53
+ "sourceUrl": "https://api.nuget.org/v3/index.json",
54
+ "apiKey": "apiKey",
55
+ "packageName": "MyApi",
56
+ "withWolverineImplementation": true
57
+ }
58
+ ```
59
+
60
+ ### 2. API host setup
61
+
62
+ Reference the generated package, then wire it up in `Program.cs`:
63
+
64
+ ```csharp
65
+ using Wolverine;
66
+ using MyApi; // generated Map{ApiName}Endpoints / MapAllMyApiEndpoints / ConfigureWolverineMessaging
67
+
68
+ var builder = WebApplication.CreateBuilder(args);
69
+ builder.Services.AddHttpContextAccessor(); // see "Authentication" below
70
+ builder.Services.AddMyApiJsonOptions(); // register the source-gen JSON context (required for Native AOT)
71
+
72
+ builder.Host.UseWolverine(opts =>
73
+ {
74
+ // Route the request types to a local queue.
75
+ opts.ConfigureWolverineMessaging();
76
+
77
+ // Static codegen (see below). Pre-generate with `dotnet run -- codegen write`.
78
+ opts.CodeGeneration.TypeLoadMode = JasperFx.CodeGeneration.TypeLoadMode.Static;
79
+
80
+ // Optional: force every InvokeAsync to run locally (in-process). With this
81
+ // on, an invocation that would otherwise be sent to a remote transport
82
+ // throws instead. Turn it on if this service must never make remote calls.
83
+ // opts.EnableRemoteInvocation = false;
84
+ });
85
+
86
+ var app = builder.Build();
87
+
88
+ app.UseAuthentication();
89
+ app.UseAuthorization();
90
+
91
+ app.MapAllMyApiEndpoints(); // aggregate; or call Map{ApiName}Endpoints() individually
92
+
93
+ app.Run();
94
+ ```
95
+
96
+ ### 3. Static codegen
97
+
98
+ Setting `opts.CodeGeneration.TypeLoadMode = TypeLoadMode.Static` (above) stops
99
+ Wolverine from compiling handler code at runtime. Pre-generate it as part of your
100
+ build/deploy:
101
+
102
+ ```bash
103
+ dotnet run -- codegen write
104
+ ```
105
+
106
+ (Alternatively, drop that line and reference `WolverineFx.RuntimeCompilation` to
107
+ opt back into runtime Roslyn compilation.)
108
+
109
+ ---
110
+
111
+ ## Usage
112
+
113
+ ### Request handling
114
+
115
+ Provide a handler for each generated `*Request` type. Wolverine discovers
116
+ handlers by convention (a `Handle`/`HandleAsync` method); the return type must
117
+ match the operation's response model (or `Task`/nothing for no-content
118
+ operations).
119
+
120
+ ```csharp
121
+ using MyApi; // *Request types
122
+ using MyApi.Models; // Pet, ...
123
+
124
+ public class PetHandlers
125
+ {
126
+ // Operation with a response body -> return the response model
127
+ public Task<Pet> Handle(AddPetRequest request)
128
+ => Task.FromResult(request.pet);
129
+
130
+ public async Task<Pet> Handle(GetPetByIdRequest request)
131
+ => await _repository.GetAsync(request.petId)
132
+ ?? throw new PetNotFoundException(request.petId);
133
+
134
+ // No-response-body operation -> return Task (the endpoint returns NoContent)
135
+ public Task Handle(DeletePetRequest request)
136
+ => _repository.DeleteAsync(request.petId);
137
+ }
138
+ ```
139
+
140
+ ### Authentication
141
+
142
+ Secured operations get `.RequireAuthorization()` on the generated endpoint, so
143
+ authorization is enforced by ASP.NET Core **before** the handler runs. Register
144
+ the matching authentication scheme and authorization policy in the host — e.g.
145
+ for the `api_key` policy emitted for an API-key-secured operation:
146
+
147
+ ```csharp
148
+ builder.Services
149
+ .AddAuthentication("ApiKey")
150
+ .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthHandler>("ApiKey", _ => { });
151
+
152
+ builder.Services.AddAuthorization(options =>
153
+ options.AddPolicy("api_key", policy => policy.RequireAuthenticatedUser()));
154
+ ```
155
+
156
+ Because `InvokeAsync` runs the handler **in-process** within the request, the
157
+ authenticated user flows through `HttpContext`. Read it in a handler via
158
+ `IHttpContextAccessor` (registered with `AddHttpContextAccessor()` above):
159
+
160
+ ```csharp
161
+ public class GetPetByIdHandler(IHttpContextAccessor http)
162
+ {
163
+ public async Task<Pet> Handle(GetPetByIdRequest request)
164
+ {
165
+ var user = http.HttpContext!.User; // authenticated principal
166
+ var tenant = user.FindFirst("tenant")?.Value;
167
+ return await _repository.GetAsync(request.petId, tenant);
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### Error handling
173
+
174
+ Wolverine scopes retry rules **per handler** (not on the routing options), so
175
+ `ConfigureWolverineMessaging` sets no retry policy. Opt into fail-fast where you
176
+ want it, on the handler:
177
+
178
+ ```csharp
179
+ using Wolverine;
180
+ using Wolverine.ErrorHandling;
181
+
182
+ public class GetPetByIdHandler
183
+ {
184
+ // No retries: a failure surfaces immediately.
185
+ public static void Configure(HandlerChain chain) => chain.OnAnyException().Discard();
186
+
187
+ public Task<Pet> Handle(GetPetByIdRequest request) => /* ... */;
188
+ }
189
+ ```
190
+
191
+ With no retries, a handler exception propagates straight back through
192
+ `InvokeAsync` into the endpoint. Map domain exceptions to HTTP responses
193
+ host-side with an `IExceptionHandler` + ProblemDetails:
194
+
195
+ ```csharp
196
+ public sealed class DomainExceptionHandler(IProblemDetailsService problemDetails)
197
+ : IExceptionHandler
198
+ {
199
+ public async ValueTask<bool> TryHandleAsync(
200
+ HttpContext context, Exception exception, CancellationToken ct)
201
+ {
202
+ var status = exception switch
203
+ {
204
+ PetNotFoundException => StatusCodes.Status404NotFound,
205
+ ValidationException => StatusCodes.Status400BadRequest,
206
+ _ => 0
207
+ };
208
+ if (status == 0) return false; // not a domain error -> let it bubble
209
+
210
+ context.Response.StatusCode = status;
211
+ return await problemDetails.TryWriteAsync(new ProblemDetailsContext
212
+ {
213
+ HttpContext = context,
214
+ ProblemDetails = { Title = exception.Message, Status = status }
215
+ });
216
+ }
217
+ }
218
+ ```
219
+
220
+ ```csharp
221
+ // Program.cs
222
+ builder.Services.AddProblemDetails();
223
+ builder.Services.AddExceptionHandler<DomainExceptionHandler>();
224
+ // ...
225
+ app.UseExceptionHandler();
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Notes & limitations
231
+
232
+ - **Without the flag**, the same endpoints delegate to a generated `I{ApiName}`
233
+ interface (resolved from DI) that you implement directly — no Wolverine.
234
+ - **Conflicting request/response pairs** are avoided because every operation gets
235
+ its own wrapper type (e.g. `AddPetRequest` and `UpdatePetRequest`, even though
236
+ both wrap a `Pet`).
237
+ - **File uploads:** the wrapper carries the path/query parameters alongside the
238
+ body, so they reach the handler. The body is a `System.IO.Stream` — fine for
239
+ the in-process local-queue routing used here, but a `Stream` is not
240
+ serializable, so if you re-route these requests to a remote transport
241
+ (RabbitMQ, Azure Service Bus, …) buffer the body to a `byte[]` instead.
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+ source $(dirname "$0")/../dotnet-common.sh
5
+
6
+ # Pass the Wolverine flag to the generator only when enabled, so the mustache
7
+ # section {{#withWolverineImplementation}} stays falsy otherwise (a string
8
+ # "false" additional-property would read as truthy).
9
+ if [ "${withWolverineImplementation}" = "true" ]; then
10
+ generatorCustomArgs="$generatorCustomArgs --additional-properties=withWolverineImplementation=true"
11
+ fi
12
+
13
+ dotnet_generate
14
+
15
+ # The aggregate minimal-API endpoints file is emitted by a SupportingFiles
16
+ # template (generator-config.json "files"), which can't template its output
17
+ # folder, so it lands at the output root. Move it into the package source dir
18
+ # so it's part of the project.
19
+ endpointsFile="out/$targetId/EndpointRouteBuilderExtensions.cs"
20
+ if [ -f "$endpointsFile" ]; then
21
+ destFile="out/$targetId/src/$packageName/EndpointRouteBuilderExtensions.cs"
22
+ mv "$endpointsFile" "$destFile"
23
+ # The generator only exposes the verb as "HttpGet"/"HttpPost"/..., so the
24
+ # template emits endpoints.MapHttpGet(...); rewrite to the real minimal-API
25
+ # method name endpoints.MapGet(...).
26
+ sed -i.bak 's/\.MapHttp/.Map/g' "$destFile" && rm -f "$destFile.bak"
27
+ fi
28
+
29
+ # Same for the JSON source-gen context / service-collection extension (SupportingFiles file).
30
+ serviceExtFile="out/$targetId/ServiceCollectionExtensions.cs"
31
+ if [ -f "$serviceExtFile" ]; then
32
+ mv "$serviceExtFile" "out/$targetId/src/$packageName/ServiceCollectionExtensions.cs"
33
+ fi
@@ -0,0 +1,29 @@
1
+ {
2
+ "inlineSchemaOptions": {
3
+ "ARRAY_ITEM_SUFFIX": "",
4
+ "MAP_ITEM_SUFFIX": "",
5
+ "SKIP_SCHEMA_REUSE": "true"
6
+ },
7
+ "additionalProperties": {
8
+ "aspnetCoreVersion": "8.0",
9
+ "operationIsAsync": true,
10
+ "buildTarget": "library",
11
+ "generateBody": false,
12
+ "nullableReferenceTypes": true,
13
+ "operationResultTask": true,
14
+ "useNewtonsoft": false,
15
+ "enumNameSuffix": "Enum",
16
+ "enumValueSuffix": "",
17
+ "wolverineVersion": "6.10.0"
18
+ },
19
+ "files": {
20
+ "minimalEndpoints.mustache": {
21
+ "destinationFilename": "EndpointRouteBuilderExtensions.cs",
22
+ "templateType": "SupportingFiles"
23
+ },
24
+ "serviceCollectionExtensions.mustache": {
25
+ "destinationFilename": "ServiceCollectionExtensions.cs",
26
+ "templateType": "SupportingFiles"
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ source $(dirname "$0")/../common.sh
4
+ source $(dirname "$0")/../dotnet-common.sh
5
+
6
+ dotnet_publish
@@ -0,0 +1,66 @@
1
+ <Project Sdk="{{projectSdk}}">
2
+ <PropertyGroup>
3
+ <Description>{{packageDescription}}{{^packageDescription}}{{packageName}}{{/packageDescription}}</Description>
4
+ <Copyright>{{packageCopyright}}</Copyright>
5
+ <Authors>{{packageAuthors}}</Authors>
6
+ <TargetFramework>net10.0</TargetFramework>
7
+ <IsAotCompatible>true</IsAotCompatible>
8
+ <!-- Statically generate the minimal-API request delegates (interceptors) so the
9
+ Map*() calls are trim-/AOT-safe instead of reflection-based at runtime.
10
+ Without this the [RequiresDynamicCode]/[RequiresUnreferencedCode] Map*
11
+ overloads raise IL3050/IL2026 under the AOT analyzer enabled above. -->
12
+ <EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
13
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
14
+ <PreserveCompilationContext>true</PreserveCompilationContext>
15
+ <Version>{{packageVersion}}</Version>
16
+ {{#nullableReferenceTypes}}
17
+ <Nullable>annotations</Nullable>
18
+ {{/nullableReferenceTypes}}
19
+ {{#isLibrary}}
20
+ <OutputType>Library</OutputType>
21
+ {{/isLibrary}}
22
+ <AssemblyName>{{packageName}}</AssemblyName>
23
+ <PackageId>{{packageName}}</PackageId>
24
+ <UserSecretsId>{{userSecretsGuid}}</UserSecretsId>
25
+ <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
26
+ <DockerfileContext>..\..</DockerfileContext>
27
+ {{#centralizedPackageVersionManagement}}
28
+ <ManagePackageVersionsCentrally>{{.}}</ManagePackageVersionsCentrally>
29
+ {{/centralizedPackageVersionManagement}}
30
+ </PropertyGroup>
31
+ <ItemGroup>
32
+ {{#withWolverineImplementation}}
33
+ <PackageReference Include="WolverineFx" Version="{{wolverineVersion}}" />
34
+ {{/withWolverineImplementation}}
35
+ {{#useSeparateModelProject}}
36
+ <ProjectReference Include="../{{modelPackage}}/{{modelPackage}}.csproj"/>
37
+ {{/useSeparateModelProject}}
38
+ {{#useFrameworkReference}}
39
+ {{#isLibrary}}
40
+ <FrameworkReference Include="Microsoft.AspNetCore.App" />
41
+ {{/isLibrary}}
42
+ {{/useFrameworkReference}}
43
+ {{^useFrameworkReference}}
44
+ <PackageReference Include="Microsoft.AspNetCore.App" />
45
+ {{/useFrameworkReference}}
46
+ {{#useSwashbuckle}}
47
+ <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" {{#usePackageVersions}}Version="1.10.8" {{/usePackageVersions}}/>
48
+ {{#useNewtonsoft}}
49
+ <PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
50
+ <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
51
+ {{/useNewtonsoft}}
52
+ {{^useNewtonsoft}}
53
+ <PackageReference Include="Swashbuckle.AspNetCore" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
54
+ {{/useNewtonsoft}}
55
+ <PackageReference Include="Swashbuckle.AspNetCore.Annotations" {{#usePackageVersions}}Version="{{swashbuckleVersion}}" {{/usePackageVersions}}/>
56
+ {{/useSwashbuckle}}
57
+ {{^useSwashbuckle}}
58
+ {{#useNewtonsoft}}
59
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" {{#usePackageVersions}}Version="{{newtonsoftVersion}}" {{/usePackageVersions}}/>
60
+ {{/useNewtonsoft}}
61
+ {{/useSwashbuckle}}
62
+ </ItemGroup>
63
+ <ItemGroup>
64
+ <!--<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="{{aspnetCoreVersion}}.0" />-->
65
+ </ItemGroup>
66
+ </Project>
@@ -0,0 +1,7 @@
1
+ {{>partial_header}}
2
+ // Intentionally empty.
3
+ //
4
+ // This target exposes its operations as minimal-API endpoints instead of MVC
5
+ // controllers (minimal APIs are Native-AOT compatible; MVC controllers are not).
6
+ // See {{packageName}}.EndpointRouteBuilderExtensions.MapAllEndpoints and the
7
+ // generated I{ApiName} interfaces in EndpointRouteBuilderExtensions.cs.
@@ -0,0 +1 @@
1
+ {{#isPathParam}}[FromRoute(Name = "{{baseName}}")] {{/isPathParam}}{{#isQueryParam}}[FromQuery(Name = "{{baseName}}")] {{/isQueryParam}}{{#isHeaderParam}}[FromHeader(Name = "{{baseName}}")] {{/isHeaderParam}}{{#isBodyParam}}[FromBody] {{/isBodyParam}}{{#isFormParam}}[FromForm] {{/isFormParam}}
@@ -0,0 +1,24 @@
1
+
2
+ /// <summary>
3
+ /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}}
4
+ /// </summary>
5
+ {{#description}}
6
+ /// <value>{{{.}}}</value>
7
+ {{/description}}
8
+ {{! AOT-safe enum (de)serialization: the source-generated, generic }}
9
+ {{! JsonStringEnumConverter<T> (no runtime MakeGenericType/Activator) plus the }}
10
+ {{! per-member JsonStringEnumMemberName below, which maps each value to its wire }}
11
+ {{! form. The attribute travels with the type, so enums round-trip as strings }}
12
+ {{! for reflection-based (JIT) hosts and source-generated (AOT) hosts alike. }}
13
+ [JsonConverter(typeof(JsonStringEnumConverter<{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))]
14
+ public enum {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
15
+ {
16
+ {{#allowableValues}}{{#enumVars}}
17
+ /// <summary>
18
+ /// Enum {{name}} for {{{value}}}
19
+ /// </summary>
20
+ {{#isString}}[EnumMember(Value = "{{{value}}}")]
21
+ [JsonStringEnumMemberName("{{{value}}}")]{{/isString}}
22
+ {{name}}{{^isString}} = {{{value}}}{{/isString}}{{#isString}} = {{-index}}{{/isString}}{{^-last}},
23
+ {{/-last}}{{/enumVars}}{{/allowableValues}}
24
+ }
@@ -0,0 +1,120 @@
1
+ // <auto-generated/>
2
+ #nullable enable
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.Threading;
6
+ using System.Threading.Tasks;
7
+ using Microsoft.AspNetCore.Builder;
8
+ using Microsoft.AspNetCore.Http;
9
+ using Microsoft.AspNetCore.Mvc;
10
+ using Microsoft.AspNetCore.Routing;
11
+ {{#withWolverineImplementation}}
12
+ using Microsoft.Extensions.DependencyInjection;
13
+ using Wolverine;
14
+ {{/withWolverineImplementation}}
15
+ using {{packageName}}.Models;
16
+
17
+ namespace {{packageName}}
18
+ {
19
+ {{^withWolverineImplementation}}
20
+ {{#apiInfo}}
21
+ {{#apis}}
22
+ /// <summary>
23
+ /// Application logic for the {{classname}} endpoints. Register an implementation in DI.
24
+ /// </summary>
25
+ public interface I{{classname}}
26
+ {
27
+ {{#operations}}
28
+ {{#operation}}
29
+ /// <summary>Application logic for the {{operationId}} operation.</summary>
30
+ Task<IResult> {{operationId}}({{#allParams}}{{>paramType}} {{paramName}}, {{/allParams}}CancellationToken cancellationToken);
31
+ {{/operation}}
32
+ {{/operations}}
33
+ }
34
+
35
+ {{/apis}}
36
+ {{/apiInfo}}
37
+ {{/withWolverineImplementation}}
38
+ {{#withWolverineImplementation}}
39
+ {{#apiInfo}}
40
+ {{#apis}}
41
+ {{#operations}}
42
+ {{#operation}}
43
+ /// <summary>Wolverine request for {{classname}}.{{operationId}}, carrying every parameter.</summary>
44
+ public record {{operationId}}Request({{#allParams}}{{>paramType}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
45
+ {{/operation}}
46
+ {{/operations}}
47
+ {{/apis}}
48
+ {{/apiInfo}}
49
+ {{/withWolverineImplementation}}
50
+
51
+ /// <summary>
52
+ /// Maps every generated API operation as a minimal-API endpoint.
53
+ {{^withWolverineImplementation}}
54
+ /// Each endpoint delegates to the matching <c>I{ApiName}</c> implementation from DI.
55
+ {{/withWolverineImplementation}}
56
+ {{#withWolverineImplementation}}
57
+ /// Each endpoint invokes the request through Wolverine (IMessageBus.InvokeAsync).
58
+ {{/withWolverineImplementation}}
59
+ /// </summary>
60
+ public static class EndpointRouteBuilderExtensions
61
+ {
62
+ {{#apiInfo}}
63
+ {{#apis}}
64
+ /// <summary>Maps the {{classname}} operations as minimal-API endpoints.</summary>
65
+ public static IEndpointRouteBuilder Map{{classname}}Endpoints(this IEndpointRouteBuilder endpoints)
66
+ {
67
+ {{#operations}}
68
+ {{#operation}}
69
+ endpoints.Map{{httpMethod}}("{{{basePathWithoutHost}}}{{{path}}}",
70
+ {{#withWolverineImplementation}}async {{/withWolverineImplementation}}({{#allParams}}{{>endpointParam}}{{>paramType}} {{paramName}}, {{/allParams}}[FromServices] {{^withWolverineImplementation}}I{{classname}} handler{{/withWolverineImplementation}}{{#withWolverineImplementation}}IMessageBus messageBus{{/withWolverineImplementation}}, CancellationToken cancellationToken)
71
+ => {{^withWolverineImplementation}}handler.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}cancellationToken){{/withWolverineImplementation}}{{#withWolverineImplementation}}{{#returnType}}Results.Ok(await messageBus.InvokeAsync<{{{returnType}}}>(new {{operationId}}Request({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}), cancellationToken)){{/returnType}}{{^returnType}}await Handle(messageBus, new {{operationId}}Request({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}), cancellationToken){{/returnType}}{{/withWolverineImplementation}}){{#hasAuthMethods}}
72
+ .RequireAuthorization(){{/hasAuthMethods}};
73
+ {{/operation}}
74
+ {{/operations}}
75
+ return endpoints;
76
+ }
77
+
78
+ {{/apis}}
79
+ {{/apiInfo}}
80
+ /// <summary>Maps the endpoints of every generated API.</summary>
81
+ public static IEndpointRouteBuilder MapAll{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}Endpoints(this IEndpointRouteBuilder endpoints)
82
+ {
83
+ {{#apiInfo}}
84
+ {{#apis}}
85
+ endpoints.Map{{classname}}Endpoints();
86
+ {{/apis}}
87
+ {{/apiInfo}}
88
+ return endpoints;
89
+ }
90
+
91
+ {{#withWolverineImplementation}}
92
+ private static async Task<IResult> Handle<TRequest>(IMessageBus messageBus, TRequest request, CancellationToken cancellationToken)
93
+ {
94
+ await messageBus.InvokeAsync(request!, cancellationToken);
95
+ return Results.NoContent();
96
+ }
97
+
98
+ /// <summary>
99
+ /// Routes every generated request type (in this assembly) to a local queue.
100
+ /// </summary>
101
+ public static WolverineOptions ConfigureWolverineMessaging(this WolverineOptions options, string localQueueName = "{{packageName}}")
102
+ {
103
+ options.Publish(rule =>
104
+ {
105
+ {{#apiInfo}}
106
+ {{#apis}}
107
+ {{#operations}}
108
+ {{#operation}}
109
+ rule.Message<{{operationId}}Request>();
110
+ {{/operation}}
111
+ {{/operations}}
112
+ {{/apis}}
113
+ {{/apiInfo}}
114
+ rule.ToLocalQueue(localQueueName);
115
+ });
116
+ return options;
117
+ }
118
+ {{/withWolverineImplementation}}
119
+ }
120
+ }