diff --git a/core/src/main/scala/besom/internal/BesomSyntax.scala b/core/src/main/scala/besom/internal/BesomSyntax.scala index 775f1f83..05a8338b 100644 --- a/core/src/main/scala/besom/internal/BesomSyntax.scala +++ b/core/src/main/scala/besom/internal/BesomSyntax.scala @@ -101,7 +101,7 @@ trait BesomSyntax: s"Urn for component resource $name is not available. This should not happen." } - val componentContext = ComponentContext(ctx, urnRes) + val componentContext = ComponentContext(ctx, urnRes, componentBase) val componentOutput = try Output(Result.pure(f(using componentContext)(using componentBase))) catch case e: Exception => Output(Result.fail(e)) diff --git a/core/src/main/scala/besom/internal/Context.scala b/core/src/main/scala/besom/internal/Context.scala index 689c95f3..96546bf0 100644 --- a/core/src/main/scala/besom/internal/Context.scala +++ b/core/src/main/scala/besom/internal/Context.scala @@ -23,6 +23,7 @@ trait Context extends TaskTracker: private[besom] def monitor: Monitor private[besom] def memo: Memo private[besom] def getParentURN: Result[URN] + private[besom] def getParent: Option[Resource] private[besom] def config: Config private[besom] def isDryRun: Boolean private[besom] def logger: BesomLogger @@ -93,11 +94,18 @@ trait Context extends TaskTracker: end Context -class ComponentContext(private val globalContext: Context, private val componentURN: Result[URN]) extends Context: - export globalContext.{getParentURN => _, *} +class ComponentContext( + private val globalContext: Context, + private val componentURN: Result[URN], + private val componentBase: ComponentBase +) extends Context: + export globalContext.{getParentURN => _, getParent => _, *} def getParentURN: Result[URN] = componentURN + // components provide themselves as the parent to facilitate provider inheritance + def getParent: Option[Resource] = Some(componentBase) + class ContextImpl( private[besom] val runInfo: RunInfo, private[besom] val featureSupport: FeatureSupport, @@ -126,6 +134,9 @@ class ContextImpl( case None => Result.fail(Exception("Stack urn is not available. This should not happen.")) } + // top level Context does not return a parent (stack is the top level resource and it's providers are default provider instances) + override private[besom] def getParent: Option[Resource] = None + override private[besom] def initializeStack: Result[Unit] = StackResource.initializeStack(runInfo)(using this).flatMap(stackPromise.fulfill) diff --git a/core/src/main/scala/besom/internal/Output.scala b/core/src/main/scala/besom/internal/Output.scala index bc196080..e90b0286 100644 --- a/core/src/main/scala/besom/internal/Output.scala +++ b/core/src/main/scala/besom/internal/Output.scala @@ -268,7 +268,7 @@ trait OutputExtensionsFactory: * @see * [[OutputFactory.fail]] for creating a failed [[Output]] with a [[Throwable]] */ - def getOrFail(throwable: Throwable)(using ctx: Context): Output[A] = + def getOrFail(throwable: => Throwable)(using ctx: Context): Output[A] = output.flatMap { case Some(a) => Output(a) case None => Output.fail(throwable) diff --git a/core/src/main/scala/besom/internal/ResourceOps.scala b/core/src/main/scala/besom/internal/ResourceOps.scala index 2189163c..ecc43bb1 100644 --- a/core/src/main/scala/besom/internal/ResourceOps.scala +++ b/core/src/main/scala/besom/internal/ResourceOps.scala @@ -60,7 +60,7 @@ class ResourceOps(using ctx: Context, mdc: BesomMDC[Label]): (resource, resolver) <- ResourceDecoder.forResource[R].makeResourceAndResolver _ <- log.debug(s"$mode resource ${mode.suffix}") options <- options.resolve - _ <- log.debug(s"$mode resource, added to cache...") + _ <- log.debug(s"$mode resource, resolved options:\n${printer.render(options)}") state <- createResourceState(typ, name, resource, options, remote) _ <- log.debug(s"Created resource state") _ <- ctx.resources.add(resource, state) @@ -597,7 +597,7 @@ class ResourceOps(using ctx: Context, mdc: BesomMDC[Label]): initialProviders <- getParentProviders _ <- log.trace(s"mergeProviders for $typ - initialProviders: $initialProviders") providers <- overwriteWithProvidersFromOptions(initialProviders) - _ <- log.trace(s"final providers for $typ - initialProviders: $initialProviders") + _ <- log.trace(s"final providers for $typ - providers: $providers") yield providers private[internal] def getProvider( diff --git a/core/src/main/scala/besom/internal/ResourceOptions.scala b/core/src/main/scala/besom/internal/ResourceOptions.scala index d91d7b33..29030713 100644 --- a/core/src/main/scala/besom/internal/ResourceOptions.scala +++ b/core/src/main/scala/besom/internal/ResourceOptions.scala @@ -93,9 +93,9 @@ trait CommonResourceOptions: end CommonResourceOptions extension (cro: CommonResourceOptions) - def resolve(using Context): Result[CommonResolvedResourceOptions] = + def resolve(implicitParent: Option[Resource])(using Context): Result[CommonResolvedResourceOptions] = for - parent <- cro.parent.getData + explicitParent <- cro.parent.getData dependsOn <- cro.dependsOn.getData protect <- cro.protect.getData ignoreChanges <- cro.ignoreChanges.getData @@ -107,7 +107,8 @@ extension (cro: CommonResourceOptions) pluginDownloadUrl <- cro.pluginDownloadUrl.getData deletedWith <- cro.deletedWith.getData yield CommonResolvedResourceOptions( - parent = parent.getValueOrElse(None), + // if no parent is provided by the user explicitly, use the implicit parent from Context + parent = explicitParent.getValueOrElse(None).orElse(implicitParent), dependsOn = dependsOn.getValueOrElse(List.empty), protect = protect.getValueOrElse(false), ignoreChanges = ignoreChanges.getValueOrElse(List.empty), @@ -147,10 +148,12 @@ sealed trait ResourceOptions: def retainOnDelete: Output[Boolean] def urn: Output[Option[URN]] - private[besom] def resolve(using Context): Result[ResolvedResourceOptions] = + private[besom] def resolve(using ctx: Context): Result[ResolvedResourceOptions] = + val maybeComponentParent = ctx.getParent + this match case cr: CustomResourceOptions => - cr.common.resolve.flatMap { common => + cr.common.resolve(maybeComponentParent).flatMap { common => for provider <- cr.provider.getValueOrElse(None) importId <- cr.importId.getValueOrElse(None) @@ -165,7 +168,7 @@ sealed trait ResourceOptions: ) } case sr: StackReferenceResourceOptions => - sr.common.resolve.flatMap { common => + sr.common.resolve(maybeComponentParent).flatMap { common => for importId <- sr.importId.getValueOrElse(None) yield StackReferenceResolvedResourceOptions( common, @@ -173,13 +176,14 @@ sealed trait ResourceOptions: ) } case co: ComponentResourceOptions => - co.common.resolve.flatMap { common => + co.common.resolve(maybeComponentParent).flatMap { common => for providers <- co.providers.getValueOrElse(List.empty) yield ComponentResolvedResourceOptions( common, providers = providers ) } + end resolve private[besom] def hasURN: Result[Boolean] = urn.map(_.isDefined).getValueOrElse(false)