Skip to content

Commit

Permalink
Fix generating constructor examples for resources that have numeric e…
Browse files Browse the repository at this point in the history
…nums as input
  • Loading branch information
Zaid-Ajaj committed May 18, 2024
1 parent dacabae commit a31a6db
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: docs
description: Fix generating constructor examples for resources that have numeric enums as input
50 changes: 34 additions & 16 deletions pkg/codegen/docs/constructor_syntax_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"

"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax"
Expand Down Expand Up @@ -149,28 +148,47 @@ func (g *constructorSyntaxGenerator) writeValue(
_, _, resourceNameFromToken, _ := pcl.DecomposeToken(valueType.Token, hcl.Range{})
write("%s", camelCase(resourceNameFromToken))
case *schema.EnumType:
cases := make([]string, len(valueType.Elements))
for index, c := range valueType.Elements {
if c.DeprecationMessage != "" {
continue
if valueType.ElementType == schema.NumberType || valueType.ElementType == schema.IntType {
var value interface{}
for _, elem := range valueType.Elements {
if elem.DeprecationMessage != "" {
continue
}

value = elem.Value
break
}

if stringCase, ok := c.Value.(string); ok && stringCase != "" {
cases[index] = stringCase
} else if intCase, ok := c.Value.(int); ok {
cases[index] = strconv.Itoa(intCase)
if value != nil {
write(fmt.Sprintf("%v", value))
} else {
if c.Name != "" {
cases[index] = c.Name
// all of them enum cases deprecated
// choose the first one
write(fmt.Sprintf("%v", valueType.Elements[0].Value))
}
} else {
cases := make([]string, len(valueType.Elements))
for index, c := range valueType.Elements {
if c.DeprecationMessage != "" {
continue
}

if stringCase, ok := c.Value.(string); ok && stringCase != "" {
cases[index] = stringCase
} else {
if c.Name != "" {
cases[index] = c.Name
}
}
}
}

if len(cases) > 0 {
write(fmt.Sprintf("%q", cases[0]))
} else {
write("null")
if len(cases) > 0 {
write(fmt.Sprintf("%q", cases[0]))
} else {
write("null")
}
}

case *schema.UnionType:
if isUnionOfObjects(valueType) && len(valueType.ElementTypes) >= 1 {
writeValue(valueType.ElementTypes[0])
Expand Down
48 changes: 45 additions & 3 deletions pkg/codegen/docs/constructor_syntax_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ func TestConstructorSyntaxGeneratorForSchema(t *testing.T) {
t.Parallel()
pkg := bindTestSchema(t, schema.PackageSpec{
Name: "test",
Types: map[string]schema.ComplexTypeSpec{
"test:index:ExampleEnum": {
ObjectTypeSpec: schema.ObjectTypeSpec{
Type: "string",
},
Enum: []schema.EnumValueSpec{
{Value: "first", Name: "first"},
{Value: "second", Name: "second"},
},
},
"test:index:ExampleNumericEnum": {
ObjectTypeSpec: schema.ObjectTypeSpec{
Type: "integer",
},
Enum: []schema.EnumValueSpec{
{Value: 10, Name: "first"},
{Value: 20, Name: "second"},
},
},
},
Resources: map[string]schema.ResourceSpec{
"test:index:First": {
InputProperties: map[string]schema.PropertySpec{
Expand All @@ -51,6 +71,16 @@ func TestConstructorSyntaxGeneratorForSchema(t *testing.T) {
Type: "boolean",
},
},
"fooEnum": {
TypeSpec: schema.TypeSpec{
Ref: "#/types/test:index:ExampleEnum",
},
},
"fooNumericEnum": {
TypeSpec: schema.TypeSpec{
Ref: "#/types/test:index:ExampleNumericEnum",
},
},
},
},
"test:index:Second": {
Expand Down Expand Up @@ -87,7 +117,9 @@ func TestConstructorSyntaxGeneratorForSchema(t *testing.T) {
var firstResource = new Test.First("firstResource", new()
{
FooBool = false,
FooEnum = Test.ExampleEnum.First,
FooInt = 0,
FooNumericEnum = Test.ExampleNumericEnum.First,
FooString = "string",
});
`)
Expand All @@ -107,7 +139,9 @@ var noInputsResource = new Test.NoInputs("noInputsResource");
equalPrograms(constructorSyntax.typescript, "test:index:First", `
const firstResource = new test.First("firstResource", {
fooBool: false,
fooEnum: test.ExampleEnum.First,
fooInt: 0,
fooNumericEnum: test.ExampleNumericEnum.First,
fooString: "string",
});`)
equalPrograms(constructorSyntax.typescript, "test:index:Second", `
Expand All @@ -122,7 +156,9 @@ const noInputsResource = new test.NoInputs("noInputsResource", {});
equalPrograms(constructorSyntax.python, "test:index:First", `
first_resource = test.First("firstResource",
foo_bool=False,
foo_enum=test.ExampleEnum.FIRST,
foo_int=0,
foo_numeric_enum=test.ExampleNumericEnum.FIRST,
foo_string="string")`)
equalPrograms(constructorSyntax.python, "test:index:Second", `
second_resource = test.Second("secondResource", bar_string="string")`)
Expand All @@ -134,9 +170,11 @@ no_inputs_resource = test.NoInputs("noInputsResource")
assert.Equal(t, expectedResources, len(constructorSyntax.golang.resources))
equalPrograms(constructorSyntax.golang, "test:index:First", `
_, err := test.NewFirst(ctx, "firstResource", &test.FirstArgs{
FooBool: pulumi.Bool(false),
FooInt: pulumi.Int(0),
FooString: pulumi.String("string"),
FooBool: pulumi.Bool(false),
FooEnum: index.ExampleEnumFirst,
FooInt: pulumi.Int(0),
FooNumericEnum: index.ExampleNumericEnumFirst,
FooString: pulumi.String("string"),
})`)

equalPrograms(constructorSyntax.golang, "test:index:Second", `
Expand All @@ -153,7 +191,9 @@ _, err = test.NewNoInputs(ctx, "noInputsResource", nil)
type: test:First
properties:
fooBool: false
fooEnum: first
fooInt: 0
fooNumericEnum: 10
fooString: string
`)

Expand All @@ -172,7 +212,9 @@ properties: {}
equalPrograms(constructorSyntax.java, "test:index:First", `
var firstResource = new First("firstResource", FirstArgs.builder()
.fooBool(false)
.fooEnum("first")
.fooInt(0)
.fooNumericEnum(10)
.fooString("string")
.build());
`)
Expand Down
23 changes: 15 additions & 8 deletions pkg/codegen/dotnet/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,12 @@ func enumName(enum *model.EnumType) (string, string) {
et := e.(*schema.EnumType)
def, err := et.PackageReference.Definition()
contract.AssertNoErrorf(err, "error loading definition for package %q", et.PackageReference.Name())
namespaceMap := def.Language["csharp"].(CSharpPackageInfo).Namespaces
var namespaceMap map[string]string
pkgInfo, ok := def.Language["csharp"].(CSharpPackageInfo)
if ok {
namespaceMap = pkgInfo.Namespaces
}

namespace := namespaceName(namespaceMap, components[0])
if components[1] != "" && components[1] != "index" {
namespace += "." + namespaceName(namespaceMap, components[1])
Expand All @@ -406,20 +411,22 @@ func (g *generator) genIntrensic(w io.Writer, from model.Expression, to model.Ty
g.Fgenf(w, "%.v", from)
return
}
var convertFn string
switch {
case to.Type.Equals(model.StringType):
convertFn = fmt.Sprintf("System.Enum.Parse<%s.%s>", pkg, name)
default:

convertFn := func() string {
if to.Type.Equals(model.StringType) {
return fmt.Sprintf("System.Enum.Parse<%s.%s>", pkg, name)
}

panic(fmt.Sprintf(
"Unsafe enum conversions from type %s not implemented yet: %s => %s",
from.Type(), from, to))
}

if isOutput {
g.Fgenf(w, "%.v.Apply(%s)", from, convertFn)
g.Fgenf(w, "%.v.Apply(%s)", from, convertFn())
} else {
diag := pcl.GenEnum(to, from, g.genSafeEnum(w, to), func(from model.Expression) {
g.Fgenf(w, "%s(%v)", convertFn, from)
g.Fgenf(w, "%s(%v)", convertFn(), from)
})
if diag != nil {
g.diagnostics = append(g.diagnostics, diag)
Expand Down
6 changes: 3 additions & 3 deletions pkg/codegen/go/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,10 @@ func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionC
switch {
case to.Type.Equals(model.StringType):
underlyingType = "string"
case to.Type.Equals(model.IntType):
underlyingType = "int"
default:
panic(fmt.Sprintf(
"Unsafe enum conversions from type %s not implemented yet: %s => %s",
from.Type(), from, to))
underlyingType = "float64"
}
pkg, mod, typ, _ := pcl.DecomposeToken(to.Token, to.SyntaxNode().Range())
mod = g.getModOrAlias(pkg, mod, mod)
Expand Down
8 changes: 7 additions & 1 deletion pkg/codegen/pcl/binder_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,13 @@ func EnumMember(t *model.EnumType, value cty.Value) (*schema.Enum, bool) {
case t.Type.Equals(model.IntType):
f, _ := value.AsBigFloat().Int64()
for _, el := range src.Elements {
if el.Value.(int64) == f {
valueInt64, ok := el.Value.(int64)
if ok && valueInt64 == f {
return el, true
}

valueInt32, ok := el.Value.(int32)
if ok && int64(valueInt32) == f {
return el, true
}
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/codegen/python/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionC
}
var moduleNameOverrides map[string]string
if pkg, err := enum.(*schema.EnumType).PackageReference.Definition(); err == nil {
moduleNameOverrides = pkg.Language["python"].(PackageInfo).ModuleNameOverrides
if pkgInfo, ok := pkg.Language["python"].(PackageInfo); ok {
moduleNameOverrides = pkgInfo.ModuleNameOverrides
}
}
pkg := strings.ReplaceAll(components[0], "-", "_")
enumName := tokenToName(to.Token)
Expand Down

0 comments on commit a31a6db

Please sign in to comment.