From cfe03842f856beb184a4423415457a418a3cd532 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Fri, 2 Feb 2024 10:14:42 -0800 Subject: [PATCH] Add RequestReadyAsync override to HttpClientService. --- Directory.Packages.props | 4 +- src/Facility.Core/Http/HttpClientService.cs | 7 +++ tools/EdgeCases.fsd | 15 ++++++ tools/EdgeCases/CustomHttpRequestDto.g.cs | 47 +++++++++++++++++++ tools/EdgeCases/CustomHttpResponseDto.g.cs | 47 +++++++++++++++++++ tools/EdgeCases/DelegatingEdgeCases.g.cs | 6 +++ tools/EdgeCases/EdgeCasesMethods.g.cs | 4 ++ .../EdgeCases/Http/EdgeCasesHttpHandler.g.cs | 9 +++- .../EdgeCases/Http/EdgeCasesHttpMapping.g.cs | 39 +++++++++++++++ tools/EdgeCases/Http/HttpClientEdgeCases.cs | 19 ++++++++ tools/EdgeCases/Http/HttpClientEdgeCases.g.cs | 6 +++ tools/EdgeCases/IEdgeCases.g.cs | 5 ++ 12 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 tools/EdgeCases/CustomHttpRequestDto.g.cs create mode 100644 tools/EdgeCases/CustomHttpResponseDto.g.cs create mode 100644 tools/EdgeCases/Http/HttpClientEdgeCases.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 1540677b..af6c6dfe 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,8 +6,8 @@ - - + + diff --git a/src/Facility.Core/Http/HttpClientService.cs b/src/Facility.Core/Http/HttpClientService.cs index 034c2c00..c89a6d8f 100644 --- a/src/Facility.Core/Http/HttpClientService.cs +++ b/src/Facility.Core/Http/HttpClientService.cs @@ -193,8 +193,15 @@ protected virtual bool ShouldCreateErrorFromException(Exception exception) /// protected virtual ServiceErrorDto CreateErrorFromException(Exception exception) => ServiceErrorUtility.CreateInternalErrorForException(exception); + /// + /// Called right before the request is sent, before aspects are applied. + /// + protected virtual Task RequestReadyAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken) => Task.CompletedTask; + private async Task SendRequestAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken) { + await AdaptTask(RequestReadyAsync(httpRequest, requestDto, cancellationToken)).ConfigureAwait(true); + if (m_aspects != null) { foreach (var aspect in m_aspects) diff --git a/tools/EdgeCases.fsd b/tools/EdgeCases.fsd index 96c159bc..c8226f3a 100644 --- a/tools/EdgeCases.fsd +++ b/tools/EdgeCases.fsd @@ -15,6 +15,21 @@ service EdgeCases { } + /// Custom HTTP method. + method customHttp + { + value: string; + + [http(from: "custom")] + extras: map; + }: + { + value: string; + + [http(from: "custom")] + extras: map; + } + /// An old DTO. [obsolete] data OldEmptyData diff --git a/tools/EdgeCases/CustomHttpRequestDto.g.cs b/tools/EdgeCases/CustomHttpRequestDto.g.cs new file mode 100644 index 00000000..83703128 --- /dev/null +++ b/tools/EdgeCases/CustomHttpRequestDto.g.cs @@ -0,0 +1,47 @@ +// +// DO NOT EDIT: generated by fsdgencsharp +// +#nullable enable +using System; +using System.Collections.Generic; +using Facility.Core; +using Facility.Core.MessagePack; + +namespace EdgeCases +{ + /// + /// Request for CustomHttp. + /// + [System.CodeDom.Compiler.GeneratedCode("fsdgencsharp", "")] + [MessagePack.MessagePackObject] + public sealed partial class CustomHttpRequestDto : ServiceDto + { + /// + /// Creates an instance. + /// + public CustomHttpRequestDto() + { + } + + [MessagePack.Key("value")] + public string? Value { get; set; } + + [MessagePack.Key("extras")] + public IReadOnlyDictionary? Extras { get; set; } + + /// + /// The JSON serializer. + /// + protected override JsonServiceSerializer JsonSerializer => SystemTextJsonServiceSerializer.Instance; + + /// + /// Determines if two DTOs are equivalent. + /// + public override bool IsEquivalentTo(CustomHttpRequestDto? other) + { + return other != null && + Value == other.Value && + ServiceDataUtility.AreEquivalentFieldValues(Extras, other.Extras); + } + } +} diff --git a/tools/EdgeCases/CustomHttpResponseDto.g.cs b/tools/EdgeCases/CustomHttpResponseDto.g.cs new file mode 100644 index 00000000..a65b4b74 --- /dev/null +++ b/tools/EdgeCases/CustomHttpResponseDto.g.cs @@ -0,0 +1,47 @@ +// +// DO NOT EDIT: generated by fsdgencsharp +// +#nullable enable +using System; +using System.Collections.Generic; +using Facility.Core; +using Facility.Core.MessagePack; + +namespace EdgeCases +{ + /// + /// Response for CustomHttp. + /// + [System.CodeDom.Compiler.GeneratedCode("fsdgencsharp", "")] + [MessagePack.MessagePackObject] + public sealed partial class CustomHttpResponseDto : ServiceDto + { + /// + /// Creates an instance. + /// + public CustomHttpResponseDto() + { + } + + [MessagePack.Key("value")] + public string? Value { get; set; } + + [MessagePack.Key("extras")] + public IReadOnlyDictionary? Extras { get; set; } + + /// + /// The JSON serializer. + /// + protected override JsonServiceSerializer JsonSerializer => SystemTextJsonServiceSerializer.Instance; + + /// + /// Determines if two DTOs are equivalent. + /// + public override bool IsEquivalentTo(CustomHttpResponseDto? other) + { + return other != null && + Value == other.Value && + ServiceDataUtility.AreEquivalentFieldValues(Extras, other.Extras); + } + } +} diff --git a/tools/EdgeCases/DelegatingEdgeCases.g.cs b/tools/EdgeCases/DelegatingEdgeCases.g.cs index 23377800..f2d47892 100644 --- a/tools/EdgeCases/DelegatingEdgeCases.g.cs +++ b/tools/EdgeCases/DelegatingEdgeCases.g.cs @@ -28,6 +28,12 @@ public DelegatingEdgeCases(ServiceDelegator delegator) => public virtual async Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default) => (await m_delegator(EdgeCasesMethods.OldMethod, request, cancellationToken).ConfigureAwait(false)).Cast(); + /// + /// Custom HTTP method. + /// + public virtual async Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default) => + (await m_delegator(EdgeCasesMethods.CustomHttp, request, cancellationToken).ConfigureAwait(false)).Cast(); + public virtual async Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default) => (await m_delegator(EdgeCasesMethods.SnakeMethod, request, cancellationToken).ConfigureAwait(false)).Cast(); diff --git a/tools/EdgeCases/EdgeCasesMethods.g.cs b/tools/EdgeCases/EdgeCasesMethods.g.cs index ae882209..9deb0c0c 100644 --- a/tools/EdgeCases/EdgeCasesMethods.g.cs +++ b/tools/EdgeCases/EdgeCasesMethods.g.cs @@ -15,6 +15,10 @@ internal static class EdgeCasesMethods ServiceMethodInfo.Create( "oldMethod", "EdgeCases", x => x.OldMethodAsync); + public static readonly IServiceMethodInfo CustomHttp = + ServiceMethodInfo.Create( + "customHttp", "EdgeCases", x => x.CustomHttpAsync); + public static readonly IServiceMethodInfo SnakeMethod = ServiceMethodInfo.Create( "snake_method", "EdgeCases", x => x.SnakeMethodAsync); diff --git a/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs b/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs index 235f272c..3f772260 100644 --- a/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs +++ b/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs @@ -40,7 +40,8 @@ public EdgeCasesHttpHandler(Func getService, Ser /// public override async Task TryHandleHttpRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) { - return await AdaptTask(TryHandleOldMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ?? + return await AdaptTask(TryHandleCustomHttpAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ?? + await AdaptTask(TryHandleOldMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ?? await AdaptTask(TryHandleSnakeMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true); } @@ -51,6 +52,12 @@ public EdgeCasesHttpHandler(Func getService, Ser public Task TryHandleOldMethodAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) => TryHandleServiceMethodAsync(EdgeCasesHttpMapping.OldMethodMapping, httpRequest, GetService(httpRequest).OldMethodAsync, cancellationToken); + /// + /// Custom HTTP method. + /// + public Task TryHandleCustomHttpAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) => + TryHandleServiceMethodAsync(EdgeCasesHttpMapping.CustomHttpMapping, httpRequest, GetService(httpRequest).CustomHttpAsync, cancellationToken); + public Task TryHandleSnakeMethodAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) => TryHandleServiceMethodAsync(EdgeCasesHttpMapping.SnakeMethodMapping, httpRequest, GetService(httpRequest).SnakeMethodAsync, cancellationToken); diff --git a/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs b/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs index 639268cc..5b607a60 100644 --- a/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs +++ b/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs @@ -35,6 +35,45 @@ public static partial class EdgeCasesHttpMapping }, }.Build(); + /// + /// Custom HTTP method. + /// + public static readonly HttpMethodMapping CustomHttpMapping = + new HttpMethodMapping.Builder + { + HttpMethod = HttpMethod.Post, + Path = "/customHttp", + RequestBodyType = typeof(CustomHttpRequestDto), + GetRequestBody = request => + new CustomHttpRequestDto + { + Value = request.Value, + }, + CreateRequest = body => + new CustomHttpRequestDto + { + Value = ((CustomHttpRequestDto) body!).Value, + }, + ResponseMappings = + { + new HttpResponseMapping.Builder + { + StatusCode = (HttpStatusCode) 200, + ResponseBodyType = typeof(CustomHttpResponseDto), + GetResponseBody = response => + new CustomHttpResponseDto + { + Value = response.Value, + }, + CreateResponse = body => + new CustomHttpResponseDto + { + Value = ((CustomHttpResponseDto) body!).Value, + }, + }.Build(), + }, + }.Build(); + public static readonly HttpMethodMapping SnakeMethodMapping = new HttpMethodMapping.Builder { diff --git a/tools/EdgeCases/Http/HttpClientEdgeCases.cs b/tools/EdgeCases/Http/HttpClientEdgeCases.cs new file mode 100644 index 00000000..14690e7b --- /dev/null +++ b/tools/EdgeCases/Http/HttpClientEdgeCases.cs @@ -0,0 +1,19 @@ +using System.Web; +using Facility.Core; + +namespace EdgeCases.Http; + +public sealed partial class HttpClientEdgeCases +{ + protected override Task RequestReadyAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken) + { + if (requestDto is CustomHttpRequestDto customDto) + { + const string prefix = "extra-"; + var query = HttpUtility.ParseQueryString(httpRequest.RequestUri.Query); + customDto.Extras = query.AllKeys.Where(x => x?.StartsWith(prefix, StringComparison.Ordinal) is true).ToDictionary(x => x.Substring(prefix.Length), x => query[x]); + } + + return Task.CompletedTask; + } +} diff --git a/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs b/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs index 55f04659..de1f69ab 100644 --- a/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs +++ b/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs @@ -28,6 +28,12 @@ public HttpClientEdgeCases(HttpClientServiceSettings? settings = null) public Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default) => TrySendRequestAsync(EdgeCasesHttpMapping.OldMethodMapping, request, cancellationToken); + /// + /// Custom HTTP method. + /// + public Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default) => + TrySendRequestAsync(EdgeCasesHttpMapping.CustomHttpMapping, request, cancellationToken); + public Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default) => TrySendRequestAsync(EdgeCasesHttpMapping.SnakeMethodMapping, request, cancellationToken); diff --git a/tools/EdgeCases/IEdgeCases.g.cs b/tools/EdgeCases/IEdgeCases.g.cs index fa3440a6..0ae2e732 100644 --- a/tools/EdgeCases/IEdgeCases.g.cs +++ b/tools/EdgeCases/IEdgeCases.g.cs @@ -18,6 +18,11 @@ public partial interface IEdgeCases [Obsolete] Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default); + /// + /// Custom HTTP method. + /// + Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default); + Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default); } }