mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-09 22:18:40 +00:00
Correctly deal with JsonNull (#84)
This commit is contained in:
@@ -211,6 +211,72 @@ module PureGymApi =
|
|||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.PostStringToString (foo : Map<string, string> option, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("some/url", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Post,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let queryParams =
|
||||||
|
new System.Net.Http.StringContent (
|
||||||
|
foo
|
||||||
|
|> (fun field ->
|
||||||
|
match field with
|
||||||
|
| None -> null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
| Some field ->
|
||||||
|
((fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (
|
||||||
|
key.ToString (),
|
||||||
|
System.Text.Json.Nodes.JsonValue.Create<string> value
|
||||||
|
)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
field)
|
||||||
|
:> System.Text.Json.Nodes.JsonNode
|
||||||
|
)
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Content <- queryParams
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
match jsonNode with
|
||||||
|
| null -> None
|
||||||
|
| v ->
|
||||||
|
v.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key)
|
||||||
|
let value = (kvp.Value).AsValue().GetValue<string> ()
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|> Some
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
member _.GetSessions (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) =
|
member _.GetSessions (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) =
|
||||||
async {
|
async {
|
||||||
let! ct = Async.CancellationToken
|
let! ct = Async.CancellationToken
|
||||||
@@ -403,7 +469,9 @@ module PureGymApi =
|
|||||||
|
|
||||||
let queryParams =
|
let queryParams =
|
||||||
new System.Net.Http.StringContent (
|
new System.Net.Http.StringContent (
|
||||||
user |> PureGym.Member.toJsonNode |> (fun node -> node.ToJsonString ())
|
user
|
||||||
|
|> PureGym.Member.toJsonNode
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
)
|
)
|
||||||
|
|
||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
@@ -436,7 +504,7 @@ module PureGymApi =
|
|||||||
new System.Net.Http.StringContent (
|
new System.Net.Http.StringContent (
|
||||||
user
|
user
|
||||||
|> System.Text.Json.Nodes.JsonValue.Create<Uri>
|
|> System.Text.Json.Nodes.JsonValue.Create<Uri>
|
||||||
|> (fun node -> node.ToJsonString ())
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
)
|
)
|
||||||
|
|
||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
@@ -469,7 +537,7 @@ module PureGymApi =
|
|||||||
new System.Net.Http.StringContent (
|
new System.Net.Http.StringContent (
|
||||||
user
|
user
|
||||||
|> System.Text.Json.Nodes.JsonValue.Create<int>
|
|> System.Text.Json.Nodes.JsonValue.Create<int>
|
||||||
|> (fun node -> node.ToJsonString ())
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
)
|
)
|
||||||
|
|
||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
|
@@ -29,6 +29,10 @@ type IPureGymApi =
|
|||||||
[<Get "some/url">]
|
[<Get "some/url">]
|
||||||
abstract GetUrl : ?ct : CancellationToken -> Task<UriThing>
|
abstract GetUrl : ?ct : CancellationToken -> Task<UriThing>
|
||||||
|
|
||||||
|
[<Post "some/url">]
|
||||||
|
abstract PostStringToString :
|
||||||
|
[<Body>] foo : Map<string, string> option * ?ct : CancellationToken -> Task<Map<string, string> option>
|
||||||
|
|
||||||
// We'll use this one to check handling of absolute URIs too
|
// We'll use this one to check handling of absolute URIs too
|
||||||
[<Get "/v2/gymSessions/member">]
|
[<Get "/v2/gymSessions/member">]
|
||||||
abstract GetSessions :
|
abstract GetSessions :
|
||||||
|
@@ -257,3 +257,37 @@ module TestPureGymRestApi =
|
|||||||
uri.ToString () |> shouldEqual "https://patrick@en.wikipedia.org/wiki/foo"
|
uri.ToString () |> shouldEqual "https://patrick@en.wikipedia.org/wiki/foo"
|
||||||
uri.UserInfo |> shouldEqual "patrick"
|
uri.UserInfo |> shouldEqual "patrick"
|
||||||
uri.Host |> shouldEqual "en.wikipedia.org"
|
uri.Host |> shouldEqual "en.wikipedia.org"
|
||||||
|
|
||||||
|
[<TestCase false>]
|
||||||
|
[<TestCase true>]
|
||||||
|
let ``Map<string, string> option example`` (isSome : bool) =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Post
|
||||||
|
|
||||||
|
message.RequestUri.ToString () |> shouldEqual "https://whatnot.com/some/url"
|
||||||
|
let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask
|
||||||
|
|
||||||
|
if isSome then
|
||||||
|
content |> shouldEqual """{"hi":"bye"}"""
|
||||||
|
else
|
||||||
|
content |> shouldEqual "null"
|
||||||
|
|
||||||
|
let content = new StringContent (content)
|
||||||
|
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.makeNoUri proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
if isSome then
|
||||||
|
[ "hi", "bye" ] |> Map.ofList |> Some
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
let actual = api.PostStringToString(expected).Result
|
||||||
|
actual |> shouldEqual expected
|
||||||
|
@@ -2,6 +2,9 @@ namespace WoofWare.Myriad.Plugins.Test
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
|
open System.IO
|
||||||
|
open System.Text
|
||||||
|
open System.Text.Json
|
||||||
open System.Text.Json.Nodes
|
open System.Text.Json.Nodes
|
||||||
open NUnit.Framework
|
open NUnit.Framework
|
||||||
open FsCheck
|
open FsCheck
|
||||||
|
@@ -516,12 +516,18 @@ module internal HttpClientGenerator =
|
|||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (
|
||||||
SynExpr.createLambda
|
SynExpr.createLambda
|
||||||
"node"
|
"node"
|
||||||
|
(SynExpr.ifThenElse
|
||||||
|
(SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateIdentString "isNull",
|
||||||
|
SynExpr.CreateIdentString "node"
|
||||||
|
))
|
||||||
(SynExpr.CreateApp (
|
(SynExpr.CreateApp (
|
||||||
SynExpr.CreateLongIdent (
|
SynExpr.CreateLongIdent (
|
||||||
SynLongIdent.Create [ "node" ; "ToJsonString" ]
|
SynLongIdent.Create [ "node" ; "ToJsonString" ]
|
||||||
),
|
),
|
||||||
SynExpr.CreateConst SynConst.Unit
|
SynExpr.CreateConst SynConst.Unit
|
||||||
))
|
))
|
||||||
|
(SynExpr.CreateConst (SynConst.CreateString "null")))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
range0
|
range0
|
||||||
|
@@ -65,11 +65,14 @@ module internal JsonSerializeGenerator =
|
|||||||
SynMatchClause.Create (
|
SynMatchClause.Create (
|
||||||
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []),
|
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []),
|
||||||
None,
|
None,
|
||||||
SynExpr.CreateApp (
|
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
|
||||||
SynExpr.CreateLongIdent (
|
// identically equal to null. We have to work around this later, but we might as well just
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
// be efficient here and whip up the null directly.
|
||||||
),
|
|
||||||
SynExpr.CreateNull
|
SynExpr.CreateNull
|
||||||
|
|> SynExpr.upcast' (
|
||||||
|
SynType.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,6 +83,12 @@ module internal JsonSerializeGenerator =
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field")
|
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field")
|
||||||
|
|> SynExpr.CreateParen
|
||||||
|
|> SynExpr.upcast' (
|
||||||
|
SynType.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@@ -263,6 +263,8 @@ module internal SynExpr =
|
|||||||
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
|
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
|
||||||
| _ -> callMethod "ToString" ident
|
| _ -> callMethod "ToString" ident
|
||||||
|
|
||||||
|
let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0)
|
||||||
|
|
||||||
let synBindingTriviaZero (isMember : bool) =
|
let synBindingTriviaZero (isMember : bool) =
|
||||||
{
|
{
|
||||||
SynBindingTrivia.EqualsRange = Some range0
|
SynBindingTrivia.EqualsRange = Some range0
|
||||||
|
Reference in New Issue
Block a user