saturnframework / saturn Goto Github PK
View Code? Open in Web Editor NEWOpinionated, web development framework for F# which implements the server-side, functional MVC pattern
Home Page: https://saturnframework.org
License: MIT License
Opinionated, web development framework for F# which implements the server-side, functional MVC pattern
Home Page: https://saturnframework.org
License: MIT License
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'
Steps to reproduce:
dotnet new SAFE -lang F# --remoting
)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
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
}
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
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.
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.
What about templates for Visual Studio?
This is a question more than issue:
How do I integrate existing Owin-pipeline to Saturn, and does Saturn support SignalR?
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
?
For example one may want to authenticate only create
action
Neither fetchmodel nor loadmodel do any await on the task from BindModelAsync
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.
Sorry on phone. Otherwise I would have sent it as PR
#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.
Original discussion in #20
by @Nhowka :
https://vtquan.github.io/fsharp/setting-up-services-with-giraffe/
I was probably assuming stuff that is not true. I was understanding that these policies could behave as a role alias so I could use them in
requires_role
. Like the link, usingAdministrators
to match Administrator andModerators
to match Administrator or Moderator.
Could we have arequires_policy
with that behaviour?
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?
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
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.
Implementing this will create an additional dependency on the Microsoft.AspNetCore.Server.HttpSys
package.
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.
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)
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?
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.
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?
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:
HostingEnvironment
UseSetting(WebHostDefaults.ApplicationKey, "NewName")
before running buildPR fix would be to use the same reflection strategy WebHostBuilderExtensions
uses on the router and override the setting before returning the builder.
Some controller routes e.g. show
use a tupled form for ctx and key. Other routes use curried form. Is there a reason why we have tupled form sometimes?
As far as I know, GraphQL tries to solve the following problems:
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.
@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
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))
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.
To reproduce:
LEARNING_F#
, title "Learning F#", author "Some Guy").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.
What about scaffolding support in for Visual Studio
Reasoning for tupled handler args controller update/delete?
Makes it a bit nasty to do nice generic things over all these handlers.
If there's no objection I'd like to have it as normal curried args.
dotnet new -i Saturn.Template
- OKdotnet 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
Unintuitive ordering of plugs, in my mind it would make more sense to have first plugs run first instead of last.
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 :)
title says it all. On OSX.
dotnet new -i Saturn.Template
dotnet new saturn -lang F#
./build.sh
--> error
chmod u+x build.sh
all ok
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]
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).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.