(minor) Version, Doc, and Minor updates.
All checks were successful
/ Build and Test Library Snapshot (pull_request) Successful in 2m5s
All checks were successful
/ Build and Test Library Snapshot (pull_request) Successful in 2m5s
This commit is contained in:
parent
f127645860
commit
4898a522fd
15 changed files with 113 additions and 16 deletions
|
@ -64,5 +64,6 @@ jobs:
|
||||||
sbtn coverageOff
|
sbtn coverageOff
|
||||||
sbtn clean
|
sbtn clean
|
||||||
sbtn compile
|
sbtn compile
|
||||||
|
sbtn doc
|
||||||
sbtn publish
|
sbtn publish
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -71,6 +71,7 @@ jobs:
|
||||||
sbtn coverageOff
|
sbtn coverageOff
|
||||||
sbtn clean
|
sbtn clean
|
||||||
sbtn semVerWriteVersionToFile
|
sbtn semVerWriteVersionToFile
|
||||||
|
sbtn doc
|
||||||
sbtn publish
|
sbtn publish
|
||||||
fi
|
fi
|
||||||
- name: 'Create Git Tag'
|
- name: 'Create Git Tag'
|
||||||
|
|
13
build.sbt
13
build.sbt
|
@ -1,4 +1,4 @@
|
||||||
val scala3: String = "3.4.1"
|
val scala3: String = "3.6.4"
|
||||||
|
|
||||||
externalResolvers := Seq(
|
externalResolvers := Seq(
|
||||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||||
|
@ -10,13 +10,16 @@ ThisBuild / versionScheme := Some("semver-spec")
|
||||||
ThisBuild / gsProjectName := "gs-config"
|
ThisBuild / gsProjectName := "gs-config"
|
||||||
|
|
||||||
lazy val sharedSettings = Seq(
|
lazy val sharedSettings = Seq(
|
||||||
scalaVersion := scala3,
|
scalaVersion := scala3,
|
||||||
version := semVerSelected.value
|
version := semVerSelected.value,
|
||||||
|
coverageFailOnMinimum := true,
|
||||||
|
coverageMinimumStmtTotal := 100,
|
||||||
|
coverageMinimumBranchTotal := 100
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val testSettings = Seq(
|
lazy val testSettings = Seq(
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"org.scalameta" %% "munit" % "1.0.0-RC1" % Test
|
"org.scalameta" %% "munit" % "1.1.0" % Test
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,6 +30,6 @@ lazy val `gs-config` = project
|
||||||
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
||||||
.settings(
|
.settings(
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"org.typelevel" %% "cats-effect" % "3.5.2"
|
"org.typelevel" %% "cats-effect" % "3.5.7"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
sbt.version=1.9.9
|
sbt.version=1.10.11
|
||||||
|
|
|
@ -28,6 +28,6 @@ externalResolvers := Seq(
|
||||||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||||
)
|
)
|
||||||
|
|
||||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.11")
|
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
|
||||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.3.0")
|
addSbtPlugin("gs" % "sbt-garrity-software" % "0.5.0")
|
||||||
addSbtPlugin("gs" % "sbt-gs-semver" % "0.3.0")
|
addSbtPlugin("gs" % "sbt-gs-semver" % "0.3.0")
|
||||||
|
|
|
@ -9,7 +9,7 @@ import gs.config.v0.source.ConfigSource
|
||||||
import gs.config.v0.source.EnvironmentConfigSource
|
import gs.config.v0.source.EnvironmentConfigSource
|
||||||
import gs.config.v0.source.MemoryConfigSource
|
import gs.config.v0.source.MemoryConfigSource
|
||||||
|
|
||||||
/** Implementation of [[gs.config.Configuration]] that tracks every call to
|
/** Implementation of [[gs.config.v0.Configuration]] that tracks every call to
|
||||||
* `getValue` and reports on whether the query succeeded or failed.
|
* `getValue` and reports on whether the query succeeded or failed.
|
||||||
*
|
*
|
||||||
* @param sources
|
* @param sources
|
||||||
|
@ -23,6 +23,8 @@ final class AuditedConfiguration[F[_]: Sync](
|
||||||
) extends BaseConfiguration[F]:
|
) extends BaseConfiguration[F]:
|
||||||
import AuditedConfiguration.Acc
|
import AuditedConfiguration.Acc
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def getValue[A: Configurable](
|
override def getValue[A: Configurable](
|
||||||
key: ConfigKey[A]
|
key: ConfigKey[A]
|
||||||
): F[Either[ConfigError, A]] =
|
): F[Either[ConfigError, A]] =
|
||||||
|
@ -93,7 +95,8 @@ object AuditedConfiguration:
|
||||||
* sources.
|
* sources.
|
||||||
*
|
*
|
||||||
* @param sources
|
* @param sources
|
||||||
* The list of [[ConfigSource]] backing this configuration.
|
* The list of [[gs.config.v0.source.ConfigSource]] backing this
|
||||||
|
* configuration.
|
||||||
* @return
|
* @return
|
||||||
* The new [[Configuration]] instance.
|
* The new [[Configuration]] instance.
|
||||||
*/
|
*/
|
||||||
|
@ -109,26 +112,73 @@ object AuditedConfiguration:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Start building a new [[AuditedConfiguration]] for some source.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* The [[gs.config.v0.source.ConfigSource]] backing this configuration.
|
||||||
|
* @return
|
||||||
|
* New builder based on the given source.
|
||||||
|
*/
|
||||||
def forSource[F[_]: Sync](source: ConfigSource[F]): Builder[F] =
|
def forSource[F[_]: Sync](source: ConfigSource[F]): Builder[F] =
|
||||||
Builder[F](NonEmptyList.of(source))
|
Builder[F](NonEmptyList.of(source))
|
||||||
|
|
||||||
|
/** Start building a new [[AuditedConfiguration]] based on the environment.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* New builder based on the environment.
|
||||||
|
*/
|
||||||
def forEnvironmentSource[F[_]: Sync]: Builder[F] =
|
def forEnvironmentSource[F[_]: Sync]: Builder[F] =
|
||||||
Builder[F](NonEmptyList.of(new EnvironmentConfigSource[F]))
|
Builder[F](NonEmptyList.of(new EnvironmentConfigSource[F]))
|
||||||
|
|
||||||
|
/** Start building a new [[AuditedConfiguration]] based on the given in-memory
|
||||||
|
* map of values.
|
||||||
|
*
|
||||||
|
* @param configs
|
||||||
|
* The configuration map.
|
||||||
|
* @return
|
||||||
|
* New builder based on the config map.
|
||||||
|
*/
|
||||||
def forMemorySource[F[_]: Sync](configs: Map[String, String]): Builder[F] =
|
def forMemorySource[F[_]: Sync](configs: Map[String, String]): Builder[F] =
|
||||||
Builder[F](NonEmptyList.of(new MemoryConfigSource[F](configs)))
|
Builder[F](NonEmptyList.of(new MemoryConfigSource[F](configs)))
|
||||||
|
|
||||||
|
/** Builder for [[AuditedConfiguration]].
|
||||||
|
*
|
||||||
|
* @param sources
|
||||||
|
* The (non-empty) list of sources for the eventual configuration.
|
||||||
|
*/
|
||||||
case class Builder[F[_]: Sync](sources: NonEmptyList[ConfigSource[F]]):
|
case class Builder[F[_]: Sync](sources: NonEmptyList[ConfigSource[F]]):
|
||||||
|
|
||||||
|
/** Add the environment as a source to this builder.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* This builder.
|
||||||
|
*/
|
||||||
def withEnvironmentSource(): Builder[F] =
|
def withEnvironmentSource(): Builder[F] =
|
||||||
copy(sources = this.sources.append(new EnvironmentConfigSource[F]))
|
copy(sources = this.sources.append(new EnvironmentConfigSource[F]))
|
||||||
|
|
||||||
|
/** Add a memory map as a source to this builder.
|
||||||
|
*
|
||||||
|
* @param configs
|
||||||
|
* The configuration map.
|
||||||
|
* @return
|
||||||
|
* This builder.
|
||||||
|
*/
|
||||||
def withMemorySource(configs: Map[String, String]): Builder[F] =
|
def withMemorySource(configs: Map[String, String]): Builder[F] =
|
||||||
copy(sources = this.sources.append(new MemoryConfigSource[F](configs)))
|
copy(sources = this.sources.append(new MemoryConfigSource[F](configs)))
|
||||||
|
|
||||||
|
/** Add the given source to this builder.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* The [[gs.config.v0.source.ConfigSource]] to add.
|
||||||
|
* @return
|
||||||
|
* This builder.
|
||||||
|
*/
|
||||||
def withSource(source: ConfigSource[F]): Builder[F] =
|
def withSource(source: ConfigSource[F]): Builder[F] =
|
||||||
copy(sources = this.sources.append(source))
|
copy(sources = this.sources.append(source))
|
||||||
|
|
||||||
|
/** @return
|
||||||
|
* A new [[AuditedConfiguration]] based on the configured sources.
|
||||||
|
*/
|
||||||
def build(): F[AuditedConfiguration[F]] =
|
def build(): F[AuditedConfiguration[F]] =
|
||||||
ConfigManifest
|
ConfigManifest
|
||||||
.standard[F]
|
.standard[F]
|
||||||
|
|
|
@ -11,7 +11,7 @@ abstract class BaseConfiguration[F[_]] extends Configuration[F]:
|
||||||
): Either[ConfigError, A] =
|
): Either[ConfigError, A] =
|
||||||
key match
|
key match
|
||||||
case ConfigKey.WithDefaultValue(_, defaultValue) =>
|
case ConfigKey.WithDefaultValue(_, defaultValue) =>
|
||||||
Right(defaultValue)
|
Right(defaultValue())
|
||||||
case _ =>
|
case _ =>
|
||||||
Left(ConfigError.MissingValue(key.name))
|
Left(ConfigError.MissingValue(key.name))
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ object ConfigError:
|
||||||
* @param candidateValue
|
* @param candidateValue
|
||||||
* The raw value that could not be parsed.
|
* The raw value that could not be parsed.
|
||||||
* @param source
|
* @param source
|
||||||
* The [[ConfigSource]] which provided the candidate value.
|
* The name of the [[gs.config.v0.source.ConfigSource]] which provided the
|
||||||
|
* candidate value.
|
||||||
*/
|
*/
|
||||||
case class CannotParseValue(
|
case class CannotParseValue(
|
||||||
configName: ConfigName,
|
configName: ConfigName,
|
||||||
|
|
|
@ -37,5 +37,5 @@ object ConfigKey:
|
||||||
*/
|
*/
|
||||||
case class WithDefaultValue[A: Configurable](
|
case class WithDefaultValue[A: Configurable](
|
||||||
name: ConfigName,
|
name: ConfigName,
|
||||||
defaultValue: A
|
defaultValue: () => A
|
||||||
) extends ConfigKey[A]
|
) extends ConfigKey[A]
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package gs.config.v0
|
package gs.config.v0
|
||||||
|
|
||||||
|
import cats.Eq
|
||||||
|
import cats.Show
|
||||||
|
|
||||||
/** Uniquely names some piece of configuration. This structure does _not_
|
/** Uniquely names some piece of configuration. This structure does _not_
|
||||||
* attempt to support every possible use case, but supports some common cases
|
* attempt to support every possible use case, but supports some common cases
|
||||||
* for users that follow some basic rules. Please review conversion functions
|
* for users that follow some basic rules. Please review conversion functions
|
||||||
|
@ -32,6 +35,13 @@ object ConfigName:
|
||||||
|
|
||||||
given CanEqual[ConfigName, ConfigName] = CanEqual.derived
|
given CanEqual[ConfigName, ConfigName] = CanEqual.derived
|
||||||
|
|
||||||
|
given Eq[ConfigName] = (
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
) => x == y
|
||||||
|
|
||||||
|
given Show[ConfigName] = _.unwrap()
|
||||||
|
|
||||||
extension (name: ConfigName)
|
extension (name: ConfigName)
|
||||||
/** Extract the unmodified string that backs this name.
|
/** Extract the unmodified string that backs this name.
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,6 +5,15 @@ import cats.effect.Sync
|
||||||
import cats.syntax.all.*
|
import cats.syntax.all.*
|
||||||
import gs.config.v0.ConfigName
|
import gs.config.v0.ConfigName
|
||||||
|
|
||||||
|
/** A `ConfigManifest` tracks all queries for individual pieces of
|
||||||
|
* configuration. It is used in conjunction with an [[AuditedConfiguration]].
|
||||||
|
* Manifests can be queried by producing a _snapshot_ of interactions so far.
|
||||||
|
*
|
||||||
|
* See:
|
||||||
|
*
|
||||||
|
* - [[AuditedConfiguration]]
|
||||||
|
* - [[ConfigQueryResult]]
|
||||||
|
*/
|
||||||
trait ConfigManifest[F[_]]:
|
trait ConfigManifest[F[_]]:
|
||||||
/** Retrieve a snapshot of the current state of this configuration manifest.
|
/** Retrieve a snapshot of the current state of this configuration manifest.
|
||||||
* This state tracks all configuration names that the caller attempted to
|
* This state tracks all configuration names that the caller attempted to
|
||||||
|
@ -46,9 +55,13 @@ object ConfigManifest:
|
||||||
private val manifest: Ref[F, Map[ConfigName, List[ConfigQueryResult]]]
|
private val manifest: Ref[F, Map[ConfigName, List[ConfigQueryResult]]]
|
||||||
) extends ConfigManifest[F]:
|
) extends ConfigManifest[F]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def snapshot(): F[Map[ConfigName, List[ConfigQueryResult]]] =
|
override def snapshot(): F[Map[ConfigName, List[ConfigQueryResult]]] =
|
||||||
manifest.get
|
manifest.get
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def record(
|
override def record(
|
||||||
name: ConfigName,
|
name: ConfigName,
|
||||||
query: ConfigQueryResult
|
query: ConfigQueryResult
|
||||||
|
|
|
@ -14,7 +14,7 @@ object ConfigQueryResult:
|
||||||
/** Represents a query for some configuration that completed successfully.
|
/** Represents a query for some configuration that completed successfully.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
* The source which provided the value.
|
* The name of the source which provided the value.
|
||||||
* @param rawValue
|
* @param rawValue
|
||||||
* The raw value that the source returned.
|
* The raw value that the source returned.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,17 +27,32 @@ trait ConfigSource[F[_]]:
|
||||||
|
|
||||||
object ConfigSource:
|
object ConfigSource:
|
||||||
|
|
||||||
|
/** Instantiate a new [[MemoryConfigSource]] based on the given map.
|
||||||
|
*
|
||||||
|
* @param configs
|
||||||
|
* The config map that should back the source.
|
||||||
|
* @return
|
||||||
|
* The new [[ConfigSource]].
|
||||||
|
*/
|
||||||
def inMemory[F[_]: Applicative](
|
def inMemory[F[_]: Applicative](
|
||||||
configs: Map[String, String]
|
configs: Map[String, String]
|
||||||
): ConfigSource[F] =
|
): ConfigSource[F] =
|
||||||
new MemoryConfigSource[F](configs)
|
new MemoryConfigSource[F](configs)
|
||||||
|
|
||||||
|
/** @return
|
||||||
|
* A new [[EnvironmentConfigSource]].
|
||||||
|
*/
|
||||||
def environment[F[_]: Sync]: ConfigSource[F] =
|
def environment[F[_]: Sync]: ConfigSource[F] =
|
||||||
new EnvironmentConfigSource[F]
|
new EnvironmentConfigSource[F]
|
||||||
|
|
||||||
|
/** @return
|
||||||
|
* An empty [[ConfigSource]].
|
||||||
|
*/
|
||||||
def empty[F[_]: Applicative]: ConfigSource[F] =
|
def empty[F[_]: Applicative]: ConfigSource[F] =
|
||||||
new Empty[F]
|
new Empty[F]
|
||||||
|
|
||||||
|
/** Default [[ConfigSource]] implementation that never returns a value.
|
||||||
|
*/
|
||||||
final class Empty[F[_]: Applicative] extends ConfigSource[F]:
|
final class Empty[F[_]: Applicative] extends ConfigSource[F]:
|
||||||
|
|
||||||
/** @inheritDocs
|
/** @inheritDocs
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package gs.config.v0
|
package gs.config.v0
|
||||||
|
|
||||||
|
import cats.syntax.all.*
|
||||||
|
|
||||||
class ConfigNameTests extends munit.FunSuite:
|
class ConfigNameTests extends munit.FunSuite:
|
||||||
|
|
||||||
test("should express some name as an environment variable") {
|
test("should express some name as an environment variable") {
|
||||||
|
@ -14,6 +16,7 @@ class ConfigNameTests extends munit.FunSuite:
|
||||||
val expected = raw
|
val expected = raw
|
||||||
val name = ConfigName(raw)
|
val name = ConfigName(raw)
|
||||||
assertEquals(name.unwrap(), expected)
|
assertEquals(name.unwrap(), expected)
|
||||||
|
assertEquals(name.show, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("should support equality") {
|
test("should support equality") {
|
||||||
|
|
|
@ -143,7 +143,7 @@ class AuditedConfigurationTests extends GsSuite:
|
||||||
|
|
||||||
iotest("should audit the use of default values") {
|
iotest("should audit the use of default values") {
|
||||||
val name = Names.KString
|
val name = Names.KString
|
||||||
val defaultValue = "value"
|
val defaultValue = () => "value"
|
||||||
val key = ConfigKey.WithDefaultValue(name, defaultValue)
|
val key = ConfigKey.WithDefaultValue(name, defaultValue)
|
||||||
for
|
for
|
||||||
config <- Configuration
|
config <- Configuration
|
||||||
|
@ -153,7 +153,7 @@ class AuditedConfigurationTests extends GsSuite:
|
||||||
value <- config.getValue(key)
|
value <- config.getValue(key)
|
||||||
manifest <- config.manifest.snapshot()
|
manifest <- config.manifest.snapshot()
|
||||||
yield
|
yield
|
||||||
assertEquals(value, Right(defaultValue))
|
assertEquals(value, Right(defaultValue()))
|
||||||
assertEquals(
|
assertEquals(
|
||||||
manifest.get(name),
|
manifest.get(name),
|
||||||
Some(
|
Some(
|
||||||
|
|
Loading…
Add table
Reference in a new issue