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 0c9178d
Show file tree
Hide file tree
Showing 17 changed files with 440 additions and 98 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
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,12 @@ var nurseryResource = new Plant.Tree.V1.Nursery("nurseryResource", new()

```go
example, err := tree.NewNursery(ctx, "nurseryResource", &tree.NurseryArgs{
Varieties: treev1.RubberTreeVarietyArray{
tree.RubberTreeVarietyBurgundy,
},
Sizes: treev1.TreeSizeMap{
"string": tree.TreeSizeSmall,
},
Varieties: treev1.RubberTreeVarietyArray{
tree.RubberTreeVarietyBurgundy,
},
Sizes: treev1.TreeSizeMap{
"string": tree.TreeSizeSmall,
},
})
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,20 @@ The following reference example uses placeholder values for all [input propertie
<pulumi-choosable type="language" values="csharp">

```csharp
Coming soon!
var rubberTreeResource = new Plant.Tree.V1.RubberTree("rubberTreeResource", new()
{
Diameter = Plant.Tree.V1.Diameter.Sixinch,
Type = Plant.Tree.V1.RubberTreeVariety.Burgundy,
Container = new Plant.Inputs.ContainerArgs
{
Size = Plant.ContainerSize.FourInch,
Brightness = Plant.ContainerBrightness.ZeroPointOne,
Color = Plant.ContainerColor.Red,
Material = "string",
},
Farm = Plant.Tree.V1.Farm.Pulumi_Planters_Inc_,
Size = Plant.Tree.V1.TreeSize.Small,
});
```

</pulumi-choosable>
Expand All @@ -243,7 +256,18 @@ Coming soon!
<pulumi-choosable type="language" values="go">

```go
Coming soon!
example, err := tree.NewRubberTree(ctx, "rubberTreeResource", &tree.RubberTreeArgs{
Diameter: tree.DiameterSixinch,
Type: tree.RubberTreeVarietyBurgundy,
Container: &plantprovider.ContainerArgs{
Size: plant.ContainerSizeFourInch,
Brightness: plant.ContainerBrightnessZeroPointOne,
Color: pulumi.String(plant.ContainerColorRed),
Material: pulumi.String("string"),
},
Farm: pulumi.String(tree.Farm_Pulumi_Planters_Inc_),
Size: tree.TreeSizeSmall,
})
```

</pulumi-choosable>
Expand All @@ -255,11 +279,11 @@ Coming soon!

```java
var rubberTreeResource = new RubberTree("rubberTreeResource", RubberTreeArgs.builder()
.diameter("sixinch")
.diameter(6)
.type("Burgundy")
.container(ContainerArgs.builder()
.size("FourInch")
.brightness("ZeroPointOne")
.size(4)
.brightness(0.1)
.color("red")
.material("string")
.build())
Expand All @@ -276,7 +300,17 @@ var rubberTreeResource = new RubberTree("rubberTreeResource", RubberTreeArgs.bui
<pulumi-choosable type="language" values="python">

```python
Coming soon!
rubber_tree_resource = plant.tree.v1.RubberTree("rubberTreeResource",
diameter=plant.tree.v1.Diameter.SIXINCH,
type=plant.tree.v1.RubberTreeVariety.BURGUNDY,
container=plant.ContainerArgs(
size=plant.ContainerSize.FOUR_INCH,
brightness=plant.ContainerBrightness.ZERO_POINT_ONE,
color=plant.ContainerColor.RED,
material="string",
),
farm=plant.tree.v1.Farm.PULUMI_PLANTERS_INC_,
size=plant.tree.v1.TreeSize.SMALL)
```

</pulumi-choosable>
Expand All @@ -287,7 +321,18 @@ Coming soon!
<pulumi-choosable type="language" values="typescript">

```typescript
Coming soon!
const rubberTreeResource = new plant.tree.v1.RubberTree("rubberTreeResource", {
diameter: plant.tree.v1.Diameter.Sixinch,
type: plant.tree.v1.RubberTreeVariety.Burgundy,
container: {
size: plant.ContainerSize.FourInch,
brightness: plant.ContainerBrightness.ZeroPointOne,
color: plant.ContainerColor.Red,
material: "string",
},
farm: plant.tree.v1.Farm.Pulumi_Planters_Inc_,
size: plant.tree.v1.TreeSize.Small,
});
```

</pulumi-choosable>
Expand All @@ -301,11 +346,11 @@ Coming soon!
type: plant:tree/v1:RubberTree
properties:
container:
brightness: ZeroPointOne
brightness: 0.1
color: red
material: string
size: FourInch
diameter: sixinch
size: 4
diameter: 6
farm: Pulumi Planters Inc.
size: small
type: Burgundy
Expand Down
Loading

0 comments on commit 0c9178d

Please sign in to comment.