Skip to content

Issue 17 : Running Dash.NET from scripts #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 20, 2021
Merged
56 changes: 56 additions & 0 deletions Dash.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.ComponentGeneratio
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.ComponentGeneration.Tests", "tests\Dash.NET.ComponentGeneration.Tests\Dash.NET.ComponentGeneration.Tests.fsproj", "{0F230F90-C64E-4CAA-8B6D-A27353375D56}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.Suave", "src\Dash.NET.Suave\Dash.NET.Suave.fsproj", "{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.Giraffe", "src\Dash.NET.Giraffe\Dash.NET.Giraffe.fsproj", "{0FD6384A-19BD-4CF9-8A14-FA5008734024}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.Suave.Example", "examples\Dash.NET.Suave.Example\Dash.NET.Suave.Example.fsproj", "{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Dash.NET.Giraffe.Example", "examples\Dash.NET.Giraffe.Example\Dash.NET.Giraffe.Example.fsproj", "{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -128,6 +136,54 @@ Global
{0F230F90-C64E-4CAA-8B6D-A27353375D56}.Release|x64.Build.0 = Release|Any CPU
{0F230F90-C64E-4CAA-8B6D-A27353375D56}.Release|x86.ActiveCfg = Release|Any CPU
{0F230F90-C64E-4CAA-8B6D-A27353375D56}.Release|x86.Build.0 = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|x64.ActiveCfg = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|x64.Build.0 = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|x86.ActiveCfg = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Debug|x86.Build.0 = Debug|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|Any CPU.Build.0 = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|x64.ActiveCfg = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|x64.Build.0 = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|x86.ActiveCfg = Release|Any CPU
{6FF28D2B-F6D6-4BC2-8446-53C66065B3EC}.Release|x86.Build.0 = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|x64.ActiveCfg = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|x64.Build.0 = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|x86.ActiveCfg = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Debug|x86.Build.0 = Debug|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|Any CPU.Build.0 = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|x64.ActiveCfg = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|x64.Build.0 = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|x86.ActiveCfg = Release|Any CPU
{0FD6384A-19BD-4CF9-8A14-FA5008734024}.Release|x86.Build.0 = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|x64.ActiveCfg = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|x64.Build.0 = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|x86.ActiveCfg = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Debug|x86.Build.0 = Debug|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|Any CPU.Build.0 = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|x64.ActiveCfg = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|x64.Build.0 = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|x86.ActiveCfg = Release|Any CPU
{37F6EACC-A7BE-48BF-815B-5E6D2EA6E728}.Release|x86.Build.0 = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|x64.ActiveCfg = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|x64.Build.0 = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|x86.ActiveCfg = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Debug|x86.Build.0 = Debug|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|Any CPU.Build.0 = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|x64.ActiveCfg = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|x64.Build.0 = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|x86.ActiveCfg = Release|Any CPU
{41D83E3B-EF7E-41F3-BB1C-CA963F172BB2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions dev/Dash.NET.Dev.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Dash.NET.Giraffe\Dash.NET.Giraffe.fsproj" />
<ProjectReference Include="..\src\Dash.NET\Dash.NET.fsproj" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions dev/Docs.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Docs

open Dash.NET
open Dash.NET.Giraffe

// temporary workaround for checking correctness of the F# snippets in the documentation.
// the usual approach does not work here, because we cannot reference Asp.NetCore in a .fsx script.
Expand Down
1 change: 1 addition & 0 deletions dev/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ let dslLayout =
open Newtonsoft.Json
open Newtonsoft.Json.Linq
open Dash.NET.Operators
open Dash.NET.Giraffe

let callbackArrayInput =
Callback.multiOut(
Expand Down
17 changes: 17 additions & 0 deletions examples/Dash.NET.Giraffe.Example/Dash.NET.Giraffe.Example.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>

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

<ItemGroup>
<ProjectReference Include="..\..\src\Dash.NET.Giraffe\Dash.NET.Giraffe.fsproj" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions examples/Dash.NET.Giraffe.Example/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

// Learn more about Dash.NET at https://plotly.github.io/Dash.NET/

open Dash.NET.Giraffe
open Dash.NET.Html
open Dash.NET.DCC
open Plotly.NET
open Microsoft.Extensions.Logging
open Giraffe

let myGraph = Chart.Line([(1,1);(2,2)])

let myLayout =
Html.div [
Attr.children [
Html.h1 [
Attr.children "Hello world from Dash.NET and Giraffe!"
];
Html.h2 [ Attr.children "Take a look at this graph:"];
Graph.graph "my-ghraph-id" [Graph.Attr.figure(GenericChart.toFigure myGraph)]
]
]

[<EntryPoint>]
let main argv =

let dashApp =
DashApp.initDefault()
|> DashApp.withLayout myLayout

let config =
{ HostName = "localhost"
; LogLevel = LogLevel.Debug
; ErrorHandler = (fun ex -> text ex.Message) }

DashApp.run [||] config dashApp
18 changes: 18 additions & 0 deletions examples/Dash.NET.Suave.Example/Dash.NET.Suave.Example.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
<None Include="Script.fsx" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Dash.NET.Suave\Dash.NET.Suave.fsproj" />
</ItemGroup>

</Project>
75 changes: 75 additions & 0 deletions examples/Dash.NET.Suave.Example/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

// Learn more about Dash.NET at https://plotly.github.io/Dash.NET/

open Dash.NET.Suave
open Dash.NET.Html
open Dash.NET.DCC
open Plotly.NET
open Dash.NET
open System
open System.Threading

let myGraph = Chart.Line([(1,1);(2,2)])

let myLayout =
Html.div [
Attr.children [
Html.h1 [
Attr.children "Hello world from Dash.NET and Suave!"
];
Html.h2 [ Attr.children "Take a look at this graph:"];
Graph.graph "my-ghraph-id" [Graph.Attr.figure(GenericChart.toFigure myGraph)];
Html.h2 [Attr.children "Tell us something!"];
Input.input "test-input" [Input.Attr.inputType InputType.Text];
Html.h3 [Attr.children "Input below will not trigger the callback"]
Input.input "test-input-state" [Input.Attr.inputType InputType.Text]
Html.div [ Attr.id "test-output" ];
]
]

let testCallback =
Callback.singleOut(
"test-input" @. Value,
"test-output" @. Children,
(fun (input:string) (state:string) ->
"test-output" @. Children => (
sprintf "You said : '%s' and we added the state: '%s'" input state)
),
State = ["test-input-state" @. Value])

let dashApp =
DashApp.initDefault()
|> DashApp.withLayout myLayout
|> DashApp.addCallback testCallback

let config =
{ hostname = "localhost"
; ip = "127.0.0.1"
; port = 0 // Request the server to be launched on a random ephemeral port
; errorHandler = Suave.Web.defaultErrorHandler }

let listening, server = DashApp.runAsync [||] config dashApp

let cts = new CancellationTokenSource()

Async.Start(server, cts.Token)

printfn "Make requests now"

// Capture assigned port
let [| Some startData |] = Async.RunSynchronously listening
let port = startData.binding.port

let url = sprintf "http://%s:%d" config.ip port

Console.WriteLine ("Opening: {0}", url)

// Open browser
let psi = new System.Diagnostics.ProcessStartInfo()
psi.UseShellExecute <- true
psi.FileName <- url
System.Diagnostics.Process.Start(psi) |> ignore

Console.WriteLine("Press any key to exit application")
Console.ReadKey true |> ignore
cts.Cancel()
34 changes: 34 additions & 0 deletions examples/Dash.NET.Suave.Example/Script.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Learn more about Dash.NET at https://plotly.github.io/Dash.NET/

#r "bin/Debug/net5.0/Dash.NET.dll"
#r "bin/Debug/net5.0/Suave.dll"
#r "bin/Debug/net5.0/Suave.Experimental.dll"
#r "bin/Debug/net5.0/Dash.NET.Suave.dll"
#r "bin/Debug/net5.0/Plotly.NET.dll"
#r "bin/Debug/net5.0/Feliz.Engine.dll"
#r "bin/Debug/net5.0/Newtonsoft.Json.dll"

open Dash.NET.Suave
open Dash.NET.Html
open Dash.NET.DCC
open Plotly.NET

let myGraph = Chart.Line([(1,1);(2,2)])

let myLayout =
Html.div [
Attr.children [
Html.h1 [
Attr.children "Hello world from Dash.NET and Suave!"
];
Html.h2 [ Attr.children "Take a look at this graph:"];
Graph.graph "my-ghraph-id" [Graph.Attr.figure(GenericChart.toFigure myGraph)]
]
]

let dashApp =
DashApp.initDefault()
|> DashApp.withLayout myLayout

// run
DashApp.run [||] dashApp
22 changes: 22 additions & 0 deletions src/Dash.NET.Giraffe/Dash.NET.Giraffe.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>

<ItemGroup>
<Compile Include="Views.fs" />
<Compile Include="DashApp.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Giraffe" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Dash.NET\Dash.NET.fsproj" />
</ItemGroup>

</Project>
40 changes: 22 additions & 18 deletions src/Dash.NET/DashApp.fs → src/Dash.NET.Giraffe/DashApp.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Dash.NET
namespace Dash.NET.Giraffe

open Giraffe
open Giraffe.ViewEngine
Expand All @@ -14,9 +14,14 @@ open Microsoft.Extensions.Hosting
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open System.Reflection
open Dash.NET

[<assembly:AutoOpen("Dash.NET.DCC")>]
do ()
//Giraffe, Logging and ASP.NET specific
type DashGiraffeConfig = {
HostName: string
LogLevel: LogLevel
ErrorHandler: Exception -> HttpHandler
}

type DashApp =
{
Expand Down Expand Up @@ -164,7 +169,7 @@ type DashApp =
setStatusCode 404 >=> text "Not Found"
]

static member run (args: string []) (app: DashApp) =
static member run (args: string []) (config: DashGiraffeConfig) (app: DashApp) =
//Go through an assembly and look for any types that inherit DashComponent
//if one is found then look for the LoadableComponentDefinition and add its script
let tryLoadComponents (innerApp: DashApp) (a: Assembly) =
Expand Down Expand Up @@ -213,34 +218,34 @@ type DashApp =

//TODO: make this customizable
let errorHandler (ex : Exception) (logger : ILogger) =
logger.LogError(ex, "An unhandled exception has occurred while executing the request.")
clearResponse >=> setStatusCode 500 >=> (loadedApp.Config.ErrorHandler ex)
logger.LogError(ex, "An unhandled exception has occurred while executing the request.")
clearResponse >=> setStatusCode 500 >=> (config.ErrorHandler ex)

let configureCors (builder : CorsPolicyBuilder) =
builder.WithOrigins(sprintf "http://%s:8080" loadedApp.Config.HostName)
builder.WithOrigins(sprintf "http://%s:5001" config.HostName)
.AllowAnyMethod()
.AllowAnyHeader()
|> ignore

let configureApp (appBuilder : IApplicationBuilder) =
let env = appBuilder.ApplicationServices.GetService<IWebHostEnvironment>()
(match env.EnvironmentName with
| "Development" -> appBuilder.UseDeveloperExceptionPage()
| _ -> appBuilder.UseGiraffeErrorHandler(errorHandler))
.UseHttpsRedirection()
.UseCors(configureCors)
.UseStaticFiles()
.UseGiraffe(DashApp.toHttpHandler loadedApp)
.UseHttpsRedirection()
.UseCors(configureCors)
.UseStaticFiles()
.UseGiraffe(DashApp.toHttpHandler loadedApp)

let configureServices (services : IServiceCollection) =
services.AddCors() |> ignore
services.AddGiraffe() |> ignore

let configureLogging (builder : ILoggingBuilder) =
builder.AddFilter(fun l -> l.Equals loadedApp.Config.LogLevel)
builder.AddFilter(fun l -> l.Equals config.LogLevel)
.AddConsole()
.AddDebug() |> ignore

// This folder has to be "<path-to-exe>/WebRoot" for the way generated component javascript injection currently works
let contentRoot = Reflection.Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName
let webRoot = Path.Combine(contentRoot, "WebRoot")
Expand All @@ -257,6 +262,5 @@ type DashApp =
|> ignore)
.Build()
.Run()

0

3 changes: 2 additions & 1 deletion src/Dash.NET/Views.fs → src/Dash.NET.Giraffe/Views.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Dash.NET
namespace Dash.NET.Giraffe

module Views =
open Dash.NET
open Giraffe.ViewEngine

let createConfigScript (config:DashConfig) =
Expand Down
Loading