Skip to content

Commit

Permalink
Add GrpcModels project for server/client interop
Browse files Browse the repository at this point in the history
Added GrpcModels project for server/client interop which
includes message models (that were in WebSocketApp), as well
as Marshalling module (code from RIM).
  • Loading branch information
webwarrior-ws committed Feb 8, 2024
1 parent fdf1c4e commit 933bb93
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 3 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageVersion Include="Giraffe" Version="3.1.0" />
<PackageVersion Include="Giraffe.Razor" Version="1.3.0" />
<PackageVersion Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.1" />
<PackageVersion Include="TaskBuilder.fs" Version="2.1.0" />
<PackageVersion Include="FSharp.Core" Version="8.0.101" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.40.0" />
Expand Down
6 changes: 6 additions & 0 deletions FX.sln
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FX.GrpcService", "src\FX.Gr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FX.GrpcClient", "src\FX.GrpcClient\FX.GrpcClient.csproj", "{578D4048-175B-41BC-8EC3-FC83FF137139}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FX.GrpcModels", "src\FX.GrpcModels\FX.GrpcModels.fsproj", "{11B2E30C-FCFE-41EB-A76D-CF9E95A844C4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -38,6 +40,10 @@ Global
{578D4048-175B-41BC-8EC3-FC83FF137139}.Debug|Any CPU.Build.0 = Debug|Any CPU
{578D4048-175B-41BC-8EC3-FC83FF137139}.Release|Any CPU.ActiveCfg = Release|Any CPU
{578D4048-175B-41BC-8EC3-FC83FF137139}.Release|Any CPU.Build.0 = Release|Any CPU
{11B2E30C-FCFE-41EB-A76D-CF9E95A844C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11B2E30C-FCFE-41EB-A76D-CF9E95A844C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11B2E30C-FCFE-41EB-A76D-CF9E95A844C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11B2E30C-FCFE-41EB-A76D-CF9E95A844C4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
4 changes: 4 additions & 0 deletions src/FX.GrpcClient/FX.GrpcClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FX.GrpcModels\FX.GrpcModels.fsproj" />
</ItemGroup>

<ItemGroup>
<Protobuf Include="..\FX.GrpcService\Protos\fx.proto" GrpcServices="Client">
<Link>Protos\fx.proto</Link>
Expand Down
9 changes: 9 additions & 0 deletions src/FX.GrpcClient/Instance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Grpc.Net.Client;
using GrpcService;

using GrpcModels;

namespace GrpcClient
{
public class Instance
Expand All @@ -31,5 +33,12 @@ public async Task<string> SendMessage(string message)
Console.WriteLine($"Got response: {reply.MsgOut}");
return reply.MsgOut;
}

public async Task<Message> SendMessage<TMessage>(TMessage message)
{
var text = Marshaller.Serialize(message);
var answer = await SendMessage(text);
return new Message{ Text = answer };
}
}
}
17 changes: 17 additions & 0 deletions src/FX.GrpcModels/FX.GrpcModels.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Models.fs" />
<Compile Include="Marshalling.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" />
</ItemGroup>

</Project>
72 changes: 72 additions & 0 deletions src/FX.GrpcModels/Marshalling.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace GrpcModels

open System
open System.Reflection
open System.Text.Json

module VersionHelper =
let CURRENT_VERSION =
Assembly
.GetExecutingAssembly()
.GetName()
.Version.ToString()

type IMarshallingWrapper =
abstract member Value: obj

type MarshallingWrapper<'T> =
{
Version: string
TypeName: string
Value: 'T
}

static member New(value: 'T) =
{
Value = value
Version = VersionHelper.CURRENT_VERSION
TypeName = typeof<'T>.FullName
}

interface IMarshallingWrapper with
member this.Value = this.Value :> obj

module Marshaller =

let ExtractMetadata(json: string) : Type * Version =
let wrapper = JsonSerializer.Deserialize<MarshallingWrapper<obj>> json
let typ = Type.GetType wrapper.TypeName
let version = Version wrapper.Version
typ, version

let Serialize<'T>(object: 'T) : string =
let wrapper = MarshallingWrapper.New object
JsonSerializer.Serialize wrapper

let Deserialize<'T>(json: string) : 'T =
if isNull json then
raise <| ArgumentNullException "json"

let wrapper = JsonSerializer.Deserialize<MarshallingWrapper<'T>> json
wrapper.Value

let DeserializeAbstract (json: string) (targetType: Type) : obj =
if isNull json then
raise <| ArgumentNullException "json"

let wrapperGenericType = typedefof<MarshallingWrapper<_>>

let wrapperType =
wrapperGenericType.MakeGenericType(Array.singleton targetType)

let wrapperObj = JsonSerializer.Deserialize(json, wrapperType)

if isNull wrapperObj then
failwith "Deserialization failed: result is null"
elif wrapperObj.GetType() <> wrapperType then
failwithf
"Deserialization failed, resulting type: %s"
(wrapperObj.GetType().FullName)

let wrapper = wrapperObj :?> IMarshallingWrapper
wrapper.Value
31 changes: 31 additions & 0 deletions src/FX.GrpcModels/Models.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace GrpcModels

open System

[<CLIMutable>]
type Message =
{
Text : string
}

[<CLIMutable>]
type LimitOrder =
{
Price: decimal;
Side: string;
Quantity: decimal;
}

[<CLIMutable>]
type MarketOrder =
{
Side: string;
Quantity: decimal;
}

[<CLIMutable>]
type CancelOrderRequest =
{
OrderId: Guid;
// TODO: add Market
}
5 changes: 5 additions & 0 deletions src/FX.GrpcService/FX.GrpcService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="FSharp.Core" />
<PackageReference Include="Grpc.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FX.GrpcModels\FX.GrpcModels.fsproj" />
</ItemGroup>
</Project>
23 changes: 22 additions & 1 deletion src/FX.GrpcService/Services/FXService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

using Grpc.Core;

using GrpcModels;

namespace GrpcService.Services
{
public class FXService : FXGrpcService.FXGrpcServiceBase
Expand All @@ -13,7 +15,26 @@ public override async Task<GenericOutputParam> GenericMethod(GenericInputParam r
{
Console.WriteLine($"Received {request.MsgIn}");

return await Task.FromResult(new GenericOutputParam { MsgOut = "received " + request.MsgIn });
var (type, _version) = Marshaller.ExtractMetadata(request.MsgIn);

var deserializedRequest = Marshaller.DeserializeAbstract(request.MsgIn, type);

if (deserializedRequest is Message message)
{
return await Task.FromResult(new GenericOutputParam { MsgOut = "received " + message.Text });
}
else if (deserializedRequest is LimitOrder limitOrder)
{
throw new NotImplementedException();
}
else if (deserializedRequest is MarketOrder marketOrder)
{
throw new NotImplementedException();
}
else
{
throw new InvalidOperationException("Unable to deserialize request: " + request.MsgIn);
}
}

public override async Task GenericStreamOutputMethod(GenericInputParam request, IServerStreamWriter<GenericOutputParam> responseStream, ServerCallContext context)
Expand Down
4 changes: 2 additions & 2 deletions src/FX.Tests/E2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ async public Task GrpcE2ETest()
client.Connect();

var message = "hello";
var response = await client.SendMessage(message);
var response = await client.SendMessage(new GrpcModels.Message{ Text = message });

Assert.That(response, Is.EqualTo("received " + message));
Assert.That(response.Text, Is.EqualTo("received " + message));
}
}
}
1 change: 1 addition & 0 deletions src/FX.Tests/FX.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
<ItemGroup>
<ProjectReference Include="..\FX.Core\FX.Core.fsproj" />
<ProjectReference Include="..\FX.GrpcClient\FX.GrpcClient.csproj" />
<ProjectReference Include="..\FX.GrpcService\FX.GrpcService.csproj" />
</ItemGroup>
</Project>

0 comments on commit 933bb93

Please sign in to comment.