diff --git a/.changes/unreleased/BUG FIXES-20231026-133048.yaml b/.changes/unreleased/BUG FIXES-20231026-133048.yaml new file mode 100644 index 00000000..178fe4e7 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20231026-133048.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: Fix nested attribute name and generated custom value method name conflicts +time: 2023-10-26T13:30:48.63762+01:00 +custom: + Issue: "81" diff --git a/internal/datasource_generate/object_attribute.go b/internal/datasource_generate/object_attribute.go index 12ea7cb0..c063baff 100644 --- a/internal/datasource_generate/object_attribute.go +++ b/internal/datasource_generate/object_attribute.go @@ -9,6 +9,7 @@ import ( "strings" "text/template" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -55,6 +56,14 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(g.AssociatedExternalType.Imports()) + for _, v := range g.AttrTypes() { + if v.Number != nil && g.AssociatedExternalType == nil { + imports.Add(code.Import{ + Path: generatorschema.MathBigImport, + }) + } + } + return imports } diff --git a/internal/datasource_generate/object_attribute_test.go b/internal/datasource_generate/object_attribute_test.go index 074251fc..b682e310 100644 --- a/internal/datasource_generate/object_attribute_test.go +++ b/internal/datasource_generate/object_attribute_test.go @@ -95,6 +95,29 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is imported when object has + // attribute type that is number type. + "object-with-attr-type-number": { + input: GeneratorObjectAttribute{ + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + { + Path: generatorschema.MathBigImport, + }, + }, + }, "object-with-attr-type-bool-with-import": { input: GeneratorObjectAttribute{ AttributeTypes: specschema.ObjectAttributeTypes{ @@ -361,6 +384,43 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is not imported when associated external type + // is specified. + "associated-external-type-with-number-attribute": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/provider_generate/object_attribute.go b/internal/provider_generate/object_attribute.go index 40b43edf..1d65b701 100644 --- a/internal/provider_generate/object_attribute.go +++ b/internal/provider_generate/object_attribute.go @@ -9,6 +9,7 @@ import ( "strings" "text/template" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -55,6 +56,14 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(g.AssociatedExternalType.Imports()) + for _, v := range g.AttrTypes() { + if v.Number != nil && g.AssociatedExternalType == nil { + imports.Add(code.Import{ + Path: generatorschema.MathBigImport, + }) + } + } + return imports } diff --git a/internal/provider_generate/object_attribute_test.go b/internal/provider_generate/object_attribute_test.go index 6958358e..aa9be32f 100644 --- a/internal/provider_generate/object_attribute_test.go +++ b/internal/provider_generate/object_attribute_test.go @@ -95,6 +95,29 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is imported when object has + // attribute type that is number type. + "object-with-attr-type-number": { + input: GeneratorObjectAttribute{ + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + { + Path: generatorschema.MathBigImport, + }, + }, + }, "object-with-attr-type-bool-with-import": { input: GeneratorObjectAttribute{ AttributeTypes: specschema.ObjectAttributeTypes{ @@ -361,6 +384,43 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is not imported when associated external type + // is specified. + "associated-external-type-with-number-attribute": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/resource_generate/object_attribute.go b/internal/resource_generate/object_attribute.go index 4f7b0901..f1b3b9cf 100644 --- a/internal/resource_generate/object_attribute.go +++ b/internal/resource_generate/object_attribute.go @@ -9,6 +9,7 @@ import ( "strings" "text/template" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -67,6 +68,14 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(g.AssociatedExternalType.Imports()) + for _, v := range g.AttrTypes() { + if v.Number != nil && g.AssociatedExternalType == nil { + imports.Add(code.Import{ + Path: generatorschema.MathBigImport, + }) + } + } + return imports } diff --git a/internal/resource_generate/object_attribute_test.go b/internal/resource_generate/object_attribute_test.go index f03e8969..3ab635df 100644 --- a/internal/resource_generate/object_attribute_test.go +++ b/internal/resource_generate/object_attribute_test.go @@ -95,6 +95,29 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is imported when object has + // attribute type that is number type. + "object-with-attr-type-number": { + input: GeneratorObjectAttribute{ + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + { + Path: generatorschema.MathBigImport, + }, + }, + }, "object-with-attr-type-bool-with-import": { input: GeneratorObjectAttribute{ AttributeTypes: specschema.ObjectAttributeTypes{ @@ -514,6 +537,43 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + // verifies that math/big is not imported when associated external type + // is specified. + "associated-external-type-with-number-attribute": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + { + Name: "number", + Number: &specschema.NumberType{}, + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/schema/custom_nested_object_test.go b/internal/schema/custom_nested_object_test.go index 8d4e088d..e64025c3 100644 --- a/internal/schema/custom_nested_object_test.go +++ b/internal/schema/custom_nested_object_test.go @@ -256,6 +256,93 @@ return ExampleValue{ BoolAttribute: boolAttributeVal, state: attr.ValueStateKnown, }, diags +}`), + }, + "attribute-name-same-as-generated-method-name": { + name: "Example", + attrValues: map[string]string{ + "type": "basetypes.BoolValue", + }, + expected: []byte(` +func NewExampleValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 +ctx := context.Background() + +for name, attributeType := range attributeTypes { +attribute, ok := attributes[name] + +if !ok { +diags.AddError( +"Missing ExampleValue Attribute Value", +"While creating a ExampleValue value, a missing attribute value was detected. "+ +"A ExampleValue must contain values for all attributes, even if null or unknown. "+ +"This is always an issue with the provider and should be reported to the provider developers.\n\n"+ +fmt.Sprintf("ExampleValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), +) + +continue +} + +if !attributeType.Equal(attribute.Type(ctx)) { +diags.AddError( +"Invalid ExampleValue Attribute Type", +"While creating a ExampleValue value, an invalid attribute value was detected. "+ +"A ExampleValue must use a matching attribute type for the value. "+ +"This is always an issue with the provider and should be reported to the provider developers.\n\n"+ +fmt.Sprintf("ExampleValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ +fmt.Sprintf("ExampleValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), +) +} +} + +for name := range attributes { +_, ok := attributeTypes[name] + +if !ok { +diags.AddError( +"Extra ExampleValue Attribute Value", +"While creating a ExampleValue value, an extra attribute value was detected. "+ +"A ExampleValue must not contain values beyond the expected attribute types. "+ +"This is always an issue with the provider and should be reported to the provider developers.\n\n"+ +fmt.Sprintf("Extra ExampleValue Attribute Name: %s", name), +) +} +} + +if diags.HasError() { +return NewExampleValueUnknown(), diags +} + + +typeAttribute, ok := attributes["type"] + +if !ok { +diags.AddError( +"Attribute Missing", +` + "`type is missing from object`" + `) + +return NewExampleValueUnknown(), diags +} + +typeVal, ok := typeAttribute.(basetypes.BoolValue) + +if !ok { +diags.AddError( +"Attribute Wrong Type", +fmt.Sprintf(` + "`type expected to be basetypes.BoolValue, was: %T`" + `, typeAttribute)) +} + + +if diags.HasError() { +return NewExampleValueUnknown(), diags +} + +return ExampleValue{ +ExampleType: typeVal, +state: attr.ValueStateKnown, +}, diags }`), }, } @@ -329,6 +416,47 @@ return ExampleValue{ BoolAttribute: boolAttributeVal, state: attr.ValueStateKnown, }, diags +}`), + }, + "attribute-name-same-as-generated-method-name": { + name: "Example", + attrValues: map[string]string{ + "type": "basetypes.BoolValue", + }, + expected: []byte(` +func (t ExampleType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { +var diags diag.Diagnostics + +attributes := in.Attributes() + + +typeAttribute, ok := attributes["type"] + +if !ok { +diags.AddError( +"Attribute Missing", +` + "`type is missing from object`" + `) + +return nil, diags +} + +typeVal, ok := typeAttribute.(basetypes.BoolValue) + +if !ok { +diags.AddError( +"Attribute Wrong Type", +fmt.Sprintf(` + "`type expected to be basetypes.BoolValue, was: %T`" + `, typeAttribute)) +} + + +if diags.HasError() { +return nil, diags +} + +return ExampleValue{ +ExampleType: typeVal, +state: attr.ValueStateKnown, +}, diags }`), }, } @@ -681,6 +809,36 @@ return false } +return true +}`), + }, + "attribute-name-same-as-generated-method-name": { + name: "Example", + attrValues: map[string]string{ + "type": "basetypes.ListValue", + }, + expected: []byte(` +func (v ExampleValue) Equal(o attr.Value) bool { +other, ok := o.(ExampleValue) + +if !ok { +return false +} + +if v.state != other.state { +return false +} + +if v.state != attr.ValueStateKnown { +return true +} + + +if !v.ExampleType.Equal(other.ExampleType) { +return false +} + + return true }`), }, @@ -832,7 +990,7 @@ func TestCustomNestedObjectValue_renderToObjectValue(t *testing.T) { expected []byte expectedError error }{ - "default": { + "non-nested": { name: "Example", attributeTypes: map[string]string{ "bool_attribute": "Bool", @@ -852,6 +1010,135 @@ map[string]attr.Value{ "bool_attribute": v.BoolAttribute, }) +return objVal, diags +}`), + }, + "nested": { + name: "Example", + attributeTypes: map[string]string{ + "list_nested_attribute": "ListNested", + }, + attrTypes: map[string]string{ + "list_nested_attribute": "basetypes.ListType{}", + }, + expected: []byte(` +func (v ExampleValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { +var diags diag.Diagnostics + +listNestedAttribute := types.ListValueMust( +ListNestedAttributeType{ +basetypes.ObjectType{ +AttrTypes: ListNestedAttributeValue{}.AttributeTypes(ctx), +}, +}, +v.ListNestedAttribute.Elements(), +) + +if v.ListNestedAttribute.IsNull() { +listNestedAttribute = types.ListNull( +ListNestedAttributeType{ +basetypes.ObjectType{ +AttrTypes: ListNestedAttributeValue{}.AttributeTypes(ctx), +}, +}, +) +} + +if v.ListNestedAttribute.IsUnknown() { +listNestedAttribute = types.ListUnknown( +ListNestedAttributeType{ +basetypes.ObjectType{ +AttrTypes: ListNestedAttributeValue{}.AttributeTypes(ctx), +}, +}, +) +} + + +objVal, diags := types.ObjectValue( +map[string]attr.Type{ +"list_nested_attribute": basetypes.ListType{}, +}, +map[string]attr.Value{ +"list_nested_attribute": listNestedAttribute, +}) + +return objVal, diags +}`), + }, + "non-nested-attribute-name-same-as-generated-method-name": { + name: "Example", + attributeTypes: map[string]string{ + "type": "Bool", + }, + attrTypes: map[string]string{ + "type": "basetypes.BoolType{}", + }, + expected: []byte(` +func (v ExampleValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { +var diags diag.Diagnostics + +objVal, diags := types.ObjectValue( +map[string]attr.Type{ +"type": basetypes.BoolType{}, +}, +map[string]attr.Value{ +"type": v.ExampleType, +}) + +return objVal, diags +}`), + }, + "nested-attribute-name-same-as-generated-method-name": { + name: "Example", + attributeTypes: map[string]string{ + "type": "ListNested", + }, + attrTypes: map[string]string{ + "type": "basetypes.ListType{}", + }, + expected: []byte(` +func (v ExampleValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { +var diags diag.Diagnostics + +exampleType := types.ListValueMust( +TypeType{ +basetypes.ObjectType{ +AttrTypes: TypeValue{}.AttributeTypes(ctx), +}, +}, +v.ExampleType.Elements(), +) + +if v.ExampleType.IsNull() { +exampleType = types.ListNull( +TypeType{ +basetypes.ObjectType{ +AttrTypes: TypeValue{}.AttributeTypes(ctx), +}, +}, +) +} + +if v.ExampleType.IsUnknown() { +exampleType = types.ListUnknown( +TypeType{ +basetypes.ObjectType{ +AttrTypes: TypeValue{}.AttributeTypes(ctx), +}, +}, +) +} + + +objVal, diags := types.ObjectValue( +map[string]attr.Type{ +"type": basetypes.ListType{}, +}, +map[string]attr.Value{ +"type": exampleType, +}) + return objVal, diags }`), }, @@ -895,6 +1182,49 @@ map[string]attr.Value{ "list_attribute": listAttributeVal, }) +return objVal, diags +}`), + }, + "collection-type-attribute-name-same-as-generated-method-name": { + name: "Example", + attributeTypes: map[string]string{ + "type": "List", + }, + attrTypes: map[string]string{ + "type": "basetypes.ListType{\nElemType: types.BoolType,\n}", + }, + collectionTypes: map[string]map[string]string{ + "type": { + "ElementType": "types.BoolType", + "TypeValueFunc": "types.ListValue", + }, + }, + expected: []byte(` +func (v ExampleValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { +var diags diag.Diagnostics + +typeVal, d := types.ListValue(types.BoolType, v.ExampleType.Elements()) + +diags.Append(d...) + +if d.HasError() { +return types.ObjectUnknown(map[string]attr.Type{ +"type": basetypes.ListType{ +ElemType: types.BoolType, +}, +}), diags +} + +objVal, diags := types.ObjectValue( +map[string]attr.Type{ +"type": basetypes.ListType{ +ElemType: types.BoolType, +}, +}, +map[string]attr.Value{ +"type": typeVal, +}) + return objVal, diags }`), }, @@ -932,6 +1262,43 @@ map[string]attr.Value{ "object_attribute": objectAttributeVal, }) +return objVal, diags +}`), + }, + "object-type-attribute-name-same-as-generated-method-name": { + name: "Example", + attributeTypes: map[string]string{ + "type": "Object", + }, + attrTypes: map[string]string{ + "type": "basetypes.ObjectType{\nAttrTypes: map[string]attr.Type{\n\"bool\": types.BoolType,\n\"float64\": types.Float64Type,\n\"int64\": types.Int64Type,\n\"number\": types.NumberType,\n\"string\": types.StringType,\n},\n}", + }, + expected: []byte(` +func (v ExampleValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { +var diags diag.Diagnostics + +typeVal, d := types.ObjectValue(v.ExampleType.AttributeTypes(ctx), v.ExampleType.Attributes()) + +diags.Append(d...) + +if d.HasError() { +return types.ObjectUnknown(map[string]attr.Type{ +"type": basetypes.ObjectType{ +AttrTypes: v.ExampleType.AttributeTypes(ctx), +}, +}), diags +} + +objVal, diags := types.ObjectValue( +map[string]attr.Type{ +"type": basetypes.ObjectType{ +AttrTypes: v.ExampleType.AttributeTypes(ctx), +}, +}, +map[string]attr.Value{ +"type": typeVal, +}) + return objVal, diags }`), }, @@ -998,6 +1365,51 @@ vals["bool_attribute"] = val +if err := tftypes.ValidateValue(objectType, vals); err != nil { +return tftypes.NewValue(objectType, tftypes.UnknownValue), err +} + +return tftypes.NewValue(objectType, vals), nil +case attr.ValueStateNull: +return tftypes.NewValue(objectType, nil), nil +case attr.ValueStateUnknown: +return tftypes.NewValue(objectType, tftypes.UnknownValue), nil +default: +panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) +} +}`), + }, + "attribute-name-same-as-generated-method-name": { + name: "Example", + attrTypes: map[string]string{ + "type": "basetypes.BoolType{}", + }, + expected: []byte(`func (v ExampleValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { +attrTypes := make(map[string]tftypes.Type, 1) + +var val tftypes.Value +var err error + + +attrTypes["type"] = basetypes.BoolType{}.TerraformType(ctx) + +objectType := tftypes.Object{AttributeTypes: attrTypes} + +switch v.state { +case attr.ValueStateKnown: +vals := make(map[string]tftypes.Value, 1) + + +val, err = v.ExampleType.ToTerraformValue(ctx) + +if err != nil { +return tftypes.NewValue(objectType, tftypes.UnknownValue), err +} + +vals["type"] = val + + + if err := tftypes.ValidateValue(objectType, vals); err != nil { return tftypes.NewValue(objectType, tftypes.UnknownValue), err } @@ -1117,12 +1529,27 @@ func TestCustomNestedObjectValue_renderValue(t *testing.T) { testCases := map[string]struct { name string + attrValues map[string]string expected []byte expectedError error }{ "default": { name: "Example", + attrValues: map[string]string{ + "bool_attribute": "basetypes.BoolValue", + }, + expected: []byte(`type ExampleValue struct { +BoolAttribute basetypes.BoolValue ` + "`" + `tfsdk:"bool_attribute"` + "`" + ` +state attr.ValueState +}`), + }, + "attribute-name-same-as-generated-method-name": { + name: "Example", + attrValues: map[string]string{ + "type": "basetypes.BoolValue", + }, expected: []byte(`type ExampleValue struct { +ExampleType basetypes.BoolValue ` + "`" + `tfsdk:"type"` + "`" + ` state attr.ValueState }`), }, @@ -1134,7 +1561,7 @@ state attr.ValueState t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, testCase.attrValues, nil) got, err := customObjectValue.renderValue() diff --git a/internal/schema/framework_identifier.go b/internal/schema/framework_identifier.go index 306c1c46..7488c1f4 100644 --- a/internal/schema/framework_identifier.go +++ b/internal/schema/framework_identifier.go @@ -42,6 +42,26 @@ func (identifier FrameworkIdentifier) ToCamelCase() string { return string(unicode.ToLower(firstLetter)) + pascal[size:] } +// ToPrefixCamelCase will return a camel case formatted string of the identifier, +// prefixed with a camel-cased version of the supplied name if the identifier is +// a generated custom value method name. +// Example: +// - equal(something) -> somethingEqual +// - type(something) -> somethingType +func (identifier FrameworkIdentifier) ToPrefixCamelCase(prefix string) string { + pascalCase := identifier.ToPascalCase() + + methodNames := identifier.methodNames() + + for _, v := range methodNames { + if pascalCase == v { + return FrameworkIdentifier(prefix + pascalCase).ToCamelCase() + } + } + + return FrameworkIdentifier(pascalCase).ToCamelCase() +} + // ToPascalCase will return a pascal case formatted string of the identifier. // Example: // - example_resource_thing -> ExampleResourceThing @@ -51,9 +71,44 @@ func (identifier FrameworkIdentifier) ToPascalCase() string { }) } +// ToPrefixPascalCase will return a pascal case formatted string of the identifier, +// prefixed with a pascal-cased version of the supplied name if the identifier is +// a generated custom value method name. +// Example: +// - equal(something) -> SomethingEqual +// - type(something) -> SomethingType +func (identifier FrameworkIdentifier) ToPrefixPascalCase(prefix string) string { + pascalCase := identifier.ToPascalCase() + + methodNames := identifier.methodNames() + + for _, v := range methodNames { + if pascalCase == v { + return FrameworkIdentifier(prefix).ToPascalCase() + pascalCase + } + } + + return pascalCase +} + // ToString returns the FrameworkIdentifier as a string without any formatting. // Example: // - example_resource_thing -> example_resource_thing func (identifier FrameworkIdentifier) ToString() string { return string(identifier) } + +// methodNames returns a slice containing generated method names for custom value +// types. +func (identifier FrameworkIdentifier) methodNames() []string { + return []string{ + "AttributeTypes", + "Equal", + "IsNull", + "IsUnknown", + "String", + "ToObjectValue", + "ToTerraformValue", + "Type", + } +} diff --git a/internal/schema/framework_identifier_test.go b/internal/schema/framework_identifier_test.go index a4ef9a5a..3a1e3a61 100644 --- a/internal/schema/framework_identifier_test.go +++ b/internal/schema/framework_identifier_test.go @@ -107,6 +107,79 @@ func TestFrameworkIdentifier_ToCamelCase(t *testing.T) { } } +func TestFrameworkIdentifier_ToPrefixCamelCase(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + identifier schema.FrameworkIdentifier + want string + }{ + "lowercase alphabet": { + identifier: "thing", + want: "thing", + }, + "leading underscore": { + identifier: "_thing", + want: "thing", + }, + "middle underscore": { + identifier: "fake_thing", + want: "fakeThing", + }, + "alphanumeric": { + identifier: "thing123", + want: "thing123", + }, + "valid - alphanumeric with underscores": { + identifier: "fake_thing_123", + want: "fakeThing123", + }, + "attribute_types": { + identifier: "attribute_types", + want: "prefixAttributeTypes", + }, + "equal": { + identifier: "Equal", + want: "prefixEqual", + }, + "is_null": { + identifier: "is_null", + want: "prefixIsNull", + }, + "is_unknown": { + identifier: "is_unknown", + want: "prefixIsUnknown", + }, + "string": { + identifier: "string", + want: "prefixString", + }, + "to_object_value": { + identifier: "to_object_value", + want: "prefixToObjectValue", + }, + "to_terraform_value": { + identifier: "to_terraform_value", + want: "prefixToTerraformValue", + }, + "type": { + identifier: "type", + want: "prefixType", + }, + } + for name, testCase := range testCases { + name, testCase := name, testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.identifier.ToPrefixCamelCase("prefix") + if got != testCase.want { + t.Fatalf("expected ToCamelCase() to return %s, got %s", testCase.want, got) + } + }) + } +} + func TestFrameworkIdentifier_ToPascalCase(t *testing.T) { t.Parallel() @@ -147,3 +220,76 @@ func TestFrameworkIdentifier_ToPascalCase(t *testing.T) { }) } } + +func TestFrameworkIdentifier_ToPrefixPascalCase(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + identifier schema.FrameworkIdentifier + want string + }{ + "lowercase alphabet": { + identifier: "thing", + want: "Thing", + }, + "leading underscore": { + identifier: "_thing", + want: "Thing", + }, + "middle underscore": { + identifier: "fake_thing", + want: "FakeThing", + }, + "alphanumeric": { + identifier: "thing123", + want: "Thing123", + }, + "valid - alphanumeric with underscores": { + identifier: "fake_thing_123", + want: "FakeThing123", + }, + "attribute_types": { + identifier: "attribute_types", + want: "PrefixAttributeTypes", + }, + "equal": { + identifier: "Equal", + want: "PrefixEqual", + }, + "is_null": { + identifier: "is_null", + want: "PrefixIsNull", + }, + "is_unknown": { + identifier: "is_unknown", + want: "PrefixIsUnknown", + }, + "string": { + identifier: "string", + want: "PrefixString", + }, + "to_object_value": { + identifier: "to_object_value", + want: "PrefixToObjectValue", + }, + "to_terraform_value": { + identifier: "to_terraform_value", + want: "PrefixToTerraformValue", + }, + "type": { + identifier: "type", + want: "PrefixType", + }, + } + for name, testCase := range testCases { + name, testCase := name, testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.identifier.ToPrefixPascalCase("prefix") + if got != testCase.want { + t.Fatalf("expected ToPascalCase() to return %s, got %s", testCase.want, got) + } + }) + } +} diff --git a/internal/schema/import.go b/internal/schema/import.go index 9692a47c..90682982 100644 --- a/internal/schema/import.go +++ b/internal/schema/import.go @@ -205,9 +205,6 @@ func GetAttrTypesImports(customType *specschema.CustomType, attrTypes specschema imports.Add(code.Import{ Path: TypesImport, }) - imports.Add(code.Import{ - Path: MathBigImport, - }) case v.Object != nil: imports.Add(GetAttrTypesImports(v.Object.CustomType, v.Object.AttributeTypes).All()...) case v.Set != nil: diff --git a/internal/schema/templates/nested_object_from.gotmpl b/internal/schema/templates/nested_object_from.gotmpl index 2a963248..2cc6209f 100644 --- a/internal/schema/templates/nested_object_from.gotmpl +++ b/internal/schema/templates/nested_object_from.gotmpl @@ -48,13 +48,13 @@ return New{{$.Name}}ValueUnknown(), diags return {{.Name}}Value{ {{- range $key, $value := .FromFuncs}} {{- if $value.AssocExtType}} -{{$key.ToPascalCase}}: {{$key.ToCamelCase}}Val, +{{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, {{- else if $value.Default}} -{{$key.ToPascalCase}}: types.{{$value.Default}}(apiObject.{{$key.ToPascalCase}}), +{{$key.ToPrefixPascalCase $.Name}}: types.{{$value.Default}}(apiObject.{{$key.ToPascalCase}}), {{- else if $value.CollectionType.ElementType}} -{{$key.ToPascalCase}}: {{$key.ToCamelCase}}Val, +{{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, {{- else if $value.ObjectType}} -{{$key.ToPascalCase}}: {{$key.ToCamelCase}}Val, +{{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, {{- end}} {{- end}} state: attr.ValueStateKnown, diff --git a/internal/schema/templates/nested_object_to.gotmpl b/internal/schema/templates/nested_object_to.gotmpl index 0fb78c4c..a34942ee 100644 --- a/internal/schema/templates/nested_object_to.gotmpl +++ b/internal/schema/templates/nested_object_to.gotmpl @@ -13,10 +13,11 @@ diags.Append(diag.NewErrorDiagnostic( return nil, diags } + {{- range $key, $value := .ToFuncs}} {{- if $value.AssocExtType}} -{{$value.AssocExtType.ToCamelCase}}, d := v.{{$key.ToPascalCase}}.To{{$value.AssocExtType.ToPascalCase}}(ctx) +{{$value.AssocExtType.ToCamelCase}}, d := v.{{$key.ToPrefixPascalCase $.Name}}.To{{$value.AssocExtType.ToPascalCase}}(ctx) diags.Append(d...) @@ -27,7 +28,7 @@ return nil, diags var {{$key.ToCamelCase}}Field {{$value.CollectionType.GoType}} -d := v.{{$key.ToPascalCase}}.ElementsAs(ctx, &{{$key.ToCamelCase}}Field, false) +d := v.{{$key.ToPrefixPascalCase $.Name}}.ElementsAs(ctx, &{{$key.ToCamelCase}}Field, false) diags.Append(d...) @@ -36,7 +37,7 @@ return nil, diags } {{- else if $value.ObjectType}} -attributes := v.{{$key.ToPascalCase}}.Attributes() +attributes := v.{{$key.ToPrefixPascalCase $.Name}}.Attributes() {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} @@ -44,8 +45,8 @@ attributes := v.{{$key.ToPascalCase}}.Attributes() if !ok { diags.Append(diag.NewErrorDiagnostic( -"{{$key.ToPascalCase}} Field {{$objectTypeKey}} Is Wrong Type", -fmt.Sprintf(`{{$key.ToPascalCase}} field {{$objectTypeKey}} expected to be {{$objectTypeVal.Type}}, was: %T`, attributes["bool"]), +"{{$key.ToPrefixPascalCase $.Name}} Field {{$objectTypeKey}} Is Wrong Type", +fmt.Sprintf(`{{$key.ToPrefixPascalCase $.Name}} field {{$objectTypeKey}} expected to be {{$objectTypeVal.Type}}, was: %T`, attributes["bool"]), )) return nil, diags @@ -59,7 +60,7 @@ return &{{.AssocExtType.TypeReference}}{ {{- if $value.AssocExtType}} {{$key.ToPascalCase}}: {{$value.AssocExtType.ToCamelCase}}, {{- else if $value.Default}} -{{$key.ToPascalCase}}: v.{{$key.ToPascalCase}}.{{$value.Default}}(), +{{$key.ToPascalCase}}: v.{{$key.ToPrefixPascalCase $.Name}}.{{$value.Default}}(), {{- else if $value.CollectionType.GoType}} {{$key.ToPascalCase}}: {{$key.ToCamelCase}}Field, {{- else if $value.ObjectType}} diff --git a/internal/schema/templates/nested_object_type_value.gotmpl b/internal/schema/templates/nested_object_type_value.gotmpl index 304f738a..70f444a7 100644 --- a/internal/schema/templates/nested_object_type_value.gotmpl +++ b/internal/schema/templates/nested_object_type_value.gotmpl @@ -76,7 +76,7 @@ return New{{.Name}}ValueUnknown(), diags return {{.Name}}Value{ {{- range $key, $value := .AttrValues }} -{{$key.ToPascalCase}}: {{$key.ToCamelCase}}Val, +{{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, {{- end}} state: attr.ValueStateKnown, }, diags diff --git a/internal/schema/templates/nested_object_type_value_from_object.gotmpl b/internal/schema/templates/nested_object_type_value_from_object.gotmpl index e727b396..3277be97 100644 --- a/internal/schema/templates/nested_object_type_value_from_object.gotmpl +++ b/internal/schema/templates/nested_object_type_value_from_object.gotmpl @@ -30,7 +30,7 @@ return nil, diags return {{.Name}}Value{ {{- range $key, $value := .AttrValues }} -{{$key.ToPascalCase}}: {{$key.ToCamelCase}}Val, +{{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, {{- end}} state: attr.ValueStateKnown, }, diags diff --git a/internal/schema/templates/nested_object_value_equal.gotmpl b/internal/schema/templates/nested_object_value_equal.gotmpl index c442ed2c..cdce8747 100644 --- a/internal/schema/templates/nested_object_value_equal.gotmpl +++ b/internal/schema/templates/nested_object_value_equal.gotmpl @@ -15,15 +15,9 @@ return true } {{range $key, $value := .AttrValues }} -{{- if eq $value "baseTypes.BoolValue" "baseTypes.Float64Value" "baseTypes.Int64Value" "baseTypes.NumberValue" "baseTypes.StringValue"}} -if v.{{$key.ToPascalCase}} != other.{{$key.ToPascalCase}} { +if !v.{{$key.ToPrefixPascalCase $.Name}}.Equal(other.{{$key.ToPrefixPascalCase $.Name}}) { return false } -{{- else}} -if !v.{{$key.ToPascalCase}}.Equal(other.{{$key.ToPascalCase}}) { -return false -} -{{- end}} {{end}} return true diff --git a/internal/schema/templates/nested_object_value_to_object_value.gotmpl b/internal/schema/templates/nested_object_value_to_object_value.gotmpl index e4c15ad7..8ec1e844 100644 --- a/internal/schema/templates/nested_object_value_to_object_value.gotmpl +++ b/internal/schema/templates/nested_object_value_to_object_value.gotmpl @@ -10,17 +10,17 @@ var diags diag.Diagnostics {{- $typesType := "Set"}} {{- end}} -{{$key.ToCamelCase}} := types.{{$typesType}}ValueMust( +{{$key.ToPrefixCamelCase $.Name}} := types.{{$typesType}}ValueMust( {{$key.ToPascalCase}}Type{ basetypes.ObjectType{ AttrTypes: {{$key.ToPascalCase}}Value{}.AttributeTypes(ctx), }, }, -v.{{$key.ToPascalCase}}.Elements(), +v.{{$key.ToPrefixPascalCase $.Name}}.Elements(), ) -if v.{{$key.ToPascalCase}}.IsNull() { -{{$key.ToCamelCase}} = types.{{$typesType}}Null( +if v.{{$key.ToPrefixPascalCase $.Name}}.IsNull() { +{{$key.ToPrefixCamelCase $.Name}} = types.{{$typesType}}Null( {{$key.ToPascalCase}}Type{ basetypes.ObjectType{ AttrTypes: {{$key.ToPascalCase}}Value{}.AttributeTypes(ctx), @@ -29,8 +29,8 @@ AttrTypes: {{$key.ToPascalCase}}Value{}.AttributeTypes(ctx), ) } -if v.{{$key.ToPascalCase}}.IsUnknown() { -{{$key.ToCamelCase}} = types.{{$typesType}}Unknown( +if v.{{$key.ToPrefixPascalCase $.Name}}.IsUnknown() { +{{$key.ToPrefixCamelCase $.Name}} = types.{{$typesType}}Unknown( {{$key.ToPascalCase}}Type{ basetypes.ObjectType{ AttrTypes: {{$key.ToPascalCase}}Value{}.AttributeTypes(ctx), @@ -64,7 +64,7 @@ v.{{$key.ToPascalCase}}.Attributes(), {{- end}} {{- range $key, $value := .CollectionTypes }} -{{$key.ToCamelCase}}Val, d := {{$value.TypeValueFunc}}({{$value.ElementType}}, v.{{$key.ToPascalCase}}.Elements()) +{{$key.ToCamelCase}}Val, d := {{$value.TypeValueFunc}}({{$value.ElementType}}, v.{{$key.ToPrefixPascalCase $.Name}}.Elements()) diags.Append(d...) @@ -80,7 +80,7 @@ return types.ObjectUnknown(map[string]attr.Type{ {{- range $key, $value := .AttributeTypes }} {{- if eq $value "Object"}} -{{$key.ToCamelCase}}Val, d := types.ObjectValue(v.{{$key.ToPascalCase}}.AttributeTypes(ctx), v.{{$key.ToPascalCase}}.Attributes()) +{{$key.ToCamelCase}}Val, d := types.ObjectValue(v.{{$key.ToPrefixPascalCase $.Name}}.AttributeTypes(ctx), v.{{$key.ToPrefixPascalCase $.Name}}.Attributes()) diags.Append(d...) @@ -89,7 +89,7 @@ return types.ObjectUnknown(map[string]attr.Type{ {{- range $attrTypeKey, $attrTypeValue := $.AttrTypes}} {{- if eq $value "Object"}} "{{$attrTypeKey}}": basetypes.ObjectType{ -AttrTypes: v.{{$key.ToPascalCase}}.AttributeTypes(ctx), +AttrTypes: v.{{$key.ToPrefixPascalCase $.Name}}.AttributeTypes(ctx), }, {{- else}} "{{$attrTypeKey}}": {{$attrTypeValue}}, @@ -105,7 +105,7 @@ map[string]attr.Type{ {{- range $key, $value := .AttributeTypes }} {{- if eq $value "Object"}} "{{$key}}": basetypes.ObjectType{ -AttrTypes: v.{{$key.ToPascalCase}}.AttributeTypes(ctx), +AttrTypes: v.{{$key.ToPrefixPascalCase $.Name}}.AttributeTypes(ctx), }, {{- else}} "{{$key}}": {{index $.AttrTypes $key}}, @@ -115,13 +115,13 @@ AttrTypes: v.{{$key.ToPascalCase}}.AttributeTypes(ctx), map[string]attr.Value{ {{- range $key, $value := .AttributeTypes }} {{- if eq $value "ListNested" "MapNested" "SetNested" "SingleNested"}} -"{{$key}}": {{$key.ToCamelCase}}, +"{{$key}}": {{$key.ToPrefixCamelCase $.Name}}, {{- else if index $.CollectionTypes $key}} "{{$key}}": {{$key.ToCamelCase}}Val, {{- else if eq $value "Object"}} "{{$key}}": {{$key.ToCamelCase}}Val, {{- else}} -"{{$key}}": v.{{$key.ToPascalCase}}, +"{{$key}}": v.{{$key.ToPrefixPascalCase $.Name}}, {{- end}} {{- end}} }) diff --git a/internal/schema/templates/nested_object_value_to_terraform_value.gotmpl b/internal/schema/templates/nested_object_value_to_terraform_value.gotmpl index 7d13d5c1..f10a2161 100644 --- a/internal/schema/templates/nested_object_value_to_terraform_value.gotmpl +++ b/internal/schema/templates/nested_object_value_to_terraform_value.gotmpl @@ -15,7 +15,7 @@ case attr.ValueStateKnown: vals := make(map[string]tftypes.Value, {{len .AttrTypes}}) {{range $key, $value := .AttrTypes }} -val, err = v.{{$key.ToPascalCase}}.ToTerraformValue(ctx) +val, err = v.{{$key.ToPrefixPascalCase $.Name}}.ToTerraformValue(ctx) if err != nil { return tftypes.NewValue(objectType, tftypes.UnknownValue), err diff --git a/internal/schema/templates/nested_object_value_value.gotmpl b/internal/schema/templates/nested_object_value_value.gotmpl index 12b74387..65b49166 100644 --- a/internal/schema/templates/nested_object_value_value.gotmpl +++ b/internal/schema/templates/nested_object_value_value.gotmpl @@ -1,6 +1,6 @@ type {{.Name}}Value struct { {{- range $key, $value := .AttrValues }} -{{$key.ToPascalCase}} {{$value}} `tfsdk:"{{$key}}"` +{{$key.ToPrefixPascalCase $.Name}} {{$value}} `tfsdk:"{{$key}}"` {{- end}} state attr.ValueState } \ No newline at end of file diff --git a/internal/schema/to_from_nested_object_test.go b/internal/schema/to_from_nested_object_test.go index 34cd1e59..592e816d 100644 --- a/internal/schema/to_from_nested_object_test.go +++ b/internal/schema/to_from_nested_object_test.go @@ -46,6 +46,33 @@ BoolAttribute: types.BoolPointerValue(apiObject.BoolAttribute), state: attr.ValueStateKnown, }, diags } +`), + }, + "default-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*apisdk.Type", + }, + }, + fromFuncs: map[string]ToFromConversion{ + "type": { + Default: "BoolPointerValue", + }, + }, + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return NewExampleValueNull(), diags +} + +return ExampleValue{ +ExampleType: types.BoolPointerValue(apiObject.Type), +state: attr.ValueStateKnown, +}, diags +} `), }, "nested-assoc-ext-type": { @@ -85,6 +112,45 @@ BoolAttribute: boolAttributeVal, state: attr.ValueStateKnown, }, diags } +`), + }, + "nested-assoc-ext-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*apisdk.Type", + }, + }, + fromFuncs: map[string]ToFromConversion{ + "type": { + AssocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + }, + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return NewExampleValueNull(), diags +} + +typeVal, d := TypeValue{}.FromApiBoolAttribute(ctx, apiObject.Type) + +diags.Append(d...) + +if diags.HasError() { +return NewExampleValueUnknown(), diags +} + +return ExampleValue{ +ExampleType: typeVal, +state: attr.ValueStateKnown, +}, diags +} `), }, "collection-type": { @@ -123,6 +189,44 @@ BoolAttribute: boolAttributeVal, state: attr.ValueStateKnown, }, diags } +`), + }, + "collection-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*apisdk.Type", + }, + }, + fromFuncs: map[string]ToFromConversion{ + "type": { + CollectionType: CollectionFields{ + ElementType: "types.BoolType", + TypeValueFrom: "types.ListValueFrom", + }, + }, + }, + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return NewExampleValueNull(), diags +} + +typeVal, d := types.ListValueFrom(ctx, types.BoolType, apiObject.Type) + +diags.Append(d...) + +if diags.HasError() { +return NewExampleValueUnknown(), diags +} + +return ExampleValue{ +ExampleType: typeVal, +state: attr.ValueStateKnown, +}, diags +} `), }, "object-type": { @@ -192,6 +296,75 @@ ObjectAttribute: objectAttributeVal, state: attr.ValueStateKnown, }, diags } +`), + }, + "object-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*apisdk.Type", + }, + }, + fromFuncs: map[string]ToFromConversion{ + "type": { + ObjectType: map[FrameworkIdentifier]ObjectField{ + FrameworkIdentifier("bool"): { + Type: "types.BoolType", + FromFunc: "BoolPointerValue", + }, + FrameworkIdentifier("float64"): { + Type: "types.Float64Type", + FromFunc: "Float64PointerValue", + }, + FrameworkIdentifier("int64"): { + Type: "types.Int64Type", + FromFunc: "Int64PointerValue", + }, + FrameworkIdentifier("number"): { + Type: "types.NumberType", + FromFunc: "NumberValue", + }, + FrameworkIdentifier("string"): { + Type: "types.StringType", + FromFunc: "StringPointerValue", + }, + }, + }, + }, + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return NewExampleValueNull(), diags +} + +typeVal, d := basetypes.NewObjectValue( +map[string]attr.Type{ +"bool": types.BoolType, +"float64": types.Float64Type, +"int64": types.Int64Type, +"number": types.NumberType, +"string": types.StringType, +}, map[string]attr.Value{ +"bool": types.BoolPointerValue(apiObject.Type.Bool), +"float64": types.Float64PointerValue(apiObject.Type.Float64), +"int64": types.Int64PointerValue(apiObject.Type.Int64), +"number": types.NumberValue(apiObject.Type.Number), +"string": types.StringPointerValue(apiObject.Type.String), +}) + +diags.Append(d...) + +if diags.HasError() { +return NewExampleValueUnknown(), diags +} + +return ExampleValue{ +ExampleType: typeVal, +state: attr.ValueStateKnown, +}, diags +} `), }, } @@ -261,6 +434,42 @@ return nil, diags return &apisdk.Type{ BoolAttribute: v.BoolAttribute.ValueBoolPointer(), }, diags +}`), + }, + "default-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + toFuncs: map[string]ToFromConversion{ + "type": { + Default: "ValueBoolPointer", + }, + }, + expected: []byte(`func (v ExampleValue) ToApisdkType(ctx context.Context) (*apisdk.Type, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue Value Is Unknown", +` + "`" + `"ExampleValue" is unknown.` + "`" + `, +)) + +return nil, diags +} + +return &apisdk.Type{ +Type: v.ExampleType.ValueBoolPointer(), +}, diags }`), }, "nested-assoc-ext-type": { @@ -309,6 +518,54 @@ return nil, diags return &apisdk.Type{ BoolAttribute: apiBoolAttribute, }, diags +}`), + }, + "nested-assoc-ext-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + toFuncs: map[string]ToFromConversion{ + "type": { + AssocExtType: &AssocExtType{ + AssociatedExternalType: &schema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + }, + expected: []byte(`func (v ExampleValue) ToApisdkType(ctx context.Context) (*apisdk.Type, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue Value Is Unknown", +` + "`" + `"ExampleValue" is unknown.` + "`" + `, +)) + +return nil, diags +} + +apiBoolAttribute, d := v.ExampleType.ToApiBoolAttribute(ctx) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdk.Type{ +Type: apiBoolAttribute, +}, diags }`), }, "collection-type": { @@ -357,6 +614,54 @@ return nil, diags return &apisdk.Type{ BoolAttribute: boolAttributeField, }, diags +}`), + }, + "collection-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + toFuncs: map[string]ToFromConversion{ + "type": { + CollectionType: CollectionFields{ + GoType: "[]*bool", + }, + }, + }, + expected: []byte(`func (v ExampleValue) ToApisdkType(ctx context.Context) (*apisdk.Type, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue Value Is Unknown", +` + "`" + `"ExampleValue" is unknown.` + "`" + `, +)) + +return nil, diags +} + +var typeField []*bool + +d := v.ExampleType.ElementsAs(ctx, &typeField, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdk.Type{ +Type: typeField, +}, diags }`), }, "object-type": { @@ -488,6 +793,137 @@ Number: objectAttributeFieldNumber.ValueBigFloat(), String: objectAttributeFieldString.ValueStringPointer(), }, }, diags +}`), + }, + "object-type-attribute-name-same-as-generated-method-name": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + toFuncs: map[string]ToFromConversion{ + "type": { + ObjectType: map[FrameworkIdentifier]ObjectField{ + FrameworkIdentifier("bool"): { + GoType: "*bool", + Type: "types.Bool", + ToFunc: "ValueBoolPointer", + }, + FrameworkIdentifier("float64"): { + GoType: "*float64", + Type: "types.Float64", + ToFunc: "ValueFloat64Pointer", + }, + FrameworkIdentifier("int64"): { + GoType: "*int64", + Type: "types.Int64", + ToFunc: "ValueInt64Pointer", + }, + FrameworkIdentifier("number"): { + GoType: "*big.Float", + Type: "types.Number", + ToFunc: "ValueBigFloat", + }, + FrameworkIdentifier("string"): { + GoType: "*string", + Type: "types.String", + ToFunc: "ValueStringPointer", + }, + }, + }, + }, + expected: []byte(`func (v ExampleValue) ToApisdkType(ctx context.Context) (*apisdk.Type, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue Value Is Unknown", +` + "`" + `"ExampleValue" is unknown.` + "`" + `, +)) + +return nil, diags +} + +attributes := v.ExampleType.Attributes() + +typeFieldBool, ok := attributes["bool"].(types.Bool) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleType Field bool Is Wrong Type", +fmt.Sprintf(` + "`" + `ExampleType field bool expected to be types.Bool, was: %T` + "`" + `, attributes["bool"]), +)) + +return nil, diags +} + +typeFieldFloat64, ok := attributes["float64"].(types.Float64) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleType Field float64 Is Wrong Type", +fmt.Sprintf(` + "`" + `ExampleType field float64 expected to be types.Float64, was: %T` + "`" + `, attributes["bool"]), +)) + +return nil, diags +} + +typeFieldInt64, ok := attributes["int64"].(types.Int64) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleType Field int64 Is Wrong Type", +fmt.Sprintf(` + "`" + `ExampleType field int64 expected to be types.Int64, was: %T` + "`" + `, attributes["bool"]), +)) + +return nil, diags +} + +typeFieldNumber, ok := attributes["number"].(types.Number) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleType Field number Is Wrong Type", +fmt.Sprintf(` + "`" + `ExampleType field number expected to be types.Number, was: %T` + "`" + `, attributes["bool"]), +)) + +return nil, diags +} + +typeFieldString, ok := attributes["string"].(types.String) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleType Field string Is Wrong Type", +fmt.Sprintf(` + "`" + `ExampleType field string expected to be types.String, was: %T` + "`" + `, attributes["bool"]), +)) + +return nil, diags +} + +return &apisdk.Type{ +Type: struct { +Bool *bool +Float64 *float64 +Int64 *int64 +Number *big.Float +String *string +}{ +Bool: typeFieldBool.ValueBoolPointer(), +Float64: typeFieldFloat64.ValueFloat64Pointer(), +Int64: typeFieldInt64.ValueInt64Pointer(), +Number: typeFieldNumber.ValueBigFloat(), +String: typeFieldString.ValueStringPointer(), +}, +}, diags }`), }, }