Giter Site home page Giter Site logo

saturnframework / saturn Goto Github PK

View Code? Open in Web Editor NEW
696.0 27.0 105.0 5.19 MB

Opinionated, web development framework for F# which implements the server-side, functional MVC pattern

Home Page: https://saturnframework.org

License: MIT License

F# 98.89% Dockerfile 0.71% Shell 0.41%
fsharp web backend cross-platform saturn mvc-framework mvc mvc-architecture server-side server-side-rendering

saturn's People

Contributors

64j0 avatar baronfel avatar brase avatar cartermp avatar chrsteinert avatar draptik avatar forki avatar frassle avatar gfritz avatar groma84 avatar jeremyabbott avatar kaashyapan avatar krzysztof-cieslak avatar mexx avatar nhowka avatar ninofloris avatar onofretzk avatar retendo avatar rmunn avatar rusanov-vladimir avatar saboco avatar shalokshalom avatar simonmcconnell avatar stachudotnet avatar tforkmann avatar thorium avatar titaye avatar twith2sugars avatar viktorvan avatar walternative avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

saturn's Issues

scaffolding breaks the build

after running dotnet saturn gen Book Books id:string title:string author:string the build fails with multiple errors in BooksController.fs because of Controller.render expects string but the generated code (Views.index) returns Giraffe.GiraffeViewEngine.XmlNode

error message:
file: 'SaturnSample/src/SaturnSample/Books/BooksController.fs'
severity: 'Error'
message: 'This expression was expected to have type
'string'
but here has type
'Giraffe.GiraffeViewEngine.XmlNode' '
at: '16,40'

SAFE template application fails to start when diagnostics are enabled

Steps to reproduce:

  • Create new app using SAFE template with remoting enabled (dotnet new SAFE -lang F# --remoting)
  • Try to build and start the app (build run)

Actual behaviour: server side (Saturn) application crashes with following exception:

Unhandled Exception: System.ArgumentException: The input sequence was empty.
Parameter name: source
   at Microsoft.FSharp.Collections.SeqModule.Last[T](IEnumerable`1 source)
   at Saturn.SiteMap.generate() in D:\Programowanie\Projekty\Saturn\Saturn\src\Saturn\Diagnostics.fs:line 55
   at Saturn.Application.run(IWebHostBuilder app) in D:\Programowanie\Projekty\Saturn\Saturn\src\Saturn\Application.fs:line 381
   at <StartupCode$Server>.$Server.main@() in C:\work\dotnet\SAFESample\src\Server\Server.fs:line 33

It looks like SiteMap.generate function is called on empty state. It should be updated with SiteMap.add function which seems to be never called.

Disabling diagnostics (using disable_diagnostics directive) solves the issue.

Environment: .NET Core SDK 2.1.300, Windows 10, Saturn 0.5

TypeInitializationException with simple controller / routes

I'm having issues modifying the the example.
I'm trying to adapt things to a json-only api, and am getting this exception:

Exception thrown: 'System.Exception' in FSharp.Core.dll
Exception thrown: 'System.TypeInitializationException' in api.dll
An unhandled exception of type 'System.TypeInitializationException' occurred in api.dll
The type initializer for '<StartupCode$api>.$Server' threw an exception.

relevant code:

// Program.fs
module Server

open Saturn
open Config

let endpointPipe = pipeline {
    plug head
    plug requestId
}

let app = application {
    pipe_through endpointPipe

    router Router.router
    url "http://0.0.0.0:8085/"
    memory_cache 
    use_gzip
    use_config (fun _ -> {connectionString = "DataSource=database.sqlite"} ) 
}

[<EntryPoint>]
let main _ =
    printfn "Working directory - %s" (System.IO.Directory.GetCurrentDirectory())
    run app
    0 // return an integer exit code
// Router.fs
module Router

open Saturn
open Giraffe.Core
open Giraffe.ResponseWriters

open Resources

let apiPipeline = pipeline {
    plug acceptJson
    plug putSecureBrowserHeaders
}

let handleIndex (ctx) =
    "No Index Handler"
    |> Controller.text ctx

let routes = controller {

    //index handleIndex
    index handleIndex
}

let apiRouter = scope {
    forward "/arbitrage-configurations" routes
}

let router = scope {
    not_found_handler (setStatusCode 404 >=> text "Not Found")
    pipe_through apiPipeline
    forward "/api" apiRouter
}

Saturn template build fails "Cycle Detected"

C:\Users\Alan\projects\Saturn\src\Saturn\Saturn.fsproj : error NU1108: Cycle detected.
C:\Users\Alan\projects\Saturn\src\Saturn\Saturn.fsproj : error NU1108: Saturn -> Saturn (>= 0.2.0).

Delete requests are not routed

delete as well as deletef routes are currently not routed. This happens as the respective routes are not generated in the Router module.

I fixed this in this pull request. Just logging the issue for completeness sake.

How would a forwardf work?

I'm attempting to create a forwardf extension to scope so that I can pass along an id to a sub resource.

let subResource projectId = scope {
  get "/" getAllItems projectId
  getf "/%i" getItem projectId
}

let project = scope {
  get "/" allProjects
  getf "/%i" getProject
  forwardf "%i/items" subResource 
}

that snippet probably explains it best on what I'm trying do.

I'm happy to create a pr and work on this but I'm not 100% sure how to go about this.

OWIN + SignalR?

This is a question more than issue:

How do I integrate existing Owin-pipeline to Saturn, and does Saturn support SignalR?

Can't use `set_body` in pipeline CE

set_body referes to an overloaded member of PipelineBuilder and it looks like overloaded members can't be used in CEs. The compiler has to say the following about that:

The custom operation 'set_body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

Is it possible to just rename the overload that works on strings to something like set_string_body or set_body_from_string?

Swagger integration

I have to say this is exactly the Web Framework that I'm looking for in F#'s ecosystem! Suave is great, but in most case, I just need a fully functional framework which doesn't need to manually setup all infrastructures again and again to get things done.

Swagger is useful for implementing Restful API, I'm wondering it's possible to implement or include something like Suave.Swagger for Saturn.

XML documentation not yet being generated

#36 set GenerateDocumentationFile in the Saturn.fsproj file, with the intent of generating the XML docs so that tooltips would be visible in, e.g., Ionide.

However, 1edfbd3 later removed GenerateDocumentationFile from the .fsproj file, so the Saturn NuGet package (as of 0.5.0) does not have XML doc tooltips.

I suspect that some tool, perhaps Forge, stripped GenerateDocumentationFile when recreating the .fsproj file and nobody noticed.

Add /// comments for all CE keywords.

The biggest drawback to custom keywords in CEs is their lack of discoverability. We're already discussed this in another issue, and I don't want to dig that all up again, but I think that it's still something that should be addressed. The only way I can realistically think of it is to a /// docs to the associated function in the builder e.g.

memory_cache has an associated function, application.MemoryCache. If this was given documentation such as: "Turns on in-memory caching for Saturn. Use the memory_cache keyword to turn this on." it could help. The only alternative is to direct people to the docs site to see what the capabilities are, which isn't great IMHO.

What do you think?

Add Windows Auth support in the ApplicationBuilder

I would like to configure Windows Auth, similar to how currently OAuth or JWT tokens can be configured.
Currently, Kestrel does not support Windows Auth, so IIS or HttpSys has to be used. IIS can be configured, but HttpSys is currently completely missing (is it?)

Therefore I would like to have

  1. an option to configure HttpSys
  2. configure Windows Auth on ISS/HttpSys.

I already started playing around with possible implementations H E R E
, but I am not at all happy with the APIs yet and would love some advice on how to do the API and pursue an implementation.

Downsides

Implementing this will create an additional dependency on the Microsoft.AspNetCore.Server.HttpSys package.

Provide a way to access the IWebHostBuilder

I want to test stuff using the TestServer from Microsoft.AspNetCore.TestHost.
However it requires a IWebHostBuilder, which is not accessible when using the application CE.
It would be great when there would be a way to put Saturn application under tests easily.

Non-code configuration

I couldn't find anything in the post/README/samples yet, is there any idea about how to handle configuration with an external file (like a JSON)? This can be useful to have different configurations according to the environment and maybe even make some changes on the fly. Using ASP.NET Core JSON configuration has some benefits as expressed in this comment: SAFE-Stack/SAFE-BookStore#255 (comment)

Strategies for sharing data with Fable frontend apps

As Saturn will become the S in SAFE pretty soon and we want to also release Fable 2 in the upcoming weeks, it'll be good to start writing down ideas on how to easily communicate between the server and client sides to increase the value of full-stack F#.

Right now most of the users (myself included) are using Fable ofJson/toJson with the Fable.JsonConverter on the server side. However Fable 2 alpha will be released initially without reflection support in order to have lighter types and reduce bundle sizes (reflection may be added later depending on the feedback from users). Because of that, we're planning to promote @MangelMaxime Thot.Json for data exchange. It doesn't serialize types automagically, but it gives more control to developers and better validation. Maxime has also adapted it recently to server side.

Thot will soon also include helpers for Http requests which will improve the current Fetch bindings (not really nice in F# when building complex queries), and we can try to provide a similar API on the server side. Maxime already sent a proposal to Http.fs (see haf/Http.fs#142) but it may make sense to add it directly to Saturn.

Any thoughts on this, @Krzysztof-Cieslak @forki?

Controller CustomOperation suggestions

I think you could reduce some of the cognitive overhead of things like task { return! ... } with custom operations for model binding against a context or returning a result in the form of a known media type. I would love to hear what you have already tried and will try to find an opportunity to implement a prototype to show how I think this might work. It may require you change the ControllerState to always use Task<_>, if it doesn't already.

Controller DELETE fails with null ref

This trivial controller fails with the following exception:

let meetingsEndpoint = controller {
    delete (fun (ctx, id) -> (sprintf "Delete meeting %d" id) |> Controller.text ctx) }

let app = application {
    url "http://localhost:8085"
    router meetingsEndpoint }

run app
Giraffe.Middleware.GiraffeErrorHandlerMiddleware[0]
      An unhandled exception has occurred while executing the request.
System.NullReferenceException: Object reference not set to an instance of an object.
   at [email protected](Tuple`2 tupledArg) in D:\Programowanie\Projekty\Saturn\Saturn\src\Saturn\Controller.fs:line 48
   at [email protected](Exception _arg2) in D:\Programowanie\Projekty\Saturn\Saturn\src\Saturn\Controller.fs:line 152
   at [email protected](Unit unitVar0)
   at FSharp.Control.Tasks.TaskBuilder.StepStateMachine`1.nextAwaitable()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at [email protected](Unit unitVar0)
   at FSharp.Control.Tasks.TaskBuilder.StepStateMachine`1.nextAwaitable()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at [email protected](Unit unitVar0)
   at FSharp.Control.Tasks.TaskBuilder.StepStateMachine`1.nextAwaitable()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at [email protected](Unit unitVar0)
   at FSharp.Control.Tasks.TaskBuilder.tryWith[a](FSharpFunc`2 step, FSharpFunc`2 catch)

May be put this as an up-for-grabs as an opportunity for someone in the community to fix?

Application name defaults to "Saturn" majorly breaking framework expectations on this

Things like IHostingStartup auto discovery and IStartup discovery fail, also distributed cache and quite a few other things expect this to be unique/equal to your startup assembly name. This stuff is for instance used by ApplicationInsights auto wireup.

The ApplicationName comes from the assembly that hosts the Configure delegate passed into builder.Configure as can be seen here https://github.com/aspnet/Hosting/blob/ae9da9290eb825f4145aaaa23603186d51211e53/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs#L30

Temp fixes are:

  • Change name inside HostingEnvironment
  • Change it with UseSetting(WebHostDefaults.ApplicationKey, "NewName") before running build

PR fix would be to use the same reflection strategy WebHostBuilderExtensions uses on the router and override the setting before returning the builder.

Controller/app versioning

As far as I know, GraphQL tries to solve the following problems:

  1. Give more power to the client to build queries to the server
  2. Provide a data schema that clients can inspect with appropriate tooling
  3. Allow different old client versions (particularly mobile apps which are not always up-to-date) to interact with the server

Point 1 is a bit controversial as some think it's not a good idea to give clients the power to query anything in your database. Point 2 is not that necessary if you use a typed language in both ends and share the domain model. But I'm interested in point 3, as with server frequent updates the ability to respond gracefully to out-of-date clients is important. Not only for mobile apps but also in SPAs that can remain outdated until the user refreshes for a long time.

Forcing devs to give a version number can lead them through the pit of success and make it simpler to update the server while keeping different versions of a controller alive during a transition period. This is one of the features I like the most in IndexedDB even if it looks cumbersome at the beginning.

The version number could be in the URL or in a header if we want to hide it, and the framework would take care of calling the appropriate controller. It would be easy to create a helper for Fable so it automatically adds the version to the requests too.

index-only controller shouldn't require a type parameter

@jeremyabbott highlighted that a controller with only an index definition with no path parameter requires a type parameter in order to run, even though it compiles. I agree with him it would be nice to not require the type parameter in order to run. Example:

let resource = controller {
    index indexHandler
}

☝️ compiles but fails to run

let resource = controller<int> {
    index indexHandler
}

☝️ runs fine

Getting error when running build.sh

I got this error when setting up the project from the saturn template, looks like it's related to the paket.exe version

Error: SecureChannelFailure (The authentication or decryption has failed.) (Github - cached (temporarily ignore updates))

Query builders vs ordinary lists

I was just randomly browsing through the source without actually running and using it - so please feel free to completely ignore this, because I don't know what I'm talking about!

Anyway, I was curious why Saturn uses custom query operators rather than relying on normal F# lists. For example:

let topRouter = scope {
    get "/" helloWorld
    getf "/name/%s" helloWorldName
    forward "/other" (scope {
        error_handler (text "Other 404")
       get "/" otherHelloWorld
    }) 
}

I imagine this could very well be written as just a list:

let topRouter = scope [
    get "/" helloWorld
    getf "/name/%s" helloWorldName
    forward "/other" (scope [
        error_handler (text "Other 404")
       get "/" otherHelloWorld
    ])
]

(Here scope is just a function that takes a list of primitive things and composes them accordingly.) What are the benefits of using custom query builders here? I can think of one reason why standard lists might be a bit nicer which is that it could let you generate different constructs based on some configuration:

let topRouter = scope [
    yield get "/" helloWorld
    if debugging then
        yield get "/debug" debugINfo ]

I think the custom queries look very nice - but I'm slightly worried that it's introducing strange syntax that people will need to get used to - without having huge technical benefits that would make a strong enough case for them.

IDs with # in them need to be URL-quoted

To reproduce:

  1. Follow the "How to start in 60 seconds" guide from the site.
  2. Create a book whose ID has a # character in it (I created a book with ID LEARNING_F#, title "Learning F#", author "Some Guy").
  3. That book's Show, Edit and Delete links fail to work: Show and Edit produce 404 errors, while Delete just silently does nothing at all.

The reason this fails is because the Show and Edit buttons generate the following links:

http://localhost:8085/books/LEARNING_F#
http://localhost:8085/books/LEARNING_F#/edit

And, of course, the # character in those links is seen as the URL fragment separator. If I manually replace the # with %23 in those links, then they work as intended:

http://localhost:8085/books/LEARNING_F%23
http://localhost:8085/books/LEARNING_F%23/edit

Other characters, like /, would almost certainly cause similar issues. The fix is, of course, to URL-quote IDs before building URLs. The question that needs to be answered is where such URL-quoting should happen (in Links.withId and similar functions? Or elsewhere?), and how to ensure that an ID or other part of a constructed URL isn't URL-quoted twice.

dotnet new saturn doesn't work (macOS)

  • macOS 10.13.2
  • dotnet --version: 2.1.4
  • Installed Saturn template with dotnet new -i Saturn.Template - OK
  • Creating project with dotnet new saturn - I get
$ dotnet new saturn
Usage: new [options]

Options:
  -h, --help          Displays help for this command.
  -l, --list          Lists templates containing the specified name. If no name is specified, lists all templates.
  -n, --name          The name for the output being created. If no name is specified, the name of the current directory is used.
  -o, --output        Location to place the generated output.
  -i, --install       Installs a source or a template pack.
  -u, --uninstall     Uninstalls a source or a template pack.
  --type              Filters templates based on available types. Predefined values are "project", "item" or "other".
  --force             Forces content to be generated even if it would change existing files.
  -lang, --language   Specifies the language of the template to create.


Saturn v0.2.1 (F#)
Author: Krzysztof Cieslak
    (No Parameters)

and nothing created in current directory

use_static also sets contentroot?

use_static breaks resolving of things like configs which are copied by msbuild to be next to the project dll.

This search path is set through ContentRoot(path) which is basically asp.nets version of the CWD (if you don't override it) and has very little to do with where your static files are served from which uses webroot.

I'd like to see it go :)

Adding a new controller - View expected string but got xml node

When following along with the quick start and adding a new controller using tool, I get the following compile error:

`D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(16,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(16,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(27,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(27,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(29,39): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(35,28): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(35,28): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(43,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(43,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(45,39): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(64,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(64,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(80,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]
D:\Projects\ToRead\src\ToRead\Books\BooksController.fs(80,40): error FS0001: This expression was expected to have type� 'string' �but here has type� 'Giraffe.GiraffeViewEngine.XmlNode' [D:\Projects\ToRead\src\ToRead\ToRead.fsproj]

Build FAILED.`
compileerror

not_found_handler doesn't seem to respect forward.

We have two simple routers set up:

let router = scope {
    forward "/api" ApiRouter.apiRouter
    forward "" ViewRouter.browserRouter
}

Inside browserRouter we have defined a not_found_handler

not_found_handler (htmlView NotFound.layout) //Use the default 404 webpage

However, this seems to also apply to missing /api routes. I want non-existant api routes to have a different behaviour i.e. to return a 404 instead of the HTML view we set above, which should only apply to browser routes (and returns a 200 with some HTML).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.