-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial dotnet client, proxy creation complete, field invocation is s…
…till WIP
- Loading branch information
Showing
16 changed files
with
340 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
Fable.Remoting.DotnetClient/Fable.Remoting.DotnetClient.fsproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,21 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Description>Dotnet client proxy that is compatible with Fable.Remoting webservers</Description> | ||
<PackageProjectUrl>https://github.com/Zaid-Ajaj/Fable.Remoting</PackageProjectUrl> | ||
<RepositoryUrl>https://github.com/Zaid-Ajaj/Fable.Remoting.git</RepositoryUrl> | ||
<PackageLicenseUrl>https://github.com/Zaid-Ajaj/Fable.Remoting/blob/master/LICENSE</PackageLicenseUrl> | ||
<PackageIconUrl></PackageIconUrl> | ||
<PackageTags>fsharp;fable;remoting;rpc;webserver;json</PackageTags> | ||
<Authors>Zaid Ajaj</Authors> | ||
<Version>1.0.0</Version> | ||
<TargetFrameworks>netstandard2.0</TargetFrameworks> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="Proxy.fs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\Fable.Remoting.Json\Fable.Remoting.Json.fsproj" /> | ||
</ItemGroup> | ||
<Import Project="..\.paket\Paket.Restore.targets" /> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,100 @@ | ||
namespace Fable.Remoting.DotnetClient | ||
|
||
open FSharp.Reflection | ||
open System.Reflection | ||
open Fable.Remoting.Json | ||
open System | ||
open System.Text | ||
open HttpFs.Client | ||
open Newtonsoft.Json | ||
open Newtonsoft.Json.Linq | ||
open Hopac | ||
|
||
[<RequireQualifiedAccess>] | ||
module Proxy = | ||
|
||
[<RequireQualifiedAccess>] | ||
type ProxyField = | ||
| Function of name:string * funcType:Type * returnType: Type | ||
| AsyncValue of name:string * genericType:Type | ||
| IgnoredInvalidField of name:string * Type | ||
|
||
/// Flattens functions of `(type1 -> type2 -> ... -> typeN)` types to `[type1; type2; typeN]`. Intermediate functions types are expanded as well. | ||
let rec flattenFunction (functionType: System.Type) = | ||
[ if FSharpType.IsFunction functionType then | ||
let domain, range = FSharpType.GetFunctionElements functionType | ||
yield! flattenFunction domain | ||
yield! flattenFunction range | ||
else | ||
yield functionType ] | ||
|
||
/// Extracts proxy fields with their relevant data from a record type | ||
let proxyFieldsOf<'t>() = | ||
FSharpType.GetRecordFields typeof<'t> | ||
|> Array.map (fun propInfo -> | ||
let fieldName = propInfo.Name | ||
let fieldType = propInfo.PropertyType | ||
if FSharpType.IsFunction fieldType | ||
then | ||
let returnType = List.last (flattenFunction fieldType) | ||
if returnType.Name = "FSharpAsync`1" | ||
then ProxyField.Function(fieldName, fieldType, returnType.GetGenericArguments().[0]) | ||
else ProxyField.IgnoredInvalidField(fieldName, fieldType) | ||
elif fieldType.Name = "FSharpAsync`1" | ||
then ProxyField.AsyncValue(fieldName, fieldType.GetGenericArguments().[0]) | ||
else ProxyField.IgnoredInvalidField(fieldName, fieldType)) | ||
|> List.ofSeq | ||
|
||
let private converter = FableJsonConverter() | ||
let private serializer = JsonSerializer() | ||
serializer.Converters.Add converter | ||
|
||
/// Parses a JSON iput string to a .NET type using Fable JSON converter | ||
let parseDynamicallyAs (valueType: Type) (json: string) = | ||
JToken.Parse(json).ToObject(valueType, serializer) | ||
|
||
/// Sends a POST request to the calulated url with the arguments of serialized to an input list | ||
let proxyPost (functionArguments: obj list) url (returnType: Type) = | ||
let serializedInputArgs = JsonConvert.SerializeObject(functionArguments, converter) | ||
Request.createUrl Post url | ||
|> Request.bodyStringEncoded serializedInputArgs (Encoding.UTF8) | ||
|> getResponse | ||
|> Job.bind Response.readBodyAsString | ||
|> Job.map (parseDynamicallyAs returnType) | ||
|> Job.toAsync | ||
|
||
let createField (serverType: Type) endpoint routeBuilder = function | ||
| ProxyField.Function(funcName, funcType, returnType) -> | ||
let route = routeBuilder serverType.Name funcName | ||
let argCount = List.length (flattenFunction funcType) - 1 | ||
let url = sprintf "%s%s" endpoint route | ||
printfn "Mapping record field '%s' to route %s" funcName url | ||
match argCount with | ||
| 1 -> FSharpValue.MakeFunction(funcType, fun a -> box (proxyPost [a] url returnType)) | ||
| 2 -> box (fun a b -> proxyPost [a; b] url returnType) | ||
| 3 -> box (fun a b c -> proxyPost [a; b; c] url returnType) | ||
| 4 -> box (fun a b c d e -> proxyPost [a; b; c; d; e] url returnType) | ||
| 5 -> box (fun a b c d e f -> proxyPost [a; b; c; d; e; f] url returnType) | ||
| 6 -> box (fun a b c d e f g -> proxyPost [a; b; c; d; e; f; g] url returnType) | ||
| n -> failwith "Only up to 6 paramters are supported" | ||
|> Some | ||
| ProxyField.AsyncValue(name, returnType) -> | ||
let customRoute = routeBuilder serverType.Name name | ||
let url = sprintf "%s%s" endpoint customRoute | ||
box (proxyPost [] url returnType) | ||
|> Some | ||
| ProxyField.IgnoredInvalidField(name, _) -> | ||
printfn "Record field '%s' is not a valid proxy field and will be ignored" name | ||
None | ||
|
||
let createAn<'t> (endpoint: string) routeBuilder = | ||
let serverType = typeof<'t> | ||
let fieldCreator = createField serverType endpoint routeBuilder | ||
let fields = | ||
proxyFieldsOf<'t>() | ||
|> List.choose fieldCreator | ||
|> List.map unbox<obj> | ||
|> Array.ofList | ||
|
||
FSharpValue.MakeRecord(serverType, fields, false) | ||
|> unbox<'t> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,2 @@ | ||
FSharp.Core | ||
Http.fs |
18 changes: 18 additions & 0 deletions
18
Fable.Remoting.IntegrationTests/DotnetClient/DotnetClient.fsproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>netcoreapp2.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\Shared\SharedTypes.fs" /> | ||
<Compile Include="Program.fs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="../../Fable.Remoting.DotnetClient/Fable.Remoting.DotnetClient.fsproj" /> | ||
</ItemGroup> | ||
|
||
<Import Project="..\..\.paket\Paket.Restore.targets" /> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,26 @@ | ||
// Learn more about F# at http://fsharp.org | ||
|
||
open System | ||
open SharedTypes | ||
open Fable.Remoting.DotnetClient | ||
open Expecto | ||
open Expecto.Logging | ||
let dotnetClientTests = | ||
testList "Dotnet Client tests" [ | ||
testCase "Proxy can be created" <| fun _ -> | ||
let server = Proxy.createAn<ISimpleServer> "http://localhost:8080" (sprintf "/api/%s/%s") | ||
() | ||
|
||
testCaseAsync "Calling server works" <| async { | ||
let server = Proxy.createAn<ISimpleServer> "http://localhost:8080" (sprintf "/api/%s/%s") | ||
let! (result : int) = server.getLength "hello" | ||
Expect.equal 5 result "Length returned is correct" | ||
} | ||
] | ||
|
||
let testConfig = { Expecto.Tests.defaultConfig with | ||
parallelWorkers = 4 | ||
verbosity = LogLevel.Debug } | ||
|
||
[<EntryPoint>] | ||
let main argv = runTests testConfig dotnetClientTests |
2 changes: 2 additions & 0 deletions
2
Fable.Remoting.IntegrationTests/DotnetClient/paket.references
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,2 @@ | ||
FSharp.Core | ||
Expecto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.