Implement AllowAnyStatusCode (#41)

This commit is contained in:
Patrick Stevens
2023-12-29 23:01:45 +00:00
committed by GitHub
parent 346d6e8321
commit 59be1f1806
5 changed files with 135 additions and 10 deletions

View File

@@ -361,4 +361,43 @@ module PureGymApi =
return node
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetWithAnyReturnCode (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 node = response
return node
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetWithoutAnyReturnCode (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
return node
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}

View File

@@ -16,13 +16,13 @@ type IPureGymApi =
[<Get "v1/gyms/{gym_id}/attendance">]
abstract GetGymAttendance : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
[<Get "v1/member">]
[<RestEase.GetAttribute "v1/member">]
abstract GetMember : ?ct : CancellationToken -> Task<Member>
[<Get "v1/gyms/{gym_id}">]
[<RestEase.Get "v1/gyms/{gym_id}">]
abstract GetGym : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<Gym>
[<Get "v1/member/activity">]
[<GetAttribute "v1/member/activity">]
abstract GetMemberActivity : ?ct : CancellationToken -> Task<MemberActivityDto>
// We'll use this one to check handling of absolute URIs too
@@ -53,3 +53,10 @@ type IPureGymApi =
[<Get "endpoint">]
abstract GetResponseMessage''' : ?ct : CancellationToken -> Task<HttpResponseMessage>
[<Get "endpoint">]
[<AllowAnyStatusCode>]
abstract GetWithAnyReturnCode : ?ct : CancellationToken -> Task<HttpResponseMessage>
[<Get "endpoint">]
abstract GetWithoutAnyReturnCode : ?ct : CancellationToken -> Task<HttpResponseMessage>

View File

@@ -10,6 +10,7 @@
<Compile Include="HttpClient.fs"/>
<Compile Include="TestPathParam.fs" />
<Compile Include="TestReturnTypes.fs" />
<Compile Include="TestAllowAnyStatusCode.fs" />
<Compile Include="TestSurface.fs"/>
<Compile Include="TestRemoveOptions.fs"/>
<Compile Include="TestJsonParse.fs"/>

View File

@@ -0,0 +1,62 @@
namespace MyriadPlugin.Test
open System
open System.Net
open System.Net.Http
open NUnit.Framework
open FsUnitTyped
open PureGym
[<TestFixture>]
module TestAllowAnyStatusCode =
[<Test>]
let ``Without AllowAnyStatusCode we throw`` () =
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
async {
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent ("nothing was here :(")
let resp = new HttpResponseMessage (HttpStatusCode.NotFound)
resp.Content <- content
return resp
}
use client = HttpClientMock.make (Uri "https://example.com") proc
let api = PureGymApi.make client
let exc =
async {
let! message = Async.AwaitTask (api.GetWithoutAnyReturnCode ()) |> Async.Catch
match message with
| Choice1Of2 _ -> return failwith "test failure"
| Choice2Of2 exc -> return exc
}
|> Async.RunSynchronously
let exc =
match exc with
| :? AggregateException as exc -> exc
| exc -> failwith $"Test failure: expected AggregateException, got %+A{exc}"
match exc.InnerException with
| :? HttpRequestException as exc -> exc.Message.Contains "404 (Not Found)" |> shouldEqual true
| e -> failwith $"Test failure: %+A{e}"
[<Test>]
let ``With AllowAnyStatusCode we do not throw`` () =
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
async {
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent ("nothing was here :(")
let resp = new HttpResponseMessage (HttpStatusCode.NotFound)
resp.Content <- content
return resp
}
use client = HttpClientMock.make (Uri "https://example.com") proc
let api = PureGymApi.make client
let message = api.GetWithAnyReturnCode().Result
message.StatusCode |> shouldEqual HttpStatusCode.NotFound
message.Content.ReadAsStringAsync().Result |> shouldEqual "nothing was here :("

View File

@@ -52,6 +52,7 @@ module internal HttpClientGenerator =
Arity : SynArgInfo list
Args : Parameter list
Identifier : Ident
EnsureSuccessHttpCode : bool
}
let httpMethodString (m : HttpMethod) : string =
@@ -116,6 +117,17 @@ module internal HttpClientGenerator =
| matchingAttrs ->
failwithf "Required exactly one recognised RestEase attribute on member, but got %i" matchingAttrs.Length
let shouldAllowAnyStatusCode (attrs : SynAttribute list) : bool =
attrs
|> List.exists (fun attr ->
match attr.TypeName.AsString with
| "AllowAnyStatusCode"
| "AllowAnyStatusCodeAttribute"
| "RestEase.AllowAnyStatusCode"
| "RestEase.AllowAnyStatusCodeAttribute" -> true
| _ -> false
)
let constructMember (info : MemberInfo) : SynMemberDefn =
let valInfo =
SynValInfo.SynValInfo (
@@ -392,14 +404,15 @@ module internal HttpClientGenerator =
)
)
)
yield
Let (
"response",
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "response" ; "EnsureSuccessStatusCode" ]),
SynExpr.CreateConst SynConst.Unit
if info.EnsureSuccessHttpCode then
yield
Let (
"response",
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "response" ; "EnsureSuccessStatusCode" ]),
SynExpr.CreateConst SynConst.Unit
)
)
)
match info.ReturnType with
| HttpResponseMessage -> yield Let ("node", SynExpr.CreateIdentString "response")
| String ->
@@ -617,6 +630,8 @@ module internal HttpClientGenerator =
let httpMethod, url = extractHttpInformation attrs
let shouldEnsureSuccess = not (shouldAllowAnyStatusCode attrs)
{
HttpMethod = httpMethod
UrlTemplate = url
@@ -624,6 +639,7 @@ module internal HttpClientGenerator =
Arity = arity
Args = args
Identifier = ident
EnsureSuccessHttpCode = shouldEnsureSuccess
}
| _ -> failwithf "Unrecognised member definition: %+A" defn
)