Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combining Env and File providers #108

Open
yeraydiazdiaz opened this issue Dec 21, 2020 · 2 comments
Open

Combining Env and File providers #108

yeraydiazdiaz opened this issue Dec 21, 2020 · 2 comments

Comments

@yeraydiazdiaz
Copy link

Hi, thanks so much for Vapor and everything you do for the Elixir community.

In my app I was trying to define the configuration in a file but be able override some of it using environment variables. This, however, doesn't seem possible at the moment because both Env and File providers require all non-default bindings to be present. For example:

Having a config YAML file like:

HOST: localhost
PORT: 4000

and a setup like:

bindings = [
      {:host, "HOST"},
      {:port, "PORT"},
]
providers = [
    %Vapor.Provider.File{path: "config.yaml", bindings: bindings},
    %Vapor.Provider.Env{bindings: bindings},
]

Will fail if either HOST or PORT are not present in the environment variables.

Personally, I was expecting the combined results of all providers to raise if some of the bindings were missing. I realize that's probably not what the bindings were designed to do though, but it seems the only way to ensure that the config file includes all the required variables.

Thanks again 💯

@lafka
Copy link

lafka commented Apr 22, 2021

Environment variables by default is set with required: true. If you use [{:host, "HOST", required: false},...] in your bindings this should probably work.

One consideration, the env will default to nil which overrides whatever was there in file. I've sucesfully used this patch to allow ENV to override previous variables:

diff --git a/lib/vapor/providers/env.ex b/lib/vapor/providers/env.ex
index cfee96f..6c27fdb 100644
--- a/lib/vapor/providers/env.ex
+++ b/lib/vapor/providers/env.ex
@@ -30,7 +30,7 @@ defmodule Vapor.Provider.Env do
       else
         envs =
           bound_envs
-          |> Enum.reject(fn {_, data} -> data.val == :missing end)
+          |> Enum.reject(fn {_, data} -> data.val in [:missing, :skip] end)
           |> Enum.map(fn {name, data} -> {name, data.val} end)
           |> Enum.into(%{})
 
@@ -51,7 +51,7 @@ defmodule Vapor.Provider.Env do
           val = if data.opts[:default] != nil do
             data.opts[:default]
           else
-            if data.opts[:required], do: :missing, else: nil
+            if data.opts[:required], do: :missing, else: :skip
           end
           {name, %{data | val: val}}

If you're using %Group{}, or the DSL, then the default loader will fail since it does not do recursive merge. Making a merge strategy that fits every use I assume is out of scope for this library. If you need this you can copy out Vapor.loader and make it recursive and then use something like this (instead of Vapor.load(Provider)):

      case CustomLoader.load(CustomConfigProvider) do
        {:ok, cfg} ->
          cfg

        {:error, errors} ->
          raise Vapor.LoadError.exception(errors)
      end

@autodidaddict
Copy link

I've run into this exact same problem. The use of "precedence" with file and env providers doesn't seem to work as advertised / expected. If I put a setting in a file, and then it shows up as a binding in the environment provider configuration, but that env var hasn't been set, then I should be able to use the file-sourced value. Today, if I toggle required: false then I get nil for the merged config value rather than what I had in the file.

Also, the file provider doesn't actually deal with missing files. If the file is missing, it should do nothing and move on to the next provider. Today, it crashes meaning I can't use the nice DSL because I have to drop a File.exists? in the mix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants