diff --git a/src/Immediate.Apis.Generators/ImmediateApisGenerator.Models.cs b/src/Immediate.Apis.Generators/ImmediateApisGenerator.Models.cs index edaa9e3..ee7748f 100644 --- a/src/Immediate.Apis.Generators/ImmediateApisGenerator.Models.cs +++ b/src/Immediate.Apis.Generators/ImmediateApisGenerator.Models.cs @@ -6,6 +6,7 @@ private sealed record Method { public required string MapMethod { get; init; } public required string? HttpMethod { get; init; } + public required EquatableReadOnlyList Attributes { get; init; } public required string ParameterAttribute { get; init; } public required string Route { get; init; } diff --git a/src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs b/src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs index b8a876a..688ea89 100644 --- a/src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs +++ b/src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs @@ -1,5 +1,7 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; namespace Immediate.Apis.Generators; @@ -73,6 +75,10 @@ CancellationToken token token.ThrowIfCancellationRequested(); + var handleMethodAttributes = GetHandleMethodAttributes(handleMethod); + + token.ThrowIfCancellationRequested(); + var useCustomization = HasCustomizationMethod(symbol); token.ThrowIfCancellationRequested(); @@ -85,6 +91,7 @@ CancellationToken token { MapMethod = mapMethod, HttpMethod = httpMethod, + Attributes = handleMethodAttributes, ParameterAttribute = parameterAttribute, Route = route, @@ -217,4 +224,40 @@ private static string GetMapMethod(AttributeData attributeData) .Value !.ToString(); } + + private static EquatableReadOnlyList GetHandleMethodAttributes(IMethodSymbol methodSymbol) => + methodSymbol.GetAttributes() + .Select(GetAttributeString) + .ToEquatableReadOnlyList(); + + private static string GetAttributeString(AttributeData attributeData) + { + var @class = attributeData.AttributeClass!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + var parameters = new List(); + + foreach (var tc in attributeData.ConstructorArguments) + { + if (GetTypedConstantString(tc) is { } str) + parameters.Add(str); + } + + foreach (var na in attributeData.NamedArguments) + { + if (GetTypedConstantString(na.Value) is { } str) + parameters.Add($"{na.Key} = {str}"); + } + + return parameters.Count == 0 + ? @class + : $"{@class}({string.Join(", ", parameters)})"; + } + + [SuppressMessage("Style", "IDE0072:Add missing cases")] + private static string? GetTypedConstantString(TypedConstant tc) => + tc.Kind switch + { + TypedConstantKind.Array => $"[{string.Join(", ", tc.Values.Select(GetTypedConstantString))}]", + _ => tc.ToCSharpString(), + }; } diff --git a/src/Immediate.Apis.Generators/Templates/Route.sbntxt b/src/Immediate.Apis.Generators/Templates/Route.sbntxt index dffc728..15386ac 100644 --- a/src/Immediate.Apis.Generators/Templates/Route.sbntxt +++ b/src/Immediate.Apis.Generators/Templates/Route.sbntxt @@ -17,6 +17,9 @@ namespace Microsoft.AspNetCore.Builder {{~ if !string.empty method.http_method ~}} ["{{ method.http_method }}"], {{~ end ~}} + {{~ for a in method.attributes ~}} + [{{ a }}] + {{~ end ~}} async ( [{{ method.parameter_attribute }}] {{ method.parameter_type }} parameters, [FromServices] {{ method.class_full_name }}.Handler handler, diff --git a/tests/Immediate.Apis.FunctionalTests/Features/WeatherForecast/Get.cs b/tests/Immediate.Apis.FunctionalTests/Features/WeatherForecast/Get.cs index f32c560..27bf142 100644 --- a/tests/Immediate.Apis.FunctionalTests/Features/WeatherForecast/Get.cs +++ b/tests/Immediate.Apis.FunctionalTests/Features/WeatherForecast/Get.cs @@ -1,6 +1,7 @@ using Immediate.Apis.Shared; using Immediate.Handlers.Shared; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Immediate.Apis.FunctionalTests.Features.WeatherForecast; @@ -27,6 +28,8 @@ public sealed record Result public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } + [ProducesResponseType>(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] private static async ValueTask> Handle( Query _, CancellationToken token diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.Dummy.GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.Dummy.GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..825796b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.Dummy.GetUsersQuery.g.verified.cs @@ -0,0 +1,69 @@ +//HintName: IH.Dummy.GetUsersQuery.g.cs +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable CS1591 + +namespace Dummy; + +partial class GetUsersQuery +{ + public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler + { + private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior; + + public Handler( + global::Dummy.GetUsersQuery.HandleBehavior handleBehavior + ) + { + var handlerType = typeof(GetUsersQuery); + + _handleBehavior = handleBehavior; + + } + + public async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken = default + ) + { + return await _handleBehavior + .HandleAsync(request, cancellationToken) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior + { + + public HandleBehavior( + ) + { + } + + public override async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken + ) + { + return await global::Dummy.GetUsersQuery + .HandleAsync( + request + , cancellationToken + ) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static IServiceCollection AddHandlers( + IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime)); + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.ServiceCollectionExtensions.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.ServiceCollectionExtensions.g.verified.cs new file mode 100644 index 0000000..96e0a6b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#IH.ServiceCollectionExtensions.g.verified.cs @@ -0,0 +1,25 @@ +//HintName: IH.ServiceCollectionExtensions.g.cs +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +#pragma warning disable CS1591 + +public static class HandlerServiceCollectionExtensions +{ + public static IServiceCollection AddTestsBehaviors( + this IServiceCollection services) + { + + return services; + } + + public static IServiceCollection AddTestsHandlers( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + global::Dummy.GetUsersQuery.AddHandlers(services, lifetime); + + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..3dde501 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs @@ -0,0 +1,40 @@ +//HintName: RouteBuilder.Dummy_GetUsersQuery.g.cs +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder +{ + public static partial class TestsRoutesBuilder + { + private static void MapDummy_GetUsersQueryEndpoint(IEndpointRouteBuilder app) + { + var endpoint = app + .MapDelete( + "/test", + [global::Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(200)] + async ( + [AsParameters] global::Dummy.GetUsersQuery.Query parameters, + [FromServices] global::Dummy.GetUsersQuery.Handler handler, + CancellationToken token + ) => + { + var ret = await handler.HandleAsync(parameters, token); + return ret; + } + ); + + _ = endpoint.AllowAnonymous(); + } + } +} + +namespace Dummy +{ + + /// registered using [AsParameters] + partial class GetUsersQuery; +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RoutesBuilder.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RoutesBuilder.g.verified.cs new file mode 100644 index 0000000..2134c39 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Delete#RoutesBuilder.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: RoutesBuilder.g.cs +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder; + +public static partial class TestsRoutesBuilder +{ + public static IEndpointRouteBuilder MapTestsEndpoints( + this IEndpointRouteBuilder app + ) + { + MapDummy_GetUsersQueryEndpoint(app); + + return app; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.Dummy.GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.Dummy.GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..825796b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.Dummy.GetUsersQuery.g.verified.cs @@ -0,0 +1,69 @@ +//HintName: IH.Dummy.GetUsersQuery.g.cs +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable CS1591 + +namespace Dummy; + +partial class GetUsersQuery +{ + public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler + { + private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior; + + public Handler( + global::Dummy.GetUsersQuery.HandleBehavior handleBehavior + ) + { + var handlerType = typeof(GetUsersQuery); + + _handleBehavior = handleBehavior; + + } + + public async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken = default + ) + { + return await _handleBehavior + .HandleAsync(request, cancellationToken) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior + { + + public HandleBehavior( + ) + { + } + + public override async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken + ) + { + return await global::Dummy.GetUsersQuery + .HandleAsync( + request + , cancellationToken + ) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static IServiceCollection AddHandlers( + IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime)); + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.ServiceCollectionExtensions.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.ServiceCollectionExtensions.g.verified.cs new file mode 100644 index 0000000..96e0a6b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#IH.ServiceCollectionExtensions.g.verified.cs @@ -0,0 +1,25 @@ +//HintName: IH.ServiceCollectionExtensions.g.cs +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +#pragma warning disable CS1591 + +public static class HandlerServiceCollectionExtensions +{ + public static IServiceCollection AddTestsBehaviors( + this IServiceCollection services) + { + + return services; + } + + public static IServiceCollection AddTestsHandlers( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + global::Dummy.GetUsersQuery.AddHandlers(services, lifetime); + + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..491a727 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs @@ -0,0 +1,40 @@ +//HintName: RouteBuilder.Dummy_GetUsersQuery.g.cs +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder +{ + public static partial class TestsRoutesBuilder + { + private static void MapDummy_GetUsersQueryEndpoint(IEndpointRouteBuilder app) + { + var endpoint = app + .MapGet( + "/test", + [global::Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(200)] + async ( + [AsParameters] global::Dummy.GetUsersQuery.Query parameters, + [FromServices] global::Dummy.GetUsersQuery.Handler handler, + CancellationToken token + ) => + { + var ret = await handler.HandleAsync(parameters, token); + return ret; + } + ); + + _ = endpoint.AllowAnonymous(); + } + } +} + +namespace Dummy +{ + + /// registered using [AsParameters] + partial class GetUsersQuery; +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RoutesBuilder.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RoutesBuilder.g.verified.cs new file mode 100644 index 0000000..2134c39 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Get#RoutesBuilder.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: RoutesBuilder.g.cs +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder; + +public static partial class TestsRoutesBuilder +{ + public static IEndpointRouteBuilder MapTestsEndpoints( + this IEndpointRouteBuilder app + ) + { + MapDummy_GetUsersQueryEndpoint(app); + + return app; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.Dummy.GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.Dummy.GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..825796b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.Dummy.GetUsersQuery.g.verified.cs @@ -0,0 +1,69 @@ +//HintName: IH.Dummy.GetUsersQuery.g.cs +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable CS1591 + +namespace Dummy; + +partial class GetUsersQuery +{ + public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler + { + private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior; + + public Handler( + global::Dummy.GetUsersQuery.HandleBehavior handleBehavior + ) + { + var handlerType = typeof(GetUsersQuery); + + _handleBehavior = handleBehavior; + + } + + public async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken = default + ) + { + return await _handleBehavior + .HandleAsync(request, cancellationToken) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior + { + + public HandleBehavior( + ) + { + } + + public override async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken + ) + { + return await global::Dummy.GetUsersQuery + .HandleAsync( + request + , cancellationToken + ) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static IServiceCollection AddHandlers( + IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime)); + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.ServiceCollectionExtensions.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.ServiceCollectionExtensions.g.verified.cs new file mode 100644 index 0000000..96e0a6b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#IH.ServiceCollectionExtensions.g.verified.cs @@ -0,0 +1,25 @@ +//HintName: IH.ServiceCollectionExtensions.g.cs +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +#pragma warning disable CS1591 + +public static class HandlerServiceCollectionExtensions +{ + public static IServiceCollection AddTestsBehaviors( + this IServiceCollection services) + { + + return services; + } + + public static IServiceCollection AddTestsHandlers( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + global::Dummy.GetUsersQuery.AddHandlers(services, lifetime); + + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..86c2ca5 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs @@ -0,0 +1,40 @@ +//HintName: RouteBuilder.Dummy_GetUsersQuery.g.cs +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder +{ + public static partial class TestsRoutesBuilder + { + private static void MapDummy_GetUsersQueryEndpoint(IEndpointRouteBuilder app) + { + var endpoint = app + .MapPatch( + "/test", + [global::Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(200)] + async ( + [FromBody] global::Dummy.GetUsersQuery.Query parameters, + [FromServices] global::Dummy.GetUsersQuery.Handler handler, + CancellationToken token + ) => + { + var ret = await handler.HandleAsync(parameters, token); + return ret; + } + ); + + _ = endpoint.AllowAnonymous(); + } + } +} + +namespace Dummy +{ + + /// registered using [FromBody] + partial class GetUsersQuery; +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RoutesBuilder.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RoutesBuilder.g.verified.cs new file mode 100644 index 0000000..2134c39 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Patch#RoutesBuilder.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: RoutesBuilder.g.cs +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder; + +public static partial class TestsRoutesBuilder +{ + public static IEndpointRouteBuilder MapTestsEndpoints( + this IEndpointRouteBuilder app + ) + { + MapDummy_GetUsersQueryEndpoint(app); + + return app; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.Dummy.GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.Dummy.GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..825796b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.Dummy.GetUsersQuery.g.verified.cs @@ -0,0 +1,69 @@ +//HintName: IH.Dummy.GetUsersQuery.g.cs +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable CS1591 + +namespace Dummy; + +partial class GetUsersQuery +{ + public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler + { + private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior; + + public Handler( + global::Dummy.GetUsersQuery.HandleBehavior handleBehavior + ) + { + var handlerType = typeof(GetUsersQuery); + + _handleBehavior = handleBehavior; + + } + + public async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken = default + ) + { + return await _handleBehavior + .HandleAsync(request, cancellationToken) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior + { + + public HandleBehavior( + ) + { + } + + public override async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken + ) + { + return await global::Dummy.GetUsersQuery + .HandleAsync( + request + , cancellationToken + ) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static IServiceCollection AddHandlers( + IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime)); + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.ServiceCollectionExtensions.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.ServiceCollectionExtensions.g.verified.cs new file mode 100644 index 0000000..96e0a6b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#IH.ServiceCollectionExtensions.g.verified.cs @@ -0,0 +1,25 @@ +//HintName: IH.ServiceCollectionExtensions.g.cs +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +#pragma warning disable CS1591 + +public static class HandlerServiceCollectionExtensions +{ + public static IServiceCollection AddTestsBehaviors( + this IServiceCollection services) + { + + return services; + } + + public static IServiceCollection AddTestsHandlers( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + global::Dummy.GetUsersQuery.AddHandlers(services, lifetime); + + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..0bf87c0 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs @@ -0,0 +1,40 @@ +//HintName: RouteBuilder.Dummy_GetUsersQuery.g.cs +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder +{ + public static partial class TestsRoutesBuilder + { + private static void MapDummy_GetUsersQueryEndpoint(IEndpointRouteBuilder app) + { + var endpoint = app + .MapPost( + "/test", + [global::Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(200)] + async ( + [FromBody] global::Dummy.GetUsersQuery.Query parameters, + [FromServices] global::Dummy.GetUsersQuery.Handler handler, + CancellationToken token + ) => + { + var ret = await handler.HandleAsync(parameters, token); + return ret; + } + ); + + _ = endpoint.AllowAnonymous(); + } + } +} + +namespace Dummy +{ + + /// registered using [FromBody] + partial class GetUsersQuery; +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RoutesBuilder.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RoutesBuilder.g.verified.cs new file mode 100644 index 0000000..2134c39 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Post#RoutesBuilder.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: RoutesBuilder.g.cs +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder; + +public static partial class TestsRoutesBuilder +{ + public static IEndpointRouteBuilder MapTestsEndpoints( + this IEndpointRouteBuilder app + ) + { + MapDummy_GetUsersQueryEndpoint(app); + + return app; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.Dummy.GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.Dummy.GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..825796b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.Dummy.GetUsersQuery.g.verified.cs @@ -0,0 +1,69 @@ +//HintName: IH.Dummy.GetUsersQuery.g.cs +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable CS1591 + +namespace Dummy; + +partial class GetUsersQuery +{ + public sealed partial class Handler : global::Immediate.Handlers.Shared.IHandler + { + private readonly global::Dummy.GetUsersQuery.HandleBehavior _handleBehavior; + + public Handler( + global::Dummy.GetUsersQuery.HandleBehavior handleBehavior + ) + { + var handlerType = typeof(GetUsersQuery); + + _handleBehavior = handleBehavior; + + } + + public async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken = default + ) + { + return await _handleBehavior + .HandleAsync(request, cancellationToken) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public sealed class HandleBehavior : global::Immediate.Handlers.Shared.Behavior + { + + public HandleBehavior( + ) + { + } + + public override async global::System.Threading.Tasks.ValueTask HandleAsync( + global::Dummy.GetUsersQuery.Query request, + global::System.Threading.CancellationToken cancellationToken + ) + { + return await global::Dummy.GetUsersQuery + .HandleAsync( + request + , cancellationToken + ) + .ConfigureAwait(false); + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static IServiceCollection AddHandlers( + IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + services.Add(new(typeof(global::Dummy.GetUsersQuery.Handler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Immediate.Handlers.Shared.IHandler), typeof(global::Dummy.GetUsersQuery.Handler), lifetime)); + services.Add(new(typeof(global::Dummy.GetUsersQuery.HandleBehavior), typeof(global::Dummy.GetUsersQuery.HandleBehavior), lifetime)); + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.ServiceCollectionExtensions.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.ServiceCollectionExtensions.g.verified.cs new file mode 100644 index 0000000..96e0a6b --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#IH.ServiceCollectionExtensions.g.verified.cs @@ -0,0 +1,25 @@ +//HintName: IH.ServiceCollectionExtensions.g.cs +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +#pragma warning disable CS1591 + +public static class HandlerServiceCollectionExtensions +{ + public static IServiceCollection AddTestsBehaviors( + this IServiceCollection services) + { + + return services; + } + + public static IServiceCollection AddTestsHandlers( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + { + global::Dummy.GetUsersQuery.AddHandlers(services, lifetime); + + return services; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs new file mode 100644 index 0000000..6207ae0 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RouteBuilder.Dummy_GetUsersQuery.g.verified.cs @@ -0,0 +1,40 @@ +//HintName: RouteBuilder.Dummy_GetUsersQuery.g.cs +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder +{ + public static partial class TestsRoutesBuilder + { + private static void MapDummy_GetUsersQueryEndpoint(IEndpointRouteBuilder app) + { + var endpoint = app + .MapPut( + "/test", + [global::Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(200)] + async ( + [FromBody] global::Dummy.GetUsersQuery.Query parameters, + [FromServices] global::Dummy.GetUsersQuery.Handler handler, + CancellationToken token + ) => + { + var ret = await handler.HandleAsync(parameters, token); + return ret; + } + ); + + _ = endpoint.AllowAnonymous(); + } + } +} + +namespace Dummy +{ + + /// registered using [FromBody] + partial class GetUsersQuery; +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RoutesBuilder.g.verified.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RoutesBuilder.g.verified.cs new file mode 100644 index 0000000..2134c39 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.MapMethodWithProducesAttribute_method=Put#RoutesBuilder.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: RoutesBuilder.g.cs +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +#pragma warning disable CS1591 + +namespace Microsoft.AspNetCore.Builder; + +public static partial class TestsRoutesBuilder +{ + public static IEndpointRouteBuilder MapTestsEndpoints( + this IEndpointRouteBuilder app + ) + { + MapDummy_GetUsersQueryEndpoint(app); + + return app; + } +} diff --git a/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.cs b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.cs new file mode 100644 index 0000000..c1ddc08 --- /dev/null +++ b/tests/Immediate.Apis.Tests/GeneratorTests/HandleMethodAttributeTests.cs @@ -0,0 +1,50 @@ +namespace Immediate.Apis.Tests.GeneratorTests; + +public sealed class HandleMethodAttributeTests +{ + [Test] + [MethodDataSource(typeof(Utility), nameof(Utility.Methods))] + public async Task MapMethodWithProducesAttribute(string method) + { + var result = GeneratorTestHelper.RunGenerator( + $$""" + using System.Threading; + using System.Threading.Tasks; + using Immediate.Apis.Shared; + using Immediate.Handlers.Shared; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + + namespace Dummy; + + [Handler] + [Map{{method}}("/test")] + [AllowAnonymous] + public static partial class GetUsersQuery + { + public record Query; + + [ProducesResponseType(StatusCodes.Status200OK)] + private static ValueTask HandleAsync( + Query _, + CancellationToken token) + { + return ValueTask.FromResult(0); + } + } + """); + + Assert.Equal( + [ + @"Immediate.Apis.Generators/Immediate.Apis.Generators.ImmediateApisGenerator/RouteBuilder.Dummy_GetUsersQuery.g.cs", + @"Immediate.Apis.Generators/Immediate.Apis.Generators.ImmediateApisGenerator/RoutesBuilder.g.cs", + @"Immediate.Handlers.Generators/Immediate.Handlers.Generators.ImmediateHandlers.ImmediateHandlersGenerator/IH.Dummy.GetUsersQuery.g.cs", + @"Immediate.Handlers.Generators/Immediate.Handlers.Generators.ImmediateHandlers.ImmediateHandlersGenerator/IH.ServiceCollectionExtensions.g.cs", + ], + result.GeneratedTrees.Select(t => t.FilePath.Replace('\\', '/')) + ); + + _ = await Verify(result).UseParameters(method); + } +}