Skip to content

Commit

Permalink
Added MiniProfiler and examples for fetch and Fable.Remoting
Browse files Browse the repository at this point in the history
  • Loading branch information
AkosLukacs committed Jul 27, 2018
1 parent a756b48 commit b63e5cd
Show file tree
Hide file tree
Showing 11 changed files with 531 additions and 113 deletions.
6 changes: 6 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SAFE demo with MiniProfiler for .NET

## Running it

- Get everything needed to build and run a SAFE app
- `fake build --target run`
3 changes: 2 additions & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ group Server
nuget Giraffe ~> 1
nuget Microsoft.AspNetCore
nuget Microsoft.AspNetCore.StaticFiles
nuget Fable.Remoting.Giraffe
nuget Fable.Remoting.Giraffe
nuget MiniProfiler.AspNetCore.Mvc

group Client
storage: none
Expand Down
478 changes: 392 additions & 86 deletions paket.lock

Large diffs are not rendered by default.

55 changes: 52 additions & 3 deletions src/Client/Client.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ open Fable.PowerPack.Fetch
open Shared

open Fulma
open Fable.PowerPack
open Fable.Import


// The model holds data that you want to keep track of while the application is running
Expand All @@ -24,6 +26,9 @@ type Msg =
| Increment
| Decrement
| InitialCountLoaded of Result<Counter, exn>
| PingFetch
| PingRemoting
| GotPing of Result<string, exn>


module Server =
Expand Down Expand Up @@ -64,6 +69,33 @@ let update (msg : Msg) (currentModel : Model) : Model * Cmd<Msg> =
let nextModel = { Counter = Some initialCount }
nextModel, Cmd.none

| _, PingFetch ->
let fetchPing () =
fetch "/ping" []
|> Promise.bind (fun res -> res.text())
let cmd = Cmd.ofPromise
fetchPing
()
(Ok >> GotPing)
(Error >> GotPing)
currentModel, cmd

| _, PingRemoting ->
let cmd = Cmd.ofAsync
Server.api.ping
()
(Ok >> GotPing)
(Error >> GotPing)
currentModel, cmd

| _, GotPing p ->
match p with
| Ok pingResp ->
Browser.console.log("Ping ok:", pingResp)
| Error err ->
Browser.console.error("Ping error:", err)
currentModel, Cmd.none

| _ -> currentModel, Cmd.none


Expand All @@ -80,6 +112,7 @@ let safeComponents =
"Elmish", "https://elmish.github.io/elmish/"
"Fulma", "https://mangelmaxime.github.io/Fulma"
"Fable.Remoting", "https://zaid-ajaj.github.io/Fable.Remoting/"
"MiniProfiler for .NET", "https://miniprofiler.com/dotnet/"
]
|> List.map (fun (desc,link) -> a [ Href link ] [ str desc ] )
|> intersperse (str ", ")
Expand All @@ -106,14 +139,30 @@ let view (model : Model) (dispatch : Msg -> unit) =
[ Navbar.navbar [ Navbar.Color IsPrimary ]
[ Navbar.Item.div [ ]
[ Heading.h2 [ ]
[ str "SAFE Template" ] ] ]
[ str "SAFE demo with "
a [ Href "https://miniprofiler.com/dotnet" ] [ str "MiniProfiler for .NET"] ] ] ]

Container.container []
[ Content.content [ Content.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
[ Heading.h3 [] [ str ("Press buttons to manipulate counter: " + show model) ] ]
[ Heading.h3 [] [ str ("Press buttons to manipulate counter: " + show model) ] ]
Columns.columns []
[ Column.column [] [ button "-" (fun _ -> dispatch Decrement) ]
Column.column [] [ button "+" (fun _ -> dispatch Increment) ] ] ]
Column.column [] [ button "+" (fun _ -> dispatch Increment) ] ]

Card.card [] [
Card.header [] [ Heading.h3 [ ] [ str "Miniprofiler-demo" ] ]
Card.content [] [
Content.content [ Content.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
[ Heading.h3 [] [ str "Press 'Ping!' buttons to call the server (and watch miniprofiler timings)" ] ]
Columns.columns []
[ Column.column [] [ button "Ping! (fetch)" (fun _ -> dispatch PingFetch) ]
Column.column [] [ button "Ping! (remoting)" (fun _ -> dispatch PingRemoting) ] ]
Content.content []
[ str "You can see the list or results at "
a [ Href "/mini-profiler-resources/results-index"] [ str "/mini-profiler-resources/results-index" ] ]
]
]
]

Footer.footer [ ]
[ Content.content [ Content.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ]
Expand Down
18 changes: 0 additions & 18 deletions src/Client/public/index.html

This file was deleted.

5 changes: 5 additions & 0 deletions src/Client/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ module.exports = {
},
devServer: {
proxy: {
// request the index page from Giraffe to get MiniProfiler scripts...
'/': {
target: 'http://localhost:' + port,
changeOrigin: true
},
'/api/*': {
target: 'http://localhost:' + port,
changeOrigin: true
Expand Down
43 changes: 39 additions & 4 deletions src/Server/Server.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,65 @@ open Shared
open Fable.Remoting.Server
open Fable.Remoting.Giraffe

open StackExchange.Profiling

let publicPath = Path.GetFullPath "../Client/public"
let port = 8085us

let getInitCounter () : Task<Counter> = task { return 42 }

let ping () = async {
// How to profile code: https://miniprofiler.com/dotnet/HowTo/ProfileCode

// something sync
using (MiniProfiler.Current.Step("Let's pretend we calculate something...")) (fun _ ->
System.Threading.Thread.Sleep 123
)

// something async
use _mp = MiniProfiler.Current.Step("Let's call a db asynchronously...")
do! Async.Sleep 42

return "pong (from remoting)"
}

let counterApi = {
initialCounter = getInitCounter >> Async.AwaitTask
ping = ping
}

let webApp =
Remoting.createApi()
|> Remoting.withRouteBuilder Route.builder
|> Remoting.fromValue counterApi
|> Remoting.buildHttpHandler
choose [
GET >=> choose [
route "/" >=> (fun next ctx -> htmlView (Views.index ctx) next ctx)
// do nothing special, and the overall time of the request is logged
route "/ping" >=> json "pong"
]

Remoting.createApi()
|> Remoting.withRouteBuilder Route.builder
|> Remoting.fromValue counterApi
|> Remoting.buildHttpHandler
]


let configureApp (app : IApplicationBuilder) =
app.UseDefaultFiles()
.UseStaticFiles()
.UseMiniProfiler()
.UseGiraffe webApp

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

// IMemoryCache is required for MiniProfiler's in-memory storage
services.AddMemoryCache()
.AddMiniProfiler(fun mp ->
// Config options for ASP.NET Core: https://miniprofiler.com/dotnet/AspDotNetCore
mp.ShowControls <- true
mp.PopupRenderPosition <- RenderPosition.Right
()) |> ignore

WebHost
.CreateDefaultBuilder()
.UseWebRoot(publicPath)
Expand Down
1 change: 1 addition & 0 deletions src/Server/Server.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\Shared.fs" />
<Compile Include="Views\Index.fs" />
<Compile Include="Server.fs" />
</ItemGroup>
<Import Project="..\..\.paket\Paket.Restore.targets" />
Expand Down
31 changes: 31 additions & 0 deletions src/Server/Views/Index.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Views

open Giraffe
open GiraffeViewEngine
open Microsoft.AspNetCore.Http
open StackExchange.Profiling

let index (ctx: HttpContext) =
let mp = MiniProfiler.Current.RenderIncludes(ctx)
html [] [
head [] [
meta [ _httpEquiv "Content-Type"; _content "text/html"; _charset "utf-8" ]
title [ ] [ encodedText "SAFE demo with MiniProfiler for .NET" ]

meta [ _name "viewport"; _content "width=device-width, initial-scale=1"]

link [ _rel "stylesheet"; _type "text/css"; _href "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css" ]
link [ _rel "stylesheet"; _type "text/css"; _href "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" ]
link [ _rel "stylesheet"; _type "text/css"; _href "https://fonts.googleapis.com/css?family=Open+Sans" ]

link [ _rel "shortcut icon"; _href "/Images/safe_favicon.png"; _type "image/png" ]
]
body [ ] [
div [ _id "elmish-app" ] []

// Include MiniProfiler script before your main js bundle, because MiniProfiler 'patches' XHR, fetch, etc to profile those requests...
rawText mp.Value

script [ _src "./js/bundle.js" ] [ ]
]
]
1 change: 1 addition & 0 deletions src/Server/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ group Server
Microsoft.AspNetCore
Microsoft.AspNetCore.StaticFiles
Fable.Remoting.Giraffe
MiniProfiler.AspNetCore.Mvc
3 changes: 2 additions & 1 deletion src/Shared/Shared.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ module Route =
/// A type that specifies the communication protocol between client and server
/// to learn more, read the docs at https://zaid-ajaj.github.io/Fable.Remoting/src/basics.html
type ICounterApi =
{ initialCounter : unit -> Async<Counter> }
{ initialCounter : unit -> Async<Counter>
ping: unit -> Async<string> }

0 comments on commit b63e5cd

Please sign in to comment.