@@ -114,10 +114,10 @@ public static IEndpointConventionBuilder MapQuery(
114
114
string nullRouteParameterPattern = "-" ,
115
115
bool enableHead = false )
116
116
{
117
- var returnType = EnsureReturnTypeIsQuery ( handler ) ;
117
+ var ( queryType , returnType ) = EnsureReturnTypeIsQuery ( handler ) ;
118
118
if ( mapNullableRouteParameters is MapNullableRouteParameter . Disable )
119
119
{
120
- return MapRoutes ( route ) ;
120
+ return MapRoutes ( queryType , returnType , route ) ;
121
121
}
122
122
123
123
if ( string . IsNullOrWhiteSpace ( nullRouteParameterPattern ) )
@@ -129,7 +129,7 @@ public static IEndpointConventionBuilder MapQuery(
129
129
130
130
var parsedRoute = RoutePatternFactory . Parse ( route ) ;
131
131
var context = new NullabilityInfoContext ( ) ;
132
- var nullableRouteProperties = returnType . GetProperties ( )
132
+ var nullableRouteProperties = queryType . GetProperties ( )
133
133
. Where (
134
134
p => p . GetMethod != null
135
135
&& p . SetMethod != null
@@ -150,15 +150,24 @@ public static IEndpointConventionBuilder MapQuery(
150
150
var regex = new Regex ( "{" + x . Name + "[^}]*?}" , RegexOptions . IgnoreCase ) ;
151
151
return regex . Replace ( r , nullRouteParameterPattern ) ;
152
152
} ) ;
153
- MapRoutes ( newRoute ) ;
153
+ MapRoutes ( queryType , returnType , newRoute ) ;
154
154
}
155
155
156
- return MapRoutes ( route ) ;
156
+ return MapRoutes ( queryType , returnType , route ) ;
157
157
158
- IEndpointConventionBuilder MapRoutes ( string r )
158
+ IEndpointConventionBuilder MapRoutes ( Type query , Type queryFor , string r)
159
159
{
160
160
var endpoint = enableHead ? app . MapMethods ( r , GetAndHeadMethods , handler ) : app . MapGet ( r , handler ) ;
161
- return endpoint . AddEndpointFilter < QueryEndpointHandler > ( ) ;
161
+ var builder = endpoint . AddEndpointFilter < QueryEndpointHandler > ( )
162
+ . Produces ( 200 , queryFor )
163
+ . WithTags ( "Queries" ) ;
164
+ if ( query . GetInterfaces ( ) . Any ( i => i . IsGenericType && i . GetGenericTypeDefinition ( ) == typeof ( IQuery < > ) ) )
165
+ {
166
+ // may be null
167
+ builder . Produces ( 404 , queryFor ) ;
168
+ }
169
+
170
+ return builder ;
162
171
}
163
172
}
164
173
@@ -220,7 +229,7 @@ public static IEndpointConventionBuilder MapCommand(
220
229
[ StringSyntax ( "Route" ) ] string route ,
221
230
Delegate handler )
222
231
{
223
- var commandTypeName = EnsureReturnTypeIsCommand ( handler ) . Name ;
232
+ var commandTypeName = EnsureReturnTypeIsCommand ( handler ) . CommandType . Name ;
224
233
if ( PostCommandPrefixes . Any ( x => commandTypeName . StartsWith ( x ) ) )
225
234
{
226
235
return app . MapPostCommand ( route , handler ) ;
@@ -265,8 +274,11 @@ public static IEndpointConventionBuilder MapPostCommand(
265
274
[ StringSyntax ( "Route" ) ] string route ,
266
275
Delegate handler )
267
276
{
268
- EnsureReturnTypeIsCommand ( handler ) ;
269
- return app . MapPost ( route , handler ) . AddEndpointFilter < CommandEndpointHandler > ( ) ;
277
+ var ( commandType , responseType , errorType ) = EnsureReturnTypeIsCommand ( handler ) ;
278
+ var builder = app . MapPost ( route , handler )
279
+ . AddEndpointFilter < CommandEndpointHandler > ( )
280
+ . AddCommandOpenApiDescriptions ( commandType , responseType , errorType ) ;
281
+ return builder ;
270
282
}
271
283
272
284
/// <summary>
@@ -295,8 +307,9 @@ public static IEndpointConventionBuilder MapPutCommand(
295
307
[ StringSyntax ( "Route" ) ] string route ,
296
308
Delegate handler )
297
309
{
298
- EnsureReturnTypeIsCommand ( handler ) ;
299
- return app . MapPut ( route , handler ) . AddEndpointFilter < CommandEndpointHandler > ( ) ;
310
+ var ( commandType , responseType , errorType ) = EnsureReturnTypeIsCommand ( handler ) ;
311
+ return app . MapPut ( route , handler ) . AddEndpointFilter < CommandEndpointHandler > ( )
312
+ . AddCommandOpenApiDescriptions ( commandType , responseType , errorType ) ;
300
313
}
301
314
302
315
/// <summary>
@@ -325,8 +338,9 @@ public static IEndpointConventionBuilder MapDeleteCommand(
325
338
[ StringSyntax ( "Route" ) ] string route ,
326
339
Delegate handler )
327
340
{
328
- EnsureReturnTypeIsCommand ( handler ) ;
329
- return app . MapDelete ( route , handler ) . AddEndpointFilter < CommandEndpointHandler > ( ) ;
341
+ var ( commandType , responseType , errorType ) = EnsureReturnTypeIsCommand ( handler ) ;
342
+ return app . MapDelete ( route , handler ) . AddEndpointFilter < CommandEndpointHandler > ( )
343
+ . AddCommandOpenApiDescriptions ( commandType , responseType , errorType ) ;
330
344
}
331
345
332
346
/// <summary>
@@ -395,42 +409,48 @@ public static IEndpointRouteBuilder StopMappingPrefixToDelete(this IEndpointRout
395
409
return app ;
396
410
}
397
411
398
- private static Type EnsureReturnTypeIsCommand ( Delegate handler )
412
+ private static ( Type CommandType , Type ? ResponseType , Type ErrorType ) EnsureReturnTypeIsCommand ( Delegate handler )
399
413
{
400
414
var returnType = handler . Method . ReturnType ;
401
415
if ( returnType . IsGenericType && returnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
402
416
{
403
417
returnType = returnType . GenericTypeArguments . First ( ) ;
404
418
}
405
419
406
- var isCommand = returnType . GetInterfaces ( ) . Where ( x => x . IsGenericType )
407
- . Any ( x => CommandTypes . Contains ( x . GetGenericTypeDefinition ( ) ) ) ;
408
- if ( isCommand == false )
420
+ var commandType = returnType . GetInterfaces ( ) . Where ( x => x . IsGenericType )
421
+ . FirstOrDefault ( x => CommandTypes . Contains ( x . GetGenericTypeDefinition ( ) ) ) ;
422
+ if ( commandType == null )
409
423
{
410
424
throw new ArgumentException (
411
425
"handler does not return command, check if delegate returns type that implements ICommand<> or ICommand<,>" ) ;
412
426
}
413
427
414
- return returnType ;
428
+ Type ? [ ] genericParams = commandType . GetGenericArguments ( ) ;
429
+ if ( genericParams . Length == 1 )
430
+ {
431
+ genericParams = [ null , genericParams [ 0 ] ] ;
432
+ }
433
+
434
+ return ( returnType , genericParams [ 0 ] , genericParams [ 1 ] ! ) ;
415
435
}
416
436
417
- private static Type EnsureReturnTypeIsQuery ( Delegate handler )
437
+ private static ( Type , Type ) EnsureReturnTypeIsQuery ( Delegate handler )
418
438
{
419
439
var returnType = handler . Method . ReturnType ;
420
440
if ( returnType . IsGenericType && returnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
421
441
{
422
442
returnType = returnType . GenericTypeArguments . First ( ) ;
423
443
}
424
444
425
- var isCommand = returnType . GetInterfaces ( ) . Where ( x => x . IsGenericType )
426
- . Any ( x => QueryTypes . Contains ( x . GetGenericTypeDefinition ( ) ) ) ;
427
- if ( isCommand == false )
445
+ var queryInterface = returnType . GetInterfaces ( ) . Where ( x => x . IsGenericType )
446
+ . FirstOrDefault ( x => QueryTypes . Contains ( x . GetGenericTypeDefinition ( ) ) ) ;
447
+ if ( queryInterface == null )
428
448
{
429
449
throw new ArgumentException (
430
450
"handler does not return query, check if delegate returns type that implements IQuery<>" ) ;
431
451
}
432
452
433
- return returnType ;
453
+ return ( returnType , queryInterface . GenericTypeArguments [ 0 ] ) ;
434
454
}
435
455
436
456
private static List < T [ ] > GetNotEmptySubsets < T > ( ICollection < T > items )
@@ -446,4 +466,24 @@ private static List<T[]> GetNotEmptySubsets<T>(ICollection<T> items)
446
466
447
467
return results ;
448
468
}
469
+
470
+ private static RouteHandlerBuilder AddCommandOpenApiDescriptions (
471
+ this RouteHandlerBuilder builder ,
472
+ Type commandType ,
473
+ Type ? responseType ,
474
+ Type errorType )
475
+ {
476
+ var commandResponseType = responseType is null
477
+ ? typeof ( CommandResponse < > ) . MakeGenericType ( errorType )
478
+ : typeof ( CommandResponse < , > ) . MakeGenericType ( responseType , errorType ) ;
479
+ builder . Produces ( 200 , commandResponseType )
480
+ . Produces ( 400 , commandResponseType )
481
+ . WithTags ( "Commands" ) ;
482
+ if ( commandType . GetInterfaces ( ) . Any ( i => i == typeof ( ILockableRequest ) ) )
483
+ {
484
+ builder . Produces ( 429 ) ;
485
+ }
486
+
487
+ return builder ;
488
+ }
449
489
}
0 commit comments