From 9e9744d37b713798180268da1cc461489c9519bc Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 29 Dec 2023 18:36:10 +0000 Subject: [PATCH] Allow stream return type (#39) --- ConsumePlugin/GeneratedRestClient.fs | 61 +++++++++++++++++++ ConsumePlugin/RestApiExample.fs | 10 +++ MyriadPlugin.Test/MyriadPlugin.Test.fsproj | 1 + MyriadPlugin.Test/TestReturnTypes.fs | 58 ++++++++++++++++++ WoofWare.Myriad.Plugins/AstHelper.fs | 10 +++ .../HttpClientGenerator.fs | 16 ++++- 6 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 MyriadPlugin.Test/TestReturnTypes.fs diff --git a/ConsumePlugin/GeneratedRestClient.fs b/ConsumePlugin/GeneratedRestClient.fs index 7e8facb..c228486 100644 --- a/ConsumePlugin/GeneratedRestClient.fs +++ b/ConsumePlugin/GeneratedRestClient.fs @@ -9,6 +9,7 @@ namespace PureGym open System open System.Threading open System.Threading.Tasks +open System.IO open RestEase /// Module for constructing a REST client. @@ -218,4 +219,64 @@ module PureGymApi = return node } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) + + member _.GetStream (ct : CancellationToken option) = + async { + let! ct = Async.CancellationToken + + let uri = + System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative)) + + let httpMessage = + new System.Net.Http.HttpRequestMessage ( + Method = System.Net.Http.HttpMethod.Get, + RequestUri = uri + ) + + let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask + let response = response.EnsureSuccessStatusCode () + let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + return node + } + |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) + + member _.GetStream' (ct : CancellationToken option) = + async { + let! ct = Async.CancellationToken + + let uri = + System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative)) + + let httpMessage = + new System.Net.Http.HttpRequestMessage ( + Method = System.Net.Http.HttpMethod.Get, + RequestUri = uri + ) + + let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask + let response = response.EnsureSuccessStatusCode () + let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + return node + } + |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) + + member _.GetStream'' (ct : CancellationToken option) = + async { + let! ct = Async.CancellationToken + + let uri = + System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative)) + + let httpMessage = + new System.Net.Http.HttpRequestMessage ( + Method = System.Net.Http.HttpMethod.Get, + RequestUri = uri + ) + + let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask + let response = response.EnsureSuccessStatusCode () + let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + return node + } + |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) } diff --git a/ConsumePlugin/RestApiExample.fs b/ConsumePlugin/RestApiExample.fs index df33809..ba86716 100644 --- a/ConsumePlugin/RestApiExample.fs +++ b/ConsumePlugin/RestApiExample.fs @@ -3,6 +3,7 @@ namespace PureGym open System open System.Threading open System.Threading.Tasks +open System.IO open RestEase [] @@ -29,3 +30,12 @@ type IPureGymApi = [] abstract GetPathParam : [] parameter : string * ?ct : CancellationToken -> Task + + [] + abstract GetStream : ?ct : CancellationToken -> Task + + [] + abstract GetStream' : ?ct : CancellationToken -> Task + + [] + abstract GetStream'' : ?ct : CancellationToken -> Task diff --git a/MyriadPlugin.Test/MyriadPlugin.Test.fsproj b/MyriadPlugin.Test/MyriadPlugin.Test.fsproj index cd133b3..8b1a0e7 100644 --- a/MyriadPlugin.Test/MyriadPlugin.Test.fsproj +++ b/MyriadPlugin.Test/MyriadPlugin.Test.fsproj @@ -9,6 +9,7 @@ + diff --git a/MyriadPlugin.Test/TestReturnTypes.fs b/MyriadPlugin.Test/TestReturnTypes.fs new file mode 100644 index 0000000..01b8bf7 --- /dev/null +++ b/MyriadPlugin.Test/TestReturnTypes.fs @@ -0,0 +1,58 @@ +namespace MyriadPlugin.Test + +open System +open System.IO +open System.Net +open FsUnitTyped +open System.Net.Http +open PureGym +open NUnit.Framework + +[] +module TestReturnTypes = + + [] + let ``String return`` () = + let proc (message : HttpRequestMessage) : HttpResponseMessage Async = + async { + message.Method |> shouldEqual HttpMethod.Get + let content = new StringContent ("this is not a JSON string") + let resp = new HttpResponseMessage (HttpStatusCode.OK) + resp.Content <- content + return resp + } + + use client = HttpClientMock.make (Uri "https://example.com") proc + let api = PureGymApi.make client + + api.GetPathParam("hi").Result |> shouldEqual "this is not a JSON string" + + [] + [] + [] + let ``Stream return`` (case : string) = + let result = [| 1uy ; 2uy ; 3uy ; 4uy |] + + let proc (message : HttpRequestMessage) : HttpResponseMessage Async = + async { + message.Method |> shouldEqual HttpMethod.Get + let result = new MemoryStream (result) + let content = new StreamContent (result) + let resp = new HttpResponseMessage (HttpStatusCode.OK) + resp.Content <- content + return resp + } + + use client = HttpClientMock.make (Uri "https://example.com") proc + let api = PureGymApi.make client + + use stream = + match case with + | "GetStream" -> api.GetStream().Result + | "GetStream'" -> api.GetStream'().Result + | "GetStream''" -> api.GetStream''().Result + | _ -> failwith $"unrecognised case: %s{case}" + + let buf = Array.zeroCreate 10 + stream.Read (buf, 0, 10) |> shouldEqual 4 + Array.take 4 buf |> shouldEqual result diff --git a/WoofWare.Myriad.Plugins/AstHelper.fs b/WoofWare.Myriad.Plugins/AstHelper.fs index 03b2d99..bdeddaf 100644 --- a/WoofWare.Myriad.Plugins/AstHelper.fs +++ b/WoofWare.Myriad.Plugins/AstHelper.fs @@ -116,6 +116,16 @@ module internal SynTypePatterns = | _ -> None | _ -> None + let (|Stream|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map (fun i -> i.idText) with + | [ "System" ; "IO" ; "Stream" ] + | [ "IO" ; "Stream" ] + | [ "Stream" ] -> Some () + | _ -> None + | _ -> None + let (|NumberType|_|) (fieldType : SynType) = match fieldType with | SynType.LongIdent ident -> diff --git a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs index 2eea758..96212a7 100644 --- a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs +++ b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs @@ -331,7 +331,8 @@ module internal HttpClientGenerator = let returnExpr = match info.ReturnType with - | String -> SynExpr.CreateIdentString "node" + | String + | Stream -> SynExpr.CreateIdentString "node" | _ -> JsonParseGenerator.parseNode None @@ -412,6 +413,19 @@ module internal HttpClientGenerator = ) ) ) + | Stream -> + yield + LetBang ( + "node", + SynExpr.awaitTask ( + SynExpr.CreateApp ( + SynExpr.CreateLongIdent ( + SynLongIdent.Create [ "response" ; "Content" ; "ReadAsStreamAsync" ] + ), + SynExpr.CreateIdentString "ct" + ) + ) + ) | _ -> yield LetBang (