Some minor changes and additional documentation.

This commit is contained in:
Pat Garrity 2024-01-04 22:34:10 -06:00
parent 642d8dfc73
commit 1fdde0d7fb
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
2 changed files with 50 additions and 13 deletions

View file

@ -1,5 +1,6 @@
package gs.config package gs.config
import cats.data.NonEmptyList
import cats.effect.Sync import cats.effect.Sync
import cats.syntax.all.* import cats.syntax.all.*
import gs.config.audit.ConfigManifest import gs.config.audit.ConfigManifest
@ -8,8 +9,16 @@ import gs.config.source.ConfigSource
import gs.config.source.EnvironmentConfigSource import gs.config.source.EnvironmentConfigSource
import gs.config.source.MemoryConfigSource 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]( final class AuditedConfiguration[F[_]: Sync](
val sources: List[ConfigSource[F]], val sources: NonEmptyList[ConfigSource[F]],
val manifest: ConfigManifest[F] val manifest: ConfigManifest[F]
) extends BaseConfiguration[F]: ) extends BaseConfiguration[F]:
import AuditedConfiguration.Acc import AuditedConfiguration.Acc
@ -70,8 +79,17 @@ final class AuditedConfiguration[F[_]: Sync](
object AuditedConfiguration: object AuditedConfiguration:
def forSources[F[_]: Sync](sources: List[ConfigSource[F]]) /** Instantiate a new [[AuditedConfiguration]] based on the given list of
: F[Configuration[F]] = * 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 ConfigManifest
.standard[F] .standard[F]
.map(manifest => .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] = 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] = 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] = def withSource(source: ConfigSource[F]): Builder[F] =
copy(sources = this.sources.appended(source)) copy(sources = this.sources.append(source))
def build(): F[Configuration[F]] = def build(): F[Configuration[F]] =
ConfigManifest ConfigManifest
.standard[F] .standard[F]
.map(manifest => .map(manifest =>
new AuditedConfiguration[F]( new AuditedConfiguration[F](
sources = sources.toList, sources = sources,
manifest = manifest manifest = manifest
) )
) )

View file

@ -1,21 +1,33 @@
package gs.config package gs.config
import cats.effect.Sync 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[_]]: 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]] def getValue[A: Configurable](key: ConfigKey[A]): F[Either[ConfigError, A]]
object Configuration: object Configuration:
/** Start building a new [[AuditedConfiguration]]. /** Start building a new [[AuditedConfiguration]].
* *
* @param source
* The first source relevant to the configuration.
* @return * @return
* Fluent builder for the [[AuditedConfiguration]] class. * Fluent builder for the [[AuditedConfiguration]] class.
*/ */
def audited[F[_]: Sync]: AuditedConfiguration.Builder[F] = def audited[F[_]: Sync](source: ConfigSource[F])
AuditedConfiguration.builder[F] : AuditedConfiguration.Builder[F] =
AuditedConfiguration.forSource[F](source)
/** Create a new [[AuditedConfiguration]] that reads from the environment. /** Create a new [[AuditedConfiguration]] that reads from the environment.
* *
@ -23,6 +35,6 @@ object Configuration:
* The new [[Configuration]]. * The new [[Configuration]].
*/ */
def auditedEnvironmentOnly[F[_]: Sync]: F[Configuration[F]] = def auditedEnvironmentOnly[F[_]: Sync]: F[Configuration[F]] =
audited[F].withEnvironmentSource().build() AuditedConfiguration.forEnvironmentSource[F].build()
end Configuration end Configuration