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

ConfigReader: Reproduce Map types minimization. #2085

Merged
merged 5 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,43 +120,9 @@ final class ConfigWriter[F[_]](
.reboot(bootstrapOverride, Some(correctedAppConfig))
.makePlan(Set(roleDIKey))

def getConfig(plan: Plan): Seq[ConfigPath] = {
val configTags = plan.stepsUnordered.toSeq.flatMap {
op =>
op.origin.value match {
case defined: OperationOrigin.Defined =>
defined.binding.tags.collect {
case t: ConfTag =>
t
}
case _ =>
Seq.empty
}
}

val paths = configTags.flatMap(t => unpack(Seq(t.confPath), t.fieldsMeta))
paths
}

def unpack(path: Seq[String], meta: ConfigMeta): Seq[ConfigPath] = {
meta match {
case ConfigMeta.ConfigMetaCaseClass(fields) =>
fields.flatMap {
case (name, meta) =>
unpack(path :+ name, meta)
}
case ConfigMeta.ConfigMetaSealedTrait(branches) =>
branches.toSeq.flatMap {
case (name, meta) =>
unpack(path :+ name, meta)
}
case ConfigMeta.ConfigMetaEmpty() => Seq(ConfigPath(path.mkString(".")))
case ConfigMeta.ConfigMetaUnknown() => Seq(ConfigPath(path.mkString(".")))
}
}

val resolvedConfig =
val resolvedConfig = {
getConfig(plans.app).toSet + _HackyMandatorySection
}

if (plans.app.stepsUnordered.exists(_.target == roleDIKey)) {
Some(ConfigWriter.minimized(resolvedConfig, roleConfig))
Expand All @@ -180,6 +146,41 @@ final class ConfigWriter[F[_]](
subLogger.error(s"Can't write reference config to $target, $error")
}
}

private def getConfig(plan: Plan): Seq[ConfigPath] = {
val configTags = plan.stepsUnordered.toSeq.flatMap {
op =>
op.origin.value match {
case defined: OperationOrigin.Defined =>
defined.binding.tags.collect {
case t: ConfTag =>
t
}
case _ =>
Seq.empty
}
}

configTags.flatMap(t => unpack(Seq(t.confPath), t.fieldsMeta))
}

private def unpack(path: Seq[String], meta0: ConfigMeta): Seq[ConfigPath] = {
meta0 match {
case ConfigMeta.ConfigMetaCaseClass(fields) =>
fields.flatMap {
case (name, meta) =>
unpack(path :+ name, meta)
}
case ConfigMeta.ConfigMetaSealedTrait(branches) =>
branches.toSeq.flatMap {
case (name, meta) =>
unpack(path :+ name, meta)
}
case ConfigMeta.ConfigMetaEmpty() => Seq(ConfigPath(path.mkString(".")))
case ConfigMeta.ConfigMetaUnknown() => Seq(ConfigPath(path.mkString("."), wildcard = true))
}
}

}

object ConfigWriter extends RoleDescriptor {
Expand All @@ -201,12 +202,6 @@ object ConfigWriter extends RoleDescriptor {
useLauncherVersion: Boolean,
)

final case class ConfigurableComponent(
roleId: String,
version: Option[ArtifactVersion],
parent: Option[Config],
)

object Options extends ParserDef {
final val targetDir = arg("target", "t", "target directory", "<path>")
final val excludeCommon = flag("exclude-common", "ec", "do not include shared sections")
Expand All @@ -233,14 +228,15 @@ object ConfigWriter extends RoleDescriptor {
import scala.jdk.CollectionConverters.*

val paths = requiredPaths.map(_.toPath)
val wildcards = requiredPaths.collect { case c if c.wildcard => c.toPath }

def filter(path: Seq[String], config: ConfigObject): ConfigObject = {
config.entrySet().asScala.foldLeft(config) {
case (c, e) =>
val key = e.getKey
val npath = path :+ key
val pathKey = npath.mkString(".")
if (paths.contains(pathKey) || paths.exists(p => p.startsWith(pathKey + "."))) {
if (paths.contains(pathKey) || paths.exists(_.startsWith(pathKey + ".")) || wildcards.exists(pathKey.startsWith)) {
e.getValue match {
case configObject: ConfigObject => c.withValue(key, filter(npath, configObject))
case _ => c
Expand All @@ -255,11 +251,11 @@ object ConfigWriter extends RoleDescriptor {
filtered
}

final case class ConfigPath(parts: Seq[String]) {
final case class ConfigPath(parts: Seq[String], wildcard: Boolean) {
def toPath: String = parts.mkString(".")
}
object ConfigPath {
def apply(path: String): ConfigPath = new ConfigPath(ArraySeq.unsafeWrapArray(path.split('.')))
def apply(path: String, wildcard: Boolean = false): ConfigPath = new ConfigPath(ArraySeq.unsafeWrapArray(path.split('.')), wildcard)
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
testservice2 {
strval: "xxx"
map: {
requiredEntry: "entry"
}
list: ["requiredEntry"]
}

testservice {
Expand All @@ -14,6 +18,11 @@ testservice {
}
}

genericservice {
genericField: 0
addedField: 0
}

integrationOnlyCfg {
flag: false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import distage.config.AppConfig
import distage.plugins.{PluginBase, PluginDef}
import distage.{DIKey, Injector, Locator, LocatorRef}
import izumi.distage.framework.config.PlanningOptions
import izumi.distage.model.provisioning.IntegrationCheck
import izumi.distage.framework.services.RoleAppPlanner
import izumi.distage.model.PlannerInput
import izumi.distage.model.definition.{Activation, BootstrapModule, Lifecycle}
import izumi.distage.model.provisioning.IntegrationCheck
import izumi.distage.modules.DefaultModule
import izumi.distage.plugins.PluginConfig
import izumi.distage.roles.DebugProperties
Expand Down Expand Up @@ -327,7 +327,13 @@ class RoleAppTest extends AnyWordSpec with WithProperties {
assert(role0CfgMinParsed.hasPath("setElementConfig"))
assert(role0CfgMinParsed.hasPath("testservice2"))
assert(role0CfgMinParsed.hasPath("testservice"))
assert(role0CfgMinParsed.hasPath("genericservice"))

assert(role0CfgMinParsed.hasPath("genericservice.genericField"))
assert(role0CfgMinParsed.hasPath("genericservice.addedField"))

assert(role0CfgMinParsed.getList("testservice2.list").unwrapped().asScala.toList == List("requiredEntry"))
assert(role0CfgMinParsed.getString("testservice2.map.requiredEntry") == "entry")
assert(role0CfgMinParsed.getString("testservice2.strval") == "xxx")
assert(role0CfgMinParsed.getString("testservice.overridenInt") == "555")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package izumi.distage.roles.test.fixtures

import distage.LocatorRef
import izumi.distage.model.provisioning.IntegrationCheck
import distage.config.ConfigModuleDef
import distage.{LocatorRef, Tag}
import izumi.distage.config.codec.DIConfigReader
import izumi.distage.model.definition.Axis
import izumi.functional.quasi.QuasiIO
import izumi.distage.model.provisioning.IntegrationCheck
import izumi.distage.roles.test.fixtures.roles.TestRole00.SetElementOnlyCfg
import izumi.functional.quasi.QuasiIO
import izumi.fundamentals.platform.integration.ResourceCheck
import izumi.fundamentals.platform.language.Quirks._
import izumi.fundamentals.platform.language.Quirks.*

import scala.collection.mutable

Expand All @@ -16,8 +18,20 @@ object Fixture {
trait SetElement
final case class SetElement1(setElementOnlyCfg: SetElementOnlyCfg) extends SetElement

trait GenericServiceConf {
def genericField: Int
}
object GenericServiceConf {
case class Impl(genericField: Int, addedField: Int) extends GenericServiceConf
def module[Conf <: GenericServiceConf: Tag: DIConfigReader](path: String): ConfigModuleDef = new ConfigModuleDef {
makeConfig[Conf](path)
}
}

case class TestServiceConf2(
strval: String
strval: String,
map: Map[String, String],
list: List[String],
)

case class TestServiceConf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ class TestPluginBase[F[_]: TagK] extends PluginDef with ConfigModuleDef with Rol
makeConfig[TestServiceConf2]("testservice2")
modify[TestServiceConf2] {
(conf: TestServiceConf2) =>
TestServiceConf2(conf.strval + ":updated")
TestServiceConf2(conf.strval + ":updated", conf.map, conf.list)
}
makeConfig[ListConf]("listconf")

include(GenericServiceConf.module[GenericServiceConf.Impl]("genericservice"))
}

class TestPluginCatsIO extends TestPluginBase[IO]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ object roles {
notCloseable: NotCloseable,
val conf: TestServiceConf,
val conf2: TestServiceConf2,
val genconf: GenericServiceConf.Impl,
val dummies: Set[Dummy],
val setElems: Set[SetElement],
val resource: TestRole00Resource[F],
Expand Down
Loading