From c51038448a01fa51539ccafacc9602f133825b4b Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:46:14 +0100 Subject: [PATCH] Be more forgiving about the source of the attributes (#129) --- ConsumePlugin/GeneratedRestClient.fs | 63 +++++++++++++++++++ ConsumePlugin/RestApiExample.fs | 13 ++++ .../RestEase.fs | 8 ++- .../SurfaceBaseline.txt | 4 ++ .../version.json | 2 +- .../HttpClientGenerator.fs | 25 ++++++-- 6 files changed, 107 insertions(+), 8 deletions(-) diff --git a/ConsumePlugin/GeneratedRestClient.fs b/ConsumePlugin/GeneratedRestClient.fs index 1033e74..c1a99b5 100644 --- a/ConsumePlugin/GeneratedRestClient.fs +++ b/ConsumePlugin/GeneratedRestClient.fs @@ -1140,6 +1140,69 @@ module ApiWithHeaders = member _.SomeHeader : string = someHeader () member _.SomeOtherHeader : int = someOtherHeader () + member this.GetPathParam (parameter : string, ct : CancellationToken option) = + async { + let! ct = Async.CancellationToken + + let uri = + System.Uri ( + (match client.BaseAddress with + | null -> + raise ( + System.ArgumentNullException ( + nameof (client.BaseAddress), + "No base address was supplied on the type, and no BaseAddress was on the HttpClient." + ) + ) + | v -> v), + System.Uri ( + "endpoint/{param}" + .Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode), + System.UriKind.Relative + ) + ) + + let httpMessage = + new System.Net.Http.HttpRequestMessage ( + Method = System.Net.Http.HttpMethod.Get, + RequestUri = uri + ) + + do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ()) + do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ()) + do httpMessage.Headers.Add ("Header-Name", "Header-Value") + let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask + let response = response.EnsureSuccessStatusCode () + let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask + return responseString + } + |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) + } +namespace PureGym + +open System +open System.Threading +open System.Threading.Tasks +open System.IO +open System.Net +open System.Net.Http +open RestEase + +/// Module for constructing a REST client. +[] +[] +module ApiWithHeaders2 = + /// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties. + let make + (someHeader : unit -> string) + (someOtherHeader : unit -> int) + (client : System.Net.Http.HttpClient) + : IApiWithHeaders2 + = + { new IApiWithHeaders2 with + member _.SomeHeader : string = someHeader () + member _.SomeOtherHeader : int = someOtherHeader () + member this.GetPathParam (parameter : string, ct : CancellationToken option) = async { let! ct = Async.CancellationToken diff --git a/ConsumePlugin/RestApiExample.fs b/ConsumePlugin/RestApiExample.fs index 9b1ca03..c3cc673 100644 --- a/ConsumePlugin/RestApiExample.fs +++ b/ConsumePlugin/RestApiExample.fs @@ -142,3 +142,16 @@ type IApiWithHeaders = [] abstract GetPathParam : [] parameter : string * ?ct : CancellationToken -> Task + +[] +[] +type IApiWithHeaders2 = + [] + abstract SomeHeader : string + + [] + abstract SomeOtherHeader : int + + [] + abstract GetPathParam : + [] parameter : string * ?ct : CancellationToken -> Task diff --git a/WoofWare.Myriad.Plugins.Attributes/RestEase.fs b/WoofWare.Myriad.Plugins.Attributes/RestEase.fs index ba4dd8b..3213c71 100644 --- a/WoofWare.Myriad.Plugins.Attributes/RestEase.fs +++ b/WoofWare.Myriad.Plugins.Attributes/RestEase.fs @@ -50,10 +50,14 @@ module RestEase = /// Indicates that this interface member causes the interface to set a header with the given name, /// whose value is obtained whenever required by a fresh call to the interface member. - type HeaderAttribute (header : string) = + type HeaderAttribute (header : string, value : string option) = inherit Attribute () + new (header : string) = HeaderAttribute (header, None) + new (header : string, value : string) = HeaderAttribute (header, Some value) /// Indicates that this argument to a method is interpolated into the request path at runtime /// by writing it into the templated string that specifies the HTTP query e.g. in the `[]`. - type PathAttribute () = + type PathAttribute (path : string option) = inherit Attribute () + new (path : string) = PathAttribute (Some path) + new () = PathAttribute None diff --git a/WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt b/WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt index f22b412..9655f64 100644 --- a/WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt +++ b/WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt @@ -29,12 +29,16 @@ WoofWare.Myriad.Plugins.RestEase+GetAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+HeadAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+HeadAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+HeaderAttribute inherit System.Attribute +WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string option) +WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string) WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+OptionsAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+OptionsAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+PatchAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+PatchAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+PathAttribute inherit System.Attribute +WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string +WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string option WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.RestEase+PostAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+PostAttribute..ctor [constructor]: string diff --git a/WoofWare.Myriad.Plugins.Attributes/version.json b/WoofWare.Myriad.Plugins.Attributes/version.json index c8ba3f6..c53eb2b 100644 --- a/WoofWare.Myriad.Plugins.Attributes/version.json +++ b/WoofWare.Myriad.Plugins.Attributes/version.json @@ -1,5 +1,5 @@ { - "version": "2.3", + "version": "3.0", "publicReleaseRefSpec": [ "^refs/heads/main$" ], diff --git a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs index 3ae70c7..e688e6c 100644 --- a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs +++ b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs @@ -1,8 +1,6 @@ namespace WoofWare.Myriad.Plugins -open System open System.Net.Http -open System.Text open Fantomas.FCS.Syntax open Fantomas.FCS.SyntaxTrivia open Fantomas.FCS.Xml @@ -143,7 +141,8 @@ module internal HttpClientGenerator = |> List.choose (fun attr -> match attr.TypeName.AsString with | "Header" - | "RestEase.Header" -> + | "RestEase.Header" + | "WoofWare.Myriad.Plugins.RestEase.Header" -> match attr.ArgExpr with | SynExpr.Paren (SynExpr.Tuple (_, [ v1 ; v2 ], _, _), _, _, _) -> Some [ SynExpr.stripOptionalParen v1 ; SynExpr.stripOptionalParen v2 ] @@ -725,6 +724,10 @@ module internal HttpClientGenerator = attrs |> List.choose (fun attr -> match attr.TypeName.AsString with + | "RestEase.Query" + | "RestEase.QueryAttribute" + | "WoofWare.Myriad.Plugins.RestEase.Query" + | "WoofWare.Myriad.Plugins.RestEase.QueryAttribute" | "Query" | "QueryAttribute" -> match attr.ArgExpr with @@ -733,6 +736,10 @@ module internal HttpClientGenerator = Some (HttpAttribute.Query (Some s)) | SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Query attribute: %+A{a}" | _ -> None + | "RestEase.Path" + | "RestEase.PathAttribute" + | "WoofWare.Myriad.Plugins.RestEase.Path" + | "WoofWare.Myriad.Plugins.RestEase.PathAttribute" | "Path" | "PathAttribute" -> match attr.ArgExpr with @@ -741,6 +748,10 @@ module internal HttpClientGenerator = | SynExpr.Const (SynConst.Unit, _) -> Some (HttpAttribute.Path PathSpec.MatchArgName) | SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Path attribute: %+A{a}" | _ -> None + | "RestEase.Body" + | "RestEase.BodyAttribute" + | "WoofWare.Myriad.Plugins.RestEase.Body" + | "WoofWare.Myriad.Plugins.RestEase.BodyAttribute" | "Body" | "BodyAttribute" -> match attr.ArgExpr with @@ -756,8 +767,10 @@ module internal HttpClientGenerator = match attr.TypeName.AsString with | "BasePath" | "RestEase.BasePath" + | "WoofWare.Myriad.Plugins.RestEase.BasePath" | "BasePathAttribute" - | "RestEase.BasePathAttribute" -> Some attr.ArgExpr + | "RestEase.BasePathAttribute" + | "WoofWare.Myriad.Plugins.RestEase.BasePathAttribute" -> Some attr.ArgExpr | _ -> None ) @@ -767,8 +780,10 @@ module internal HttpClientGenerator = match attr.TypeName.AsString with | "BaseAddress" | "RestEase.BaseAddress" + | "WoofWare.Myriad.Plugins.RestEase.BaseAddress" | "BaseAddressAttribute" - | "RestEase.BaseAddressAttribute" -> Some attr.ArgExpr + | "RestEase.BaseAddressAttribute" + | "WoofWare.Myriad.Plugins.RestEase.BaseAddressAttribute" -> Some attr.ArgExpr | _ -> None )