Skip to content
This repository has been archived by the owner on Apr 1, 2024. It is now read-only.

Commit

Permalink
custom json converters
Browse files Browse the repository at this point in the history
  • Loading branch information
valbers committed Feb 7, 2023
1 parent 3ab7706 commit c466997
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 5 deletions.
9 changes: 9 additions & 0 deletions graphql-ws.fsharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2BBAEFA0-4E9
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Main", "src\Main\Main.fsproj", "{72471B86-58DF-42DC-8220-4E9E658AE781}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E2ECB101-4BA2-4884-9AE2-012BF8F8F433}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Main.UnitTests", "tests\unit-tests\Main.UnitTests.fsproj", "{85297F41-0524-400A-89EC-E4360CB033D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -20,8 +24,13 @@ Global
{72471B86-58DF-42DC-8220-4E9E658AE781}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72471B86-58DF-42DC-8220-4E9E658AE781}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72471B86-58DF-42DC-8220-4E9E658AE781}.Release|Any CPU.Build.0 = Release|Any CPU
{85297F41-0524-400A-89EC-E4360CB033D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85297F41-0524-400A-89EC-E4360CB033D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85297F41-0524-400A-89EC-E4360CB033D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85297F41-0524-400A-89EC-E4360CB033D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{72471B86-58DF-42DC-8220-4E9E658AE781} = {2BBAEFA0-4E9F-467B-ABEA-A9854E821495}
{85297F41-0524-400A-89EC-E4360CB033D9} = {E2ECB101-4BA2-4884-9AE2-012BF8F8F433}
EndGlobalSection
EndGlobal
8 changes: 5 additions & 3 deletions src/Main/GraphQLWebsocketMiddleware.fs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ module internal GraphQLSubscriptionsManagement =

type GraphQLWebSocketMiddleware<'Root>(next : RequestDelegate, applicationLifetime : IHostApplicationLifetime, jsonOptions : IOptions<JsonOptions>, executor : Executor<'Root>, root : unit -> 'Root, url: string) =

let serializeServerMessage (jsonOptions: JsonOptions) obj =
let serializeServerMessage (jsonOptions: JsonOptions) serverMessage =
task { return "dummySerializedServerMessage" }

let deserializeClientMessage (jsonOptions: JsonOptions) (msg: string) =
Expand Down Expand Up @@ -153,6 +153,7 @@ type GraphQLWebSocketMiddleware<'Root>(next : RequestDelegate, applicationLifeti
let logMsgWithIdReceived id msgAsStr =
printfn "%s (id: %s)" msgAsStr id

let mutable socketClosed = false
// <--------------
// <-- Helpers --|
// <--------------
Expand All @@ -162,7 +163,7 @@ type GraphQLWebSocketMiddleware<'Root>(next : RequestDelegate, applicationLifeti
// ------->
task {
try
while not cancellationToken.IsCancellationRequested do
while not cancellationToken.IsCancellationRequested && not socketClosed do
let! receivedMessage = safe_Receive()
match receivedMessage with
| None ->
Expand Down Expand Up @@ -192,7 +193,8 @@ type GraphQLWebSocketMiddleware<'Root>(next : RequestDelegate, applicationLifeti
do! Complete id |> safe_Send
| InvalidMessage explanation ->
"InvalidMessage" |> logMsgReceivedWithOptionalPayload None
do! Error (None, explanation) |> safe_Send
do! socket.CloseAsync(enum CustomWebSocketStatus.invalidMessage, explanation, cancellationToken)
socketClosed <- true
with // TODO: MAKE A PROPER GRAPHQL ERROR HANDLING!
| ex ->
printfn "Unexpected exception \"%s\" in GraphQLWebsocketMiddleware (handleMessages). More:\n%s" (ex.GetType().Name) (ex.ToString())
Expand Down
72 changes: 72 additions & 0 deletions src/Main/JsonConverters.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace GraphQLTransportWS

open System
open System.Text.Json
open System.Text.Json.Serialization

[<Sealed>]
type WebSocketServerMessageConverter() =
inherit JsonConverter<WebSocketServerMessage>()

override __.Read(reader: byref<Utf8JsonReader>, typeToConvert: Type, options: JsonSerializerOptions) =
ConnectionAck

override __.Write(writer: Utf8JsonWriter, value: WebSocketServerMessage, options: JsonSerializerOptions) =
let thenAddProperty (propName: string) (propStrValue: string) =
writer.WritePropertyName(propName)
writer.WriteStringValue(propStrValue)

let beginSerializedObjWith (propName: string) (propStrValue: string) =
writer.WriteStartObject()
thenAddProperty propName propStrValue

let endSerializedObj () =
writer.WriteEndObject()

let addOptionalPayloadProperty (optionalPayload: string option) =
match optionalPayload with
| Some p ->
thenAddProperty "payload" p
| None ->
()

match value with
| Error (id, errs) ->
beginSerializedObjWith "type" "error"
thenAddProperty "id" id
writer.WriteStartArray("payload")
errs
|> List.iter
(fun err ->
writer.WriteStartObject()
writer.WritePropertyName("message")
writer.WriteStringValue(err)
writer.WriteEndObject()
)
writer.WriteEndArray()
endSerializedObj()
| ConnectionAck ->
beginSerializedObjWith "type" "connection_ack"
endSerializedObj()
| ServerPing p ->
beginSerializedObjWith "type" "ping"
addOptionalPayloadProperty p
endSerializedObj()
| ServerPong p ->
beginSerializedObjWith "type" "pong"
addOptionalPayloadProperty p
endSerializedObj()
| Next(id, result) ->
beginSerializedObjWith "type" "next"
thenAddProperty "id" id
writer.WritePropertyName("payload")
writer.WriteRawValue(JsonSerializer.Serialize(result), skipInputValidation = true)
endSerializedObj()
| Complete(id) ->
beginSerializedObjWith "type" "complete"
thenAddProperty "id" id
endSerializedObj()



// writer.WriteStartObject(propertyName = )
1 change: 1 addition & 0 deletions src/Main/Main.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="Rop/Rop.fs" />
<Compile Include="Rop/RopAsync.fs" />
<Compile Include="WebSocketMessages.fs" />
<Compile Include="JsonConverters.fs" />
<Compile Include="GraphQLWebsocketMiddleware.fs" />
<Compile Include="Library.fs" />
</ItemGroup>
Expand Down
7 changes: 5 additions & 2 deletions src/Main/WebSocketMessages.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ type WebSocketServerMessage =
| ServerPing of payload: string option
| ServerPong of payload: string option
| Next of id : string * payload : Output
| Error of id : string option * err : string
| Complete of id : string
| Error of id : string * err : string list
| Complete of id : string

module CustomWebSocketStatus =
let invalidMessage = 4400
32 changes: 32 additions & 0 deletions tests/unit-tests/Main.UnitTests.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Tests.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Main\Main.fsproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions tests/unit-tests/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Program = let [<EntryPoint>] main _ = 0
30 changes: 30 additions & 0 deletions tests/unit-tests/Tests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Tests

open GraphQLTransportWS
open System
open System.Text.Json
open Xunit

[<Fact>]
let ``Serializes ServerPing correctly`` () =
let jsonOptions = new JsonSerializerOptions()
jsonOptions.Converters.Add(new WebSocketServerMessageConverter())

let input: WebSocketServerMessage =
ServerPing (Some "Peekaboo!")

let result = JsonSerializer.Serialize(input, jsonOptions)

Assert.Equal("{\"type\":\"ping\",\"payload\":\"Peekaboo!\"}", result)

[<Fact>]
let ``Serializes Error correctly`` () =
let jsonOptions = new JsonSerializerOptions()
jsonOptions.Converters.Add(new WebSocketServerMessageConverter())

let input: WebSocketServerMessage =
Error ("myId", [ "An error ocurred during GraphQL execution" ])

let result = JsonSerializer.Serialize(input, jsonOptions)

Assert.Equal("{\"type\":\"error\",\"id\":\"myId\",\"payload\":[{\"message\":\"An error ocurred during GraphQL execution\"}]}", result)

0 comments on commit c466997

Please sign in to comment.