rstudio / config

config package for R
https://rstudio.github.io/config/
256 stars 27 forks source link

Option to mix configuration types #48

Closed billelev closed 1 year ago

billelev commented 1 year ago

I have a configuration file for database connections, but for different DBs e.g. Postgres and Sqlite. I therefore need different fields.

When I run config::get the function appears to merge values from the configurations I am not asking for. Could functionality be added so that separate and unique configurations can be included in one file?

E.g. For the following configuration file, config::get(config = "sqlite_local") would return only type, path, not type, host, name, port, user, pass, path.

default:
  type: postgres
  host: xxx
  name: yyy
  port: 5432
  user: user1
  pass: pass1

postgres:
  type: postgres
  host: xxx
  name: yyy
  port: 5432
  user: user2
  pass: pass2

sqlite_local:
  type: sqlite
  path: "~/data/db/test.db"

Another style option would be to allow configuration types to nest under a particular config type.

e.g.

config::get(config = "postgres") would return type, host, name, port, user1, pass1 config::get(config = "postgres", key = "key1") would return type, host, name, port, user2, pass2 config::get(config = "sqlite_local") would return type, path

postgres:
  type: postgres
  host: xxx
  name: yyy
  port: 5432
  default:
    user: user1
    pass: pass1
  key1:
    user: user2
    pass: pass2

sqlite_local:
  type: sqlite
  path: "~/data/db/test.db"
andrie commented 1 year ago

The config::get() function does indeed inherit properties from the default configuration, as you observed, and as documented.

If you want different behaviour, I think you have broadly two options:

Neither of these workarounds are probably going to be satisfactory, but this is what I mean for the second option:

default:
  type: postgres
  host: xxx
  name: yyy
  port: 5432
  user: user1
  pass: pass1

empty:
  type: null
  host: null
  name: null
  port: null
  user: null
  pass: null

postgres:
  type: postgres
  host: xxx
  name: yyy
  port: 5432
  user: user2
  pass: pass2

sqlite_local:
  inherits: empty
  type: sqlite
  path: "~/data/db/test.db"

If you can think of a way to get your desired behaviour that also satisfied backward compatibility, please let me know.

billelev commented 1 year ago

Thanks, Andrie. Below is a configuration that does a lot of what I am looking for. There is a minimal default definition, then multiple type_default definitions tied to child configurations.

default:
  type: ""

pg_default:
  type: postgres
  host: host_xxx
  name: name_xxx
  port: port_xxx
pg_ro:
  inherits: pg_default
  user: user_ro
  pass: pass_ro
pg_rw:
  inherits: pg_default
  user: user_rw
  pass: pass_rw

sql_default:
  type: sqlite
  path: path/to/file
sql_local:
  inherits: sql_default
  path: path/to/file2

slack_default:
  type: slack
  account: account_xxx
  emoji: emoji_xxx
slack_u1:
  inherits: slack_default
  user: user1
  pass: pass1
slack_u2:
  inherits: slack_default
  user: user2
  pass: pass2
andrie commented 1 year ago

That's sensible.

I have one suggestion, though, and that is to make use of environment variables to store your usernames and passwords.

Right now it seems you're trying to encode multiple usernames and passwords in a single plain text file.

A better pattern would be to use something like this:

pg_default:
  type: postgres
  host: host_xxx
  name: !expr Sys.getenv("postgres_user")
  port: !expr Sys.getenv("postgres_password")

See the vignette at https://rstudio.github.io/config/articles/inheritance.html#using-r-code-inside-the-yaml-file

billelev commented 1 year ago

Yes, absolutely. Thanks for the suggestion.