-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
327 additions
and
18 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
namespace TodoBackend.Controllers | ||
|
||
open Microsoft.AspNetCore.Mvc | ||
open TodoBackend | ||
|
||
type TodoView = | ||
{ id: int | ||
url: string | ||
order: int; title: string; completed: bool } | ||
|
||
type GetByIdArgsTemplate = { id: int } | ||
|
||
// Fulfuls contract dictated by https://www.todobackend.com/specs/index.html?https://localhost:5001/todos | ||
// To run, start: | ||
// & dotnet run -f netcoreapp2.1 -p samples/TodoBackend | ||
// https://www.todobackend.com/client/index.html?https://localhost:5001/todos | ||
// Stolen from https://github.com/ChristianAlexander/dotnetcore-todo-webapi/blob/master/src/TodoWebApi/Controllers/TodosController.cs | ||
// even more stolen from https://github.com/joeaudette/playground/blob/master/spa-stack/src/FSharp.WebLib/Controllers.fs | ||
[<Route "[controller]">] | ||
type TodosController(service: Service) = | ||
inherit Controller() | ||
|
||
let toModel (value : TodoView) : Todo = { id = value.id; order = value.order; title = value.title; completed = value.completed } | ||
|
||
member private __.WithUri(x : Todo) : TodoView = | ||
let url = __.Url.RouteUrl("GetTodo", { id=x.id }, __.Request.Scheme) // Supplying scheme is secret sauce for making it absolute as required by client | ||
{ id = x.id; url = url; order = x.order; title = x.title; completed = x.completed } | ||
|
||
[<HttpGet>] | ||
member __.Get() : Async<TodoView seq> = async { | ||
let! xs = service.List | ||
return seq { for x in xs -> __.WithUri(x) } | ||
} | ||
|
||
[<HttpGet("{id}", Name="GetTodo")>] | ||
member __.Get id : Async<IActionResult> = async { | ||
let! x = service.TryGet id | ||
return match x with None -> __.NotFound() :> _ | Some x -> ObjectResult(__.WithUri x) :> _ | ||
} | ||
|
||
[<HttpPost>] | ||
member __.Post([<FromBody>]value : TodoView) : Async<TodoView> = async { | ||
let! created = service.Create(toModel value) | ||
return __.WithUri created | ||
} | ||
|
||
[<HttpPatch "{id}">] | ||
member __.Patch( id, [<FromBody>]value : TodoView) : Async<TodoView> = async { | ||
let! updated = service.Patch { toModel value with id = id } | ||
return __.WithUri updated | ||
} | ||
|
||
[<HttpDelete "{id}">] | ||
member __.Delete id : Async<unit> = | ||
service.Execute <| Delete id | ||
|
||
[<HttpDelete>] | ||
member __.DeleteAll(): Async<unit> = | ||
service.Execute <| Clear |
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,39 @@ | ||
namespace TodoBackend | ||
|
||
open Argu | ||
open Microsoft.AspNetCore | ||
open Microsoft.AspNetCore.Hosting | ||
open Microsoft.Extensions.DependencyInjection | ||
open Serilog | ||
|
||
module Program = | ||
let createWebHostBuilder (args,parsed) : IWebHostBuilder = | ||
WebHost | ||
.CreateDefaultBuilder(args) | ||
.ConfigureServices(fun services -> Startup.ConfigureServices(services, parsed)) | ||
.Configure(fun app -> Startup.Configure(app, app.ApplicationServices.GetService<IHostingEnvironment>())) | ||
.UseSerilog() | ||
|
||
[<EntryPoint>] | ||
let main argv = | ||
try | ||
let programName = System.Reflection.Assembly.GetEntryAssembly().GetName().Name | ||
let args = ArgumentParser.Create<Arguments>(programName = programName).ParseCommandLine(argv) | ||
// Replace logger chain with https://github.com/serilog/serilog-aspnetcore | ||
let c = | ||
LoggerConfiguration() | ||
.MinimumLevel.Debug() | ||
.MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning) | ||
.Enrich.FromLogContext() | ||
.WriteTo.Console() | ||
let c = | ||
let maybeSeq = if args.Contains LocalSeq then Some "http://localhost:5341" else None | ||
match maybeSeq with None -> c | Some endpoint -> c.WriteTo.Seq(endpoint) | ||
let log : ILogger = c.CreateLogger() :> _ | ||
Log.Logger <- log | ||
log.Information("Running with Process Id: {pid}", System.Diagnostics.Process.GetCurrentProcess().Id) | ||
createWebHostBuilder(argv, args).Build().Run() | ||
0 | ||
with e -> | ||
eprintfn "%s" e.Message | ||
1 |
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,78 @@ | ||
namespace TodoBackend | ||
|
||
open Argu | ||
open Microsoft.AspNetCore.Builder | ||
open Microsoft.AspNetCore.Hosting | ||
open Microsoft.AspNetCore.Mvc | ||
open Microsoft.Extensions.DependencyInjection | ||
open Samples.Infrastructure.Services | ||
open Samples.Infrastructure.Storage | ||
open Serilog | ||
open Serilog.Events | ||
open System | ||
|
||
[<NoComparison>] | ||
type Arguments = | ||
| [<AltCommandLine("-vc")>] VerboseConsole | ||
| [<AltCommandLine("-S")>] LocalSeq | ||
| [<AltCommandLine("-C")>] Cached | ||
| [<AltCommandLine("-U")>] Unfolds | ||
| [<CliPrefix(CliPrefix.None); Last; Unique>] Memory of ParseResults<MemArguments> | ||
| [<CliPrefix(CliPrefix.None); Last; Unique>] Es of ParseResults<EsArguments> | ||
interface IArgParserTemplate with | ||
member a.Usage = a |> function | ||
| VerboseConsole -> "Include low level Domain and Store logging in screen output." | ||
| LocalSeq -> "configures writing to a local Seq endpoint at http://localhost:5341, see https://getseq.net" | ||
| Cached -> "employ a 50MB cache." | ||
| Unfolds -> "employ a store-appropriate Rolling Snapshots and/or Unfolding strategy." | ||
| Memory _ -> "specify In-Memory Volatile Store (Default store)." | ||
| Es _ -> "specify storage in EventStore (--help for options)." | ||
|
||
type App = class end | ||
|
||
// :shame: This should be a class used via UseStartup, but I couldnt figure out how to pass the parsed args in as MS have changed stuff around too much to make it googleable within my boredom threshold | ||
type Startup() = | ||
// This method gets called by the runtime. Use this method to add services to the container. | ||
static member ConfigureServices(services: IServiceCollection, args: ParseResults<Arguments>) : unit = | ||
services.AddCors().AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) |> ignore | ||
|
||
let verboseConsole = args.Contains VerboseConsole | ||
let maybeSeq = if args.Contains LocalSeq then Some "http://localhost:5341" else None | ||
let createStoreLog verboseStore = | ||
let c = LoggerConfiguration().Destructure.FSharpTypes() | ||
let c = if verboseStore then c.MinimumLevel.Debug() else c | ||
let c = c.WriteTo.Console((if verboseStore && verboseConsole then LogEventLevel.Debug else LogEventLevel.Warning), theme = Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code) | ||
let c = match maybeSeq with None -> c | Some endpoint -> c.WriteTo.Seq(endpoint) | ||
c.CreateLogger() :> ILogger | ||
|
||
let storeConfig, storeLog : StorageConfig * ILogger = | ||
let options = args.GetResults Cached @ args.GetResults Unfolds | ||
let cache, unfolds = options |> List.contains Cached, options |> List.contains Unfolds | ||
let log = Log.ForContext<App>() | ||
|
||
match args.TryGetSubCommand() with | ||
| Some (Es sargs) -> | ||
let storeLog = createStoreLog <| sargs.Contains EsArguments.VerboseStore | ||
log.Information("EventStore Storage options: {options:l}", options) | ||
EventStore.config (log,storeLog) (cache, unfolds) sargs, storeLog | ||
| _ | Some (Memory _) -> | ||
log.Fatal("Web App is using Volatile Store; Storage options: {options:l}", options) | ||
MemoryStore.config (), log | ||
|
||
let regF (factory : IServiceProvider -> 'T) = services.AddSingleton<'T>(fun (sp: IServiceProvider) -> factory sp) |> ignore | ||
|
||
let resolver = StreamResolver(storeConfig) | ||
|
||
regF <| fun _sp -> | ||
let codec = genCodec<Events.Event>() | ||
let fold, initial, snapshot = Folds.fold, Folds.initial, Folds.snapshot | ||
Service(storeLog, resolver.Resolve(codec,fold,initial,snapshot)) | ||
|
||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | ||
static member Configure(app: IApplicationBuilder, env: IHostingEnvironment) : unit = | ||
if env.IsDevelopment() then app.UseDeveloperExceptionPage() |> ignore | ||
else app.UseHsts() |> ignore | ||
|
||
app.UseHttpsRedirection() | ||
.UseCors(fun x -> x.WithOrigins([|"https://www.todobackend.com"|]).AllowAnyHeader().AllowAnyMethod() |> ignore) | ||
.UseMvc() |> ignore |
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,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="TodosService.fs" /> | ||
<Compile Include="Startup.fs" /> | ||
<Compile Include="Controllers\TodosController.fs" /> | ||
<Compile Include="Program.fs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Store\Infrastructure\Infrastructure.fsproj" /> | ||
<ProjectReference Include="..\..\src\Equinox.MemoryStore\Equinox.MemoryStore.fsproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Argu" Version="5.1.0" /> | ||
<PackageReference Include="Microsoft.AspNetCore.App" /> | ||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.