From 69fa1a9fb3d1705c395057c31a6fcb7cc1bd61ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pra=C5=BCak?= Date: Wed, 17 Apr 2024 14:45:49 +0200 Subject: [PATCH] Update mangled properties list --- codegen/src/CodeGen.test.scala | 87 +++++++++++++++++++ codegen/src/PropertyInfo.scala | 12 ++- .../main/scala/besom/internal/codecs.scala | 29 +++++-- .../besom/internal/ResourceDecoderTest.scala | 68 +++++++++++++++ 4 files changed, 187 insertions(+), 9 deletions(-) diff --git a/codegen/src/CodeGen.test.scala b/codegen/src/CodeGen.test.scala index ce6e3b7e..4f98b97d 100644 --- a/codegen/src/CodeGen.test.scala +++ b/codegen/src/CodeGen.test.scala @@ -847,6 +847,93 @@ class CodeGenTest extends munit.FunSuite { tags = Set( munit.Ignore ) // FIXME: un-ignore when this is fixed: https://github.com/pulumi/pulumi-kubernetes/issues/2683 + ), + Data( + name = "Error on urn property", + json = """|{ + | "name": "mangled-provider", + | "version": "0.0.1", + | "resources": { + | "mangled-provider:index:mangled": { + | "properties": { + | "asString": { + | "type": "string" + | }, + | "toString": { + | "type": "string" + | }, + | "scala": { + | "type": "string" + | } + | }, + | "type": "object" + | } + | } + |} + |""".stripMargin, + ignored = List( + "src/index/Provider.scala", + "src/index/ProviderArgs.scala", + "src/index/MangledArgs.scala" + ), + expected = Map( + "src/index/Mangled.scala" -> + """|package besom.api.mangledprovider + | + |final case class Mangled private( + | urn: besom.types.Output[besom.types.URN], + | id: besom.types.Output[besom.types.ResourceId], + | asString_ : besom.types.Output[scala.Option[String]], + | scala_ : besom.types.Output[scala.Option[String]], + | toString_ : besom.types.Output[scala.Option[String]] + |) extends besom.CustomResource + | + |object Mangled extends besom.ResourceCompanion[Mangled]: + | /** Resource constructor for Mangled. + | * + | * @param name [[besom.util.NonEmptyString]] The unique (stack-wise) name of the resource in Pulumi state (not on provider's side). + | * NonEmptyString is inferred automatically from non-empty string literals, even when interpolated. If you encounter any + | * issues with this, please try using `: NonEmptyString` type annotation. If you need to convert a dynamically generated + | * string to NonEmptyString, use `NonEmptyString.apply` method - `NonEmptyString(str): Option[NonEmptyString]`. + | * + | * @param args [[MangledArgs]] The configuration to use to create this resource. This resource has a default configuration. + | * + | * @param opts [[besom.CustomResourceOptions]] Resource options to use for this resource. + | * Defaults to empty options. If you need to set some options, use [[besom.opts]] function to create them, for example: + | * + | * {{{ + | * val res = Mangled( + | * "my-resource", + | * MangledArgs(...), // your args + | * opts(provider = myProvider) + | * ) + | * }}} + | */ + | def apply(using ctx: besom.types.Context)( + | name: besom.util.NonEmptyString, + | args: MangledArgs = MangledArgs(), + | opts: besom.ResourceOptsVariant.Custom ?=> besom.CustomResourceOptions = besom.CustomResourceOptions() + | ): besom.types.Output[Mangled] = + | ctx.readOrRegisterResource[Mangled, MangledArgs]("mangled-provider:index:mangled", name, args, opts(using besom.ResourceOptsVariant.Custom)) + | + | private[besom] def typeToken: besom.types.ResourceType = "mangled-provider:index:mangled" + | + | given resourceDecoder(using besom.types.Context): besom.types.ResourceDecoder[Mangled] = + | besom.internal.ResourceDecoder.derived[Mangled] + | + | given decoder(using besom.types.Context): besom.types.Decoder[Mangled] = + | besom.internal.Decoder.customResourceDecoder[Mangled] + | + | + | given outputOps: {} with + | extension(output: besom.types.Output[Mangled]) + | def urn : besom.types.Output[besom.types.URN] = output.flatMap(_.urn) + | def id : besom.types.Output[besom.types.ResourceId] = output.flatMap(_.id) + | def asString_ : besom.types.Output[scala.Option[String]] = output.flatMap(_.asString_) + | def scala_ : besom.types.Output[scala.Option[String]] = output.flatMap(_.scala_) + | def toString_ : besom.types.Output[scala.Option[String]] = output.flatMap(_.toString_) + |""".stripMargin + ) ) ).foreach(data => test(data.name.withTags(data.tags)) { diff --git a/codegen/src/PropertyInfo.scala b/codegen/src/PropertyInfo.scala index a938bec3..c679fdd2 100644 --- a/codegen/src/PropertyInfo.scala +++ b/codegen/src/PropertyInfo.scala @@ -87,7 +87,13 @@ object PropertyInfo: "getClass", "hashCode", "isInstanceOf", - "toString" + "toString", + "finalize" + ) + + private val reservedMethods = Set( + "pulumiResourceName", + "asString" ) private val reservedPackages = Set( @@ -97,10 +103,12 @@ object PropertyInfo: "besom" ) + private val reserved = anyRefMethodNames ++ reservedMethods ++ reservedPackages + // This logic must be undone the same way in codecs // Keep in sync with `unmanglePropertyName` in codecs.scala private def manglePropertyName(name: String)(implicit logger: Logger): String = - if (anyRefMethodNames ++ reservedPackages).contains(name) then + if reserved.contains(name) then val mangledName = name + "_" logger.debug(s"Mangled property name '$name' as '$mangledName'") mangledName diff --git a/core/src/main/scala/besom/internal/codecs.scala b/core/src/main/scala/besom/internal/codecs.scala index f562a1e0..024ab343 100644 --- a/core/src/main/scala/besom/internal/codecs.scala +++ b/core/src/main/scala/besom/internal/codecs.scala @@ -153,7 +153,7 @@ trait Decoder[A]: end Decoder object NameUnmangler: - private val mangledAnyRefMethodNames = Set( + private val anyRefMethodNames = Set( "eq", "ne", "notify", @@ -166,14 +166,29 @@ object NameUnmangler: "getClass", "hashCode", "isInstanceOf", - "toString" - ).map(_ + "_") + "toString", + "finalize" + ) + + private val reservedMethods = Set( + "pulumiResourceName", + "asString" + ) + + private val reservedPackages = Set( + "java", + "javax", + "scala", + "besom" + ) + + private val reserved = (anyRefMethodNames ++ reservedMethods ++ reservedPackages).map(_ + "_") - /** Keep in sync with `manglePropertyName` in CodeGen.scala */ + // This logic must be undone the same way in codegen + // Keep in sync with `manglePropertyName` in CodeGen.scala def unmanglePropertyName(name: String): String = - if (mangledAnyRefMethodNames.contains(name)) { - name.dropRight(1) - } else name + if reserved.contains(name) then name.dropRight(1) // drop the underscore + else name object Decoder extends DecoderInstancesLowPrio1: import besom.json.* diff --git a/core/src/test/scala/besom/internal/ResourceDecoderTest.scala b/core/src/test/scala/besom/internal/ResourceDecoderTest.scala index 8792f176..bbc21377 100644 --- a/core/src/test/scala/besom/internal/ResourceDecoderTest.scala +++ b/core/src/test/scala/besom/internal/ResourceDecoderTest.scala @@ -281,4 +281,72 @@ class ResourceDecoderTest extends munit.FunSuite: // assertDecodingError (resource.ipAddress.map(_.map(_.fqdn)) } } + + final case class Mangled( + urn: Output[URN], + id: Output[ResourceId], + asString_ : Output[String], + toString_ : Output[String], + scala_ : Output[String] + ) extends CustomResource + object Mangled: + given resourceDecoder(using Context): ResourceDecoder[Mangled] = ResourceDecoder.derived + given decoder(using Context): Decoder[Mangled] = Decoder.customResourceDecoder + + runWithBothOutputCodecs { + test(s"mangled fields") { + val resourceDecoder = summon[ResourceDecoder[Mangled]] + + val errorOrResourceResult = Right( + RawResourceResult( + urn = URN("urn:pulumi:dev::test::test:test:Mangled::test"), + id = Some( + ResourceId.unsafeOf( + "/test/1234" + ) + ), + data = Struct( + fields = Map( + "asString" -> Value(kind = Value.Kind.StringValue(value = "test1")), + "toString" -> Value(kind = Value.Kind.StringValue(value = "test2")), + "scala" -> Value(kind = Value.Kind.StringValue(value = "test3")) + ) + ), + dependencies = Map.empty + ) + ) + + val (resource: Mangled, resourceResolver) = resourceDecoder.makeResourceAndResolver.unsafeRunSync() + resourceResolver.resolve(errorOrResourceResult).unsafeRunSync() + + checkOutput(resource.urn)( + "urn:pulumi:dev::test::test:test:Mangled::test", + expectedDependencies = Set(resource), + expectedIsSecret = false + ) + + checkOutput(resource.id)( + "/test/1234", + expectedDependencies = Set(resource), + expectedIsSecret = false + ) + + checkOutput(resource.asString_)( + "test1", + expectedDependencies = Set(resource), + expectedIsSecret = false + ) + checkOutput(resource.toString_)( + "test2", + expectedDependencies = Set(resource), + expectedIsSecret = false + ) + checkOutput(resource.scala_)( + "test3", + expectedDependencies = Set(resource), + expectedIsSecret = false + ) + } + } + end ResourceDecoderTest