mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-05 12:08:46 +00:00
Respect BasePath attribute (#44)
This commit is contained in:
@@ -26,7 +26,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("v1/gyms/", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("v1/gyms/", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -52,7 +57,9 @@ module PureGymApi =
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
client.BaseAddress,
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
"v1/gyms/{gym_id}/attendance"
|
||||
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||
@@ -83,7 +90,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("v1/member", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("v1/member", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -109,7 +121,9 @@ module PureGymApi =
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
client.BaseAddress,
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
"v1/gyms/{gym_id}"
|
||||
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||
@@ -140,7 +154,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("v1/member/activity", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("v1/member/activity", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -166,7 +185,9 @@ module PureGymApi =
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
client.BaseAddress,
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
("/v2/gymSessions/member"
|
||||
+ "?fromDate="
|
||||
@@ -201,7 +222,9 @@ module PureGymApi =
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
client.BaseAddress,
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
"endpoint/{param}"
|
||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||
@@ -227,7 +250,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -247,7 +275,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -267,7 +300,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -287,7 +325,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -307,7 +350,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -327,7 +375,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -347,7 +400,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -367,7 +425,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -386,7 +449,12 @@ module PureGymApi =
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (client.BaseAddress, System.Uri ("endpoint", System.UriKind.Relative))
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://whatnot.com"
|
||||
| v -> v),
|
||||
System.Uri ("endpoint", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
@@ -401,3 +469,55 @@ module PureGymApi =
|
||||
}
|
||||
|> (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.
|
||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||
[<RequireQualifiedAccess>]
|
||||
module ApiWithoutBasePath =
|
||||
/// Create a REST client.
|
||||
let make (client : System.Net.Http.HttpClient) : IApiWithoutBasePath =
|
||||
{ new IApiWithoutBasePath with
|
||||
member _.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 path 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
|
||||
)
|
||||
|
||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||
let response = response.EnsureSuccessStatusCode ()
|
||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||
return node
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ open System.Net.Http
|
||||
open RestEase
|
||||
|
||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||
[<BasePath "https://whatnot.com">]
|
||||
type IPureGymApi =
|
||||
[<Get "v1/gyms/">]
|
||||
abstract GetGyms : ?ct : CancellationToken -> Task<Gym list>
|
||||
@@ -60,3 +61,8 @@ type IPureGymApi =
|
||||
|
||||
[<Get "endpoint">]
|
||||
abstract GetWithoutAnyReturnCode : ?ct : CancellationToken -> Task<HttpResponseMessage>
|
||||
|
||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||
type IApiWithoutBasePath =
|
||||
[<Get "endpoint/{param}">]
|
||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||
|
@@ -11,7 +11,11 @@ type HttpClientMock (result : HttpRequestMessage -> Async<HttpResponseMessage>)
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module HttpClientMock =
|
||||
let make (baseUrl : System.Uri) (handler : HttpRequestMessage -> Async<HttpResponseMessage>) =
|
||||
let makeNoUri (handler : HttpRequestMessage -> Async<HttpResponseMessage>) =
|
||||
let result = new HttpClientMock (handler)
|
||||
result
|
||||
|
||||
let make (baseUrl : System.Uri) (handler : HttpRequestMessage -> Async<HttpResponseMessage>) =
|
||||
let result = makeNoUri handler
|
||||
result.BaseAddress <- baseUrl
|
||||
result
|
||||
|
@@ -8,15 +8,16 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="HttpClient.fs"/>
|
||||
<Compile Include="TestPathParam.fs" />
|
||||
<Compile Include="TestReturnTypes.fs" />
|
||||
<Compile Include="TestAllowAnyStatusCode.fs" />
|
||||
<Compile Include="PureGymDtos.fs"/>
|
||||
<Compile Include="TestJsonParse\TestJsonParse.fs" />
|
||||
<Compile Include="TestJsonParse\TestPureGymJson.fs" />
|
||||
<Compile Include="TestHttpClient\TestPureGymRestApi.fs" />
|
||||
<Compile Include="TestHttpClient\TestPathParam.fs" />
|
||||
<Compile Include="TestHttpClient\TestReturnTypes.fs" />
|
||||
<Compile Include="TestHttpClient\TestAllowAnyStatusCode.fs" />
|
||||
<Compile Include="TestHttpClient\TestBasePath.fs" />
|
||||
<Compile Include="TestSurface.fs"/>
|
||||
<Compile Include="TestRemoveOptions.fs"/>
|
||||
<Compile Include="TestJsonParse.fs"/>
|
||||
<Compile Include="PureGymDtos.fs"/>
|
||||
<Compile Include="TestPureGymJson.fs"/>
|
||||
<Compile Include="TestPureGymRestApi.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
80
MyriadPlugin.Test/TestHttpClient/TestBasePath.fs
Normal file
80
MyriadPlugin.Test/TestHttpClient/TestBasePath.fs
Normal file
@@ -0,0 +1,80 @@
|
||||
namespace MyriadPlugin.Test
|
||||
|
||||
open System
|
||||
open System.Net
|
||||
open System.Net.Http
|
||||
open NUnit.Framework
|
||||
open PureGym
|
||||
open FsUnitTyped
|
||||
|
||||
[<TestFixture>]
|
||||
module TestBasePath =
|
||||
[<Test>]
|
||||
let ``Base path is respected`` () =
|
||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||
async {
|
||||
message.Method |> shouldEqual HttpMethod.Get
|
||||
let content = new StringContent (message.RequestUri.ToString ())
|
||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||
resp.Content <- content
|
||||
return resp
|
||||
}
|
||||
|
||||
use client = HttpClientMock.makeNoUri proc
|
||||
let api = PureGymApi.make client
|
||||
|
||||
let observedUri = api.GetPathParam("param").Result
|
||||
observedUri |> shouldEqual "https://whatnot.com/endpoint/param"
|
||||
|
||||
[<Test>]
|
||||
let ``Without a base path but with BaseAddress, request goes through`` () =
|
||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||
async {
|
||||
message.Method |> shouldEqual HttpMethod.Get
|
||||
let content = new StringContent (message.RequestUri.ToString ())
|
||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||
resp.Content <- content
|
||||
return resp
|
||||
}
|
||||
|
||||
use client = HttpClientMock.make (System.Uri "https://baseaddress.com") proc
|
||||
let api = ApiWithoutBasePath.make client
|
||||
|
||||
let observedUri = api.GetPathParam("param").Result
|
||||
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
||||
|
||||
[<Test>]
|
||||
let ``Without a base path, request throws`` () =
|
||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||
async {
|
||||
message.Method |> shouldEqual HttpMethod.Get
|
||||
let content = new StringContent (message.RequestUri.ToString ())
|
||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||
resp.Content <- content
|
||||
return resp
|
||||
}
|
||||
|
||||
use client = HttpClientMock.makeNoUri proc
|
||||
let api = ApiWithoutBasePath.make client
|
||||
|
||||
let observedExc =
|
||||
async {
|
||||
let! result = api.GetPathParam ("param") |> Async.AwaitTask |> Async.Catch
|
||||
|
||||
match result with
|
||||
| Choice1Of2 _ -> return failwith "test failure"
|
||||
| Choice2Of2 exc -> return exc
|
||||
}
|
||||
|> Async.RunSynchronously
|
||||
|
||||
let observedExc =
|
||||
match observedExc with
|
||||
| :? AggregateException as exc ->
|
||||
match exc.InnerException with
|
||||
| :? ArgumentNullException as exc -> exc
|
||||
| _ -> failwith "test failure"
|
||||
| _ -> failwith "test failure"
|
||||
|
||||
observedExc.Message
|
||||
|> shouldEqual
|
||||
"No base path was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
@@ -53,6 +53,7 @@ module internal HttpClientGenerator =
|
||||
Args : Parameter list
|
||||
Identifier : Ident
|
||||
EnsureSuccessHttpCode : bool
|
||||
BasePath : SynExpr option
|
||||
}
|
||||
|
||||
let httpMethodString (m : HttpMethod) : string =
|
||||
@@ -296,13 +297,55 @@ module internal HttpClientGenerator =
|
||||
let requestUri =
|
||||
let uriIdent = SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ])
|
||||
|
||||
let baseAddress =
|
||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "client" ; "BaseAddress" ])
|
||||
|
||||
let baseAddress =
|
||||
SynExpr.CreateMatch (
|
||||
baseAddress,
|
||||
[
|
||||
SynMatchClause.Create (
|
||||
SynPat.CreateNull,
|
||||
None,
|
||||
match info.BasePath with
|
||||
| None ->
|
||||
SynExpr.CreateApp (
|
||||
SynExpr.CreateIdentString "raise",
|
||||
SynExpr.CreateParen (
|
||||
SynExpr.CreateApp (
|
||||
SynExpr.CreateLongIdent (
|
||||
SynLongIdent.Create [ "System" ; "ArgumentNullException" ]
|
||||
),
|
||||
SynExpr.CreateParenedTuple
|
||||
[
|
||||
SynExpr.CreateApp (
|
||||
SynExpr.CreateIdentString "nameof",
|
||||
SynExpr.CreateParen baseAddress
|
||||
)
|
||||
SynExpr.CreateConstString
|
||||
"No base path was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
| Some expr -> SynExpr.CreateApp (uriIdent, expr)
|
||||
)
|
||||
SynMatchClause.Create (
|
||||
SynPat.CreateNamed (Ident.Create "v"),
|
||||
None,
|
||||
SynExpr.CreateIdentString "v"
|
||||
)
|
||||
]
|
||||
)
|
||||
|> SynExpr.CreateParen
|
||||
|
||||
SynExpr.App (
|
||||
ExprAtomicFlag.Atomic,
|
||||
false,
|
||||
uriIdent,
|
||||
SynExpr.CreateParenedTuple
|
||||
[
|
||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "client" ; "BaseAddress" ])
|
||||
baseAddress
|
||||
SynExpr.CreateApp (
|
||||
uriIdent,
|
||||
SynExpr.CreateParenedTuple
|
||||
@@ -551,15 +594,31 @@ module internal HttpClientGenerator =
|
||||
convertSigParam param :: extractTypes rest
|
||||
| _ -> failwithf "Didn't have alternating type-and-star in interface member definition: %+A" tupleType
|
||||
|
||||
let extractBasePath (attrs : SynAttributes) : SynExpr option =
|
||||
attrs
|
||||
|> List.tryPick (fun attr ->
|
||||
attr.Attributes
|
||||
|> List.tryPick (fun attr ->
|
||||
match attr.TypeName.AsString with
|
||||
| "BasePath"
|
||||
| "RestEase.BasePath"
|
||||
| "BasePathAttribute"
|
||||
| "RestEase.BasePathAttribute" -> Some attr.ArgExpr
|
||||
| _ -> None
|
||||
)
|
||||
)
|
||||
|
||||
let createModule
|
||||
(opens : SynOpenDeclTarget list)
|
||||
(ns : LongIdent)
|
||||
(interfaceType : SynTypeDefn)
|
||||
: SynModuleOrNamespace
|
||||
=
|
||||
let (SynTypeDefn (SynComponentInfo (_, _, _, interfaceName, _, _, _, _), synTypeDefnRepr, _, _, _, _)) =
|
||||
let (SynTypeDefn (SynComponentInfo (attrs, _, _, interfaceName, _, _, _, _), synTypeDefnRepr, _, _, _, _)) =
|
||||
interfaceType
|
||||
|
||||
let basePath = extractBasePath attrs
|
||||
|
||||
let members =
|
||||
match synTypeDefnRepr with
|
||||
| SynTypeDefnRepr.ObjectModel (_kind, members, _) ->
|
||||
@@ -640,6 +699,7 @@ module internal HttpClientGenerator =
|
||||
Args = args
|
||||
Identifier = ident
|
||||
EnsureSuccessHttpCode = shouldEnsureSuccess
|
||||
BasePath = basePath
|
||||
}
|
||||
| _ -> failwithf "Unrecognised member definition: %+A" defn
|
||||
)
|
||||
|
Reference in New Issue
Block a user