diff --git a/src/main/scala/gs/config/AuditedConfiguration.scala b/src/main/scala/gs/config/AuditedConfiguration.scala index 8204fa2..4f47b78 100644 --- a/src/main/scala/gs/config/AuditedConfiguration.scala +++ b/src/main/scala/gs/config/AuditedConfiguration.scala @@ -1,5 +1,6 @@ package gs.config +import cats.data.NonEmptyList import cats.effect.Sync import cats.syntax.all.* import gs.config.audit.ConfigManifest @@ -8,8 +9,16 @@ import gs.config.source.ConfigSource import gs.config.source.EnvironmentConfigSource import gs.config.source.MemoryConfigSource +/** Implementation of [[gs.config.Configuration]] that tracks every call to + * `getValue` and reports on whether the query succeeded or failed. + * + * @param sources + * List of sources to use, in order, when resolving keys. + * @param manifest + * The manifest used to audit configuration loaded by this class. + */ final class AuditedConfiguration[F[_]: Sync]( - val sources: List[ConfigSource[F]], + val sources: NonEmptyList[ConfigSource[F]], val manifest: ConfigManifest[F] ) extends BaseConfiguration[F]: import AuditedConfiguration.Acc @@ -70,8 +79,17 @@ final class AuditedConfiguration[F[_]: Sync]( object AuditedConfiguration: - def forSources[F[_]: Sync](sources: List[ConfigSource[F]]) - : F[Configuration[F]] = + /** Instantiate a new [[AuditedConfiguration]] based on the given list of + * sources. + * + * @param sources + * The list of [[ConfigSource]] backing this configuration. + * @return + * The new [[Configuration]] instance. + */ + def forSources[F[_]: Sync]( + sources: NonEmptyList[ConfigSource[F]] + ): F[Configuration[F]] = ConfigManifest .standard[F] .map(manifest => @@ -81,25 +99,32 @@ object AuditedConfiguration: ) ) - def builder[F[_]: Sync]: Builder[F] = Builder[F](Vector.empty) + def forSource[F[_]: Sync](source: ConfigSource[F]): Builder[F] = + Builder[F](NonEmptyList.of(source)) - case class Builder[F[_]: Sync](sources: Vector[ConfigSource[F]]): + def forEnvironmentSource[F[_]: Sync]: Builder[F] = + Builder[F](NonEmptyList.of(new EnvironmentConfigSource[F])) + + def forMemorySource[F[_]: Sync](configs: Map[String, String]): Builder[F] = + Builder[F](NonEmptyList.of(new MemoryConfigSource[F](configs))) + + case class Builder[F[_]: Sync](sources: NonEmptyList[ConfigSource[F]]): def withEnvironmentSource(): Builder[F] = - copy(sources = this.sources.appended(new EnvironmentConfigSource[F])) + copy(sources = this.sources.append(new EnvironmentConfigSource[F])) def withMemorySource(configs: Map[String, String]): Builder[F] = - copy(sources = this.sources.appended(new MemoryConfigSource[F](configs))) + copy(sources = this.sources.append(new MemoryConfigSource[F](configs))) def withSource(source: ConfigSource[F]): Builder[F] = - copy(sources = this.sources.appended(source)) + copy(sources = this.sources.append(source)) def build(): F[Configuration[F]] = ConfigManifest .standard[F] .map(manifest => new AuditedConfiguration[F]( - sources = sources.toList, + sources = sources, manifest = manifest ) ) diff --git a/src/main/scala/gs/config/Configuration.scala b/src/main/scala/gs/config/Configuration.scala index cb35aef..db129dc 100644 --- a/src/main/scala/gs/config/Configuration.scala +++ b/src/main/scala/gs/config/Configuration.scala @@ -1,21 +1,33 @@ package gs.config import cats.effect.Sync +import gs.config.source.ConfigSource -/** Interface for loading configuration. +/** Interface for loading configuration. This type should not be used for any + * sensitive configuration such as secrets or private keys. */ trait Configuration[F[_]]: + /** Retrieve a value based on some key. + * + * @param key + * The key that identifies the piece of configuration to retrieve. + * @return + * The value, or an error if no value is present. + */ def getValue[A: Configurable](key: ConfigKey[A]): F[Either[ConfigError, A]] object Configuration: /** Start building a new [[AuditedConfiguration]]. * + * @param source + * The first source relevant to the configuration. * @return * Fluent builder for the [[AuditedConfiguration]] class. */ - def audited[F[_]: Sync]: AuditedConfiguration.Builder[F] = - AuditedConfiguration.builder[F] + def audited[F[_]: Sync](source: ConfigSource[F]) + : AuditedConfiguration.Builder[F] = + AuditedConfiguration.forSource[F](source) /** Create a new [[AuditedConfiguration]] that reads from the environment. * @@ -23,6 +35,6 @@ object Configuration: * The new [[Configuration]]. */ def auditedEnvironmentOnly[F[_]: Sync]: F[Configuration[F]] = - audited[F].withEnvironmentSource().build() + AuditedConfiguration.forEnvironmentSource[F].build() end Configuration