Skip to content

Commit

Permalink
more public apis cleaned, codegen yet to be done
Browse files Browse the repository at this point in the history
  • Loading branch information
lbialy committed Sep 17, 2024
1 parent 7906c21 commit 290eeed
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 50 deletions.
4 changes: 2 additions & 2 deletions core/src/main/scala/besom/future.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ import scala.reflect.Typeable
* @return
* The component resource.
*/
def component[A <: ComponentResource & Product: RegistersOutputs: Typeable](using ctx: Context)(
def component[A <: ComponentResource & Product: RegistersOutputs: Typeable](
name: NonEmptyString,
typ: ResourceType,
opts: ComponentResourceOptions = ComponentResourceOptions()
)(
f: Context ?=> ComponentBase ?=> A
f: ComponentBase ?=> A
): Output[A] = Pulumi.component(name, typ, opts)(f)
25 changes: 13 additions & 12 deletions core/src/main/scala/besom/internal/BesomSyntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ trait BesomSyntax:
* the current project [[besom.types.URN]] instance
*/
def urn(using ctx: Context): Output[URN] =
Output.ofData(ctx.getParentURN.map(OutputData(_)))
Output.ofData(ctx.getStackURN.map(OutputData(_)))

/** @param ctx
* the Besom context
Expand Down Expand Up @@ -86,38 +86,38 @@ trait BesomSyntax:
* @return
* the component resource instance
*/
def component[A <: ComponentResource & Product: RegistersOutputs: Typeable](using ctx: Context)(
def component[A <: ComponentResource & Product: RegistersOutputs: Typeable](
name: NonEmptyString,
typ: ResourceType,
opts: ComponentResourceOptions = ComponentResourceOptions()
)(
f: Context ?=> ComponentBase ?=> A
): Output[A] =
f: ComponentBase ?=> A
): Output[A] = Output.getContext.flatMap { ctx =>
Output.ofData {
ctx
.registerComponentResource(name, typ, opts)
.registerComponentResource(name, typ, opts)(using ctx)
.flatMap { componentBase =>
val urnRes: Result[URN] = componentBase.urn.getValueOrFail {
s"Urn for component resource $name is not available. This should not happen."
}
}(using ctx)

val componentContext = ComponentContext(ctx, urnRes, componentBase)
val componentOutput =
try Output.pure(f(using componentContext)(using componentBase))
try Output.pure(f(using componentBase))
catch case e: Exception => Output.fail(e)

val componentResult = componentOutput.getValueOrFail {
s"Component resource $name of type $typ did not return a value. This should not happen."
}
}(using componentContext)

componentResult.flatMap { a =>
val serializedOutputs = RegistersOutputs[A].serializeOutputs(a)
ctx.registerResourceOutputs(name, typ, urnRes, serializedOutputs) *> Result.pure(a)
val serializedOutputs = RegistersOutputs[A].serializeOutputs(a)(using componentContext)
ctx.registerResourceOutputs(name, typ, urnRes, serializedOutputs)(using componentContext) *> Result.pure(a)
}
}
.map(OutputData(_))
}
end component
}

extension [A <: ProviderResource](pr: A)
def provider: Output[Option[ProviderResource]] = Output.getContext.flatMap { implicit ctx =>
Expand Down Expand Up @@ -148,12 +148,13 @@ trait BesomSyntax:
}

extension [A <: Resource: ResourceDecoder](companion: ResourceCompanion[A])
def get(name: Input[NonEmptyString], id: Input[ResourceId])(using ctx: Context): Output[A] =
def get(name: Input[NonEmptyString], id: Input[ResourceId]): Output[A] = Output.getContext.flatMap { implicit ctx =>
for
name <- name.asOutput()
id <- id.asOutput()
res <- ctx.readOrRegisterResource[A, EmptyArgs](companion.typeToken, name, EmptyArgs(), CustomResourceOptions(importId = id))
yield res
}

extension (s: String)
/** Converts a [[String]] to a [[NonEmptyString]] if it is not empty or blank.
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/besom/internal/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ trait Context extends TaskTracker:
private[besom] def runInfo: RunInfo
private[besom] def monitor: Monitor
private[besom] def memo: Memo
private[besom] def getStackURN: Result[URN]
private[besom] def getParentURN: Result[URN]
private[besom] def getParent: Option[Resource]
private[besom] def config: Config
Expand Down Expand Up @@ -134,6 +135,8 @@ class ContextImpl(
case None => Result.fail(Exception("Stack urn is not available. This should not happen."))
}

override private[besom] def getStackURN: Result[URN] = getParentURN

// 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

Expand Down
18 changes: 9 additions & 9 deletions core/src/main/scala/besom/internal/Output.scala
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,14 @@ trait OutputExtensionsFactory:
* @see
* [[parSequence]] for parallel execution
*/
def sequence[To](using BuildFrom[CC[Output[A]], A, To], Context): Output[To] =
def sequence[To](using BuildFrom[CC[Output[A]], A, To]): Output[To] =
Output.sequence(coll)

/** Creates an `Output` of a collection from a collection of Outputs in parallel.
* @see
* [[sequence]] for sequential execution
*/
def parSequence[To](using BuildFrom[CC[Output[A]], A, To], Context): Output[To] =
def parSequence[To](using BuildFrom[CC[Output[A]], A, To]): Output[To] =
Output.parSequence(coll)

implicit object OutputTraverseOps:
Expand All @@ -267,7 +267,7 @@ trait OutputExtensionsFactory:
* @see
* [[parTraverse]] for parallel execution
*/
def traverse[B, To](f: A => Output[B])(using BuildFrom[CC[Output[B]], B, To], Context): Output[To] =
def traverse[B, To](f: A => Output[B])(using BuildFrom[CC[Output[B]], B, To]): Output[To] =
Output.sequence(coll.map(f).asInstanceOf[CC[Output[B]]])

/** Applies an Output-returning function to each element in the collection, in parallel, and then combines the results into an Output.
Expand All @@ -277,7 +277,7 @@ trait OutputExtensionsFactory:
* @see
* [[traverse]] for sequential execution
*/
def parTraverse[B, To](f: A => Output[B])(using BuildFrom[CC[Output[B]], B, To], Context): Output[To] =
def parTraverse[B, To](f: A => Output[B])(using BuildFrom[CC[Output[B]], B, To]): Output[To] =
coll.map(f).asInstanceOf[CC[Output[B]]].parSequence

implicit final class OutputOptionOps[A](output: Output[Option[A]]):
Expand All @@ -287,7 +287,7 @@ trait OutputExtensionsFactory:
* @return
* an [[Output]] with the value of the underlying [[Some]] or the `default` value if [[None]]
*/
def getOrElse[B >: A](default: => B | Output[B])(using ctx: Context): Output[B] =
def getOrElse[B >: A](default: => B | Output[B]): Output[B] =
output.flatMap { opt =>
opt match
case Some(a) => Output.pure(a)
Expand All @@ -306,7 +306,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): Output[A] =
output.flatMap {
case Some(a) => Output.pure(a)
case None => Output.fail(throwable)
Expand All @@ -318,7 +318,7 @@ trait OutputExtensionsFactory:
* @return
* an [[Output]] with the underlying [[Some]] or the `alternative` value if [[None]]
*/
def orElse[B >: A](alternative: => Option[B] | Output[Option[B]])(using ctx: Context): Output[Option[B]] =
def orElse[B >: A](alternative: => Option[B] | Output[Option[B]]): Output[Option[B]] =
output.flatMap {
case some @ Some(_) => Output.pure(some)
case None =>
Expand All @@ -331,7 +331,7 @@ trait OutputExtensionsFactory:
* @return
* an [[Output]] of the mapped [[Option]]
*/
def mapInner[B](f: A => B | Output[B])(using ctx: Context): Output[Option[B]] =
def mapInner[B](f: A => B | Output[B]): Output[Option[B]] =
output.flatMap {
case Some(a) =>
f(a) match
Expand All @@ -344,7 +344,7 @@ trait OutputExtensionsFactory:
* @return
* an [[Output]] of the flat-mapped [[Option]]
*/
def flatMapInner[B](f: A => Option[B] | Output[Option[B]])(using ctx: Context): Output[Option[B]] =
def flatMapInner[B](f: A => Option[B] | Output[Option[B]]): Output[Option[B]] =
output.flatMap {
case Some(a) =>
f(a) match
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/besom/internal/RawResourceResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import besom.util.printer
case class RawResourceResult(urn: URN, id: Option[ResourceId], data: Struct, dependencies: Map[String, Set[Resource]])

object RawResourceResult:
def fromResponse(response: pulumirpc.resource.ReadResourceResponse, id: ResourceId)(using Context): Result[RawResourceResult] =
def fromResponse(response: pulumirpc.resource.ReadResourceResponse, id: ResourceId): Result[RawResourceResult] =
Result.evalTry(URN.from(response.urn)).map { urn =>
RawResourceResult(
urn = urn,
Expand All @@ -19,7 +19,7 @@ object RawResourceResult:
)
}

def fromResponse(response: pulumirpc.resource.RegisterResourceResponse)(using Context): Result[RawResourceResult] =
def fromResponse(response: pulumirpc.resource.RegisterResourceResponse): Result[RawResourceResult] =
val dependenciesPerField =
Result.sequenceMap {
response.propertyDependencies
Expand Down Expand Up @@ -53,7 +53,7 @@ object RawResourceResult:
dependencies = deps
)

def fromValue(tok: FunctionToken, value: Value)(using Context): Result[RawResourceResult] =
def fromValue(tok: FunctionToken, value: Value): Result[RawResourceResult] =
value match
case Value(Value.Kind.StructValue(struct), _) =>
lazy val missingUrnErr =
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/besom/internal/ResourceOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ sealed trait ResolvedResourceOptions:
def pluginDownloadUrl: Option[String]
def deletedWith: Option[Resource]

private[besom] def getImportId(using Context): Option[ResourceId] = this match
private[besom] def getImportId: Option[ResourceId] = this match
case cr: CustomResolvedResourceOptions => cr.importId
case sr: StackReferenceResolvedResourceOptions => sr.importId
case _ => None
Expand Down Expand Up @@ -232,7 +232,7 @@ final case class StackReferenceResourceOptions private[internal] (
export common.*

trait CustomResourceOptionsFactory:
def apply(using Context)(
def apply(
parent: Input.Optional[Resource] = None,
dependsOn: Input.OneOrIterable[Resource] = Iterable.empty,
deletedWith: Input.Optional[Resource] = None,
Expand Down Expand Up @@ -270,7 +270,7 @@ trait CustomResourceOptionsFactory:
)

object CustomResourceOptions:
def apply(using Context)(
def apply(
parent: Input.Optional[Resource] = None,
dependsOn: Input.OneOrIterable[Resource] = Iterable.empty,
deletedWith: Input.Optional[Resource] = None,
Expand Down
16 changes: 9 additions & 7 deletions core/src/main/scala/besom/internal/StackReference.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ end StackReference
trait StackReferenceFactory:
sealed trait StackReferenceType[T]:
type Out[T]
def transform(stackReference: StackReference)(using Context): Output[Out[T]]
def transform(stackReference: StackReference): Output[Out[T]]

object StackReferenceType:
given untyped: UntypedStackReferenceType = UntypedStackReferenceType()
Expand All @@ -66,7 +66,7 @@ trait StackReferenceFactory:

class TypedStackReferenceType[T](using JsonReader[T]) extends StackReferenceType[T]:
type Out[T] = TypedStackReference[T]
def transform(stackReference: StackReference)(using Context): Output[Out[T]] =
def transform(stackReference: StackReference): Output[Out[T]] =
val objectOutput: Output[T] =
requireObject(stackReference.outputs, stackReference.secretOutputNames)

Expand All @@ -82,17 +82,17 @@ trait StackReferenceFactory:

class UntypedStackReferenceType extends StackReferenceType[Any]:
type Out[T] = StackReference
def transform(stackReference: StackReference)(using Context): Output[StackReference] = Output.pure(stackReference)
def transform(stackReference: StackReference): Output[StackReference] = Output.pure(stackReference)

def untypedStackReference(using Context): StackReferenceType[Any] = UntypedStackReferenceType()
def untypedStackReference: StackReferenceType[Any] = UntypedStackReferenceType()

def typedStackReference[T: JsonReader]: TypedStackReferenceType[T] = TypedStackReferenceType()

def apply[T](using stackRefType: StackReferenceType[T], ctx: Context)(
def apply[T](
name: NonEmptyString,
args: Input.Optional[StackReferenceArgs] = None,
opts: StackReferenceResourceOptions = StackReferenceResourceOptions()
): Output[stackRefType.Out[T]] =
)(using stackRefType: StackReferenceType[T]): Output[stackRefType.Out[T]] =
args
.asOptionOutput(false)
.flatMap {
Expand All @@ -111,7 +111,9 @@ trait StackReferenceFactory:
Output.pure(Some(importId))
)

Context().readOrRegisterResource[StackReference, StackReferenceArgs]("pulumi:pulumi:StackReference", name, stackRefArgs, mergedOpts)
Output.getContext.flatMap { implicit ctx =>
ctx.readOrRegisterResource[StackReference, StackReferenceArgs]("pulumi:pulumi:StackReference", name, stackRefArgs, mergedOpts)
}
}
.flatMap(stackRefType.transform)

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/besom/types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ object types:
else throw IllegalArgumentException(s"URN $s is not valid")
}

def parse(value: String)(using besom.internal.Context): Output[URN] =
def parse(value: String): Output[URN] =
besom.internal.Output.ofResult(besom.internal.Result.evalTry(besom.types.URN.from(value)))

extension (urn: URN)
Expand Down
25 changes: 12 additions & 13 deletions core/src/test/scala/besom/internal/ContextPropagationTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ class ContextPropagationTest extends munit.FunSuite:
case class TestResource(urn: Output[URN], id: Output[ResourceId], url: Output[String]) extends CustomResource derives ResourceDecoder
object TestResource extends ResourceCompanion[TestResource]:
val typeToken: ResourceType = ResourceType.unsafeOf("test:resource:TestResource")
def apply(name: NonEmptyString)(using Context): Output[TestResource] =
Context().readOrRegisterResource[TestResource, EmptyArgs](
def apply(name: NonEmptyString): Output[TestResource] = Output.getContext.flatMap { implicit ctx =>
ctx.readOrRegisterResource[TestResource, EmptyArgs](
typeToken,
name,
EmptyArgs(),
CustomResourceOptions()
)
}

case class TestComponentResource(url: Output[String])(using ComponentBase) extends ComponentResource
object TestComponentResource extends ResourceCompanion[TestComponentResource]:
Expand All @@ -40,24 +41,24 @@ class ContextPropagationTest extends munit.FunSuite:
RegisterResourceResponse(urn = resourceUrn.asString, `object` = Some(obj), id = "test-id")
}

given ctx: Context = DummyContext(monitor = spyMonitor, stackURN = stackUrn).unsafeRunSync()
val ctx: Context = DummyContext(monitor = spyMonitor, stackURN = stackUrn).unsafeRunSync()

val resource = TestResource("test-resource").unsafeRunSync()
val resource = TestResource("test-resource").unsafeRunSync()(using ctx)

resource match
case None => fail("Expected resource to be defined")
case Some(res) =>
res.urn.getValue.unsafeRunSync() match
res.urn.getValue(using ctx).unsafeRunSync() match
case Some(urn) =>
assert(urn == resourceUrn)
case None => fail("Expected resource urn to be defined")

res.id.getValue.unsafeRunSync() match
res.id.getValue(using ctx).unsafeRunSync() match
case Some(id) =>
assert(id == ResourceId.unsafeOf("test-id"))
case None => fail("Expected resource id to be defined")

res.url.getValue.unsafeRunSync() match
res.url.getValue(using ctx).unsafeRunSync() match
case Some(url) =>
assert(url == "https://test.com")
case None => fail("Expected resource url to be defined")
Expand All @@ -73,8 +74,6 @@ class ContextPropagationTest extends munit.FunSuite:

val spyMonitor = new DummyContext.DummyMonitor:
override def registerResource(registerResourceRequest: RegisterResourceRequest): Result[RegisterResourceResponse] = Result.defer {
pprint.pprintln(registerResourceRequest)

registerResourceRequest.`type` -> registerResourceRequest.name match
case (TestComponentResource.typeToken, "test-component") =>
assert(registerResourceRequest.parent == stackUrn.asString)
Expand All @@ -96,23 +95,23 @@ class ContextPropagationTest extends munit.FunSuite:
override def registerResourceOutputs(registerResourceOutputsRequest: RegisterResourceOutputsRequest): Result[Unit] =
Result.pure(())

given ctx: Context = DummyContext(monitor = spyMonitor, stackURN = stackUrn).unsafeRunSync()
val ctx: Context = DummyContext(monitor = spyMonitor, stackURN = stackUrn).unsafeRunSync()

val comp = besom
.component("test-component", TestComponentResource.typeToken, ComponentResourceOptions()) {
val nested = TestResource("test-resource")

TestComponentResource(nested.flatMap(_.url))
}
.unsafeRunSync()
.unsafeRunSync()(using ctx)

comp match
case None => fail("Expected component to be defined")
case Some(compRes) =>
compRes.urn.getValue.unsafeRunSync() match
compRes.urn.getValue(using ctx).unsafeRunSync() match
case Some(urn) =>
assert(urn == componentUrn)
compRes.url.getValue.unsafeRunSync() match
compRes.url.getValue(using ctx).unsafeRunSync() match
case Some(url) =>
assert(url == "https://test.com")
case None => fail("Expected component url to be defined")
Expand Down

0 comments on commit 290eeed

Please sign in to comment.