Skip to content

Commit

Permalink
Fix #408 - reified output value codec
Browse files Browse the repository at this point in the history
- add support for serialization and deserialization of Output with full OutputData metadata
  • Loading branch information
pawelprazak committed Mar 14, 2024
1 parent 350500a commit 5149e70
Show file tree
Hide file tree
Showing 6 changed files with 1,063 additions and 378 deletions.
79 changes: 70 additions & 9 deletions core/src/main/scala/besom/internal/ProtobufUtil.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package besom.internal

import besom.internal.Constants.*
import besom.types.URN
import com.google.protobuf.struct.*
import com.google.protobuf.struct.Value.Kind
import com.google.protobuf.util.JsonFormat
Expand Down Expand Up @@ -57,28 +58,88 @@ object ProtobufUtil:
extension (s: SecretValue)
def asValue: Value = Map(
SpecialSig.Key -> SpecialSig.SecretSig.asValue,
SecretValueName -> s.value
ValueName -> s.value
).asValue

given ToValue[OutputValue] with
extension (o: OutputValue)
def asValue: Value = {
Map(
SpecialSig.Key -> SpecialSig.OutputSig.asValue
) ++ (if o.isKnown then Map(ValueName -> o.value) else Map.empty)
++ (if o.isSecret then Map(SecretName -> true.asValue) else Map.empty)
++ (if o.dependencies.nonEmpty
then Map(DependenciesName -> o.dependencies.map(_.asString.asValue).asValue)
else Map.empty)
}.asValue

extension (v: Value)
def asJsonString: Either[Throwable, String] = Try(printer.print(Value.toJavaProto(v))).toEither
def asJsonStringOrThrow: String = asJsonString.fold(t => throw Exception("Expected a JSON", t), identity)

def struct: Option[Struct] = v.kind.structValue
def asSecret: Value = SecretValue(v).asValue
def struct: Option[Struct] = v.kind.structValue
def outputValue: Option[OutputValue] = v.struct.flatMap(_.outputValue)
def secretValue: Option[SecretValue] = v.struct.flatMap(_.secretValue)
def isKnown: Boolean = !v.kind.isNullValue

def asSecretValue: Value = SecretValue(v).asValue
def asOutputValue(
isSecret: Boolean,
dependencies: List[URN]
): Value = OutputValue(
value = v,
isSecret = isSecret,
dependencies = dependencies
).asValue

def withSpecialSignature[A](f: PartialFunction[(Struct, SpecialSig), A]): Option[A] =
v.struct.flatMap(_.withSpecialSignature(f))

def withSpecialSignature[A](f: (Struct, SpecialSig) => A): Option[A] =
for
struct: Struct <- v.struct
sig: SpecialSig <- struct.specialSignature
yield f(struct, sig)
end extension

extension (s: Struct)
def specialSignatureString: Option[String] =
s.fields.get(SpecialSig.Key).flatMap(_.kind.stringValue)
def specialSignature: Option[SpecialSig] =
s.specialSignatureString.flatMap(SpecialSig.fromString)
def withSpecialSignature[A](f: PartialFunction[(Struct, SpecialSig), A]): Option[A] =
val maybeSpecial =
for sig: SpecialSig <- s.specialSignature
yield (s, sig)
maybeSpecial.collect(f)
end withSpecialSignature
def outputValue: Option[OutputValue] =
withSpecialSignature { case (struct, SpecialSig.OutputSig) =>
val value = struct.fields.getOrElse(ValueName, Null)
val isSecret = struct.fields.get(SecretName).flatMap(_.kind.boolValue).getOrElse(false)
val dependencyValues = struct.fields.get(DependenciesName).flatMap(_.kind.listValue).map(_.values.toList).getOrElse(Nil)
val dependencies = dependencyValues.flatMap(_.kind.stringValue).flatMap(s => URN.from(s).toOption)
OutputValue(value, isSecret, dependencies)
}
end outputValue
def secretValue: Option[SecretValue] =
withSpecialSignature { case (struct, SpecialSig.SecretSig) =>
val value = struct.fields.getOrElse(ValueName, Null)
SecretValue(value)
}

end ProtobufUtil

case class SecretValue(value: Value)
import ProtobufUtil.*

case class SecretValue(value: Value):
def isKnown: Boolean = !notKnown
def notKnown: Boolean = value.kind.isNullValue
object SecretValue:
def unapply(value: Value): Option[SecretValue] = value.secretValue
def unapply(struct: Struct): Option[SecretValue] = struct.secretValue

case class OutputValue(value: Value, isSecret: Boolean, dependencies: List[URN]):
def isKnown: Boolean = value.isKnown
def notKnown: Boolean = !isKnown

object OutputValue:
def unapply(value: Value): Option[OutputValue] = value.outputValue
def unapply(struct: Struct): Option[OutputValue] = struct.outputValue
def unknown(isSecret: Boolean, dependencies: List[URN]): OutputValue =
OutputValue(Null, isSecret, dependencies)
Loading

0 comments on commit 5149e70

Please sign in to comment.