3

Edit: here are some adaptions to the answer by Koenig Lear,in case someone else stumbles upon this question:

to add user secrets to the application i used this code, where the ID used by config.AddUserSecrets is the secrets id that gets added to the Server.fsproj by using dotnet user-secrets init.

app
    .ConfigureAppConfiguration(
        System.Action<Microsoft.Extensions.Hosting.HostBuilderContext,IConfigurationBuilder> ( fun ctx config ->
            config.AddUserSecrets("6de80bdf-2a05-4cf7-a1a8-d08581dfa887") |> ignore
        )
)
|> run

The secret can then be consumed in the router via:

 forward "/api" (fun next ctx ->

        let settings = ctx.GetService<IConfiguration>()
        let cString = settings.["Some:ConnectionString"]

        webApp cString next ctx

    )

Original Question:

I'm trying to wrap my head around how to access user secrets, but i cant quite translate the example provided by microsoft.

lets say i have Saturn running in a SAFE stack environment (most likely irrelevant in this case). I have a webApp, which takes a connection string and then handles requests using the provided connection string for DB access:

let router connectionString = forward "/api" (webApp connectionString)

and a user secret that contains the connection string that i initiated using dotnet user-secrets set :

{
  "Some:ConnectionString": "Data Source=..."
}

and an application that uses the router:

let app = application {
    url ("https://0.0.0.0:" + port.ToString() + "/")
    use_router (router )
}

What i am trying to do is somehow extract the user secret and pass it to the router. What i dont seem to grasp is where and how to perform the dependency injection that ASP.NetCore usually does (see where the user secret is configured via the Configuration API in the MS Docs example.).

What comes very close to what i am trying to do is this section from the example :


public class Startup
{
    private string _connection = null;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var builder = new SqlConnectionStringBuilder(
            Configuration.GetConnectionString("Movies"));
        builder.Password = Configuration["DbPassword"];
        _connection = builder.ConnectionString;
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync($"DB Connection: {_connection}");
        });
    }
}

The first problem is that i cant find how to get the Configuration object that contains the GetConnectionString method. But even if i could do that, i would configure this section via the service_config operation in the application monad right? But then I can't use the obtained connection string for the router, because the use_router operation is separated from it.

I am using Saturn 0.14.1 on netcoreapp/asp.netcore 3.1 .

Any kind of help would be greatly appreciated!

kMutagene
  • 177
  • 10

1 Answers1

2

You should be able to use the ConfigureAppConfiguration method:

open Microsoft.Extensions.Configuration
open Microsoft.Extensions.Configuration.UserSecrets

let configureAppConfiguration  (context: WebHostBuilderContext) (config:IConfigurationBuilder) =  
    config
        .AddUserSecrets()

app
   .ConfigureAppConfiguration(configureAppConfiguration)
   .run

Inside your router you could use it like this:

let webApp = router {
    get "/" (fun next ctx ->
        task {
            let settings = ctx.GetService<IConfiguration>()
            let s = settings.["Some:ConnectionString"]
            ...
        })
}
Koenig Lear
  • 2,366
  • 1
  • 14
  • 29
  • This is one of the things I initially tried, but `ConfigureAppConfiguration` does not take a function as input, but a System.Action: This expression was expected to have type 'System.Action' but here has type ''a -> 'b -> IConfigurationBuilder'. Additionally to that, this is the way to add secrets right? How would i access them from your example? – kMutagene Jul 02 '20 at 06:29
  • 1
    @Mutagene Action is just a function type :) in C# functions are not first-class so they need to create a delegate type: System.Action but in f# that is just Action<'a,'b> = 'a->'b->unit – Koenig Lear Jul 02 '20 at 10:41
  • I had to adapt your code a bit, but thanks to you i made it work. Thank you very much! ill add my adaptions to the original question in case someone else stumbles upon this. – kMutagene Jul 02 '20 at 13:39