From 9e4eea2f7169901286a2f2b312f819514b81dad1 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 09:24:17 +0100 Subject: [PATCH 01/13] Adding generation of custom type and value type with to/from methods for data source list attribute (#74) --- go.mod | 2 +- go.sum | 4 +- internal/datasource_convert/list_attribute.go | 8 +- internal/datasource_generate/embed.go | 2 +- .../datasource_generate/list_attribute.go | 78 ++- .../list_attribute_test.go | 1 - .../templates/list_attribute.gotmpl | 4 + internal/schema/custom_list.go | 349 +++++++++++++ internal/schema/custom_list_test.go | 467 ++++++++++++++++++ internal/schema/elements.go | 74 +++ internal/schema/embed.go | 45 ++ internal/schema/templates/list_from.gotmpl | 30 ++ internal/schema/templates/list_to.gotmpl | 28 ++ .../schema/templates/list_type_equal.gotmpl | 9 + .../schema/templates/list_type_string.gotmpl | 4 + .../schema/templates/list_type_typable.gotmpl | 1 + .../schema/templates/list_type_type.gotmpl | 3 + .../list_type_value_from_list.gotmpl | 6 + .../list_type_value_from_terraform.gotmpl | 22 + .../templates/list_type_value_type.gotmpl | 4 + .../schema/templates/list_value_equal.gotmpl | 10 + .../schema/templates/list_value_type.gotmpl | 8 + .../templates/list_value_valuable.gotmpl | 1 + .../schema/templates/list_value_value.gotmpl | 3 + internal/schema/to_from_list.go | 111 +++++ internal/schema/to_from_list_test.go | 169 +++++++ 26 files changed, 1432 insertions(+), 11 deletions(-) create mode 100644 internal/schema/custom_list.go create mode 100644 internal/schema/custom_list_test.go create mode 100644 internal/schema/templates/list_from.gotmpl create mode 100644 internal/schema/templates/list_to.gotmpl create mode 100644 internal/schema/templates/list_type_equal.gotmpl create mode 100644 internal/schema/templates/list_type_string.gotmpl create mode 100644 internal/schema/templates/list_type_typable.gotmpl create mode 100644 internal/schema/templates/list_type_type.gotmpl create mode 100644 internal/schema/templates/list_type_value_from_list.gotmpl create mode 100644 internal/schema/templates/list_type_value_from_terraform.gotmpl create mode 100644 internal/schema/templates/list_type_value_type.gotmpl create mode 100644 internal/schema/templates/list_value_equal.gotmpl create mode 100644 internal/schema/templates/list_value_type.gotmpl create mode 100644 internal/schema/templates/list_value_valuable.gotmpl create mode 100644 internal/schema/templates/list_value_value.gotmpl create mode 100644 internal/schema/to_from_list.go create mode 100644 internal/schema/to_from_list_test.go diff --git a/go.mod b/go.mod index c2d70b7b..b6f40153 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231019064449-867ccf6fb279 + github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca github.com/hashicorp/terraform-plugin-framework v1.4.0 github.com/mattn/go-colorable v0.1.12 github.com/mitchellh/cli v1.1.5 diff --git a/go.sum b/go.sum index 6494d33a..9641b81f 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231019064449-867ccf6fb279 h1:9D8ydY5s8Fw76pqCFtwlm9X7wVJdFLMK2FAqyQoOZT0= -github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231019064449-867ccf6fb279/go.mod h1:PQn6bDD8UWoAVJoHXqFk2i/RmLbeQBjbiP38i+E+YIw= +github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca h1:HOURkk+e71xtn3nEizi8rOd7aWawoGQ/dZ4kh+ko82M= +github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca/go.mod h1:PQn6bDD8UWoAVJoHXqFk2i/RmLbeQBjbiP38i+E+YIw= github.com/hashicorp/terraform-plugin-framework v1.4.0 h1:WKbtCRtNrjsh10eA7NZvC/Qyr7zp77j+D21aDO5th9c= github.com/hashicorp/terraform-plugin-framework v1.4.0/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0= github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= diff --git a/internal/datasource_convert/list_attribute.go b/internal/datasource_convert/list_attribute.go index 2971e541..01ad71c8 100644 --- a/internal/datasource_convert/list_attribute.go +++ b/internal/datasource_convert/list_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/datasource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertListAttribute(a *datasource.ListAttribute) (datasource_generate.GeneratorListAttribute, error) { @@ -28,8 +29,9 @@ func convertListAttribute(a *datasource.ListAttribute) (datasource_generate.Gene DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/datasource_generate/embed.go b/internal/datasource_generate/embed.go index c4ee8e00..b957b1e8 100644 --- a/internal/datasource_generate/embed.go +++ b/internal/datasource_generate/embed.go @@ -19,7 +19,7 @@ var float64AttributeTemplate string var int64AttributeTemplate string //go:embed templates/list_attribute.gotmpl -var listAttributeGoTemplate string +var listAttributeTemplate string //go:embed templates/list_nested_attribute.gotmpl var listNestedAttributeGoTemplate string diff --git a/internal/datasource_generate/list_attribute.go b/internal/datasource_generate/list_attribute.go index 506af985..ea0b29d7 100644 --- a/internal/datasource_generate/list_attribute.go +++ b/internal/datasource_generate/list_attribute.go @@ -4,6 +4,8 @@ package datasource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorListAttribute struct { schema.ListAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorListAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -103,6 +112,7 @@ func (g GeneratorListAttribute) Equal(ga generatorschema.GeneratorAttribute) boo func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorListAttribute GeneratorListAttribute } @@ -113,12 +123,19 @@ func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorListAttribute: g, } - t, err := template.New("list_attribute").Parse(listAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ListType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("list_attribute").Parse(listAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -139,9 +156,64 @@ func (g GeneratorListAttribute) ModelField(name generatorschema.FrameworkIdentif ValueType: model.ListValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorListAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomListType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomListValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorListAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromList(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/datasource_generate/list_attribute_test.go b/internal/datasource_generate/list_attribute_test.go index db4dddc1..006b4397 100644 --- a/internal/datasource_generate/list_attribute_test.go +++ b/internal/datasource_generate/list_attribute_test.go @@ -601,7 +601,6 @@ ElementType: types.StringType, }, expected: ` "list_attribute": schema.ListAttribute{ -ElementType: types.StringType, CustomType: my_custom_type, },`, }, diff --git a/internal/datasource_generate/templates/list_attribute.gotmpl b/internal/datasource_generate/templates/list_attribute.gotmpl index 383d12e3..78c2a9e1 100644 --- a/internal/datasource_generate/templates/list_attribute.gotmpl +++ b/internal/datasource_generate/templates/list_attribute.gotmpl @@ -1,6 +1,10 @@ "{{.Name}}": schema.ListAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, +{{- end}} {{- template "common_attribute" .GeneratorListAttribute }} {{- if gt (len .GeneratorListAttribute.Validators) 0 }} Validators: []validator.List{ diff --git a/internal/schema/custom_list.go b/internal/schema/custom_list.go new file mode 100644 index 00000000..90d7c687 --- /dev/null +++ b/internal/schema/custom_list.go @@ -0,0 +1,349 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type CustomListType struct { + Name FrameworkIdentifier + templates map[string]string +} + +func NewCustomListType(name string) CustomListType { + t := map[string]string{ + "equal": ListTypeEqualTemplate, + "string": ListTypeStringTemplate, + "type": ListTypeTypeTemplate, + "typable": ListTypeTypableTemplate, + "valueFromList": ListTypeValueFromListTemplate, + "valueFromTerraform": ListTypeValueFromTerraformTemplate, + "valueType": ListTypeValueTypeTemplate, + } + + return CustomListType{ + Name: FrameworkIdentifier(name), + templates: t, + } +} + +func (c CustomListType) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderTypable, + c.renderType, + c.renderEqual, + c.renderString, + c.renderValueFromList, + c.renderValueFromTerraform, + c.renderValueType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderString() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["string"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderTypable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["typable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderValueFromList() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromList"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderValueFromTerraform() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromTerraform"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListType) renderValueType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueType"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type CustomListValue struct { + Name FrameworkIdentifier + ElementType string + templates map[string]string +} + +func NewCustomListValue(name, elemType string) CustomListValue { + t := map[string]string{ + "equal": ListValueEqualTemplate, + "type": ListValueTypeTemplate, + "valuable": ListValueValuableTemplate, + "value": ListValueValueTemplate, + } + + return CustomListValue{ + Name: FrameworkIdentifier(name), + ElementType: elemType, + templates: t, + } +} + +func (c CustomListValue) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderValuable, + c.renderValue, + c.renderEqual, + c.renderType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomListValue) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListValue) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + ElementType string + }{ + Name: c.Name.ToPascalCase(), + ElementType: c.ElementType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListValue) renderValuable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valuable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomListValue) renderValue() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["value"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/custom_list_test.go b/internal/schema/custom_list_test.go new file mode 100644 index 00000000..d5cf7401 --- /dev/null +++ b/internal/schema/custom_list_test.go @@ -0,0 +1,467 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCustomListType_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`func (t ExampleType) Equal(o attr.Type) bool { +other, ok := o.(ExampleType) + +if !ok { +return false +} + +return t.ListType.Equal(other.ListType) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderString(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) String() string { +return "ExampleType" +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderString() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderTypable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`var _ basetypes.ListTypable = ExampleType{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderTypable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`type ExampleType struct { +basetypes.ListType +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderValueFromList(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + attrValues map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + attrValues: map[string]string{ + "bool_attribute": "basetypes.ListValue", + }, + expected: []byte(` +func (t ExampleType) ValueFromList(ctx context.Context, in basetypes.ListValue) (basetypes.ListValuable, diag.Diagnostics) { +return ExampleValue{ +ListValue: in, +}, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderValueFromList() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderValueFromTerraform(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.ListType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +listValue, ok := attrValue.(basetypes.ListValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +listValuable, diags := t.ValueFromList(ctx, listValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting ListValue to ListValuable: %v", diags) +} + +return listValuable, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderValueFromTerraform() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListType_renderValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueType(ctx context.Context) attr.Value { +return ExampleValue{} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListType := NewCustomListType(testCase.name) + + got, err := customListType.renderValueType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListValue_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Equal(o attr.Value) bool { +other, ok := o.(ExampleValue) + +if !ok { +return false +} + +return v.ListValue.Equal(other.ListValue) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListValue := NewCustomListValue(testCase.name, testCase.elementType) + + got, err := customListValue.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListValue_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Type(ctx context.Context) attr.Type { +return ExampleType{ +ListType: basetypes.ListType{ +ElemType: types.BoolType, +}, +} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListValue := NewCustomListValue(testCase.name, testCase.elementType) + + got, err := customListValue.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListValue_renderValuable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`var _ basetypes.ListValuable = ExampleValue{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListValue := NewCustomListValue(testCase.name, testCase.elementType) + + got, err := customListValue.renderValuable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomListValue_renderValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`type ExampleValue struct { +basetypes.ListValue +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customListValue := NewCustomListValue(testCase.name, testCase.elementType) + + got, err := customListValue.renderValue() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/schema/elements.go b/internal/schema/elements.go index 7d978f73..93e2c70d 100644 --- a/internal/schema/elements.go +++ b/internal/schema/elements.go @@ -62,3 +62,77 @@ func GetElementType(e specschema.ElementType) string { return "" } + +// GetElementValueType generates the strings for use within templates for specifying the value types +// to use with collection (i.e., list, map and set) element types. +func GetElementValueType(e specschema.ElementType) string { + switch { + case e.Bool != nil: + if e.Bool.CustomType != nil { + return e.Bool.CustomType.ValueType + } + return "types.Bool" + case e.Float64 != nil: + if e.Float64.CustomType != nil { + return e.Float64.CustomType.ValueType + } + return "types.Float64" + case e.Int64 != nil: + if e.Int64.CustomType != nil { + return e.Int64.CustomType.ValueType + } + return "types.Int64" + case e.List != nil: + if e.List.CustomType != nil { + return e.List.CustomType.ValueType + } + return "types.ListType" + case e.Map != nil: + if e.Map.CustomType != nil { + return e.Map.CustomType.ValueType + } + return "types.MapType" + case e.Number != nil: + if e.Number.CustomType != nil { + return e.Number.CustomType.ValueType + } + return "types.Number" + case e.Object != nil: + if e.Object.CustomType != nil { + return e.Object.CustomType.ValueType + } + return "types.ObjectType" + case e.Set != nil: + if e.Set.CustomType != nil { + return e.Set.CustomType.ValueType + } + return "types.SetType" + case e.String != nil: + if e.String.CustomType != nil { + return e.String.CustomType.ValueType + } + return "types.String" + } + + return "" +} + +// GetElementFromFunc returns a string representation of the function that is used +// for converting from an API Go type to a framework type. +// TODO: Handle custom type, and types other than primitives. +func GetElementFromFunc(e specschema.ElementType) string { + switch { + case e.Bool != nil: + return "types.BoolPointerValue" + case e.Float64 != nil: + return "types.Float64PointerValue" + case e.Int64 != nil: + return "types.Int64PointerValue" + case e.Number != nil: + return "types.NumberValue" + case e.String != nil: + return "types.StringPointerValue" + } + + return "" +} diff --git a/internal/schema/embed.go b/internal/schema/embed.go index a96bcf6c..b03d2fd9 100644 --- a/internal/schema/embed.go +++ b/internal/schema/embed.go @@ -142,6 +142,51 @@ var Int64ValueValueTemplate string //go:embed templates/int64_value_valuable.gotmpl var Int64ValueValuableTemplate string +// List From/To + +//go:embed templates/list_from.gotmpl +var ListFromTemplate string + +//go:embed templates/list_to.gotmpl +var ListToTemplate string + +// List Type + +//go:embed templates/list_type_equal.gotmpl +var ListTypeEqualTemplate string + +//go:embed templates/list_type_string.gotmpl +var ListTypeStringTemplate string + +//go:embed templates/list_type_type.gotmpl +var ListTypeTypeTemplate string + +//go:embed templates/list_type_typable.gotmpl +var ListTypeTypableTemplate string + +//go:embed templates/list_type_value_from_list.gotmpl +var ListTypeValueFromListTemplate string + +//go:embed templates/list_type_value_from_terraform.gotmpl +var ListTypeValueFromTerraformTemplate string + +//go:embed templates/list_type_value_type.gotmpl +var ListTypeValueTypeTemplate string + +// List Value + +//go:embed templates/list_value_equal.gotmpl +var ListValueEqualTemplate string + +//go:embed templates/list_value_type.gotmpl +var ListValueTypeTemplate string + +//go:embed templates/list_value_value.gotmpl +var ListValueValueTemplate string + +//go:embed templates/list_value_valuable.gotmpl +var ListValueValuableTemplate string + // Number From/To //go:embed templates/number_from.gotmpl diff --git a/internal/schema/templates/list_from.gotmpl b/internal/schema/templates/list_from.gotmpl new file mode 100644 index 00000000..1dc5133a --- /dev/null +++ b/internal/schema/templates/list_from.gotmpl @@ -0,0 +1,30 @@ + +func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return {{.Name}}Value{ +types.ListNull({{.ElementTypeType}}), +}, diags +} + + var elems []{{.ElementTypeValue}} + +for _, e := range *apiObject { +elems = append(elems, {{.ElementFrom}}(e)) +} + +l, d := basetypes.NewListValueFrom(ctx, {{.ElementTypeType}}, elems) + +diags.Append(d...) + +if diags.HasError() { +return ListAttributeValue{ +types.ListNull({{.ElementTypeType}}), +}, diags +} + +return ListAttributeValue{ +l, +}, diags +} diff --git a/internal/schema/templates/list_to.gotmpl b/internal/schema/templates/list_to.gotmpl new file mode 100644 index 00000000..4be7e418 --- /dev/null +++ b/internal/schema/templates/list_to.gotmpl @@ -0,0 +1,28 @@ +func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"{{.Name}}Value Value Is Unknown", +`"{{.Name}}Value" is unknown.`, +)) + +return nil, diags +} + +var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} + +d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &{{.AssocExtType.ToCamelCase}}, diags +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_equal.gotmpl b/internal/schema/templates/list_type_equal.gotmpl new file mode 100644 index 00000000..92240326 --- /dev/null +++ b/internal/schema/templates/list_type_equal.gotmpl @@ -0,0 +1,9 @@ +func (t {{.Name}}Type) Equal(o attr.Type) bool { +other, ok := o.({{.Name}}Type) + +if !ok { +return false +} + +return t.ListType.Equal(other.ListType) +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_string.gotmpl b/internal/schema/templates/list_type_string.gotmpl new file mode 100644 index 00000000..f01b8ac3 --- /dev/null +++ b/internal/schema/templates/list_type_string.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) String() string { +return "{{.Name}}Type" +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_typable.gotmpl b/internal/schema/templates/list_type_typable.gotmpl new file mode 100644 index 00000000..5d663dfb --- /dev/null +++ b/internal/schema/templates/list_type_typable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.ListTypable = {{.Name}}Type{} \ No newline at end of file diff --git a/internal/schema/templates/list_type_type.gotmpl b/internal/schema/templates/list_type_type.gotmpl new file mode 100644 index 00000000..c7639f50 --- /dev/null +++ b/internal/schema/templates/list_type_type.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Type struct { +basetypes.ListType +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_value_from_list.gotmpl b/internal/schema/templates/list_type_value_from_list.gotmpl new file mode 100644 index 00000000..dabfb55f --- /dev/null +++ b/internal/schema/templates/list_type_value_from_list.gotmpl @@ -0,0 +1,6 @@ + +func (t {{.Name}}Type) ValueFromList(ctx context.Context, in basetypes.ListValue) (basetypes.ListValuable, diag.Diagnostics) { +return {{.Name}}Value{ +ListValue: in, +}, nil +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_value_from_terraform.gotmpl b/internal/schema/templates/list_type_value_from_terraform.gotmpl new file mode 100644 index 00000000..9fb8efd3 --- /dev/null +++ b/internal/schema/templates/list_type_value_from_terraform.gotmpl @@ -0,0 +1,22 @@ + +func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.ListType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +listValue, ok := attrValue.(basetypes.ListValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +listValuable, diags := t.ValueFromList(ctx, listValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting ListValue to ListValuable: %v", diags) +} + +return listValuable, nil +} \ No newline at end of file diff --git a/internal/schema/templates/list_type_value_type.gotmpl b/internal/schema/templates/list_type_value_type.gotmpl new file mode 100644 index 00000000..1f7b7fea --- /dev/null +++ b/internal/schema/templates/list_type_value_type.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { +return {{.Name}}Value{} +} \ No newline at end of file diff --git a/internal/schema/templates/list_value_equal.gotmpl b/internal/schema/templates/list_value_equal.gotmpl new file mode 100644 index 00000000..b5047aa3 --- /dev/null +++ b/internal/schema/templates/list_value_equal.gotmpl @@ -0,0 +1,10 @@ + +func (v {{.Name}}Value) Equal(o attr.Value) bool { +other, ok := o.({{.Name}}Value) + +if !ok { +return false +} + +return v.ListValue.Equal(other.ListValue) +} \ No newline at end of file diff --git a/internal/schema/templates/list_value_type.gotmpl b/internal/schema/templates/list_value_type.gotmpl new file mode 100644 index 00000000..135463af --- /dev/null +++ b/internal/schema/templates/list_value_type.gotmpl @@ -0,0 +1,8 @@ + +func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { +return {{.Name}}Type{ +ListType: basetypes.ListType{ +ElemType: {{.ElementType}}, +}, +} +} \ No newline at end of file diff --git a/internal/schema/templates/list_value_valuable.gotmpl b/internal/schema/templates/list_value_valuable.gotmpl new file mode 100644 index 00000000..289dd3e3 --- /dev/null +++ b/internal/schema/templates/list_value_valuable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.ListValuable = {{.Name}}Value{} \ No newline at end of file diff --git a/internal/schema/templates/list_value_value.gotmpl b/internal/schema/templates/list_value_value.gotmpl new file mode 100644 index 00000000..a175d1ad --- /dev/null +++ b/internal/schema/templates/list_value_value.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Value struct { +basetypes.ListValue +} \ No newline at end of file diff --git a/internal/schema/to_from_list.go b/internal/schema/to_from_list.go new file mode 100644 index 00000000..ebd6ebc5 --- /dev/null +++ b/internal/schema/to_from_list.go @@ -0,0 +1,111 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type ToFromList struct { + Name FrameworkIdentifier + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + templates map[string]string +} + +func NewToFromList(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromList { + t := map[string]string{ + "from": ListFromTemplate, + "to": ListToTemplate, + } + + return ToFromList{ + Name: FrameworkIdentifier(name), + AssocExtType: assocExtType, + ElementTypeType: elemTypeType, + ElementTypeValue: elemTypeValue, + ElementFrom: elemFrom, + templates: t, + } +} + +func (o ToFromList) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + o.renderTo, + o.renderFrom, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (o ToFromList) renderTo() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["to"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (o ToFromList) renderFrom() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["from"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + ElementTypeType: o.ElementTypeType, + ElementTypeValue: o.ElementTypeValue, + ElementFrom: o.ElementFrom, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/to_from_list_test.go b/internal/schema/to_from_list_test.go new file mode 100644 index 00000000..075c6c5e --- /dev/null +++ b/internal/schema/to_from_list_test.go @@ -0,0 +1,169 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" + "github.com/hashicorp/terraform-plugin-codegen-spec/schema" +) + +func TestToFromList_renderFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return ExampleValue{ +types.ListNull(types.BoolType), +}, diags +} + + var elems []types.Bool + +for _, e := range *apiObject { +elems = append(elems, types.BoolPointerValue(e)) +} + +l, d := basetypes.NewListValueFrom(ctx, types.BoolType, elems) + +diags.Append(d...) + +if diags.HasError() { +return ListAttributeValue{ +types.ListNull(types.BoolType), +}, diags +} + +return ListAttributeValue{ +l, +}, diags +} +`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromList := NewToFromList(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromList.renderFrom() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestToFromList_renderTo(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + 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 apisdkType apisdk.Type + +d := v.ElementsAs(ctx, &apisdkType, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdkType, diags +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromList := NewToFromList(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromList.renderTo() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} From 08826d9a900003dac98e791a1d82e31692b3cf21 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 09:41:09 +0100 Subject: [PATCH 02/13] Adding tests for list attribute imports, schema, and model field (#74) --- .../list_attribute_test.go | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/internal/datasource_generate/list_attribute_test.go b/internal/datasource_generate/list_attribute_test.go index 006b4397..40e81317 100644 --- a/internal/datasource_generate/list_attribute_test.go +++ b/internal/datasource_generate/list_attribute_test.go @@ -313,6 +313,110 @@ func TestGeneratorListAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -605,6 +709,44 @@ CustomType: my_custom_type, },`, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ +CustomType: ListAttributeType{ +types.ListType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ +CustomType: my_custom_type, +},`, + }, + "required": { input: GeneratorListAttribute{ ListAttribute: schema.ListAttribute{ @@ -961,6 +1103,37 @@ func TestGeneratorListAttribute_ModelField(t *testing.T) { TfsdkName: "list_attribute", }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "ListAttributeValue", + TfsdkName: "list_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "list_attribute", + }, + }, } for name, testCase := range testCases { From b78f02cb598f3b6d2b2b6222e14abeadd71d7a8e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 10:45:49 +0100 Subject: [PATCH 03/13] Adding generation of custom type and value type, and to/from methods for set attribute for data source, and list, and set attribute for provider and resource (#74) --- internal/datasource_convert/set_attribute.go | 8 +- internal/datasource_generate/embed.go | 2 +- internal/datasource_generate/set_attribute.go | 78 ++- .../datasource_generate/set_attribute_test.go | 71 ++- .../templates/set_attribute.gotmpl | 4 + internal/provider_convert/list_attribute.go | 8 +- internal/provider_convert/set_attribute.go | 9 +- internal/provider_generate/embed.go | 4 +- internal/provider_generate/list_attribute.go | 78 ++- .../provider_generate/list_attribute_test.go | 174 ++++++- internal/provider_generate/set_attribute.go | 78 ++- .../provider_generate/set_attribute_test.go | 71 ++- .../templates/list_attribute.gotmpl | 5 +- .../templates/set_attribute.gotmpl | 5 +- internal/resource_convert/list_attribute.go | 13 +- internal/resource_convert/set_attribute.go | 13 +- internal/resource_generate/embed.go | 4 +- internal/resource_generate/list_attribute.go | 78 ++- .../resource_generate/list_attribute_test.go | 174 ++++++- internal/resource_generate/set_attribute.go | 78 ++- .../resource_generate/set_attribute_test.go | 71 ++- .../templates/list_attribute.gotmpl | 5 +- .../templates/set_attribute.gotmpl | 5 +- internal/schema/custom_set.go | 349 +++++++++++++ internal/schema/custom_set_test.go | 467 ++++++++++++++++++ internal/schema/embed.go | 45 ++ internal/schema/templates/set_from.gotmpl | 30 ++ internal/schema/templates/set_to.gotmpl | 28 ++ .../schema/templates/set_type_equal.gotmpl | 9 + .../schema/templates/set_type_string.gotmpl | 4 + .../schema/templates/set_type_typable.gotmpl | 1 + .../schema/templates/set_type_type.gotmpl | 3 + .../templates/set_type_value_from_set.gotmpl | 6 + .../set_type_value_from_terraform.gotmpl | 22 + .../templates/set_type_value_type.gotmpl | 4 + .../schema/templates/set_value_equal.gotmpl | 10 + .../schema/templates/set_value_type.gotmpl | 8 + .../templates/set_value_valuable.gotmpl | 1 + .../schema/templates/set_value_value.gotmpl | 3 + internal/schema/to_from_set.go | 111 +++++ internal/schema/to_from_set_test.go | 169 +++++++ 41 files changed, 2258 insertions(+), 48 deletions(-) create mode 100644 internal/schema/custom_set.go create mode 100644 internal/schema/custom_set_test.go create mode 100644 internal/schema/templates/set_from.gotmpl create mode 100644 internal/schema/templates/set_to.gotmpl create mode 100644 internal/schema/templates/set_type_equal.gotmpl create mode 100644 internal/schema/templates/set_type_string.gotmpl create mode 100644 internal/schema/templates/set_type_typable.gotmpl create mode 100644 internal/schema/templates/set_type_type.gotmpl create mode 100644 internal/schema/templates/set_type_value_from_set.gotmpl create mode 100644 internal/schema/templates/set_type_value_from_terraform.gotmpl create mode 100644 internal/schema/templates/set_type_value_type.gotmpl create mode 100644 internal/schema/templates/set_value_equal.gotmpl create mode 100644 internal/schema/templates/set_value_type.gotmpl create mode 100644 internal/schema/templates/set_value_valuable.gotmpl create mode 100644 internal/schema/templates/set_value_value.gotmpl create mode 100644 internal/schema/to_from_set.go create mode 100644 internal/schema/to_from_set_test.go diff --git a/internal/datasource_convert/set_attribute.go b/internal/datasource_convert/set_attribute.go index b55cd828..75c27e19 100644 --- a/internal/datasource_convert/set_attribute.go +++ b/internal/datasource_convert/set_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/datasource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertSetAttribute(a *datasource.SetAttribute) (datasource_generate.GeneratorSetAttribute, error) { @@ -28,8 +29,9 @@ func convertSetAttribute(a *datasource.SetAttribute) (datasource_generate.Genera DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/datasource_generate/embed.go b/internal/datasource_generate/embed.go index b957b1e8..9a160b78 100644 --- a/internal/datasource_generate/embed.go +++ b/internal/datasource_generate/embed.go @@ -37,7 +37,7 @@ var numberAttributeTemplate string var objectAttributeGoTemplate string //go:embed templates/set_attribute.gotmpl -var setAttributeGoTemplate string +var setAttributeTemplate string //go:embed templates/set_nested_attribute.gotmpl var setNestedAttributeGoTemplate string diff --git a/internal/datasource_generate/set_attribute.go b/internal/datasource_generate/set_attribute.go index fc87978d..e167086d 100644 --- a/internal/datasource_generate/set_attribute.go +++ b/internal/datasource_generate/set_attribute.go @@ -4,6 +4,8 @@ package datasource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorSetAttribute struct { schema.SetAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorSetAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -103,6 +112,7 @@ func (g GeneratorSetAttribute) Equal(ga generatorschema.GeneratorAttribute) bool func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorSetAttribute GeneratorSetAttribute } @@ -113,12 +123,19 @@ func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorSetAttribute: g, } - t, err := template.New("set_attribute").Parse(setAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.SetType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("Set_attribute").Parse(setAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -139,9 +156,64 @@ func (g GeneratorSetAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.SetValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorSetAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomSetType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomSetValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorSetAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromSet(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/datasource_generate/set_attribute_test.go b/internal/datasource_generate/set_attribute_test.go index a6a4c096..14ef5145 100644 --- a/internal/datasource_generate/set_attribute_test.go +++ b/internal/datasource_generate/set_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorSetAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "set_attribute": schema.SetAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ +CustomType: SetAttributeType{ +types.SetType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ CustomType: my_custom_type, },`, }, @@ -644,6 +682,37 @@ func TestGeneratorSetAttribute_ModelField(t *testing.T) { TfsdkName: "set_attribute", }, }, + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "SetAttributeValue", + TfsdkName: "set_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "set_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/datasource_generate/templates/set_attribute.gotmpl b/internal/datasource_generate/templates/set_attribute.gotmpl index 4e0d19ac..a36f7d01 100644 --- a/internal/datasource_generate/templates/set_attribute.gotmpl +++ b/internal/datasource_generate/templates/set_attribute.gotmpl @@ -1,6 +1,10 @@ "{{.Name}}": schema.SetAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, +{{- end}} {{- template "common_attribute" .GeneratorSetAttribute }} {{- if gt (len .GeneratorSetAttribute.Validators) 0 }} Validators: []validator.Set{ diff --git a/internal/provider_convert/list_attribute.go b/internal/provider_convert/list_attribute.go index 523c598e..f1e966ed 100644 --- a/internal/provider_convert/list_attribute.go +++ b/internal/provider_convert/list_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/provider_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertListAttribute(a *provider.ListAttribute) (provider_generate.GeneratorListAttribute, error) { @@ -27,8 +28,9 @@ func convertListAttribute(a *provider.ListAttribute) (provider_generate.Generato DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/provider_convert/set_attribute.go b/internal/provider_convert/set_attribute.go index 291324ba..a1f73986 100644 --- a/internal/provider_convert/set_attribute.go +++ b/internal/provider_convert/set_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/provider_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertSetAttribute(a *provider.SetAttribute) (provider_generate.GeneratorSetAttribute, error) { @@ -26,8 +27,10 @@ func convertSetAttribute(a *provider.SetAttribute) (provider_generate.GeneratorS MarkdownDescription: description(a.Description), DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/provider_generate/embed.go b/internal/provider_generate/embed.go index 87d684fe..1ba31363 100644 --- a/internal/provider_generate/embed.go +++ b/internal/provider_generate/embed.go @@ -19,7 +19,7 @@ var float64AttributeTemplate string var int64AttributeTemplate string //go:embed templates/list_attribute.gotmpl -var listAttributeGoTemplate string +var listAttributeTemplate string //go:embed templates/list_nested_attribute.gotmpl var listNestedAttributeGoTemplate string @@ -37,7 +37,7 @@ var numberAttributeTemplate string var objectAttributeGoTemplate string //go:embed templates/set_attribute.gotmpl -var setAttributeGoTemplate string +var setAttributeTemplate string //go:embed templates/set_nested_attribute.gotmpl var setNestedAttributeGoTemplate string diff --git a/internal/provider_generate/list_attribute.go b/internal/provider_generate/list_attribute.go index e12d7496..65a1d5a8 100644 --- a/internal/provider_generate/list_attribute.go +++ b/internal/provider_generate/list_attribute.go @@ -4,6 +4,8 @@ package provider_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorListAttribute struct { schema.ListAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorListAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -99,6 +108,7 @@ func (g GeneratorListAttribute) Equal(ga generatorschema.GeneratorAttribute) boo func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorListAttribute GeneratorListAttribute } @@ -109,12 +119,19 @@ func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorListAttribute: g, } - t, err := template.New("list_attribute").Parse(listAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ListType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("list_attribute").Parse(listAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -135,9 +152,64 @@ func (g GeneratorListAttribute) ModelField(name generatorschema.FrameworkIdentif ValueType: model.ListValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorListAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomListType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomListValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorListAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromList(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/provider_generate/list_attribute_test.go b/internal/provider_generate/list_attribute_test.go index 36f8f41a..7ab9db9d 100644 --- a/internal/provider_generate/list_attribute_test.go +++ b/internal/provider_generate/list_attribute_test.go @@ -313,6 +313,110 @@ func TestGeneratorListAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -601,7 +705,44 @@ ElementType: types.StringType, }, expected: ` "list_attribute": schema.ListAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ +CustomType: ListAttributeType{ +types.ListType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ CustomType: my_custom_type, },`, }, @@ -946,6 +1087,37 @@ func TestGeneratorListAttribute_ModelField(t *testing.T) { TfsdkName: "list_attribute", }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "ListAttributeValue", + TfsdkName: "list_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "list_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/provider_generate/set_attribute.go b/internal/provider_generate/set_attribute.go index 1f0b4f86..96356f6a 100644 --- a/internal/provider_generate/set_attribute.go +++ b/internal/provider_generate/set_attribute.go @@ -4,6 +4,8 @@ package provider_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorSetAttribute struct { schema.SetAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorSetAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -99,6 +108,7 @@ func (g GeneratorSetAttribute) Equal(ga generatorschema.GeneratorAttribute) bool func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorSetAttribute GeneratorSetAttribute } @@ -109,12 +119,19 @@ func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorSetAttribute: g, } - t, err := template.New("set_attribute").Parse(setAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.SetType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("Set_attribute").Parse(setAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -135,9 +152,64 @@ func (g GeneratorSetAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.SetValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorSetAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomSetType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomSetValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorSetAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromSet(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/provider_generate/set_attribute_test.go b/internal/provider_generate/set_attribute_test.go index 950aa7fa..a93469ed 100644 --- a/internal/provider_generate/set_attribute_test.go +++ b/internal/provider_generate/set_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorSetAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "set_attribute": schema.SetAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ +CustomType: SetAttributeType{ +types.SetType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ CustomType: my_custom_type, },`, }, @@ -628,6 +666,37 @@ func TestGeneratorSetAttribute_ModelField(t *testing.T) { TfsdkName: "set_attribute", }, }, + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "SetAttributeValue", + TfsdkName: "set_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "set_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/provider_generate/templates/list_attribute.gotmpl b/internal/provider_generate/templates/list_attribute.gotmpl index 383d12e3..44e3afdf 100644 --- a/internal/provider_generate/templates/list_attribute.gotmpl +++ b/internal/provider_generate/templates/list_attribute.gotmpl @@ -1,7 +1,10 @@ "{{.Name}}": schema.ListAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, -{{- template "common_attribute" .GeneratorListAttribute }} +{{- end}}{{- template "common_attribute" .GeneratorListAttribute }} {{- if gt (len .GeneratorListAttribute.Validators) 0 }} Validators: []validator.List{ {{- range .GeneratorListAttribute.Validators}} diff --git a/internal/provider_generate/templates/set_attribute.gotmpl b/internal/provider_generate/templates/set_attribute.gotmpl index 4e0d19ac..e38bc9fa 100644 --- a/internal/provider_generate/templates/set_attribute.gotmpl +++ b/internal/provider_generate/templates/set_attribute.gotmpl @@ -1,7 +1,10 @@ "{{.Name}}": schema.SetAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, -{{- template "common_attribute" .GeneratorSetAttribute }} +{{- end}}{{- template "common_attribute" .GeneratorSetAttribute }} {{- if gt (len .GeneratorSetAttribute.Validators) 0 }} Validators: []validator.Set{ {{- range .GeneratorSetAttribute.Validators}} diff --git a/internal/resource_convert/list_attribute.go b/internal/resource_convert/list_attribute.go index 8bb09d59..50f97fda 100644 --- a/internal/resource_convert/list_attribute.go +++ b/internal/resource_convert/list_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/resource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertListAttribute(a *resource.ListAttribute) (resource_generate.GeneratorListAttribute, error) { @@ -27,10 +28,12 @@ func convertListAttribute(a *resource.ListAttribute) (resource_generate.Generato MarkdownDescription: description(a.Description), DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - Default: a.Default, - ElementType: a.ElementType, - PlanModifiers: a.PlanModifiers, - Validators: a.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + Default: a.Default, + ElementType: a.ElementType, + PlanModifiers: a.PlanModifiers, + Validators: a.Validators, }, nil } diff --git a/internal/resource_convert/set_attribute.go b/internal/resource_convert/set_attribute.go index 68c97a33..f19d6f54 100644 --- a/internal/resource_convert/set_attribute.go +++ b/internal/resource_convert/set_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/resource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertSetAttribute(a *resource.SetAttribute) (resource_generate.GeneratorSetAttribute, error) { @@ -27,10 +28,12 @@ func convertSetAttribute(a *resource.SetAttribute) (resource_generate.GeneratorS MarkdownDescription: description(a.Description), DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - Default: a.Default, - ElementType: a.ElementType, - PlanModifiers: a.PlanModifiers, - Validators: a.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + Default: a.Default, + ElementType: a.ElementType, + PlanModifiers: a.PlanModifiers, + Validators: a.Validators, }, nil } diff --git a/internal/resource_generate/embed.go b/internal/resource_generate/embed.go index e9c19e7e..0d818c21 100644 --- a/internal/resource_generate/embed.go +++ b/internal/resource_generate/embed.go @@ -19,7 +19,7 @@ var float64AttributeTemplate string var int64AttributeTemplate string //go:embed templates/list_attribute.gotmpl -var listAttributeGoTemplate string +var listAttributeTemplate string //go:embed templates/list_nested_attribute.gotmpl var listNestedAttributeGoTemplate string @@ -37,7 +37,7 @@ var numberAttributeTemplate string var objectAttributeGoTemplate string //go:embed templates/set_attribute.gotmpl -var setAttributeGoTemplate string +var setAttributeTemplate string //go:embed templates/set_nested_attribute.gotmpl var setNestedAttributeGoTemplate string diff --git a/internal/resource_generate/list_attribute.go b/internal/resource_generate/list_attribute.go index 5344d7be..58e59725 100644 --- a/internal/resource_generate/list_attribute.go +++ b/internal/resource_generate/list_attribute.go @@ -4,6 +4,8 @@ package resource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorListAttribute struct { schema.ListAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -58,6 +61,12 @@ func (g GeneratorListAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -131,6 +140,7 @@ func listDefault(d *specschema.ListDefault) string { func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string Default string ElementType string GeneratorListAttribute GeneratorListAttribute @@ -143,12 +153,19 @@ func (g GeneratorListAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorListAttribute: g, } - t, err := template.New("list_attribute").Parse(listAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ListType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("list_attribute").Parse(listAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -169,9 +186,64 @@ func (g GeneratorListAttribute) ModelField(name generatorschema.FrameworkIdentif ValueType: model.ListValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorListAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomListType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomListValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorListAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromList(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/resource_generate/list_attribute_test.go b/internal/resource_generate/list_attribute_test.go index caef73d4..a407de80 100644 --- a/internal/resource_generate/list_attribute_test.go +++ b/internal/resource_generate/list_attribute_test.go @@ -466,6 +466,110 @@ func TestGeneratorListAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -754,7 +858,44 @@ ElementType: types.StringType, }, expected: ` "list_attribute": schema.ListAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ +CustomType: ListAttributeType{ +types.ListType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"list_attribute": schema.ListAttribute{ CustomType: my_custom_type, },`, }, @@ -1161,6 +1302,37 @@ func TestGeneratorListAttribute_ModelField(t *testing.T) { TfsdkName: "list_attribute", }, }, + "associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "ListAttributeValue", + TfsdkName: "list_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorListAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ListAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "list_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/resource_generate/set_attribute.go b/internal/resource_generate/set_attribute.go index a97195a4..908788be 100644 --- a/internal/resource_generate/set_attribute.go +++ b/internal/resource_generate/set_attribute.go @@ -4,6 +4,8 @@ package resource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorSetAttribute struct { schema.SetAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -58,6 +61,12 @@ func (g GeneratorSetAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -135,6 +144,7 @@ func setDefault(d *specschema.SetDefault) string { func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string Default string ElementType string GeneratorSetAttribute GeneratorSetAttribute @@ -147,12 +157,19 @@ func (g GeneratorSetAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorSetAttribute: g, } - t, err := template.New("set_attribute").Parse(setAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.SetType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("Set_attribute").Parse(setAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -173,9 +190,64 @@ func (g GeneratorSetAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.SetValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorSetAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomSetType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomSetValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorSetAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromSet(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/resource_generate/set_attribute_test.go b/internal/resource_generate/set_attribute_test.go index b5df26c2..203c8feb 100644 --- a/internal/resource_generate/set_attribute_test.go +++ b/internal/resource_generate/set_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorSetAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "set_attribute": schema.SetAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ +CustomType: SetAttributeType{ +types.SetType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ListAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"set_attribute": schema.SetAttribute{ CustomType: my_custom_type, },`, }, @@ -690,6 +728,37 @@ func TestGeneratorSetAttribute_ModelField(t *testing.T) { TfsdkName: "set_attribute", }, }, + "associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "SetAttributeValue", + TfsdkName: "set_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorSetAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "SetAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "set_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/resource_generate/templates/list_attribute.gotmpl b/internal/resource_generate/templates/list_attribute.gotmpl index b0088c80..953dc41b 100644 --- a/internal/resource_generate/templates/list_attribute.gotmpl +++ b/internal/resource_generate/templates/list_attribute.gotmpl @@ -1,7 +1,10 @@ "{{.Name}}": schema.ListAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, -{{- template "common_attribute" .GeneratorListAttribute }} +{{- end}}{{- template "common_attribute" .GeneratorListAttribute }} {{- if gt (len .GeneratorListAttribute.PlanModifiers) 0 }} PlanModifiers: []planmodifier.List{ {{- range $planmodifier := .GeneratorListAttribute.PlanModifiers}} diff --git a/internal/resource_generate/templates/set_attribute.gotmpl b/internal/resource_generate/templates/set_attribute.gotmpl index a72fb1b5..05bc3dce 100644 --- a/internal/resource_generate/templates/set_attribute.gotmpl +++ b/internal/resource_generate/templates/set_attribute.gotmpl @@ -1,7 +1,10 @@ "{{.Name}}": schema.SetAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, -{{- template "common_attribute" .GeneratorSetAttribute }} +{{- end}}{{- template "common_attribute" .GeneratorSetAttribute }} {{- if gt (len .GeneratorSetAttribute.PlanModifiers) 0 }} PlanModifiers: []planmodifier.Set{ {{- range $planmodifier := .GeneratorSetAttribute.PlanModifiers}} diff --git a/internal/schema/custom_set.go b/internal/schema/custom_set.go new file mode 100644 index 00000000..0985b726 --- /dev/null +++ b/internal/schema/custom_set.go @@ -0,0 +1,349 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type CustomSetType struct { + Name FrameworkIdentifier + templates map[string]string +} + +func NewCustomSetType(name string) CustomSetType { + t := map[string]string{ + "equal": SetTypeEqualTemplate, + "string": SetTypeStringTemplate, + "type": SetTypeTypeTemplate, + "typable": SetTypeTypableTemplate, + "valueFromSet": SetTypeValueFromSetTemplate, + "valueFromTerraform": SetTypeValueFromTerraformTemplate, + "valueType": SetTypeValueTypeTemplate, + } + + return CustomSetType{ + Name: FrameworkIdentifier(name), + templates: t, + } +} + +func (c CustomSetType) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderTypable, + c.renderType, + c.renderEqual, + c.renderString, + c.renderValueFromSet, + c.renderValueFromTerraform, + c.renderValueType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderString() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["string"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderTypable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["typable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderValueFromSet() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromSet"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderValueFromTerraform() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromTerraform"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetType) renderValueType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueType"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type CustomSetValue struct { + Name FrameworkIdentifier + ElementType string + templates map[string]string +} + +func NewCustomSetValue(name, elemType string) CustomSetValue { + t := map[string]string{ + "equal": SetValueEqualTemplate, + "type": SetValueTypeTemplate, + "valuable": SetValueValuableTemplate, + "value": SetValueValueTemplate, + } + + return CustomSetValue{ + Name: FrameworkIdentifier(name), + ElementType: elemType, + templates: t, + } +} + +func (c CustomSetValue) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderValuable, + c.renderValue, + c.renderEqual, + c.renderType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomSetValue) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetValue) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + ElementType string + }{ + Name: c.Name.ToPascalCase(), + ElementType: c.ElementType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetValue) renderValuable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valuable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomSetValue) renderValue() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["value"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/custom_set_test.go b/internal/schema/custom_set_test.go new file mode 100644 index 00000000..28d93897 --- /dev/null +++ b/internal/schema/custom_set_test.go @@ -0,0 +1,467 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCustomSetType_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`func (t ExampleType) Equal(o attr.Type) bool { +other, ok := o.(ExampleType) + +if !ok { +return false +} + +return t.SetType.Equal(other.SetType) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderString(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) String() string { +return "ExampleType" +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderString() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderTypable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`var _ basetypes.SetTypable = ExampleType{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderTypable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`type ExampleType struct { +basetypes.SetType +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderValueFromSet(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + attrValues map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + attrValues: map[string]string{ + "bool_attribute": "basetypes.SetValue", + }, + expected: []byte(` +func (t ExampleType) ValueFromSet(ctx context.Context, in basetypes.SetValue) (basetypes.SetValuable, diag.Diagnostics) { +return ExampleValue{ +SetValue: in, +}, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderValueFromSet() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderValueFromTerraform(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.SetType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +listValue, ok := attrValue.(basetypes.SetValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +listValuable, diags := t.ValueFromSet(ctx, listValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting SetValue to SetValuable: %v", diags) +} + +return listValuable, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderValueFromTerraform() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetType_renderValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueType(ctx context.Context) attr.Value { +return ExampleValue{} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetType := NewCustomSetType(testCase.name) + + got, err := customSetType.renderValueType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetValue_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Equal(o attr.Value) bool { +other, ok := o.(ExampleValue) + +if !ok { +return false +} + +return v.SetValue.Equal(other.SetValue) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetValue := NewCustomSetValue(testCase.name, testCase.elementType) + + got, err := customSetValue.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetValue_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Type(ctx context.Context) attr.Type { +return ExampleType{ +SetType: basetypes.SetType{ +ElemType: types.BoolType, +}, +} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetValue := NewCustomSetValue(testCase.name, testCase.elementType) + + got, err := customSetValue.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetValue_renderValuable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`var _ basetypes.SetValuable = ExampleValue{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetValue := NewCustomSetValue(testCase.name, testCase.elementType) + + got, err := customSetValue.renderValuable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomSetValue_renderValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`type ExampleValue struct { +basetypes.SetValue +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customSetValue := NewCustomSetValue(testCase.name, testCase.elementType) + + got, err := customSetValue.renderValue() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/schema/embed.go b/internal/schema/embed.go index b03d2fd9..6c7ea03b 100644 --- a/internal/schema/embed.go +++ b/internal/schema/embed.go @@ -310,6 +310,51 @@ var ObjectValueValueTemplate string //go:embed templates/schema.gotmpl var SchemaGoTemplate string +// Set From/To + +//go:embed templates/set_from.gotmpl +var SetFromTemplate string + +//go:embed templates/set_to.gotmpl +var SetToTemplate string + +// Set Type + +//go:embed templates/set_type_equal.gotmpl +var SetTypeEqualTemplate string + +//go:embed templates/set_type_string.gotmpl +var SetTypeStringTemplate string + +//go:embed templates/set_type_type.gotmpl +var SetTypeTypeTemplate string + +//go:embed templates/set_type_typable.gotmpl +var SetTypeTypableTemplate string + +//go:embed templates/set_type_value_from_set.gotmpl +var SetTypeValueFromSetTemplate string + +//go:embed templates/set_type_value_from_terraform.gotmpl +var SetTypeValueFromTerraformTemplate string + +//go:embed templates/set_type_value_type.gotmpl +var SetTypeValueTypeTemplate string + +// Set Value + +//go:embed templates/set_value_equal.gotmpl +var SetValueEqualTemplate string + +//go:embed templates/set_value_type.gotmpl +var SetValueTypeTemplate string + +//go:embed templates/set_value_value.gotmpl +var SetValueValueTemplate string + +//go:embed templates/set_value_valuable.gotmpl +var SetValueValuableTemplate string + // String From/To //go:embed templates/string_from.gotmpl diff --git a/internal/schema/templates/set_from.gotmpl b/internal/schema/templates/set_from.gotmpl new file mode 100644 index 00000000..dfb7644b --- /dev/null +++ b/internal/schema/templates/set_from.gotmpl @@ -0,0 +1,30 @@ + +func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return {{.Name}}Value{ +types.SetNull({{.ElementTypeType}}), +}, diags +} + + var elems []{{.ElementTypeValue}} + +for _, e := range *apiObject { +elems = append(elems, {{.ElementFrom}}(e)) +} + +l, d := basetypes.NewSetValueFrom(ctx, {{.ElementTypeType}}, elems) + +diags.Append(d...) + +if diags.HasError() { +return SetAttributeValue{ +types.SetNull({{.ElementTypeType}}), +}, diags +} + +return SetAttributeValue{ +l, +}, diags +} diff --git a/internal/schema/templates/set_to.gotmpl b/internal/schema/templates/set_to.gotmpl new file mode 100644 index 00000000..4be7e418 --- /dev/null +++ b/internal/schema/templates/set_to.gotmpl @@ -0,0 +1,28 @@ +func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"{{.Name}}Value Value Is Unknown", +`"{{.Name}}Value" is unknown.`, +)) + +return nil, diags +} + +var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} + +d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &{{.AssocExtType.ToCamelCase}}, diags +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_equal.gotmpl b/internal/schema/templates/set_type_equal.gotmpl new file mode 100644 index 00000000..51eaa4fe --- /dev/null +++ b/internal/schema/templates/set_type_equal.gotmpl @@ -0,0 +1,9 @@ +func (t {{.Name}}Type) Equal(o attr.Type) bool { +other, ok := o.({{.Name}}Type) + +if !ok { +return false +} + +return t.SetType.Equal(other.SetType) +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_string.gotmpl b/internal/schema/templates/set_type_string.gotmpl new file mode 100644 index 00000000..f01b8ac3 --- /dev/null +++ b/internal/schema/templates/set_type_string.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) String() string { +return "{{.Name}}Type" +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_typable.gotmpl b/internal/schema/templates/set_type_typable.gotmpl new file mode 100644 index 00000000..5493ed2b --- /dev/null +++ b/internal/schema/templates/set_type_typable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.SetTypable = {{.Name}}Type{} \ No newline at end of file diff --git a/internal/schema/templates/set_type_type.gotmpl b/internal/schema/templates/set_type_type.gotmpl new file mode 100644 index 00000000..21c6cf09 --- /dev/null +++ b/internal/schema/templates/set_type_type.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Type struct { +basetypes.SetType +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_value_from_set.gotmpl b/internal/schema/templates/set_type_value_from_set.gotmpl new file mode 100644 index 00000000..3d84d308 --- /dev/null +++ b/internal/schema/templates/set_type_value_from_set.gotmpl @@ -0,0 +1,6 @@ + +func (t {{.Name}}Type) ValueFromSet(ctx context.Context, in basetypes.SetValue) (basetypes.SetValuable, diag.Diagnostics) { +return {{.Name}}Value{ +SetValue: in, +}, nil +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_value_from_terraform.gotmpl b/internal/schema/templates/set_type_value_from_terraform.gotmpl new file mode 100644 index 00000000..4c7c1d4e --- /dev/null +++ b/internal/schema/templates/set_type_value_from_terraform.gotmpl @@ -0,0 +1,22 @@ + +func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.SetType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +listValue, ok := attrValue.(basetypes.SetValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +listValuable, diags := t.ValueFromSet(ctx, listValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting SetValue to SetValuable: %v", diags) +} + +return listValuable, nil +} \ No newline at end of file diff --git a/internal/schema/templates/set_type_value_type.gotmpl b/internal/schema/templates/set_type_value_type.gotmpl new file mode 100644 index 00000000..1f7b7fea --- /dev/null +++ b/internal/schema/templates/set_type_value_type.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { +return {{.Name}}Value{} +} \ No newline at end of file diff --git a/internal/schema/templates/set_value_equal.gotmpl b/internal/schema/templates/set_value_equal.gotmpl new file mode 100644 index 00000000..feca8855 --- /dev/null +++ b/internal/schema/templates/set_value_equal.gotmpl @@ -0,0 +1,10 @@ + +func (v {{.Name}}Value) Equal(o attr.Value) bool { +other, ok := o.({{.Name}}Value) + +if !ok { +return false +} + +return v.SetValue.Equal(other.SetValue) +} \ No newline at end of file diff --git a/internal/schema/templates/set_value_type.gotmpl b/internal/schema/templates/set_value_type.gotmpl new file mode 100644 index 00000000..1ebd991b --- /dev/null +++ b/internal/schema/templates/set_value_type.gotmpl @@ -0,0 +1,8 @@ + +func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { +return {{.Name}}Type{ +SetType: basetypes.SetType{ +ElemType: {{.ElementType}}, +}, +} +} \ No newline at end of file diff --git a/internal/schema/templates/set_value_valuable.gotmpl b/internal/schema/templates/set_value_valuable.gotmpl new file mode 100644 index 00000000..007cc556 --- /dev/null +++ b/internal/schema/templates/set_value_valuable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.SetValuable = {{.Name}}Value{} \ No newline at end of file diff --git a/internal/schema/templates/set_value_value.gotmpl b/internal/schema/templates/set_value_value.gotmpl new file mode 100644 index 00000000..e6d74d29 --- /dev/null +++ b/internal/schema/templates/set_value_value.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Value struct { +basetypes.SetValue +} \ No newline at end of file diff --git a/internal/schema/to_from_set.go b/internal/schema/to_from_set.go new file mode 100644 index 00000000..a0687ebd --- /dev/null +++ b/internal/schema/to_from_set.go @@ -0,0 +1,111 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type ToFromSet struct { + Name FrameworkIdentifier + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + templates map[string]string +} + +func NewToFromSet(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromSet { + t := map[string]string{ + "from": SetFromTemplate, + "to": SetToTemplate, + } + + return ToFromSet{ + Name: FrameworkIdentifier(name), + AssocExtType: assocExtType, + ElementTypeType: elemTypeType, + ElementTypeValue: elemTypeValue, + ElementFrom: elemFrom, + templates: t, + } +} + +func (o ToFromSet) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + o.renderTo, + o.renderFrom, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (o ToFromSet) renderTo() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["to"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (o ToFromSet) renderFrom() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["from"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + ElementTypeType: o.ElementTypeType, + ElementTypeValue: o.ElementTypeValue, + ElementFrom: o.ElementFrom, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/to_from_set_test.go b/internal/schema/to_from_set_test.go new file mode 100644 index 00000000..773ab45f --- /dev/null +++ b/internal/schema/to_from_set_test.go @@ -0,0 +1,169 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" + "github.com/hashicorp/terraform-plugin-codegen-spec/schema" +) + +func TestToFromSet_renderFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return ExampleValue{ +types.SetNull(types.BoolType), +}, diags +} + + var elems []types.Bool + +for _, e := range *apiObject { +elems = append(elems, types.BoolPointerValue(e)) +} + +l, d := basetypes.NewSetValueFrom(ctx, types.BoolType, elems) + +diags.Append(d...) + +if diags.HasError() { +return SetAttributeValue{ +types.SetNull(types.BoolType), +}, diags +} + +return SetAttributeValue{ +l, +}, diags +} +`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromSet := NewToFromSet(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromSet.renderFrom() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestToFromSet_renderTo(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + 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 apisdkType apisdk.Type + +d := v.ElementsAs(ctx, &apisdkType, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdkType, diags +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromSet := NewToFromSet(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromSet.renderTo() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} From 2ad5b4975a549948567187bf4a6cce174f583a29 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 11:49:28 +0100 Subject: [PATCH 04/13] Adding generation of custom type and value type, and to/from methods for map attribute for data source, provider and resource (#74) --- internal/datasource_convert/map_attribute.go | 8 +- internal/datasource_generate/embed.go | 2 +- internal/datasource_generate/map_attribute.go | 78 ++- .../datasource_generate/map_attribute_test.go | 71 ++- .../templates/map_attribute.gotmpl | 4 + internal/provider_convert/map_attribute.go | 8 +- internal/provider_generate/embed.go | 2 +- internal/provider_generate/map_attribute.go | 78 ++- .../provider_generate/map_attribute_test.go | 71 ++- .../templates/map_attribute.gotmpl | 4 + internal/resource_convert/map_attribute.go | 13 +- internal/resource_generate/embed.go | 2 +- internal/resource_generate/map_attribute.go | 78 ++- .../resource_generate/map_attribute_test.go | 71 ++- .../templates/map_attribute.gotmpl | 4 + internal/schema/custom_map.go | 349 +++++++++++++ internal/schema/custom_map_test.go | 467 ++++++++++++++++++ internal/schema/embed.go | 45 ++ internal/schema/templates/list_from.gotmpl | 2 +- internal/schema/templates/map_from.gotmpl | 30 ++ internal/schema/templates/map_to.gotmpl | 28 ++ .../schema/templates/map_type_equal.gotmpl | 9 + .../schema/templates/map_type_string.gotmpl | 4 + .../schema/templates/map_type_typable.gotmpl | 1 + .../schema/templates/map_type_type.gotmpl | 3 + .../templates/map_type_value_from_map.gotmpl | 6 + .../map_type_value_from_terraform.gotmpl | 22 + .../templates/map_type_value_type.gotmpl | 4 + .../schema/templates/map_value_equal.gotmpl | 10 + .../schema/templates/map_value_type.gotmpl | 8 + .../templates/map_value_valuable.gotmpl | 1 + .../schema/templates/map_value_value.gotmpl | 3 + internal/schema/templates/set_from.gotmpl | 2 +- internal/schema/to_from_list_test.go | 2 +- internal/schema/to_from_map.go | 111 +++++ internal/schema/to_from_map_test.go | 169 +++++++ internal/schema/to_from_set_test.go | 2 +- 37 files changed, 1742 insertions(+), 30 deletions(-) create mode 100644 internal/schema/custom_map.go create mode 100644 internal/schema/custom_map_test.go create mode 100644 internal/schema/templates/map_from.gotmpl create mode 100644 internal/schema/templates/map_to.gotmpl create mode 100644 internal/schema/templates/map_type_equal.gotmpl create mode 100644 internal/schema/templates/map_type_string.gotmpl create mode 100644 internal/schema/templates/map_type_typable.gotmpl create mode 100644 internal/schema/templates/map_type_type.gotmpl create mode 100644 internal/schema/templates/map_type_value_from_map.gotmpl create mode 100644 internal/schema/templates/map_type_value_from_terraform.gotmpl create mode 100644 internal/schema/templates/map_type_value_type.gotmpl create mode 100644 internal/schema/templates/map_value_equal.gotmpl create mode 100644 internal/schema/templates/map_value_type.gotmpl create mode 100644 internal/schema/templates/map_value_valuable.gotmpl create mode 100644 internal/schema/templates/map_value_value.gotmpl create mode 100644 internal/schema/to_from_map.go create mode 100644 internal/schema/to_from_map_test.go diff --git a/internal/datasource_convert/map_attribute.go b/internal/datasource_convert/map_attribute.go index c1944425..264f1809 100644 --- a/internal/datasource_convert/map_attribute.go +++ b/internal/datasource_convert/map_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/datasource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertMapAttribute(a *datasource.MapAttribute) (datasource_generate.GeneratorMapAttribute, error) { @@ -28,8 +29,9 @@ func convertMapAttribute(a *datasource.MapAttribute) (datasource_generate.Genera DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/datasource_generate/embed.go b/internal/datasource_generate/embed.go index 9a160b78..e8a7b61b 100644 --- a/internal/datasource_generate/embed.go +++ b/internal/datasource_generate/embed.go @@ -25,7 +25,7 @@ var listAttributeTemplate string var listNestedAttributeGoTemplate string //go:embed templates/map_attribute.gotmpl -var mapAttributeGoTemplate string +var mapAttributeTemplate string //go:embed templates/map_nested_attribute.gotmpl var mapNestedAttributeGoTemplate string diff --git a/internal/datasource_generate/map_attribute.go b/internal/datasource_generate/map_attribute.go index fdf6fe58..00a90c16 100644 --- a/internal/datasource_generate/map_attribute.go +++ b/internal/datasource_generate/map_attribute.go @@ -4,6 +4,8 @@ package datasource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorMapAttribute struct { schema.MapAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorMapAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -103,6 +112,7 @@ func (g GeneratorMapAttribute) Equal(ga generatorschema.GeneratorAttribute) bool func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorMapAttribute GeneratorMapAttribute } @@ -113,12 +123,19 @@ func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorMapAttribute: g, } - t, err := template.New("map_attribute").Parse(mapAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.MapType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("map_attribute").Parse(mapAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -139,9 +156,64 @@ func (g GeneratorMapAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.MapValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorMapAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomMapType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomMapValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorMapAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromMap(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/datasource_generate/map_attribute_test.go b/internal/datasource_generate/map_attribute_test.go index 65846795..a0a3f9a8 100644 --- a/internal/datasource_generate/map_attribute_test.go +++ b/internal/datasource_generate/map_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorMapAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "map_attribute": schema.MapAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ +CustomType: MapAttributeType{ +types.MapType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ CustomType: my_custom_type, },`, }, @@ -644,6 +682,37 @@ func TestGeneratorMapAttribute_ModelField(t *testing.T) { TfsdkName: "map_attribute", }, }, + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "MapAttributeValue", + TfsdkName: "map_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "map_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/datasource_generate/templates/map_attribute.gotmpl b/internal/datasource_generate/templates/map_attribute.gotmpl index a1f42ea8..e558f291 100644 --- a/internal/datasource_generate/templates/map_attribute.gotmpl +++ b/internal/datasource_generate/templates/map_attribute.gotmpl @@ -1,6 +1,10 @@ "{{.Name}}": schema.MapAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, +{{- end}} {{- template "common_attribute" .GeneratorMapAttribute }} {{- if gt (len .GeneratorMapAttribute.Validators) 0 }} Validators: []validator.Map{ diff --git a/internal/provider_convert/map_attribute.go b/internal/provider_convert/map_attribute.go index 66783cb1..e5d6a560 100644 --- a/internal/provider_convert/map_attribute.go +++ b/internal/provider_convert/map_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/provider_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertMapAttribute(a *provider.MapAttribute) (provider_generate.GeneratorMapAttribute, error) { @@ -27,8 +28,9 @@ func convertMapAttribute(a *provider.MapAttribute) (provider_generate.GeneratorM DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - ElementType: a.ElementType, - Validators: a.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + ElementType: a.ElementType, + Validators: a.Validators, }, nil } diff --git a/internal/provider_generate/embed.go b/internal/provider_generate/embed.go index 1ba31363..19e2b7e9 100644 --- a/internal/provider_generate/embed.go +++ b/internal/provider_generate/embed.go @@ -25,7 +25,7 @@ var listAttributeTemplate string var listNestedAttributeGoTemplate string //go:embed templates/map_attribute.gotmpl -var mapAttributeGoTemplate string +var mapAttributeTemplate string //go:embed templates/map_nested_attribute.gotmpl var mapNestedAttributeGoTemplate string diff --git a/internal/provider_generate/map_attribute.go b/internal/provider_generate/map_attribute.go index c2826915..fd2eee9d 100644 --- a/internal/provider_generate/map_attribute.go +++ b/internal/provider_generate/map_attribute.go @@ -4,6 +4,8 @@ package provider_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorMapAttribute struct { schema.MapAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -46,6 +49,12 @@ func (g GeneratorMapAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -99,6 +108,7 @@ func (g GeneratorMapAttribute) Equal(ga generatorschema.GeneratorAttribute) bool func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string ElementType string GeneratorMapAttribute GeneratorMapAttribute } @@ -109,12 +119,19 @@ func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorMapAttribute: g, } - t, err := template.New("map_attribute").Parse(mapAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.MapType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("map_attribute").Parse(mapAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -135,9 +152,64 @@ func (g GeneratorMapAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.MapValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorMapAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomMapType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomMapValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorMapAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromMap(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/provider_generate/map_attribute_test.go b/internal/provider_generate/map_attribute_test.go index 60d918ef..0cfa4eeb 100644 --- a/internal/provider_generate/map_attribute_test.go +++ b/internal/provider_generate/map_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorMapAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "map_attribute": schema.MapAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ +CustomType: MapAttributeType{ +types.MapType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ CustomType: my_custom_type, },`, }, @@ -628,6 +666,37 @@ func TestGeneratorMapAttribute_ModelField(t *testing.T) { TfsdkName: "map_attribute", }, }, + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "MapAttributeValue", + TfsdkName: "map_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "map_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/provider_generate/templates/map_attribute.gotmpl b/internal/provider_generate/templates/map_attribute.gotmpl index a1f42ea8..e558f291 100644 --- a/internal/provider_generate/templates/map_attribute.gotmpl +++ b/internal/provider_generate/templates/map_attribute.gotmpl @@ -1,6 +1,10 @@ "{{.Name}}": schema.MapAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, +{{- end}} {{- template "common_attribute" .GeneratorMapAttribute }} {{- if gt (len .GeneratorMapAttribute.Validators) 0 }} Validators: []validator.Map{ diff --git a/internal/resource_convert/map_attribute.go b/internal/resource_convert/map_attribute.go index f1474e52..f68859e1 100644 --- a/internal/resource_convert/map_attribute.go +++ b/internal/resource_convert/map_attribute.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/resource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func convertMapAttribute(a *resource.MapAttribute) (resource_generate.GeneratorMapAttribute, error) { @@ -27,10 +28,12 @@ func convertMapAttribute(a *resource.MapAttribute) (resource_generate.GeneratorM MarkdownDescription: description(a.Description), DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - CustomType: a.CustomType, - Default: a.Default, - ElementType: a.ElementType, - PlanModifiers: a.PlanModifiers, - Validators: a.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + CustomType: a.CustomType, + Default: a.Default, + ElementType: a.ElementType, + PlanModifiers: a.PlanModifiers, + Validators: a.Validators, }, nil } diff --git a/internal/resource_generate/embed.go b/internal/resource_generate/embed.go index 0d818c21..44a0ec33 100644 --- a/internal/resource_generate/embed.go +++ b/internal/resource_generate/embed.go @@ -25,7 +25,7 @@ var listAttributeTemplate string var listNestedAttributeGoTemplate string //go:embed templates/map_attribute.gotmpl -var mapAttributeGoTemplate string +var mapAttributeTemplate string //go:embed templates/map_nested_attribute.gotmpl var mapNestedAttributeGoTemplate string diff --git a/internal/resource_generate/map_attribute.go b/internal/resource_generate/map_attribute.go index ff39a965..4c407609 100644 --- a/internal/resource_generate/map_attribute.go +++ b/internal/resource_generate/map_attribute.go @@ -4,6 +4,8 @@ package resource_generate import ( + "bytes" + "fmt" "strings" "text/template" @@ -17,6 +19,7 @@ import ( type GeneratorMapAttribute struct { schema.MapAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. CustomType *specschema.CustomType @@ -58,6 +61,12 @@ func (g GeneratorMapAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -135,6 +144,7 @@ func mapDefault(d *specschema.MapDefault) string { func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) (string, error) { type attribute struct { Name string + CustomType string Default string ElementType string GeneratorMapAttribute GeneratorMapAttribute @@ -147,12 +157,19 @@ func (g GeneratorMapAttribute) Schema(name generatorschema.FrameworkIdentifier) GeneratorMapAttribute: g, } - t, err := template.New("map_attribute").Parse(mapAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.MapType{\nElemType: %s,\n},\n}", name.ToPascalCase(), generatorschema.GetElementType(g.ElementType)) + } + + t, err := template.New("map_attribute").Parse(mapAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -173,9 +190,64 @@ func (g GeneratorMapAttribute) ModelField(name generatorschema.FrameworkIdentifi ValueType: model.MapValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +func (g GeneratorMapAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + listType := generatorschema.NewCustomMapType(name) + + b, err := listType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + elemType := generatorschema.GetElementType(g.ElementType) + + listValue := generatorschema.NewCustomMapValue(name, elemType) + + b, err = listValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorMapAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + elementTypeType := generatorschema.GetElementType(g.ElementType) + elementTypeValue := generatorschema.GetElementValueType(g.ElementType) + elementFrom := generatorschema.GetElementFromFunc(g.ElementType) + + toFrom := generatorschema.NewToFromMap(name, g.AssociatedExternalType, elementTypeType, elementTypeValue, elementFrom) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/resource_generate/map_attribute_test.go b/internal/resource_generate/map_attribute_test.go index 7e6fe9f0..4a82cf10 100644 --- a/internal/resource_generate/map_attribute_test.go +++ b/internal/resource_generate/map_attribute_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) func TestGeneratorMapAttribute_Schema(t *testing.T) { @@ -284,7 +285,44 @@ ElementType: types.StringType, }, expected: ` "map_attribute": schema.MapAttribute{ -ElementType: types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + ElementType: specschema.ElementType{ + String: &specschema.StringType{}, + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ +CustomType: MapAttributeType{ +types.MapType{ +ElemType: types.StringType, +}, +}, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.MapAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"map_attribute": schema.MapAttribute{ CustomType: my_custom_type, },`, }, @@ -690,6 +728,37 @@ func TestGeneratorMapAttribute_ModelField(t *testing.T) { TfsdkName: "map_attribute", }, }, + "associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "MapAttributeValue", + TfsdkName: "map_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorMapAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "MapAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "map_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/resource_generate/templates/map_attribute.gotmpl b/internal/resource_generate/templates/map_attribute.gotmpl index b074b839..b33467c7 100644 --- a/internal/resource_generate/templates/map_attribute.gotmpl +++ b/internal/resource_generate/templates/map_attribute.gotmpl @@ -1,6 +1,10 @@ "{{.Name}}": schema.MapAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} ElementType: {{.ElementType}}, +{{- end}} {{- template "common_attribute" .GeneratorMapAttribute }} {{- if gt (len .GeneratorMapAttribute.PlanModifiers) 0 }} PlanModifiers: []planmodifier.Map{ diff --git a/internal/schema/custom_map.go b/internal/schema/custom_map.go new file mode 100644 index 00000000..6704e51a --- /dev/null +++ b/internal/schema/custom_map.go @@ -0,0 +1,349 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type CustomMapType struct { + Name FrameworkIdentifier + templates map[string]string +} + +func NewCustomMapType(name string) CustomMapType { + t := map[string]string{ + "equal": MapTypeEqualTemplate, + "string": MapTypeStringTemplate, + "type": MapTypeTypeTemplate, + "typable": MapTypeTypableTemplate, + "valueFromMap": MapTypeValueFromMapTemplate, + "valueFromTerraform": MapTypeValueFromTerraformTemplate, + "valueType": MapTypeValueTypeTemplate, + } + + return CustomMapType{ + Name: FrameworkIdentifier(name), + templates: t, + } +} + +func (c CustomMapType) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderTypable, + c.renderType, + c.renderEqual, + c.renderString, + c.renderValueFromMap, + c.renderValueFromTerraform, + c.renderValueType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderString() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["string"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderTypable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["typable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderValueFromMap() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromMap"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderValueFromTerraform() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromTerraform"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapType) renderValueType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueType"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type CustomMapValue struct { + Name FrameworkIdentifier + ElementType string + templates map[string]string +} + +func NewCustomMapValue(name, elemType string) CustomMapValue { + t := map[string]string{ + "equal": MapValueEqualTemplate, + "type": MapValueTypeTemplate, + "valuable": MapValueValuableTemplate, + "value": MapValueValueTemplate, + } + + return CustomMapValue{ + Name: FrameworkIdentifier(name), + ElementType: elemType, + templates: t, + } +} + +func (c CustomMapValue) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderValuable, + c.renderValue, + c.renderEqual, + c.renderType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomMapValue) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapValue) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + ElementType string + }{ + Name: c.Name.ToPascalCase(), + ElementType: c.ElementType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapValue) renderValuable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valuable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomMapValue) renderValue() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["value"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/custom_map_test.go b/internal/schema/custom_map_test.go new file mode 100644 index 00000000..e4b6e817 --- /dev/null +++ b/internal/schema/custom_map_test.go @@ -0,0 +1,467 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCustomMapType_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`func (t ExampleType) Equal(o attr.Type) bool { +other, ok := o.(ExampleType) + +if !ok { +return false +} + +return t.MapType.Equal(other.MapType) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderString(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) String() string { +return "ExampleType" +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderString() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderTypable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`var _ basetypes.MapTypable = ExampleType{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderTypable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`type ExampleType struct { +basetypes.MapType +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderValueFromMap(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + attrValues map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + attrValues: map[string]string{ + "bool_attribute": "basetypes.MapValue", + }, + expected: []byte(` +func (t ExampleType) ValueFromMap(ctx context.Context, in basetypes.MapValue) (basetypes.MapValuable, diag.Diagnostics) { +return ExampleValue{ +MapValue: in, +}, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderValueFromMap() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderValueFromTerraform(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.MapType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +mapValue, ok := attrValue.(basetypes.MapValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +mapValuable, diags := t.ValueFromMap(ctx, mapValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting MapValue to MapValuable: %v", diags) +} + +return mapValuable, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderValueFromTerraform() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapType_renderValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueType(ctx context.Context) attr.Value { +return ExampleValue{} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapType := NewCustomMapType(testCase.name) + + got, err := customMapType.renderValueType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapValue_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Equal(o attr.Value) bool { +other, ok := o.(ExampleValue) + +if !ok { +return false +} + +return v.MapValue.Equal(other.MapValue) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapValue := NewCustomMapValue(testCase.name, testCase.elementType) + + got, err := customMapValue.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapValue_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Type(ctx context.Context) attr.Type { +return ExampleType{ +MapType: basetypes.MapType{ +ElemType: types.BoolType, +}, +} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapValue := NewCustomMapValue(testCase.name, testCase.elementType) + + got, err := customMapValue.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapValue_renderValuable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`var _ basetypes.MapValuable = ExampleValue{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapValue := NewCustomMapValue(testCase.name, testCase.elementType) + + got, err := customMapValue.renderValuable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomMapValue_renderValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`type ExampleValue struct { +basetypes.MapValue +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customMapValue := NewCustomMapValue(testCase.name, testCase.elementType) + + got, err := customMapValue.renderValue() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/schema/embed.go b/internal/schema/embed.go index 6c7ea03b..5934034b 100644 --- a/internal/schema/embed.go +++ b/internal/schema/embed.go @@ -187,6 +187,51 @@ var ListValueValueTemplate string //go:embed templates/list_value_valuable.gotmpl var ListValueValuableTemplate string +// Map From/To + +//go:embed templates/map_from.gotmpl +var MapFromTemplate string + +//go:embed templates/map_to.gotmpl +var MapToTemplate string + +// Map Type + +//go:embed templates/map_type_equal.gotmpl +var MapTypeEqualTemplate string + +//go:embed templates/map_type_string.gotmpl +var MapTypeStringTemplate string + +//go:embed templates/map_type_type.gotmpl +var MapTypeTypeTemplate string + +//go:embed templates/map_type_typable.gotmpl +var MapTypeTypableTemplate string + +//go:embed templates/map_type_value_from_map.gotmpl +var MapTypeValueFromMapTemplate string + +//go:embed templates/map_type_value_from_terraform.gotmpl +var MapTypeValueFromTerraformTemplate string + +//go:embed templates/map_type_value_type.gotmpl +var MapTypeValueTypeTemplate string + +// Map Value + +//go:embed templates/map_value_equal.gotmpl +var MapValueEqualTemplate string + +//go:embed templates/map_value_type.gotmpl +var MapValueTypeTemplate string + +//go:embed templates/map_value_value.gotmpl +var MapValueValueTemplate string + +//go:embed templates/map_value_valuable.gotmpl +var MapValueValuableTemplate string + // Number From/To //go:embed templates/number_from.gotmpl diff --git a/internal/schema/templates/list_from.gotmpl b/internal/schema/templates/list_from.gotmpl index 1dc5133a..c4051bae 100644 --- a/internal/schema/templates/list_from.gotmpl +++ b/internal/schema/templates/list_from.gotmpl @@ -8,7 +8,7 @@ types.ListNull({{.ElementTypeType}}), }, diags } - var elems []{{.ElementTypeValue}} +var elems []{{.ElementTypeValue}} for _, e := range *apiObject { elems = append(elems, {{.ElementFrom}}(e)) diff --git a/internal/schema/templates/map_from.gotmpl b/internal/schema/templates/map_from.gotmpl new file mode 100644 index 00000000..9f56a55a --- /dev/null +++ b/internal/schema/templates/map_from.gotmpl @@ -0,0 +1,30 @@ + +func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return {{.Name}}Value{ +types.MapNull({{.ElementTypeType}}), +}, diags +} + +elems := make(map[string]{{.ElementTypeValue}}) + +for k, e := range *apiObject { +elems[k] = {{.ElementFrom}}(e) +} + +l, d := basetypes.NewMapValueFrom(ctx, {{.ElementTypeType}}, elems) + +diags.Append(d...) + +if diags.HasError() { +return MapAttributeValue{ +types.MapNull({{.ElementTypeType}}), +}, diags +} + +return MapAttributeValue{ +l, +}, diags +} diff --git a/internal/schema/templates/map_to.gotmpl b/internal/schema/templates/map_to.gotmpl new file mode 100644 index 00000000..4be7e418 --- /dev/null +++ b/internal/schema/templates/map_to.gotmpl @@ -0,0 +1,28 @@ +func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"{{.Name}}Value Value Is Unknown", +`"{{.Name}}Value" is unknown.`, +)) + +return nil, diags +} + +var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} + +d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &{{.AssocExtType.ToCamelCase}}, diags +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_equal.gotmpl b/internal/schema/templates/map_type_equal.gotmpl new file mode 100644 index 00000000..4fb30481 --- /dev/null +++ b/internal/schema/templates/map_type_equal.gotmpl @@ -0,0 +1,9 @@ +func (t {{.Name}}Type) Equal(o attr.Type) bool { +other, ok := o.({{.Name}}Type) + +if !ok { +return false +} + +return t.MapType.Equal(other.MapType) +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_string.gotmpl b/internal/schema/templates/map_type_string.gotmpl new file mode 100644 index 00000000..f01b8ac3 --- /dev/null +++ b/internal/schema/templates/map_type_string.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) String() string { +return "{{.Name}}Type" +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_typable.gotmpl b/internal/schema/templates/map_type_typable.gotmpl new file mode 100644 index 00000000..57299978 --- /dev/null +++ b/internal/schema/templates/map_type_typable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.MapTypable = {{.Name}}Type{} \ No newline at end of file diff --git a/internal/schema/templates/map_type_type.gotmpl b/internal/schema/templates/map_type_type.gotmpl new file mode 100644 index 00000000..4e80e289 --- /dev/null +++ b/internal/schema/templates/map_type_type.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Type struct { +basetypes.MapType +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_value_from_map.gotmpl b/internal/schema/templates/map_type_value_from_map.gotmpl new file mode 100644 index 00000000..426126ee --- /dev/null +++ b/internal/schema/templates/map_type_value_from_map.gotmpl @@ -0,0 +1,6 @@ + +func (t {{.Name}}Type) ValueFromMap(ctx context.Context, in basetypes.MapValue) (basetypes.MapValuable, diag.Diagnostics) { +return {{.Name}}Value{ +MapValue: in, +}, nil +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_value_from_terraform.gotmpl b/internal/schema/templates/map_type_value_from_terraform.gotmpl new file mode 100644 index 00000000..0da65a1a --- /dev/null +++ b/internal/schema/templates/map_type_value_from_terraform.gotmpl @@ -0,0 +1,22 @@ + +func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.MapType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +mapValue, ok := attrValue.(basetypes.MapValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +mapValuable, diags := t.ValueFromMap(ctx, mapValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting MapValue to MapValuable: %v", diags) +} + +return mapValuable, nil +} \ No newline at end of file diff --git a/internal/schema/templates/map_type_value_type.gotmpl b/internal/schema/templates/map_type_value_type.gotmpl new file mode 100644 index 00000000..1f7b7fea --- /dev/null +++ b/internal/schema/templates/map_type_value_type.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { +return {{.Name}}Value{} +} \ No newline at end of file diff --git a/internal/schema/templates/map_value_equal.gotmpl b/internal/schema/templates/map_value_equal.gotmpl new file mode 100644 index 00000000..7662fabb --- /dev/null +++ b/internal/schema/templates/map_value_equal.gotmpl @@ -0,0 +1,10 @@ + +func (v {{.Name}}Value) Equal(o attr.Value) bool { +other, ok := o.({{.Name}}Value) + +if !ok { +return false +} + +return v.MapValue.Equal(other.MapValue) +} \ No newline at end of file diff --git a/internal/schema/templates/map_value_type.gotmpl b/internal/schema/templates/map_value_type.gotmpl new file mode 100644 index 00000000..15438603 --- /dev/null +++ b/internal/schema/templates/map_value_type.gotmpl @@ -0,0 +1,8 @@ + +func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { +return {{.Name}}Type{ +MapType: basetypes.MapType{ +ElemType: {{.ElementType}}, +}, +} +} \ No newline at end of file diff --git a/internal/schema/templates/map_value_valuable.gotmpl b/internal/schema/templates/map_value_valuable.gotmpl new file mode 100644 index 00000000..ea7a924b --- /dev/null +++ b/internal/schema/templates/map_value_valuable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.MapValuable = {{.Name}}Value{} \ No newline at end of file diff --git a/internal/schema/templates/map_value_value.gotmpl b/internal/schema/templates/map_value_value.gotmpl new file mode 100644 index 00000000..dc684fc8 --- /dev/null +++ b/internal/schema/templates/map_value_value.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Value struct { +basetypes.MapValue +} \ No newline at end of file diff --git a/internal/schema/templates/set_from.gotmpl b/internal/schema/templates/set_from.gotmpl index dfb7644b..f7be55a6 100644 --- a/internal/schema/templates/set_from.gotmpl +++ b/internal/schema/templates/set_from.gotmpl @@ -8,7 +8,7 @@ types.SetNull({{.ElementTypeType}}), }, diags } - var elems []{{.ElementTypeValue}} +var elems []{{.ElementTypeValue}} for _, e := range *apiObject { elems = append(elems, {{.ElementFrom}}(e)) diff --git a/internal/schema/to_from_list_test.go b/internal/schema/to_from_list_test.go index 075c6c5e..e5575e49 100644 --- a/internal/schema/to_from_list_test.go +++ b/internal/schema/to_from_list_test.go @@ -46,7 +46,7 @@ types.ListNull(types.BoolType), }, diags } - var elems []types.Bool +var elems []types.Bool for _, e := range *apiObject { elems = append(elems, types.BoolPointerValue(e)) diff --git a/internal/schema/to_from_map.go b/internal/schema/to_from_map.go new file mode 100644 index 00000000..c814fe26 --- /dev/null +++ b/internal/schema/to_from_map.go @@ -0,0 +1,111 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type ToFromMap struct { + Name FrameworkIdentifier + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + templates map[string]string +} + +func NewToFromMap(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromMap { + t := map[string]string{ + "from": MapFromTemplate, + "to": MapToTemplate, + } + + return ToFromMap{ + Name: FrameworkIdentifier(name), + AssocExtType: assocExtType, + ElementTypeType: elemTypeType, + ElementTypeValue: elemTypeValue, + ElementFrom: elemFrom, + templates: t, + } +} + +func (o ToFromMap) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + o.renderTo, + o.renderFrom, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (o ToFromMap) renderTo() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["to"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (o ToFromMap) renderFrom() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["from"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + ElementTypeType string + ElementTypeValue string + ElementFrom string + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + ElementTypeType: o.ElementTypeType, + ElementTypeValue: o.ElementTypeValue, + ElementFrom: o.ElementFrom, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/to_from_map_test.go b/internal/schema/to_from_map_test.go new file mode 100644 index 00000000..d4a5664f --- /dev/null +++ b/internal/schema/to_from_map_test.go @@ -0,0 +1,169 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" + "github.com/hashicorp/terraform-plugin-codegen-spec/schema" +) + +func TestToFromMap_renderFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return ExampleValue{ +types.MapNull(types.BoolType), +}, diags +} + +elems := make(map[string]types.Bool) + +for k, e := range *apiObject { +elems[k] = types.BoolPointerValue(e) +} + +l, d := basetypes.NewMapValueFrom(ctx, types.BoolType, elems) + +diags.Append(d...) + +if diags.HasError() { +return MapAttributeValue{ +types.MapNull(types.BoolType), +}, diags +} + +return MapAttributeValue{ +l, +}, diags +} +`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromMap := NewToFromMap(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromMap.renderFrom() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestToFromMap_renderTo(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + elemTypeType string + elemTypeValue string + elemFrom string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + elemTypeType: "types.BoolType", + elemTypeValue: "types.Bool", + elemFrom: "types.BoolPointerValue", + 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 apisdkType apisdk.Type + +d := v.ElementsAs(ctx, &apisdkType, false) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdkType, diags +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromMap := NewToFromMap(testCase.name, testCase.assocExtType, testCase.elemTypeType, testCase.elemTypeValue, testCase.elemFrom) + + got, err := toFromMap.renderTo() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/schema/to_from_set_test.go b/internal/schema/to_from_set_test.go index 773ab45f..ef529d2a 100644 --- a/internal/schema/to_from_set_test.go +++ b/internal/schema/to_from_set_test.go @@ -46,7 +46,7 @@ types.SetNull(types.BoolType), }, diags } - var elems []types.Bool +var elems []types.Bool for _, e := range *apiObject { elems = append(elems, types.BoolPointerValue(e)) From 668c420f8151462ecf8037ba783eaef0e4382f23 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 13:29:00 +0100 Subject: [PATCH 05/13] Refactoring import, schema, and model field methods on object attribute to use associated external type (#74) --- .../datasource_convert/object_attribute.go | 26 +-- internal/datasource_generate/embed.go | 2 +- .../datasource_generate/object_attribute.go | 57 +++++- .../object_attribute_test.go | 183 +++++++++++++++++- .../templates/object_attribute.gotmpl | 4 + internal/provider_convert/object_attribute.go | 25 +-- internal/provider_generate/embed.go | 2 +- .../provider_generate/object_attribute.go | 25 ++- .../object_attribute_test.go | 183 +++++++++++++++++- .../templates/object_attribute.gotmpl | 4 + internal/resource_convert/object_attribute.go | 31 +-- internal/resource_generate/embed.go | 2 +- .../resource_generate/object_attribute.go | 25 ++- .../object_attribute_test.go | 183 +++++++++++++++++- .../templates/object_attribute.gotmpl | 4 + 15 files changed, 697 insertions(+), 59 deletions(-) diff --git a/internal/datasource_convert/object_attribute.go b/internal/datasource_convert/object_attribute.go index 908306e6..b9f8a93c 100644 --- a/internal/datasource_convert/object_attribute.go +++ b/internal/datasource_convert/object_attribute.go @@ -10,26 +10,28 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/datasource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) -func convertObjectAttribute(o *datasource.ObjectAttribute) (datasource_generate.GeneratorObjectAttribute, error) { - if o == nil { +func convertObjectAttribute(a *datasource.ObjectAttribute) (datasource_generate.GeneratorObjectAttribute, error) { + if a == nil { return datasource_generate.GeneratorObjectAttribute{}, fmt.Errorf("*datasource.ObjectAttribute is nil") } return datasource_generate.GeneratorObjectAttribute{ ObjectAttribute: schema.ObjectAttribute{ - Required: isRequired(o.ComputedOptionalRequired), - Optional: isOptional(o.ComputedOptionalRequired), - Computed: isComputed(o.ComputedOptionalRequired), - Sensitive: isSensitive(o.Sensitive), - Description: description(o.Description), - MarkdownDescription: description(o.Description), - DeprecationMessage: deprecationMessage(o.DeprecationMessage), + Required: isRequired(a.ComputedOptionalRequired), + Optional: isOptional(a.ComputedOptionalRequired), + Computed: isComputed(a.ComputedOptionalRequired), + Sensitive: isSensitive(a.Sensitive), + Description: description(a.Description), + MarkdownDescription: description(a.Description), + DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - AttributeTypes: o.AttributeTypes, - CustomType: o.CustomType, - Validators: o.Validators, + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + AttributeTypes: a.AttributeTypes, + CustomType: a.CustomType, + Validators: a.Validators, }, nil } diff --git a/internal/datasource_generate/embed.go b/internal/datasource_generate/embed.go index e8a7b61b..9e089cb2 100644 --- a/internal/datasource_generate/embed.go +++ b/internal/datasource_generate/embed.go @@ -34,7 +34,7 @@ var mapNestedAttributeGoTemplate string var numberAttributeTemplate string //go:embed templates/object_attribute.gotmpl -var objectAttributeGoTemplate string +var objectAttributeTemplate string //go:embed templates/set_attribute.gotmpl var setAttributeTemplate string diff --git a/internal/datasource_generate/object_attribute.go b/internal/datasource_generate/object_attribute.go index 9ae24c0b..1380c2f5 100644 --- a/internal/datasource_generate/object_attribute.go +++ b/internal/datasource_generate/object_attribute.go @@ -4,6 +4,7 @@ package datasource_generate import ( + "fmt" "strings" "text/template" @@ -17,6 +18,7 @@ import ( type GeneratorObjectAttribute struct { schema.ObjectAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. AttributeTypes specschema.ObjectAttributeTypes @@ -46,6 +48,12 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -75,6 +83,7 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie type attribute struct { Name string AttributeTypes string + CustomType string GeneratorObjectAttribute GeneratorObjectAttribute } @@ -84,16 +93,19 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie GeneratorObjectAttribute: g, } - funcMap := template.FuncMap{ - "getAttrTypes": generatorschema.GetAttrTypes, + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ObjectType{\nAttrTypes: %sValue{}.AttributeTypes(ctx),\n},\n}", name.ToPascalCase(), name.ToPascalCase()) } - t, err := template.New("object_attribute").Funcs(funcMap).Parse(objectAttributeGoTemplate) + t, err := template.New("object_attribute").Parse(objectAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -114,9 +126,44 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent ValueType: model.ObjectValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil } + +//func (g GeneratorObjectAttribute) CustomTypeAndValue(name string) ([]byte, error) { +// if g.AssociatedExternalType == nil { +// return nil, nil +// } +// +// var buf bytes.Buffer +// +// listType := generatorschema.NewCustomObjectType(name) +// +// b, err := listType.Render() +// +// if err != nil { +// return nil, err +// } +// +// buf.Write(b) +// +// elemType := generatorschema.GetElementType(g.ElementType) +// +// listValue := generatorschema.NewCustomObjectValue(name, elemType) +// +// b, err = listValue.Render() +// +// if err != nil { +// return nil, err +// } +// +// buf.Write(b) +// +// return buf.Bytes(), nil +//} diff --git a/internal/datasource_generate/object_attribute_test.go b/internal/datasource_generate/object_attribute_test.go index b62e87e2..074251fc 100644 --- a/internal/datasource_generate/object_attribute_test.go +++ b/internal/datasource_generate/object_attribute_test.go @@ -257,6 +257,110 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -634,9 +738,53 @@ AttributeTypes: map[string]attr.Type{ }, expected: ` "object_attribute": schema.ObjectAttribute{ -AttributeTypes: map[string]attr.Type{ -"str": types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ +CustomType: ObjectAttributeType{ +types.ObjectType{ +AttrTypes: ObjectAttributeValue{}.AttributeTypes(ctx), +}, }, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ CustomType: my_custom_type, },`, }, @@ -1075,6 +1223,37 @@ func TestGeneratorObjectAttribute_ModelField(t *testing.T) { TfsdkName: "object_attribute", }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "ObjectAttributeValue", + TfsdkName: "object_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "object_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/datasource_generate/templates/object_attribute.gotmpl b/internal/datasource_generate/templates/object_attribute.gotmpl index 612a1a52..9d846a34 100644 --- a/internal/datasource_generate/templates/object_attribute.gotmpl +++ b/internal/datasource_generate/templates/object_attribute.gotmpl @@ -1,8 +1,12 @@ "{{.Name}}": schema.ObjectAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} AttributeTypes: map[string]attr.Type{ {{.AttributeTypes}} }, +{{- end}} {{- template "common_attribute" .GeneratorObjectAttribute }} {{- if gt (len .GeneratorObjectAttribute.Validators) 0 }} Validators: []validator.Object{ diff --git a/internal/provider_convert/object_attribute.go b/internal/provider_convert/object_attribute.go index 4cdd983f..4039a2ea 100644 --- a/internal/provider_convert/object_attribute.go +++ b/internal/provider_convert/object_attribute.go @@ -10,24 +10,27 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/provider_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) -func convertObjectAttribute(o *provider.ObjectAttribute) (provider_generate.GeneratorObjectAttribute, error) { - if o == nil { +func convertObjectAttribute(a *provider.ObjectAttribute) (provider_generate.GeneratorObjectAttribute, error) { + if a == nil { return provider_generate.GeneratorObjectAttribute{}, fmt.Errorf("*provider.ObjectAttribute is nil") } return provider_generate.GeneratorObjectAttribute{ ObjectAttribute: schema.ObjectAttribute{ - Required: isRequired(o.OptionalRequired), - Optional: isOptional(o.OptionalRequired), - Sensitive: isSensitive(o.Sensitive), - Description: description(o.Description), - MarkdownDescription: description(o.Description), - DeprecationMessage: deprecationMessage(o.DeprecationMessage), + Required: isRequired(a.OptionalRequired), + Optional: isOptional(a.OptionalRequired), + Sensitive: isSensitive(a.Sensitive), + Description: description(a.Description), + MarkdownDescription: description(a.Description), + DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - AttributeTypes: o.AttributeTypes, - CustomType: o.CustomType, - Validators: o.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + AttributeTypes: a.AttributeTypes, + CustomType: a.CustomType, + Validators: a.Validators, }, nil } diff --git a/internal/provider_generate/embed.go b/internal/provider_generate/embed.go index 19e2b7e9..fc876dbc 100644 --- a/internal/provider_generate/embed.go +++ b/internal/provider_generate/embed.go @@ -34,7 +34,7 @@ var mapNestedAttributeGoTemplate string var numberAttributeTemplate string //go:embed templates/object_attribute.gotmpl -var objectAttributeGoTemplate string +var objectAttributeTemplate string //go:embed templates/set_attribute.gotmpl var setAttributeTemplate string diff --git a/internal/provider_generate/object_attribute.go b/internal/provider_generate/object_attribute.go index f982a964..d1074341 100644 --- a/internal/provider_generate/object_attribute.go +++ b/internal/provider_generate/object_attribute.go @@ -4,6 +4,7 @@ package provider_generate import ( + "fmt" "strings" "text/template" @@ -17,6 +18,7 @@ import ( type GeneratorObjectAttribute struct { schema.ObjectAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. AttributeTypes specschema.ObjectAttributeTypes @@ -46,6 +48,12 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -75,6 +83,7 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie type attribute struct { Name string AttributeTypes string + CustomType string GeneratorObjectAttribute GeneratorObjectAttribute } @@ -84,16 +93,19 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie GeneratorObjectAttribute: g, } - funcMap := template.FuncMap{ - "getAttrTypes": generatorschema.GetAttrTypes, + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ObjectType{\nAttrTypes: %sValue{}.AttributeTypes(ctx),\n},\n}", name.ToPascalCase(), name.ToPascalCase()) } - t, err := template.New("object_attribute").Funcs(funcMap).Parse(objectAttributeGoTemplate) + t, err := template.New("object_attribute").Parse(objectAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -114,8 +126,11 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent ValueType: model.ObjectValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil diff --git a/internal/provider_generate/object_attribute_test.go b/internal/provider_generate/object_attribute_test.go index d6dfbff5..6958358e 100644 --- a/internal/provider_generate/object_attribute_test.go +++ b/internal/provider_generate/object_attribute_test.go @@ -257,6 +257,110 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -634,9 +738,53 @@ AttributeTypes: map[string]attr.Type{ }, expected: ` "object_attribute": schema.ObjectAttribute{ -AttributeTypes: map[string]attr.Type{ -"str": types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ +CustomType: ObjectAttributeType{ +types.ObjectType{ +AttrTypes: ObjectAttributeValue{}.AttributeTypes(ctx), +}, }, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ CustomType: my_custom_type, },`, }, @@ -1054,6 +1202,37 @@ func TestGeneratorObjectAttribute_ModelField(t *testing.T) { TfsdkName: "object_attribute", }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "ObjectAttributeValue", + TfsdkName: "object_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "object_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/provider_generate/templates/object_attribute.gotmpl b/internal/provider_generate/templates/object_attribute.gotmpl index 612a1a52..9d846a34 100644 --- a/internal/provider_generate/templates/object_attribute.gotmpl +++ b/internal/provider_generate/templates/object_attribute.gotmpl @@ -1,8 +1,12 @@ "{{.Name}}": schema.ObjectAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} AttributeTypes: map[string]attr.Type{ {{.AttributeTypes}} }, +{{- end}} {{- template "common_attribute" .GeneratorObjectAttribute }} {{- if gt (len .GeneratorObjectAttribute.Validators) 0 }} Validators: []validator.Object{ diff --git a/internal/resource_convert/object_attribute.go b/internal/resource_convert/object_attribute.go index 8dd5687f..0cd81bcd 100644 --- a/internal/resource_convert/object_attribute.go +++ b/internal/resource_convert/object_attribute.go @@ -10,27 +10,30 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-codegen-framework/internal/resource_generate" + generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) -func convertObjectAttribute(o *resource.ObjectAttribute) (resource_generate.GeneratorObjectAttribute, error) { - if o == nil { +func convertObjectAttribute(a *resource.ObjectAttribute) (resource_generate.GeneratorObjectAttribute, error) { + if a == nil { return resource_generate.GeneratorObjectAttribute{}, fmt.Errorf("*resource.ObjectAttribute is nil") } return resource_generate.GeneratorObjectAttribute{ ObjectAttribute: schema.ObjectAttribute{ - Required: isRequired(o.ComputedOptionalRequired), - Optional: isOptional(o.ComputedOptionalRequired), - Computed: isComputed(o.ComputedOptionalRequired), - Sensitive: isSensitive(o.Sensitive), - Description: description(o.Description), - MarkdownDescription: description(o.Description), - DeprecationMessage: deprecationMessage(o.DeprecationMessage), + Required: isRequired(a.ComputedOptionalRequired), + Optional: isOptional(a.ComputedOptionalRequired), + Computed: isComputed(a.ComputedOptionalRequired), + Sensitive: isSensitive(a.Sensitive), + Description: description(a.Description), + MarkdownDescription: description(a.Description), + DeprecationMessage: deprecationMessage(a.DeprecationMessage), }, - AttributeTypes: o.AttributeTypes, - CustomType: o.CustomType, - Default: o.Default, - PlanModifiers: o.PlanModifiers, - Validators: o.Validators, + + AssociatedExternalType: generatorschema.NewAssocExtType(a.AssociatedExternalType), + AttributeTypes: a.AttributeTypes, + CustomType: a.CustomType, + Default: a.Default, + PlanModifiers: a.PlanModifiers, + Validators: a.Validators, }, nil } diff --git a/internal/resource_generate/embed.go b/internal/resource_generate/embed.go index 44a0ec33..9b6f703d 100644 --- a/internal/resource_generate/embed.go +++ b/internal/resource_generate/embed.go @@ -34,7 +34,7 @@ var mapNestedAttributeGoTemplate string var numberAttributeTemplate string //go:embed templates/object_attribute.gotmpl -var objectAttributeGoTemplate string +var objectAttributeTemplate string //go:embed templates/set_attribute.gotmpl var setAttributeTemplate string diff --git a/internal/resource_generate/object_attribute.go b/internal/resource_generate/object_attribute.go index 063a199a..623a4a8e 100644 --- a/internal/resource_generate/object_attribute.go +++ b/internal/resource_generate/object_attribute.go @@ -4,6 +4,7 @@ package resource_generate import ( + "fmt" "strings" "text/template" @@ -17,6 +18,7 @@ import ( type GeneratorObjectAttribute struct { schema.ObjectAttribute + AssociatedExternalType *generatorschema.AssocExtType // The "specschema" types are used instead of the types within the attribute // because support for extracting custom import information is required. AttributeTypes specschema.ObjectAttributeTypes @@ -58,6 +60,12 @@ func (g GeneratorObjectAttribute) Imports() *generatorschema.Imports { imports.Append(customValidatorImports) } + if g.AssociatedExternalType != nil { + imports.Append(generatorschema.AssociatedExternalTypeImports()) + } + + imports.Append(g.AssociatedExternalType.Imports()) + return imports } @@ -106,6 +114,7 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie type attribute struct { Name string AttributeTypes string + CustomType string Default string GeneratorObjectAttribute GeneratorObjectAttribute } @@ -117,12 +126,19 @@ func (g GeneratorObjectAttribute) Schema(name generatorschema.FrameworkIdentifie GeneratorObjectAttribute: g, } - t, err := template.New("object_attribute").Parse(objectAttributeGoTemplate) + switch { + case g.CustomType != nil: + a.CustomType = g.CustomType.Type + case g.AssociatedExternalType != nil: + a.CustomType = fmt.Sprintf("%sType{\ntypes.ObjectType{\nAttrTypes: %sValue{}.AttributeTypes(ctx),\n},\n}", name.ToPascalCase(), name.ToPascalCase()) + } + + t, err := template.New("object_attribute").Parse(objectAttributeTemplate) if err != nil { return "", err } - if _, err = addCommonAttributeTemplate(t); err != nil { + if _, err = addAttributeTemplate(t); err != nil { return "", err } @@ -143,8 +159,11 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent ValueType: model.ObjectValueType, } - if g.CustomType != nil { + switch { + case g.CustomType != nil: field.ValueType = g.CustomType.ValueType + case g.AssociatedExternalType != nil: + field.ValueType = fmt.Sprintf("%sValue", name.ToPascalCase()) } return field, nil diff --git a/internal/resource_generate/object_attribute_test.go b/internal/resource_generate/object_attribute_test.go index 35367e1d..f03e8969 100644 --- a/internal/resource_generate/object_attribute_test.go +++ b/internal/resource_generate/object_attribute_test.go @@ -410,6 +410,110 @@ func TestGeneratorObjectAttribute_Imports(t *testing.T) { }, }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + }, + }, + "associated-external-type-with-import": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/hashicorp/terraform-plugin-framework/types", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, + "associated-external-type-with-custom-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Import: &code.Import{ + Path: "github.com/api", + }, + Type: "*api.ObjectAttribute", + }, + }, + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "fmt", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/diag", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/attr", + }, + { + Path: "github.com/hashicorp/terraform-plugin-go/tftypes", + }, + { + Path: "github.com/hashicorp/terraform-plugin-framework/types/basetypes", + }, + { + Path: "github.com/api", + }, + }, + }, } for name, testCase := range testCases { @@ -787,9 +891,53 @@ AttributeTypes: map[string]attr.Type{ }, expected: ` "object_attribute": schema.ObjectAttribute{ -AttributeTypes: map[string]attr.Type{ -"str": types.StringType, +CustomType: my_custom_type, +},`, + }, + + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ +CustomType: ObjectAttributeType{ +types.ObjectType{ +AttrTypes: ObjectAttributeValue{}.AttributeTypes(ctx), +}, }, +},`, + }, + + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.ObjectAttribute", + }, + }, + AttributeTypes: specschema.ObjectAttributeTypes{ + specschema.ObjectAttributeType{ + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + CustomType: &specschema.CustomType{ + Type: "my_custom_type", + }, + }, + expected: ` +"object_attribute": schema.ObjectAttribute{ CustomType: my_custom_type, },`, }, @@ -1284,6 +1432,37 @@ func TestGeneratorObjectAttribute_ModelField(t *testing.T) { TfsdkName: "object_attribute", }, }, + "associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "ObjectAttributeValue", + TfsdkName: "object_attribute", + }, + }, + "custom-type-overriding-associated-external-type": { + input: GeneratorObjectAttribute{ + AssociatedExternalType: &generatorschema.AssocExtType{ + AssociatedExternalType: &specschema.AssociatedExternalType{ + Type: "*api.BoolAttribute", + }, + }, + CustomType: &specschema.CustomType{ + ValueType: "my_custom_value_type", + }, + }, + expected: model.Field{ + Name: "ObjectAttribute", + ValueType: "my_custom_value_type", + TfsdkName: "object_attribute", + }, + }, } for name, testCase := range testCases { diff --git a/internal/resource_generate/templates/object_attribute.gotmpl b/internal/resource_generate/templates/object_attribute.gotmpl index efe91445..763513bf 100644 --- a/internal/resource_generate/templates/object_attribute.gotmpl +++ b/internal/resource_generate/templates/object_attribute.gotmpl @@ -1,8 +1,12 @@ "{{.Name}}": schema.ObjectAttribute{ +{{- if .CustomType}} +CustomType: {{.CustomType}}, +{{- else}} AttributeTypes: map[string]attr.Type{ {{.AttributeTypes}} }, +{{- end}} {{- template "common_attribute" .GeneratorObjectAttribute }} {{- if gt (len .GeneratorObjectAttribute.PlanModifiers) 0 }} PlanModifiers: []planmodifier.Object{ From 454e9bd9ba73f89dbca5913a83f732479193b2fb Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 13:41:49 +0100 Subject: [PATCH 06/13] Refactoring to use nested object as reference to object within list, map, set, single nested attribute, or list, set, single nested block (#74) --- .../list_nested_attribute.go | 6 +- .../datasource_generate/list_nested_block.go | 6 +- .../map_nested_attribute.go | 6 +- .../set_nested_attribute.go | 6 +- .../datasource_generate/set_nested_block.go | 6 +- .../single_nested_attribute.go | 6 +- .../single_nested_block.go | 6 +- .../list_nested_attribute.go | 6 +- .../provider_generate/list_nested_block.go | 6 +- .../provider_generate/map_nested_attribute.go | 6 +- .../provider_generate/set_nested_attribute.go | 6 +- .../provider_generate/set_nested_block.go | 6 +- .../single_nested_attribute.go | 6 +- .../provider_generate/single_nested_block.go | 6 +- .../list_nested_attribute.go | 6 +- .../resource_generate/list_nested_block.go | 6 +- .../resource_generate/map_nested_attribute.go | 6 +- .../resource_generate/set_nested_attribute.go | 6 +- .../resource_generate/set_nested_block.go | 6 +- .../single_nested_attribute.go | 6 +- .../resource_generate/single_nested_block.go | 6 +- ...stom_object.go => custom_nested_object.go} | 100 +++++++++--------- ...t_test.go => custom_nested_object_test.go} | 42 ++++---- internal/schema/embed.go | 98 ++++++++--------- ..._from.gotmpl => nested_object_from.gotmpl} | 0 ...ject_to.gotmpl => nested_object_to.gotmpl} | 0 ...gotmpl => nested_object_type_equal.gotmpl} | 0 ...otmpl => nested_object_type_string.gotmpl} | 0 ...tmpl => nested_object_type_typable.gotmpl} | 0 ....gotmpl => nested_object_type_type.gotmpl} | 0 ...gotmpl => nested_object_type_value.gotmpl} | 0 ...sted_object_type_value_from_object.gotmpl} | 0 ...d_object_type_value_from_terraform.gotmpl} | 0 ...l => nested_object_type_value_must.gotmpl} | 0 ...l => nested_object_type_value_null.gotmpl} | 0 ...l => nested_object_type_value_type.gotmpl} | 0 ...> nested_object_type_value_unknown.gotmpl} | 0 ...ested_object_value_attribute_types.gotmpl} | 0 ...otmpl => nested_object_value_equal.gotmpl} | 0 ...mpl => nested_object_value_is_null.gotmpl} | 0 ... => nested_object_value_is_unknown.gotmpl} | 0 ...tmpl => nested_object_value_string.gotmpl} | 0 ...ested_object_value_to_object_value.gotmpl} | 0 ...ed_object_value_to_terraform_value.gotmpl} | 0 ...gotmpl => nested_object_value_type.gotmpl} | 0 ...pl => nested_object_value_valuable.gotmpl} | 0 ...otmpl => nested_object_value_value.gotmpl} | 0 ...rom_object.go => to_from_nested_object.go} | 16 +-- ..._test.go => to_from_nested_object_test.go} | 4 +- 49 files changed, 193 insertions(+), 193 deletions(-) rename internal/schema/{custom_object.go => custom_nested_object.go} (73%) rename internal/schema/{custom_object_test.go => custom_nested_object_test.go} (93%) rename internal/schema/templates/{object_from.gotmpl => nested_object_from.gotmpl} (100%) rename internal/schema/templates/{object_to.gotmpl => nested_object_to.gotmpl} (100%) rename internal/schema/templates/{object_type_equal.gotmpl => nested_object_type_equal.gotmpl} (100%) rename internal/schema/templates/{object_type_string.gotmpl => nested_object_type_string.gotmpl} (100%) rename internal/schema/templates/{object_type_typable.gotmpl => nested_object_type_typable.gotmpl} (100%) rename internal/schema/templates/{object_type_type.gotmpl => nested_object_type_type.gotmpl} (100%) rename internal/schema/templates/{object_type_value.gotmpl => nested_object_type_value.gotmpl} (100%) rename internal/schema/templates/{object_type_value_from_object.gotmpl => nested_object_type_value_from_object.gotmpl} (100%) rename internal/schema/templates/{object_type_value_from_terraform.gotmpl => nested_object_type_value_from_terraform.gotmpl} (100%) rename internal/schema/templates/{object_type_value_must.gotmpl => nested_object_type_value_must.gotmpl} (100%) rename internal/schema/templates/{object_type_value_null.gotmpl => nested_object_type_value_null.gotmpl} (100%) rename internal/schema/templates/{object_type_value_type.gotmpl => nested_object_type_value_type.gotmpl} (100%) rename internal/schema/templates/{object_type_value_unknown.gotmpl => nested_object_type_value_unknown.gotmpl} (100%) rename internal/schema/templates/{object_value_attribute_types.gotmpl => nested_object_value_attribute_types.gotmpl} (100%) rename internal/schema/templates/{object_value_equal.gotmpl => nested_object_value_equal.gotmpl} (100%) rename internal/schema/templates/{object_value_is_null.gotmpl => nested_object_value_is_null.gotmpl} (100%) rename internal/schema/templates/{object_value_is_unknown.gotmpl => nested_object_value_is_unknown.gotmpl} (100%) rename internal/schema/templates/{object_value_string.gotmpl => nested_object_value_string.gotmpl} (100%) rename internal/schema/templates/{object_value_to_object_value.gotmpl => nested_object_value_to_object_value.gotmpl} (100%) rename internal/schema/templates/{object_value_to_terraform_value.gotmpl => nested_object_value_to_terraform_value.gotmpl} (100%) rename internal/schema/templates/{object_value_type.gotmpl => nested_object_value_type.gotmpl} (100%) rename internal/schema/templates/{object_value_valuable.gotmpl => nested_object_value_valuable.gotmpl} (100%) rename internal/schema/templates/{object_value_value.gotmpl => nested_object_value_value.gotmpl} (100%) rename internal/schema/{to_from_object.go => to_from_nested_object.go} (81%) rename internal/schema/{to_from_object_test.go => to_from_nested_object_test.go} (95%) diff --git a/internal/datasource_generate/list_nested_attribute.go b/internal/datasource_generate/list_nested_attribute.go index 5fda1523..9c9f35c2 100644 --- a/internal/datasource_generate/list_nested_attribute.go +++ b/internal/datasource_generate/list_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorListNestedAttribute) ToFromFunctions(name string) ([]byte, erro fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/list_nested_block.go b/internal/datasource_generate/list_nested_block.go index 51231ec2..a9fef335 100644 --- a/internal/datasource_generate/list_nested_block.go +++ b/internal/datasource_generate/list_nested_block.go @@ -185,7 +185,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -239,7 +239,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -293,7 +293,7 @@ func (g GeneratorListNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/map_nested_attribute.go b/internal/datasource_generate/map_nested_attribute.go index 73a12fa3..172d4443 100644 --- a/internal/datasource_generate/map_nested_attribute.go +++ b/internal/datasource_generate/map_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorMapNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/set_nested_attribute.go b/internal/datasource_generate/set_nested_attribute.go index e71e5f59..d13b1eea 100644 --- a/internal/datasource_generate/set_nested_attribute.go +++ b/internal/datasource_generate/set_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorSetNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/set_nested_block.go b/internal/datasource_generate/set_nested_block.go index bbc51f81..f9e82127 100644 --- a/internal/datasource_generate/set_nested_block.go +++ b/internal/datasource_generate/set_nested_block.go @@ -185,7 +185,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -239,7 +239,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -293,7 +293,7 @@ func (g GeneratorSetNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/single_nested_attribute.go b/internal/datasource_generate/single_nested_attribute.go index c98ec5c3..2aacdd5c 100644 --- a/internal/datasource_generate/single_nested_attribute.go +++ b/internal/datasource_generate/single_nested_attribute.go @@ -156,7 +156,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -178,7 +178,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -218,7 +218,7 @@ func (g GeneratorSingleNestedAttribute) ToFromFunctions(name string) ([]byte, er fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/datasource_generate/single_nested_block.go b/internal/datasource_generate/single_nested_block.go index 43f0c981..c500d23d 100644 --- a/internal/datasource_generate/single_nested_block.go +++ b/internal/datasource_generate/single_nested_block.go @@ -199,7 +199,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -253,7 +253,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -307,7 +307,7 @@ func (g GeneratorSingleNestedBlock) ToFromFunctions(name string) ([]byte, error) fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/list_nested_attribute.go b/internal/provider_generate/list_nested_attribute.go index b74a430e..ca2bc13f 100644 --- a/internal/provider_generate/list_nested_attribute.go +++ b/internal/provider_generate/list_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorListNestedAttribute) ToFromFunctions(name string) ([]byte, erro fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/list_nested_block.go b/internal/provider_generate/list_nested_block.go index 87247e4c..78be3ef1 100644 --- a/internal/provider_generate/list_nested_block.go +++ b/internal/provider_generate/list_nested_block.go @@ -185,7 +185,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -239,7 +239,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -293,7 +293,7 @@ func (g GeneratorListNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/map_nested_attribute.go b/internal/provider_generate/map_nested_attribute.go index df29bb74..b0b694b0 100644 --- a/internal/provider_generate/map_nested_attribute.go +++ b/internal/provider_generate/map_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorMapNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/set_nested_attribute.go b/internal/provider_generate/set_nested_attribute.go index 2da67f98..03c1dcd8 100644 --- a/internal/provider_generate/set_nested_attribute.go +++ b/internal/provider_generate/set_nested_attribute.go @@ -153,7 +153,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -175,7 +175,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -215,7 +215,7 @@ func (g GeneratorSetNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/set_nested_block.go b/internal/provider_generate/set_nested_block.go index 1a5fd1d7..a1003366 100644 --- a/internal/provider_generate/set_nested_block.go +++ b/internal/provider_generate/set_nested_block.go @@ -185,7 +185,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -239,7 +239,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -293,7 +293,7 @@ func (g GeneratorSetNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/single_nested_attribute.go b/internal/provider_generate/single_nested_attribute.go index 772834d0..37223c86 100644 --- a/internal/provider_generate/single_nested_attribute.go +++ b/internal/provider_generate/single_nested_attribute.go @@ -156,7 +156,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -178,7 +178,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -218,7 +218,7 @@ func (g GeneratorSingleNestedAttribute) ToFromFunctions(name string) ([]byte, er fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/single_nested_block.go b/internal/provider_generate/single_nested_block.go index 94036cf5..86deef7c 100644 --- a/internal/provider_generate/single_nested_block.go +++ b/internal/provider_generate/single_nested_block.go @@ -199,7 +199,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -253,7 +253,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -307,7 +307,7 @@ func (g GeneratorSingleNestedBlock) ToFromFunctions(name string) ([]byte, error) fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/list_nested_attribute.go b/internal/resource_generate/list_nested_attribute.go index b61cbdec..00a9ce9a 100644 --- a/internal/resource_generate/list_nested_attribute.go +++ b/internal/resource_generate/list_nested_attribute.go @@ -178,7 +178,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -200,7 +200,7 @@ func (g GeneratorListNestedAttribute) CustomTypeAndValue(name string) ([]byte, e return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -240,7 +240,7 @@ func (g GeneratorListNestedAttribute) ToFromFunctions(name string) ([]byte, erro fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/list_nested_block.go b/internal/resource_generate/list_nested_block.go index 98ccc117..2702c1b6 100644 --- a/internal/resource_generate/list_nested_block.go +++ b/internal/resource_generate/list_nested_block.go @@ -200,7 +200,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -254,7 +254,7 @@ func (g GeneratorListNestedBlock) CustomTypeAndValue(name string) ([]byte, error attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -310,7 +310,7 @@ func (g GeneratorListNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/map_nested_attribute.go b/internal/resource_generate/map_nested_attribute.go index 8e883d06..c5a4b009 100644 --- a/internal/resource_generate/map_nested_attribute.go +++ b/internal/resource_generate/map_nested_attribute.go @@ -178,7 +178,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -200,7 +200,7 @@ func (g GeneratorMapNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -240,7 +240,7 @@ func (g GeneratorMapNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/set_nested_attribute.go b/internal/resource_generate/set_nested_attribute.go index 60d955cf..9aad7d8e 100644 --- a/internal/resource_generate/set_nested_attribute.go +++ b/internal/resource_generate/set_nested_attribute.go @@ -178,7 +178,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -200,7 +200,7 @@ func (g GeneratorSetNestedAttribute) CustomTypeAndValue(name string) ([]byte, er return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -240,7 +240,7 @@ func (g GeneratorSetNestedAttribute) ToFromFunctions(name string) ([]byte, error fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/set_nested_block.go b/internal/resource_generate/set_nested_block.go index 35ff7979..2ec949db 100644 --- a/internal/resource_generate/set_nested_block.go +++ b/internal/resource_generate/set_nested_block.go @@ -200,7 +200,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -254,7 +254,7 @@ func (g GeneratorSetNestedBlock) CustomTypeAndValue(name string) ([]byte, error) attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -308,7 +308,7 @@ func (g GeneratorSetNestedBlock) ToFromFunctions(name string) ([]byte, error) { fromFuncs := g.NestedObject.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.NestedObject.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/single_nested_attribute.go b/internal/resource_generate/single_nested_attribute.go index cc50e94a..dbf120ac 100644 --- a/internal/resource_generate/single_nested_attribute.go +++ b/internal/resource_generate/single_nested_attribute.go @@ -176,7 +176,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectType := generatorschema.NewCustomObjectType(name, attributeAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributeAttrValues) b, err := objectType.Render() @@ -198,7 +198,7 @@ func (g GeneratorSingleNestedAttribute) CustomTypeAndValue(name string) ([]byte, return nil, err } - objectValue := generatorschema.NewCustomObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributeTypes, attributeAttrTypes, attributeAttrValues) b, err = objectValue.Render() @@ -238,7 +238,7 @@ func (g GeneratorSingleNestedAttribute) ToFromFunctions(name string) ([]byte, er fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/single_nested_block.go b/internal/resource_generate/single_nested_block.go index c16b3cf5..2f981380 100644 --- a/internal/resource_generate/single_nested_block.go +++ b/internal/resource_generate/single_nested_block.go @@ -209,7 +209,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrValues[k] = v } - objectType := generatorschema.NewCustomObjectType(name, attributesBlocksAttrValues) + objectType := generatorschema.NewCustomNestedObjectType(name, attributesBlocksAttrValues) b, err := objectType.Render() @@ -263,7 +263,7 @@ func (g GeneratorSingleNestedBlock) CustomTypeAndValue(name string) ([]byte, err attributesBlocksAttrTypes[k] = v } - objectValue := generatorschema.NewCustomObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) + objectValue := generatorschema.NewCustomNestedObjectValue(name, attributesBlocksTypes, attributesBlocksAttrTypes, attributesBlocksAttrValues) b, err = objectValue.Render() @@ -317,7 +317,7 @@ func (g GeneratorSingleNestedBlock) ToFromFunctions(name string) ([]byte, error) fromFuncs := g.Attributes.FromFuncs() - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) + toFrom := generatorschema.NewToFromNestedObject(name, g.AssociatedExternalType, toFuncs, fromFuncs) b, err := toFrom.Render() diff --git a/internal/schema/custom_object.go b/internal/schema/custom_nested_object.go similarity index 73% rename from internal/schema/custom_object.go rename to internal/schema/custom_nested_object.go index 0191f8a0..55b3096e 100644 --- a/internal/schema/custom_object.go +++ b/internal/schema/custom_nested_object.go @@ -8,25 +8,25 @@ import ( "text/template" ) -type CustomObjectType struct { +type CustomNestedObjectType struct { Name FrameworkIdentifier AttrValues map[FrameworkIdentifier]string templates map[string]string } -func NewCustomObjectType(name string, attrValues map[string]string) CustomObjectType { +func NewCustomNestedObjectType(name string, attrValues map[string]string) CustomNestedObjectType { t := map[string]string{ - "equal": ObjectTypeEqualTemplate, - "string": ObjectTypeStringTemplate, - "typable": ObjectTypeTypableTemplate, - "type": ObjectTypeTypeTemplate, - "value": ObjectTypeValueTemplate, - "valueFromObject": ObjectTypeValueFromObjectTemplate, - "valueFromTerraform": ObjectTypeValueFromTerraformTemplate, - "valueMust": ObjectTypeValueMustTemplate, - "valueNull": ObjectTypeValueNullTemplate, - "valueType": ObjectTypeValueTypeTemplate, - "valueUnknown": ObjectTypeValueUnknownTemplate, + "equal": NestedObjectTypeEqualTemplate, + "string": NestedObjectTypeStringTemplate, + "typable": NestedObjectTypeTypableTemplate, + "type": NestedObjectTypeTypeTemplate, + "value": NestedObjectTypeValueTemplate, + "valueFromObject": NestedObjectTypeValueFromObjectTemplate, + "valueFromTerraform": NestedObjectTypeValueFromTerraformTemplate, + "valueMust": NestedObjectTypeValueMustTemplate, + "valueNull": NestedObjectTypeValueNullTemplate, + "valueType": NestedObjectTypeValueTypeTemplate, + "valueUnknown": NestedObjectTypeValueUnknownTemplate, } a := make(map[FrameworkIdentifier]string, len(attrValues)) @@ -35,14 +35,14 @@ func NewCustomObjectType(name string, attrValues map[string]string) CustomObject a[FrameworkIdentifier(k)] = v } - return CustomObjectType{ + return CustomNestedObjectType{ Name: FrameworkIdentifier(name), AttrValues: a, templates: t, } } -func (c CustomObjectType) Render() ([]byte, error) { +func (c CustomNestedObjectType) Render() ([]byte, error) { var buf bytes.Buffer renderFuncs := []func() ([]byte, error){ @@ -74,7 +74,7 @@ func (c CustomObjectType) Render() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderEqual() ([]byte, error) { +func (c CustomNestedObjectType) renderEqual() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["equal"]) @@ -96,7 +96,7 @@ func (c CustomObjectType) renderEqual() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderString() ([]byte, error) { +func (c CustomNestedObjectType) renderString() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["string"]) @@ -118,7 +118,7 @@ func (c CustomObjectType) renderString() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderTypable() ([]byte, error) { +func (c CustomNestedObjectType) renderTypable() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["typable"]) @@ -140,7 +140,7 @@ func (c CustomObjectType) renderTypable() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderType() ([]byte, error) { +func (c CustomNestedObjectType) renderType() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["type"]) @@ -162,7 +162,7 @@ func (c CustomObjectType) renderType() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValue() ([]byte, error) { +func (c CustomNestedObjectType) renderValue() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["value"]) @@ -186,7 +186,7 @@ func (c CustomObjectType) renderValue() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueFromObject() ([]byte, error) { +func (c CustomNestedObjectType) renderValueFromObject() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueFromObject"]) @@ -210,7 +210,7 @@ func (c CustomObjectType) renderValueFromObject() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueFromTerraform() ([]byte, error) { +func (c CustomNestedObjectType) renderValueFromTerraform() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueFromTerraform"]) @@ -232,7 +232,7 @@ func (c CustomObjectType) renderValueFromTerraform() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueMust() ([]byte, error) { +func (c CustomNestedObjectType) renderValueMust() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueMust"]) @@ -254,7 +254,7 @@ func (c CustomObjectType) renderValueMust() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueNull() ([]byte, error) { +func (c CustomNestedObjectType) renderValueNull() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueNull"]) @@ -276,7 +276,7 @@ func (c CustomObjectType) renderValueNull() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueType() ([]byte, error) { +func (c CustomNestedObjectType) renderValueType() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueType"]) @@ -298,7 +298,7 @@ func (c CustomObjectType) renderValueType() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectType) renderValueUnknown() ([]byte, error) { +func (c CustomNestedObjectType) renderValueUnknown() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valueUnknown"]) @@ -320,7 +320,7 @@ func (c CustomObjectType) renderValueUnknown() ([]byte, error) { return buf.Bytes(), nil } -type CustomObjectValue struct { +type CustomNestedObjectValue struct { Name FrameworkIdentifier AttributeTypes map[FrameworkIdentifier]string AttrTypes map[FrameworkIdentifier]string @@ -328,18 +328,18 @@ type CustomObjectValue struct { templates map[string]string } -func NewCustomObjectValue(name string, attributeTypes, attrTypes, attrValues map[string]string) CustomObjectValue { +func NewCustomNestedObjectValue(name string, attributeTypes, attrTypes, attrValues map[string]string) CustomNestedObjectValue { t := map[string]string{ - "attributeTypes": ObjectValueAttributeTypesTemplate, - "equal": ObjectValueEqualTemplate, - "isNull": ObjectValueIsNullTemplate, - "isUnknown": ObjectValueIsUnknownTemplate, - "string": ObjectValueStringTemplate, - "toObjectValue": ObjectValueToObjectValueTemplate, - "toTerraformValue": ObjectValueToTerraformValueTemplate, - "type": ObjectValueTypeTemplate, - "valuable": ObjectValueValuableTemplate, - "value": ObjectValueValueTemplate, + "attributeTypes": NestedObjectValueAttributeTypesTemplate, + "equal": NestedObjectValueEqualTemplate, + "isNull": NestedObjectValueIsNullTemplate, + "isUnknown": NestedObjectValueIsUnknownTemplate, + "string": NestedObjectValueStringTemplate, + "toObjectValue": NestedObjectValueToObjectValueTemplate, + "toTerraformValue": NestedObjectValueToTerraformValueTemplate, + "type": NestedObjectValueTypeTemplate, + "valuable": NestedObjectValueValuableTemplate, + "value": NestedObjectValueValueTemplate, } attribTypes := make(map[FrameworkIdentifier]string, len(attributeTypes)) @@ -360,7 +360,7 @@ func NewCustomObjectValue(name string, attributeTypes, attrTypes, attrValues map attrVals[FrameworkIdentifier(k)] = v } - return CustomObjectValue{ + return CustomNestedObjectValue{ Name: FrameworkIdentifier(name), AttributeTypes: attribTypes, AttrTypes: attrTyps, @@ -369,7 +369,7 @@ func NewCustomObjectValue(name string, attributeTypes, attrTypes, attrValues map } } -func (c CustomObjectValue) Render() ([]byte, error) { +func (c CustomNestedObjectValue) Render() ([]byte, error) { var buf bytes.Buffer renderFuncs := []func() ([]byte, error){ @@ -400,7 +400,7 @@ func (c CustomObjectValue) Render() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderAttributeTypes() ([]byte, error) { +func (c CustomNestedObjectValue) renderAttributeTypes() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["attributeTypes"]) @@ -424,7 +424,7 @@ func (c CustomObjectValue) renderAttributeTypes() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderEqual() ([]byte, error) { +func (c CustomNestedObjectValue) renderEqual() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["equal"]) @@ -448,7 +448,7 @@ func (c CustomObjectValue) renderEqual() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderIsNull() ([]byte, error) { +func (c CustomNestedObjectValue) renderIsNull() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["isNull"]) @@ -470,7 +470,7 @@ func (c CustomObjectValue) renderIsNull() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderIsUnknown() ([]byte, error) { +func (c CustomNestedObjectValue) renderIsUnknown() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["isUnknown"]) @@ -492,7 +492,7 @@ func (c CustomObjectValue) renderIsUnknown() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderString() ([]byte, error) { +func (c CustomNestedObjectValue) renderString() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["string"]) @@ -514,7 +514,7 @@ func (c CustomObjectValue) renderString() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderToObjectValue() ([]byte, error) { +func (c CustomNestedObjectValue) renderToObjectValue() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["toObjectValue"]) @@ -540,7 +540,7 @@ func (c CustomObjectValue) renderToObjectValue() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderToTerraformValue() ([]byte, error) { +func (c CustomNestedObjectValue) renderToTerraformValue() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["toTerraformValue"]) @@ -564,7 +564,7 @@ func (c CustomObjectValue) renderToTerraformValue() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderType() ([]byte, error) { +func (c CustomNestedObjectValue) renderType() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["type"]) @@ -586,7 +586,7 @@ func (c CustomObjectValue) renderType() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderValuable() ([]byte, error) { +func (c CustomNestedObjectValue) renderValuable() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["valuable"]) @@ -608,7 +608,7 @@ func (c CustomObjectValue) renderValuable() ([]byte, error) { return buf.Bytes(), nil } -func (c CustomObjectValue) renderValue() ([]byte, error) { +func (c CustomNestedObjectValue) renderValue() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(c.templates["value"]) diff --git a/internal/schema/custom_object_test.go b/internal/schema/custom_nested_object_test.go similarity index 93% rename from internal/schema/custom_object_test.go rename to internal/schema/custom_nested_object_test.go index 753148fb..35affcef 100644 --- a/internal/schema/custom_object_test.go +++ b/internal/schema/custom_nested_object_test.go @@ -37,7 +37,7 @@ return t.ObjectType.Equal(other.ObjectType) t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderEqual() @@ -75,7 +75,7 @@ return "ExampleType" t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderString() @@ -110,7 +110,7 @@ func TestCustomObjectType_renderTypable(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderTypable() @@ -147,7 +147,7 @@ basetypes.ObjectType t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderType() @@ -266,7 +266,7 @@ state: attr.ValueStateKnown, t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, testCase.attrValues) + customObjectType := NewCustomNestedObjectType(testCase.name, testCase.attrValues) got, err := customObjectType.renderValue() @@ -339,7 +339,7 @@ state: attr.ValueStateKnown, t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, testCase.attrValues) + customObjectType := NewCustomNestedObjectType(testCase.name, testCase.attrValues) got, err := customObjectType.renderValueFromObject() @@ -413,7 +413,7 @@ return NewExampleValueMust(t.AttrTypes, attributes), nil t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderValueFromTerraform() @@ -468,7 +468,7 @@ return object t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderValueMust() @@ -508,7 +508,7 @@ state: attr.ValueStateNull, t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderValueNull() @@ -546,7 +546,7 @@ return ExampleValue{} t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderValueType() @@ -586,7 +586,7 @@ state: attr.ValueStateUnknown, t.Run(name, func(t *testing.T) { t.Parallel() - customObjectType := NewCustomObjectType(testCase.name, nil) + customObjectType := NewCustomNestedObjectType(testCase.name, nil) got, err := customObjectType.renderValueUnknown() @@ -630,7 +630,7 @@ return map[string]attr.Type{ t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, testCase.attrTypes, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, testCase.attrTypes, nil) got, err := customObjectValue.renderAttributeTypes() @@ -692,7 +692,7 @@ return true t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, testCase.attrValues) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, testCase.attrValues) got, err := customObjectValue.renderEqual() @@ -730,7 +730,7 @@ return v.state == attr.ValueStateNull t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderIsNull() @@ -768,7 +768,7 @@ return v.state == attr.ValueStateUnknown t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderIsUnknown() @@ -806,7 +806,7 @@ return "ExampleValue" t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderString() @@ -860,7 +860,7 @@ return objVal, diags t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, testCase.attributeTypes, testCase.attrTypes, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, testCase.attributeTypes, testCase.attrTypes, nil) got, err := customObjectValue.renderToObjectValue() @@ -937,7 +937,7 @@ panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, testCase.attrTypes, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, testCase.attrTypes, nil) got, err := customObjectValue.renderToTerraformValue() @@ -979,7 +979,7 @@ AttrTypes: v.AttributeTypes(ctx), t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderType() @@ -1014,7 +1014,7 @@ func TestCustomObjectValue_renderValuable(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderValuable() @@ -1051,7 +1051,7 @@ state attr.ValueState t.Run(name, func(t *testing.T) { t.Parallel() - customObjectValue := NewCustomObjectValue(testCase.name, nil, nil, nil) + customObjectValue := NewCustomNestedObjectValue(testCase.name, nil, nil, nil) got, err := customObjectValue.renderValue() diff --git a/internal/schema/embed.go b/internal/schema/embed.go index 5934034b..4a2fbe5c 100644 --- a/internal/schema/embed.go +++ b/internal/schema/embed.go @@ -277,80 +277,80 @@ var NumberValueValueTemplate string //go:embed templates/number_value_valuable.gotmpl var NumberValueValuableTemplate string -// Object From/To +// NestedObject From/To -//go:embed templates/object_from.gotmpl -var ObjectFromTemplate string +//go:embed templates/nested_object_from.gotmpl +var NestedObjectFromTemplate string -//go:embed templates/object_to.gotmpl -var ObjectToTemplate string +//go:embed templates/nested_object_to.gotmpl +var NestedObjectToTemplate string -// Object Type +// NestedObject Type -//go:embed templates/object_type_equal.gotmpl -var ObjectTypeEqualTemplate string +//go:embed templates/nested_object_type_equal.gotmpl +var NestedObjectTypeEqualTemplate string -//go:embed templates/object_type_string.gotmpl -var ObjectTypeStringTemplate string +//go:embed templates/nested_object_type_string.gotmpl +var NestedObjectTypeStringTemplate string -//go:embed templates/object_type_typable.gotmpl -var ObjectTypeTypableTemplate string +//go:embed templates/nested_object_type_typable.gotmpl +var NestedObjectTypeTypableTemplate string -//go:embed templates/object_type_type.gotmpl -var ObjectTypeTypeTemplate string +//go:embed templates/nested_object_type_type.gotmpl +var NestedObjectTypeTypeTemplate string -//go:embed templates/object_type_value.gotmpl -var ObjectTypeValueTemplate string +//go:embed templates/nested_object_type_value.gotmpl +var NestedObjectTypeValueTemplate string -//go:embed templates/object_type_value_from_object.gotmpl -var ObjectTypeValueFromObjectTemplate string +//go:embed templates/nested_object_type_value_from_object.gotmpl +var NestedObjectTypeValueFromObjectTemplate string -//go:embed templates/object_type_value_from_terraform.gotmpl -var ObjectTypeValueFromTerraformTemplate string +//go:embed templates/nested_object_type_value_from_terraform.gotmpl +var NestedObjectTypeValueFromTerraformTemplate string -//go:embed templates/object_type_value_must.gotmpl -var ObjectTypeValueMustTemplate string +//go:embed templates/nested_object_type_value_must.gotmpl +var NestedObjectTypeValueMustTemplate string -//go:embed templates/object_type_value_null.gotmpl -var ObjectTypeValueNullTemplate string +//go:embed templates/nested_object_type_value_null.gotmpl +var NestedObjectTypeValueNullTemplate string -//go:embed templates/object_type_value_type.gotmpl -var ObjectTypeValueTypeTemplate string +//go:embed templates/nested_object_type_value_type.gotmpl +var NestedObjectTypeValueTypeTemplate string -//go:embed templates/object_type_value_unknown.gotmpl -var ObjectTypeValueUnknownTemplate string +//go:embed templates/nested_object_type_value_unknown.gotmpl +var NestedObjectTypeValueUnknownTemplate string -// Object Value +// NestedObject Value -//go:embed templates/object_value_attribute_types.gotmpl -var ObjectValueAttributeTypesTemplate string +//go:embed templates/nested_object_value_attribute_types.gotmpl +var NestedObjectValueAttributeTypesTemplate string -//go:embed templates/object_value_equal.gotmpl -var ObjectValueEqualTemplate string +//go:embed templates/nested_object_value_equal.gotmpl +var NestedObjectValueEqualTemplate string -//go:embed templates/object_value_is_null.gotmpl -var ObjectValueIsNullTemplate string +//go:embed templates/nested_object_value_is_null.gotmpl +var NestedObjectValueIsNullTemplate string -//go:embed templates/object_value_is_unknown.gotmpl -var ObjectValueIsUnknownTemplate string +//go:embed templates/nested_object_value_is_unknown.gotmpl +var NestedObjectValueIsUnknownTemplate string -//go:embed templates/object_value_string.gotmpl -var ObjectValueStringTemplate string +//go:embed templates/nested_object_value_string.gotmpl +var NestedObjectValueStringTemplate string -//go:embed templates/object_value_to_object_value.gotmpl -var ObjectValueToObjectValueTemplate string +//go:embed templates/nested_object_value_to_object_value.gotmpl +var NestedObjectValueToObjectValueTemplate string -//go:embed templates/object_value_to_terraform_value.gotmpl -var ObjectValueToTerraformValueTemplate string +//go:embed templates/nested_object_value_to_terraform_value.gotmpl +var NestedObjectValueToTerraformValueTemplate string -//go:embed templates/object_value_type.gotmpl -var ObjectValueTypeTemplate string +//go:embed templates/nested_object_value_type.gotmpl +var NestedObjectValueTypeTemplate string -//go:embed templates/object_value_valuable.gotmpl -var ObjectValueValuableTemplate string +//go:embed templates/nested_object_value_valuable.gotmpl +var NestedObjectValueValuableTemplate string -//go:embed templates/object_value_value.gotmpl -var ObjectValueValueTemplate string +//go:embed templates/nested_object_value_value.gotmpl +var NestedObjectValueValueTemplate string //go:embed templates/schema.gotmpl var SchemaGoTemplate string diff --git a/internal/schema/templates/object_from.gotmpl b/internal/schema/templates/nested_object_from.gotmpl similarity index 100% rename from internal/schema/templates/object_from.gotmpl rename to internal/schema/templates/nested_object_from.gotmpl diff --git a/internal/schema/templates/object_to.gotmpl b/internal/schema/templates/nested_object_to.gotmpl similarity index 100% rename from internal/schema/templates/object_to.gotmpl rename to internal/schema/templates/nested_object_to.gotmpl diff --git a/internal/schema/templates/object_type_equal.gotmpl b/internal/schema/templates/nested_object_type_equal.gotmpl similarity index 100% rename from internal/schema/templates/object_type_equal.gotmpl rename to internal/schema/templates/nested_object_type_equal.gotmpl diff --git a/internal/schema/templates/object_type_string.gotmpl b/internal/schema/templates/nested_object_type_string.gotmpl similarity index 100% rename from internal/schema/templates/object_type_string.gotmpl rename to internal/schema/templates/nested_object_type_string.gotmpl diff --git a/internal/schema/templates/object_type_typable.gotmpl b/internal/schema/templates/nested_object_type_typable.gotmpl similarity index 100% rename from internal/schema/templates/object_type_typable.gotmpl rename to internal/schema/templates/nested_object_type_typable.gotmpl diff --git a/internal/schema/templates/object_type_type.gotmpl b/internal/schema/templates/nested_object_type_type.gotmpl similarity index 100% rename from internal/schema/templates/object_type_type.gotmpl rename to internal/schema/templates/nested_object_type_type.gotmpl diff --git a/internal/schema/templates/object_type_value.gotmpl b/internal/schema/templates/nested_object_type_value.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value.gotmpl rename to internal/schema/templates/nested_object_type_value.gotmpl diff --git a/internal/schema/templates/object_type_value_from_object.gotmpl b/internal/schema/templates/nested_object_type_value_from_object.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_from_object.gotmpl rename to internal/schema/templates/nested_object_type_value_from_object.gotmpl diff --git a/internal/schema/templates/object_type_value_from_terraform.gotmpl b/internal/schema/templates/nested_object_type_value_from_terraform.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_from_terraform.gotmpl rename to internal/schema/templates/nested_object_type_value_from_terraform.gotmpl diff --git a/internal/schema/templates/object_type_value_must.gotmpl b/internal/schema/templates/nested_object_type_value_must.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_must.gotmpl rename to internal/schema/templates/nested_object_type_value_must.gotmpl diff --git a/internal/schema/templates/object_type_value_null.gotmpl b/internal/schema/templates/nested_object_type_value_null.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_null.gotmpl rename to internal/schema/templates/nested_object_type_value_null.gotmpl diff --git a/internal/schema/templates/object_type_value_type.gotmpl b/internal/schema/templates/nested_object_type_value_type.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_type.gotmpl rename to internal/schema/templates/nested_object_type_value_type.gotmpl diff --git a/internal/schema/templates/object_type_value_unknown.gotmpl b/internal/schema/templates/nested_object_type_value_unknown.gotmpl similarity index 100% rename from internal/schema/templates/object_type_value_unknown.gotmpl rename to internal/schema/templates/nested_object_type_value_unknown.gotmpl diff --git a/internal/schema/templates/object_value_attribute_types.gotmpl b/internal/schema/templates/nested_object_value_attribute_types.gotmpl similarity index 100% rename from internal/schema/templates/object_value_attribute_types.gotmpl rename to internal/schema/templates/nested_object_value_attribute_types.gotmpl diff --git a/internal/schema/templates/object_value_equal.gotmpl b/internal/schema/templates/nested_object_value_equal.gotmpl similarity index 100% rename from internal/schema/templates/object_value_equal.gotmpl rename to internal/schema/templates/nested_object_value_equal.gotmpl diff --git a/internal/schema/templates/object_value_is_null.gotmpl b/internal/schema/templates/nested_object_value_is_null.gotmpl similarity index 100% rename from internal/schema/templates/object_value_is_null.gotmpl rename to internal/schema/templates/nested_object_value_is_null.gotmpl diff --git a/internal/schema/templates/object_value_is_unknown.gotmpl b/internal/schema/templates/nested_object_value_is_unknown.gotmpl similarity index 100% rename from internal/schema/templates/object_value_is_unknown.gotmpl rename to internal/schema/templates/nested_object_value_is_unknown.gotmpl diff --git a/internal/schema/templates/object_value_string.gotmpl b/internal/schema/templates/nested_object_value_string.gotmpl similarity index 100% rename from internal/schema/templates/object_value_string.gotmpl rename to internal/schema/templates/nested_object_value_string.gotmpl diff --git a/internal/schema/templates/object_value_to_object_value.gotmpl b/internal/schema/templates/nested_object_value_to_object_value.gotmpl similarity index 100% rename from internal/schema/templates/object_value_to_object_value.gotmpl rename to internal/schema/templates/nested_object_value_to_object_value.gotmpl diff --git a/internal/schema/templates/object_value_to_terraform_value.gotmpl b/internal/schema/templates/nested_object_value_to_terraform_value.gotmpl similarity index 100% rename from internal/schema/templates/object_value_to_terraform_value.gotmpl rename to internal/schema/templates/nested_object_value_to_terraform_value.gotmpl diff --git a/internal/schema/templates/object_value_type.gotmpl b/internal/schema/templates/nested_object_value_type.gotmpl similarity index 100% rename from internal/schema/templates/object_value_type.gotmpl rename to internal/schema/templates/nested_object_value_type.gotmpl diff --git a/internal/schema/templates/object_value_valuable.gotmpl b/internal/schema/templates/nested_object_value_valuable.gotmpl similarity index 100% rename from internal/schema/templates/object_value_valuable.gotmpl rename to internal/schema/templates/nested_object_value_valuable.gotmpl diff --git a/internal/schema/templates/object_value_value.gotmpl b/internal/schema/templates/nested_object_value_value.gotmpl similarity index 100% rename from internal/schema/templates/object_value_value.gotmpl rename to internal/schema/templates/nested_object_value_value.gotmpl diff --git a/internal/schema/to_from_object.go b/internal/schema/to_from_nested_object.go similarity index 81% rename from internal/schema/to_from_object.go rename to internal/schema/to_from_nested_object.go index b0e5ed00..e0ab1154 100644 --- a/internal/schema/to_from_object.go +++ b/internal/schema/to_from_nested_object.go @@ -8,7 +8,7 @@ import ( "text/template" ) -type ToFromObject struct { +type ToFromNestedObject struct { Name FrameworkIdentifier AssocExtType *AssocExtType ToFuncs map[FrameworkIdentifier]ToFromConversion @@ -16,10 +16,10 @@ type ToFromObject struct { templates map[string]string } -func NewToFromObject(name string, assocExtType *AssocExtType, toFuncs, fromFuncs map[string]ToFromConversion) ToFromObject { +func NewToFromNestedObject(name string, assocExtType *AssocExtType, toFuncs, fromFuncs map[string]ToFromConversion) ToFromNestedObject { t := map[string]string{ - "from": ObjectFromTemplate, - "to": ObjectToTemplate, + "from": NestedObjectFromTemplate, + "to": NestedObjectToTemplate, } tf := make(map[FrameworkIdentifier]ToFromConversion, len(toFuncs)) @@ -34,7 +34,7 @@ func NewToFromObject(name string, assocExtType *AssocExtType, toFuncs, fromFuncs ff[FrameworkIdentifier(k)] = v } - return ToFromObject{ + return ToFromNestedObject{ Name: FrameworkIdentifier(name), AssocExtType: assocExtType, FromFuncs: ff, @@ -43,7 +43,7 @@ func NewToFromObject(name string, assocExtType *AssocExtType, toFuncs, fromFuncs } } -func (o ToFromObject) Render() ([]byte, error) { +func (o ToFromNestedObject) Render() ([]byte, error) { var buf bytes.Buffer renderFuncs := []func() ([]byte, error){ @@ -66,7 +66,7 @@ func (o ToFromObject) Render() ([]byte, error) { return buf.Bytes(), nil } -func (o ToFromObject) renderTo() ([]byte, error) { +func (o ToFromNestedObject) renderTo() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(o.templates["to"]) @@ -92,7 +92,7 @@ func (o ToFromObject) renderTo() ([]byte, error) { return buf.Bytes(), nil } -func (o ToFromObject) renderFrom() ([]byte, error) { +func (o ToFromNestedObject) renderFrom() ([]byte, error) { var buf bytes.Buffer t, err := template.New("").Parse(o.templates["from"]) diff --git a/internal/schema/to_from_object_test.go b/internal/schema/to_from_nested_object_test.go similarity index 95% rename from internal/schema/to_from_object_test.go rename to internal/schema/to_from_nested_object_test.go index 9fcb0f56..e41e8e56 100644 --- a/internal/schema/to_from_object_test.go +++ b/internal/schema/to_from_nested_object_test.go @@ -95,7 +95,7 @@ state: attr.ValueStateKnown, t.Run(name, func(t *testing.T) { t.Parallel() - toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, nil, testCase.fromFuncs) + toFromObject := NewToFromNestedObject(testCase.name, testCase.assocExtType, nil, testCase.fromFuncs) got, err := toFromObject.renderFrom() @@ -212,7 +212,7 @@ BoolAttribute: apiBoolAttribute, t.Run(name, func(t *testing.T) { t.Parallel() - toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.toFuncs, nil) + toFromObject := NewToFromNestedObject(testCase.name, testCase.assocExtType, testCase.toFuncs, nil) got, err := toFromObject.renderTo() From 4cba2bc4c04e50b754a3d421e88bd8ea75cac2ec Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 17:47:39 +0100 Subject: [PATCH 07/13] Add custom type and value type, and to/from method generation for object attribute for data source, provider, and resource (#74) --- .../datasource_generate/object_attribute.go | 81 +-- .../provider_generate/object_attribute.go | 51 ++ .../resource_generate/object_attribute.go | 51 ++ internal/schema/custom_nested_object_test.go | 42 +- internal/schema/custom_object.go | 375 ++++++++++++++ internal/schema/custom_object_test.go | 467 ++++++++++++++++++ internal/schema/elements.go | 24 + internal/schema/embed.go | 48 ++ internal/schema/templates/object_from.gotmpl | 28 ++ internal/schema/templates/object_to.gotmpl | 28 ++ .../schema/templates/object_type_equal.gotmpl | 9 + .../templates/object_type_string.gotmpl | 4 + .../templates/object_type_typable.gotmpl | 1 + .../schema/templates/object_type_type.gotmpl | 3 + .../object_type_value_from_object.gotmpl | 6 + .../object_type_value_from_terraform.gotmpl | 22 + .../templates/object_type_value_type.gotmpl | 4 + .../object_value_attribute_types.gotmpl | 6 + .../templates/object_value_equal.gotmpl | 10 + .../schema/templates/object_value_type.gotmpl | 8 + .../templates/object_value_valuable.gotmpl | 1 + .../templates/object_value_value.gotmpl | 3 + internal/schema/to_from_nested_object_test.go | 4 +- internal/schema/to_from_object.go | 109 ++++ internal/schema/to_from_object_test.go | 173 +++++++ 25 files changed, 1504 insertions(+), 54 deletions(-) create mode 100644 internal/schema/custom_object.go create mode 100644 internal/schema/custom_object_test.go create mode 100644 internal/schema/templates/object_from.gotmpl create mode 100644 internal/schema/templates/object_to.gotmpl create mode 100644 internal/schema/templates/object_type_equal.gotmpl create mode 100644 internal/schema/templates/object_type_string.gotmpl create mode 100644 internal/schema/templates/object_type_typable.gotmpl create mode 100644 internal/schema/templates/object_type_type.gotmpl create mode 100644 internal/schema/templates/object_type_value_from_object.gotmpl create mode 100644 internal/schema/templates/object_type_value_from_terraform.gotmpl create mode 100644 internal/schema/templates/object_type_value_type.gotmpl create mode 100644 internal/schema/templates/object_value_attribute_types.gotmpl create mode 100644 internal/schema/templates/object_value_equal.gotmpl create mode 100644 internal/schema/templates/object_value_type.gotmpl create mode 100644 internal/schema/templates/object_value_valuable.gotmpl create mode 100644 internal/schema/templates/object_value_value.gotmpl create mode 100644 internal/schema/to_from_object.go create mode 100644 internal/schema/to_from_object_test.go diff --git a/internal/datasource_generate/object_attribute.go b/internal/datasource_generate/object_attribute.go index 1380c2f5..e3b150a7 100644 --- a/internal/datasource_generate/object_attribute.go +++ b/internal/datasource_generate/object_attribute.go @@ -4,6 +4,7 @@ package datasource_generate import ( + "bytes" "fmt" "strings" "text/template" @@ -136,34 +137,52 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent return field, nil } -//func (g GeneratorObjectAttribute) CustomTypeAndValue(name string) ([]byte, error) { -// if g.AssociatedExternalType == nil { -// return nil, nil -// } -// -// var buf bytes.Buffer -// -// listType := generatorschema.NewCustomObjectType(name) -// -// b, err := listType.Render() -// -// if err != nil { -// return nil, err -// } -// -// buf.Write(b) -// -// elemType := generatorschema.GetElementType(g.ElementType) -// -// listValue := generatorschema.NewCustomObjectValue(name, elemType) -// -// b, err = listValue.Render() -// -// if err != nil { -// return nil, err -// } -// -// buf.Write(b) -// -// return buf.Bytes(), nil -//} +func (g GeneratorObjectAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + objectType := generatorschema.NewCustomObjectType(name) + + b, err := objectType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + attrTypes := generatorschema.GetAttrTypes(g.AttrTypes()) + + objectValue := generatorschema.NewCustomObjectValue(name, attrTypes) + + b, err = objectValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) + + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/provider_generate/object_attribute.go b/internal/provider_generate/object_attribute.go index d1074341..5c7eee1d 100644 --- a/internal/provider_generate/object_attribute.go +++ b/internal/provider_generate/object_attribute.go @@ -4,6 +4,7 @@ package provider_generate import ( + "bytes" "fmt" "strings" "text/template" @@ -135,3 +136,53 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent return field, nil } + +func (g GeneratorObjectAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + objectType := generatorschema.NewCustomObjectType(name) + + b, err := objectType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + attrTypes := generatorschema.GetAttrTypes(g.AttrTypes()) + + objectValue := generatorschema.NewCustomObjectValue(name, attrTypes) + + b, err = objectValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) + + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/resource_generate/object_attribute.go b/internal/resource_generate/object_attribute.go index 623a4a8e..350ad101 100644 --- a/internal/resource_generate/object_attribute.go +++ b/internal/resource_generate/object_attribute.go @@ -4,6 +4,7 @@ package resource_generate import ( + "bytes" "fmt" "strings" "text/template" @@ -168,3 +169,53 @@ func (g GeneratorObjectAttribute) ModelField(name generatorschema.FrameworkIdent return field, nil } + +func (g GeneratorObjectAttribute) CustomTypeAndValue(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + var buf bytes.Buffer + + objectType := generatorschema.NewCustomObjectType(name) + + b, err := objectType.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + attrTypes := generatorschema.GetAttrTypes(g.AttrTypes()) + + objectValue := generatorschema.NewCustomObjectValue(name, attrTypes) + + b, err = objectValue.Render() + + if err != nil { + return nil, err + } + + buf.Write(b) + + return buf.Bytes(), nil +} + +func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { + if g.AssociatedExternalType == nil { + return nil, nil + } + + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) + + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + + b, err := toFrom.Render() + + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/internal/schema/custom_nested_object_test.go b/internal/schema/custom_nested_object_test.go index 35affcef..09405074 100644 --- a/internal/schema/custom_nested_object_test.go +++ b/internal/schema/custom_nested_object_test.go @@ -9,7 +9,7 @@ import ( "github.com/google/go-cmp/cmp" ) -func TestCustomObjectType_renderEqual(t *testing.T) { +func TestCustomNestedObjectType_renderEqual(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -52,7 +52,7 @@ return t.ObjectType.Equal(other.ObjectType) } } -func TestCustomObjectType_renderString(t *testing.T) { +func TestCustomNestedObjectType_renderString(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -90,7 +90,7 @@ return "ExampleType" } } -func TestCustomObjectType_renderTypable(t *testing.T) { +func TestCustomNestedObjectType_renderTypable(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -125,7 +125,7 @@ func TestCustomObjectType_renderTypable(t *testing.T) { } } -func TestCustomObjectType_renderType(t *testing.T) { +func TestCustomNestedObjectType_renderType(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -162,7 +162,7 @@ basetypes.ObjectType } } -func TestCustomObjectType_renderValue(t *testing.T) { +func TestCustomNestedObjectType_renderValue(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -281,7 +281,7 @@ state: attr.ValueStateKnown, } } -func TestCustomObjectType_renderValueFromObject(t *testing.T) { +func TestCustomNestedObjectType_renderValueFromObject(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -354,7 +354,7 @@ state: attr.ValueStateKnown, } } -func TestCustomObjectType_renderValueFromTerraform(t *testing.T) { +func TestCustomNestedObjectType_renderValueFromTerraform(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -428,7 +428,7 @@ return NewExampleValueMust(t.AttrTypes, attributes), nil } } -func TestCustomObjectType_renderValueMust(t *testing.T) { +func TestCustomNestedObjectType_renderValueMust(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -483,7 +483,7 @@ return object } } -func TestCustomObjectType_renderValueNull(t *testing.T) { +func TestCustomNestedObjectType_renderValueNull(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -523,7 +523,7 @@ state: attr.ValueStateNull, } } -func TestCustomObjectType_renderValueType(t *testing.T) { +func TestCustomNestedObjectType_renderValueType(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -561,7 +561,7 @@ return ExampleValue{} } } -func TestCustomObjectType_renderValueUnknown(t *testing.T) { +func TestCustomNestedObjectType_renderValueUnknown(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -601,7 +601,7 @@ state: attr.ValueStateUnknown, } } -func TestCustomObjectValue_renderAttributeTypes(t *testing.T) { +func TestCustomNestedObjectValue_renderAttributeTypes(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -645,7 +645,7 @@ return map[string]attr.Type{ } } -func TestCustomObjectValue_renderEqual(t *testing.T) { +func TestCustomNestedObjectValue_renderEqual(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -707,7 +707,7 @@ return true } } -func TestCustomObjectValue_renderIsNull(t *testing.T) { +func TestCustomNestedObjectValue_renderIsNull(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -745,7 +745,7 @@ return v.state == attr.ValueStateNull } } -func TestCustomObjectValue_renderIsUnknown(t *testing.T) { +func TestCustomNestedObjectValue_renderIsUnknown(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -783,7 +783,7 @@ return v.state == attr.ValueStateUnknown } } -func TestCustomObjectValue_renderString(t *testing.T) { +func TestCustomNestedObjectValue_renderString(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -821,7 +821,7 @@ return "ExampleValue" } } -func TestCustomObjectValue_renderToObjectValue(t *testing.T) { +func TestCustomNestedObjectValue_renderToObjectValue(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -875,7 +875,7 @@ return objVal, diags } } -func TestCustomObjectValue_renderToTerraformValue(t *testing.T) { +func TestCustomNestedObjectValue_renderToTerraformValue(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -952,7 +952,7 @@ panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) } } -func TestCustomObjectValue_renderType(t *testing.T) { +func TestCustomNestedObjectValue_renderType(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -994,7 +994,7 @@ AttrTypes: v.AttributeTypes(ctx), } } -func TestCustomObjectValue_renderValuable(t *testing.T) { +func TestCustomNestedObjectValue_renderValuable(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -1029,7 +1029,7 @@ func TestCustomObjectValue_renderValuable(t *testing.T) { } } -func TestCustomObjectValue_renderValue(t *testing.T) { +func TestCustomNestedObjectValue_renderValue(t *testing.T) { t.Parallel() testCases := map[string]struct { diff --git a/internal/schema/custom_object.go b/internal/schema/custom_object.go new file mode 100644 index 00000000..abc1d55e --- /dev/null +++ b/internal/schema/custom_object.go @@ -0,0 +1,375 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type CustomObjectType struct { + Name FrameworkIdentifier + templates map[string]string +} + +func NewCustomObjectType(name string) CustomObjectType { + t := map[string]string{ + "equal": ObjectTypeEqualTemplate, + "string": ObjectTypeStringTemplate, + "type": ObjectTypeTypeTemplate, + "typable": ObjectTypeTypableTemplate, + "valueFromObject": ObjectTypeValueFromObjectTemplate, + "valueFromTerraform": ObjectTypeValueFromTerraformTemplate, + "valueType": ObjectTypeValueTypeTemplate, + } + + return CustomObjectType{ + Name: FrameworkIdentifier(name), + templates: t, + } +} + +func (c CustomObjectType) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderTypable, + c.renderType, + c.renderEqual, + c.renderString, + c.renderValueFromObject, + c.renderValueFromTerraform, + c.renderValueType, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderString() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["string"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderTypable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["typable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderValueFromObject() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromObject"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderValueFromTerraform() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueFromTerraform"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectType) renderValueType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valueType"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type CustomObjectValue struct { + Name FrameworkIdentifier + AttrTypes string + templates map[string]string +} + +func NewCustomObjectValue(name, attrTypes string) CustomObjectValue { + t := map[string]string{ + "attributeTypes": ObjectValueAttributeTypesTemplate, + "equal": ObjectValueEqualTemplate, + "type": ObjectValueTypeTemplate, + "valuable": ObjectValueValuableTemplate, + "value": ObjectValueValueTemplate, + } + + return CustomObjectValue{ + Name: FrameworkIdentifier(name), + AttrTypes: attrTypes, + templates: t, + } +} + +func (c CustomObjectValue) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + c.renderValuable, + c.renderValue, + c.renderEqual, + c.renderType, + c.renderAttributeTypes, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (c CustomObjectValue) renderAttributeTypes() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["attributeTypes"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AttrTypes string + }{ + Name: c.Name.ToPascalCase(), + AttrTypes: c.AttrTypes, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectValue) renderEqual() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["equal"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectValue) renderType() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["type"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + ElementType string + }{ + Name: c.Name.ToPascalCase(), + ElementType: c.AttrTypes, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectValue) renderValuable() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["valuable"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c CustomObjectValue) renderValue() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(c.templates["value"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + }{ + Name: c.Name.ToPascalCase(), + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/custom_object_test.go b/internal/schema/custom_object_test.go new file mode 100644 index 00000000..07a83e45 --- /dev/null +++ b/internal/schema/custom_object_test.go @@ -0,0 +1,467 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCustomObjectType_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`func (t ExampleType) Equal(o attr.Type) bool { +other, ok := o.(ExampleType) + +if !ok { +return false +} + +return t.ObjectType.Equal(other.ObjectType) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderString(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) String() string { +return "ExampleType" +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderString() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderTypable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`var _ basetypes.ObjectTypable = ExampleType{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderTypable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(`type ExampleType struct { +basetypes.ObjectType +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderValueFromObject(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + attrValues map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + attrValues: map[string]string{ + "bool_attribute": "basetypes.ObjectValue", + }, + expected: []byte(` +func (t ExampleType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { +return ExampleValue{ +ObjectValue: in, +}, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderValueFromObject() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderValueFromTerraform(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.ObjectType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +objectValue, ok := attrValue.(basetypes.ObjectValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +objectValuable, diags := t.ValueFromObject(ctx, objectValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting ObjectValue to ObjectValuable: %v", diags) +} + +return objectValuable, nil +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderValueFromTerraform() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectType_renderValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + expected: []byte(` +func (t ExampleType) ValueType(ctx context.Context) attr.Value { +return ExampleValue{} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectType := NewCustomObjectType(testCase.name) + + got, err := customObjectType.renderValueType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectValue_renderEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Equal(o attr.Value) bool { +other, ok := o.(ExampleValue) + +if !ok { +return false +} + +return v.ObjectValue.Equal(other.ObjectValue) +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectValue := NewCustomObjectValue(testCase.name, testCase.elementType) + + got, err := customObjectValue.renderEqual() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectValue_renderType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(` +func (v ExampleValue) Type(ctx context.Context) attr.Type { +return ExampleType{ +ObjectType: basetypes.ObjectType{ +AttrTypes: v.AttributeTypes(ctx), +}, +} +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectValue := NewCustomObjectValue(testCase.name, testCase.elementType) + + got, err := customObjectValue.renderType() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectValue_renderValuable(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`var _ basetypes.ObjectValuable = ExampleValue{}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectValue := NewCustomObjectValue(testCase.name, testCase.elementType) + + got, err := customObjectValue.renderValuable() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCustomObjectValue_renderValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + elementType string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + elementType: "types.BoolType", + expected: []byte(`type ExampleValue struct { +basetypes.ObjectValue +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + customObjectValue := NewCustomObjectValue(testCase.name, testCase.elementType) + + got, err := customObjectValue.renderValue() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/schema/elements.go b/internal/schema/elements.go index 93e2c70d..71b1e9ef 100644 --- a/internal/schema/elements.go +++ b/internal/schema/elements.go @@ -136,3 +136,27 @@ func GetElementFromFunc(e specschema.ElementType) string { return "" } + +// GetAttrTypesFromFuncs returns string representations of the function that is used +// for converting from an API Go type to a framework type. +// TODO: Handle custom type, and types other than primitives. +func GetAttrTypesFromFuncs(a specschema.ObjectAttributeTypes) map[string]string { + attrTypesFuncs := make(map[string]string, len(a)) + + for _, v := range a { + switch { + case v.Bool != nil: + attrTypesFuncs[v.Name] = "types.BoolPointerValue" + case v.Float64 != nil: + attrTypesFuncs[v.Name] = "types.Float64PointerValue" + case v.Int64 != nil: + attrTypesFuncs[v.Name] = "types.Int64PointerValue" + case v.Number != nil: + attrTypesFuncs[v.Name] = "types.NumberValue" + case v.String != nil: + attrTypesFuncs[v.Name] = "types.StringPointerValue" + } + } + + return attrTypesFuncs +} diff --git a/internal/schema/embed.go b/internal/schema/embed.go index 4a2fbe5c..7cb48717 100644 --- a/internal/schema/embed.go +++ b/internal/schema/embed.go @@ -355,6 +355,54 @@ var NestedObjectValueValueTemplate string //go:embed templates/schema.gotmpl var SchemaGoTemplate string +// Object From/To + +//go:embed templates/object_from.gotmpl +var ObjectFromTemplate string + +//go:embed templates/object_to.gotmpl +var ObjectToTemplate string + +// Object Type + +//go:embed templates/object_type_equal.gotmpl +var ObjectTypeEqualTemplate string + +//go:embed templates/object_type_string.gotmpl +var ObjectTypeStringTemplate string + +//go:embed templates/object_type_type.gotmpl +var ObjectTypeTypeTemplate string + +//go:embed templates/object_type_typable.gotmpl +var ObjectTypeTypableTemplate string + +//go:embed templates/object_type_value_from_object.gotmpl +var ObjectTypeValueFromObjectTemplate string + +//go:embed templates/object_type_value_from_terraform.gotmpl +var ObjectTypeValueFromTerraformTemplate string + +//go:embed templates/object_type_value_type.gotmpl +var ObjectTypeValueTypeTemplate string + +// Object Value + +//go:embed templates/object_value_attribute_types.gotmpl +var ObjectValueAttributeTypesTemplate string + +//go:embed templates/object_value_equal.gotmpl +var ObjectValueEqualTemplate string + +//go:embed templates/object_value_type.gotmpl +var ObjectValueTypeTemplate string + +//go:embed templates/object_value_value.gotmpl +var ObjectValueValueTemplate string + +//go:embed templates/object_value_valuable.gotmpl +var ObjectValueValuableTemplate string + // Set From/To //go:embed templates/set_from.gotmpl diff --git a/internal/schema/templates/object_from.gotmpl b/internal/schema/templates/object_from.gotmpl new file mode 100644 index 00000000..dc151b8d --- /dev/null +++ b/internal/schema/templates/object_from.gotmpl @@ -0,0 +1,28 @@ + +func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return {{.Name}}Value{ +types.ObjectNull(v.AttributeTypes(ctx)), +}, diags +} + +o, d := basetypes.NewObjectValue(v.AttributeTypes(ctx), map[string]attr.Value{ +{{- range $key, $value := .AttrTypesFromFuncs}} +"{{$key}}": {{$value}}(apiObject.{{$key.ToPascalCase}}), +{{- end}} +}) + +diags.Append(d...) + +if diags.HasError() { +return {{.Name}}Value{ +types.ObjectNull(v.AttributeTypes(ctx)), +}, diags +} + +return {{.Name}}Value{ +o, +}, diags +} diff --git a/internal/schema/templates/object_to.gotmpl b/internal/schema/templates/object_to.gotmpl new file mode 100644 index 00000000..47cb436f --- /dev/null +++ b/internal/schema/templates/object_to.gotmpl @@ -0,0 +1,28 @@ +func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { +var diags diag.Diagnostics + +if v.IsNull() { +return nil, diags +} + +if v.IsUnknown() { +diags.Append(diag.NewErrorDiagnostic( +"{{.Name}}Value Value Is Unknown", +`"{{.Name}}Value" is unknown.`, +)) + +return nil, diags +} + +var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} + +d := v.As(ctx, &{{.AssocExtType.ToCamelCase}}, basetypes.ObjectAsOptions{}) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &{{.AssocExtType.ToCamelCase}}, diags +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_equal.gotmpl b/internal/schema/templates/object_type_equal.gotmpl new file mode 100644 index 00000000..11eb5c47 --- /dev/null +++ b/internal/schema/templates/object_type_equal.gotmpl @@ -0,0 +1,9 @@ +func (t {{.Name}}Type) Equal(o attr.Type) bool { +other, ok := o.({{.Name}}Type) + +if !ok { +return false +} + +return t.ObjectType.Equal(other.ObjectType) +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_string.gotmpl b/internal/schema/templates/object_type_string.gotmpl new file mode 100644 index 00000000..f01b8ac3 --- /dev/null +++ b/internal/schema/templates/object_type_string.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) String() string { +return "{{.Name}}Type" +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_typable.gotmpl b/internal/schema/templates/object_type_typable.gotmpl new file mode 100644 index 00000000..5195a01a --- /dev/null +++ b/internal/schema/templates/object_type_typable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.ObjectTypable = {{.Name}}Type{} \ No newline at end of file diff --git a/internal/schema/templates/object_type_type.gotmpl b/internal/schema/templates/object_type_type.gotmpl new file mode 100644 index 00000000..fdde0ae9 --- /dev/null +++ b/internal/schema/templates/object_type_type.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Type struct { +basetypes.ObjectType +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_value_from_object.gotmpl b/internal/schema/templates/object_type_value_from_object.gotmpl new file mode 100644 index 00000000..595aa1f5 --- /dev/null +++ b/internal/schema/templates/object_type_value_from_object.gotmpl @@ -0,0 +1,6 @@ + +func (t {{.Name}}Type) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { +return {{.Name}}Value{ +ObjectValue: in, +}, nil +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_value_from_terraform.gotmpl b/internal/schema/templates/object_type_value_from_terraform.gotmpl new file mode 100644 index 00000000..6584125e --- /dev/null +++ b/internal/schema/templates/object_type_value_from_terraform.gotmpl @@ -0,0 +1,22 @@ + +func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { +attrValue, err := t.ObjectType.ValueFromTerraform(ctx, in) + +if err != nil { +return nil, err +} + +objectValue, ok := attrValue.(basetypes.ObjectValue) + +if !ok { +return nil, fmt.Errorf("unexpected value type of %T", attrValue) +} + +objectValuable, diags := t.ValueFromObject(ctx, objectValue) + +if diags.HasError() { +return nil, fmt.Errorf("unexpected error converting ObjectValue to ObjectValuable: %v", diags) +} + +return objectValuable, nil +} \ No newline at end of file diff --git a/internal/schema/templates/object_type_value_type.gotmpl b/internal/schema/templates/object_type_value_type.gotmpl new file mode 100644 index 00000000..1f7b7fea --- /dev/null +++ b/internal/schema/templates/object_type_value_type.gotmpl @@ -0,0 +1,4 @@ + +func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { +return {{.Name}}Value{} +} \ No newline at end of file diff --git a/internal/schema/templates/object_value_attribute_types.gotmpl b/internal/schema/templates/object_value_attribute_types.gotmpl new file mode 100644 index 00000000..181d1172 --- /dev/null +++ b/internal/schema/templates/object_value_attribute_types.gotmpl @@ -0,0 +1,6 @@ + +func (v {{.Name}}Value) AttributeTypes(ctx context.Context) map[string]attr.Type { +return map[string]attr.Type{ +{{.AttrTypes }} +} +} \ No newline at end of file diff --git a/internal/schema/templates/object_value_equal.gotmpl b/internal/schema/templates/object_value_equal.gotmpl new file mode 100644 index 00000000..36623186 --- /dev/null +++ b/internal/schema/templates/object_value_equal.gotmpl @@ -0,0 +1,10 @@ + +func (v {{.Name}}Value) Equal(o attr.Value) bool { +other, ok := o.({{.Name}}Value) + +if !ok { +return false +} + +return v.ObjectValue.Equal(other.ObjectValue) +} \ No newline at end of file diff --git a/internal/schema/templates/object_value_type.gotmpl b/internal/schema/templates/object_value_type.gotmpl new file mode 100644 index 00000000..ef6064c1 --- /dev/null +++ b/internal/schema/templates/object_value_type.gotmpl @@ -0,0 +1,8 @@ + +func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { +return {{.Name}}Type{ +ObjectType: basetypes.ObjectType{ +AttrTypes: v.AttributeTypes(ctx), +}, +} +} \ No newline at end of file diff --git a/internal/schema/templates/object_value_valuable.gotmpl b/internal/schema/templates/object_value_valuable.gotmpl new file mode 100644 index 00000000..0ddaed93 --- /dev/null +++ b/internal/schema/templates/object_value_valuable.gotmpl @@ -0,0 +1 @@ +var _ basetypes.ObjectValuable = {{.Name}}Value{} \ No newline at end of file diff --git a/internal/schema/templates/object_value_value.gotmpl b/internal/schema/templates/object_value_value.gotmpl new file mode 100644 index 00000000..c954d8dd --- /dev/null +++ b/internal/schema/templates/object_value_value.gotmpl @@ -0,0 +1,3 @@ +type {{.Name}}Value struct { +basetypes.ObjectValue +} \ 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 e41e8e56..ade0ba47 100644 --- a/internal/schema/to_from_nested_object_test.go +++ b/internal/schema/to_from_nested_object_test.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-codegen-spec/schema" ) -func TestToFromObject_renderFrom(t *testing.T) { +func TestToFromNestedObject_renderFrom(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -110,7 +110,7 @@ state: attr.ValueStateKnown, } } -func TestToFromObject_renderTo(t *testing.T) { +func TestToFromNestedObject_renderTo(t *testing.T) { t.Parallel() testCases := map[string]struct { diff --git a/internal/schema/to_from_object.go b/internal/schema/to_from_object.go new file mode 100644 index 00000000..cbafd54a --- /dev/null +++ b/internal/schema/to_from_object.go @@ -0,0 +1,109 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "bytes" + "text/template" +) + +type ToFromObject struct { + Name FrameworkIdentifier + AssocExtType *AssocExtType + AttrTypesFromFuncs map[FrameworkIdentifier]string + templates map[string]string +} + +func NewToFromObject(name string, assocExtType *AssocExtType, attrTypesFromFuncs map[string]string) ToFromObject { + t := map[string]string{ + "from": ObjectFromTemplate, + "to": ObjectToTemplate, + } + + atff := make(map[FrameworkIdentifier]string, len(attrTypesFromFuncs)) + + for k, v := range attrTypesFromFuncs { + atff[FrameworkIdentifier(k)] = v + } + + return ToFromObject{ + Name: FrameworkIdentifier(name), + AssocExtType: assocExtType, + AttrTypesFromFuncs: atff, + templates: t, + } +} + +func (o ToFromObject) Render() ([]byte, error) { + var buf bytes.Buffer + + renderFuncs := []func() ([]byte, error){ + o.renderTo, + o.renderFrom, + } + + for _, f := range renderFuncs { + b, err := f() + + if err != nil { + return nil, err + } + + buf.Write([]byte("\n")) + + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (o ToFromObject) renderTo() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["to"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (o ToFromObject) renderFrom() ([]byte, error) { + var buf bytes.Buffer + + t, err := template.New("").Parse(o.templates["from"]) + + if err != nil { + return nil, err + } + + err = t.Execute(&buf, struct { + Name string + AssocExtType *AssocExtType + AttrTypesFromFuncs map[FrameworkIdentifier]string + }{ + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + AttrTypesFromFuncs: o.AttrTypesFromFuncs, + }) + + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/internal/schema/to_from_object_test.go b/internal/schema/to_from_object_test.go new file mode 100644 index 00000000..602e7b53 --- /dev/null +++ b/internal/schema/to_from_object_test.go @@ -0,0 +1,173 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" + "github.com/hashicorp/terraform-plugin-codegen-spec/schema" +) + +func TestToFromObject_renderFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + attrTypesFromFuncs map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + attrTypesFromFuncs: map[string]string{ + "bool": "types.BoolPointerValue", + "float64": "types.Float64PointerValue", + "int64": "types.Int64PointerValue", + "number": "types.NumberValue", + "string": "types.StringPointerValue", + }, + expected: []byte(` +func (v ExampleValue) FromApisdkType(ctx context.Context, apiObject *apisdk.Type) (ExampleValue, diag.Diagnostics) { +var diags diag.Diagnostics + +if apiObject == nil { +return ExampleValue{ +types.ObjectNull(v.AttributeTypes(ctx)), +}, diags +} + +o, d := basetypes.NewObjectValue(v.AttributeTypes(ctx), map[string]attr.Value{ +"bool": types.BoolPointerValue(apiObject.Bool), +"float64": types.Float64PointerValue(apiObject.Float64), +"int64": types.Int64PointerValue(apiObject.Int64), +"number": types.NumberValue(apiObject.Number), +"string": types.StringPointerValue(apiObject.String), +}) + +diags.Append(d...) + +if diags.HasError() { +return ExampleValue{ +types.ObjectNull(v.AttributeTypes(ctx)), +}, diags +} + +return ExampleValue{ +o, +}, diags +} +`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.attrTypesFromFuncs) + + got, err := toFromObject.renderFrom() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestToFromObject_renderTo(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + name string + assocExtType *AssocExtType + attrTypesFromFuncs map[string]string + expected []byte + expectedError error + }{ + "default": { + name: "Example", + assocExtType: &AssocExtType{ + &schema.AssociatedExternalType{ + Import: &code.Import{ + Path: "example.com/apisdk", + }, + Type: "*apisdk.Type", + }, + }, + attrTypesFromFuncs: map[string]string{ + "bool": "types.BoolPointerValue", + "float64": "types.Float64PointerValue", + "int64": "types.Int64PointerValue", + "number": "types.NumberValue", + "string": "types.StringPointerValue", + }, + 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 apisdkType apisdk.Type + +d := v.As(ctx, &apisdkType, basetypes.ObjectAsOptions{}) + +diags.Append(d...) + +if diags.HasError() { +return nil, diags +} + +return &apisdkType, diags +}`), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.attrTypesFromFuncs) + + got, err := toFromObject.renderTo() + + if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { + t.Errorf("unexpected error: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} From b19684616a410d505eb7f87e0a3cdbf5dffce32d Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 17:50:55 +0100 Subject: [PATCH 08/13] Removing hardcoded attribute names (#74) --- internal/schema/templates/list_from.gotmpl | 4 ++-- internal/schema/templates/map_from.gotmpl | 4 ++-- internal/schema/templates/set_from.gotmpl | 4 ++-- internal/schema/to_from_list_test.go | 4 ++-- internal/schema/to_from_map_test.go | 4 ++-- internal/schema/to_from_set_test.go | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/schema/templates/list_from.gotmpl b/internal/schema/templates/list_from.gotmpl index c4051bae..668c058f 100644 --- a/internal/schema/templates/list_from.gotmpl +++ b/internal/schema/templates/list_from.gotmpl @@ -19,12 +19,12 @@ l, d := basetypes.NewListValueFrom(ctx, {{.ElementTypeType}}, elems) diags.Append(d...) if diags.HasError() { -return ListAttributeValue{ +return {{.Name}}Value{ types.ListNull({{.ElementTypeType}}), }, diags } -return ListAttributeValue{ +return {{.Name}}Value{ l, }, diags } diff --git a/internal/schema/templates/map_from.gotmpl b/internal/schema/templates/map_from.gotmpl index 9f56a55a..f522e226 100644 --- a/internal/schema/templates/map_from.gotmpl +++ b/internal/schema/templates/map_from.gotmpl @@ -19,12 +19,12 @@ l, d := basetypes.NewMapValueFrom(ctx, {{.ElementTypeType}}, elems) diags.Append(d...) if diags.HasError() { -return MapAttributeValue{ +return {{.Name}}Value{ types.MapNull({{.ElementTypeType}}), }, diags } -return MapAttributeValue{ +return {{.Name}}Value{ l, }, diags } diff --git a/internal/schema/templates/set_from.gotmpl b/internal/schema/templates/set_from.gotmpl index f7be55a6..8435fa2d 100644 --- a/internal/schema/templates/set_from.gotmpl +++ b/internal/schema/templates/set_from.gotmpl @@ -19,12 +19,12 @@ l, d := basetypes.NewSetValueFrom(ctx, {{.ElementTypeType}}, elems) diags.Append(d...) if diags.HasError() { -return SetAttributeValue{ +return {{.Name}}Value{ types.SetNull({{.ElementTypeType}}), }, diags } -return SetAttributeValue{ +return {{.Name}}Value{ l, }, diags } diff --git a/internal/schema/to_from_list_test.go b/internal/schema/to_from_list_test.go index e5575e49..c4a92a83 100644 --- a/internal/schema/to_from_list_test.go +++ b/internal/schema/to_from_list_test.go @@ -57,12 +57,12 @@ l, d := basetypes.NewListValueFrom(ctx, types.BoolType, elems) diags.Append(d...) if diags.HasError() { -return ListAttributeValue{ +return ExampleValue{ types.ListNull(types.BoolType), }, diags } -return ListAttributeValue{ +return ExampleValue{ l, }, diags } diff --git a/internal/schema/to_from_map_test.go b/internal/schema/to_from_map_test.go index d4a5664f..2d57e0c7 100644 --- a/internal/schema/to_from_map_test.go +++ b/internal/schema/to_from_map_test.go @@ -57,12 +57,12 @@ l, d := basetypes.NewMapValueFrom(ctx, types.BoolType, elems) diags.Append(d...) if diags.HasError() { -return MapAttributeValue{ +return ExampleValue{ types.MapNull(types.BoolType), }, diags } -return MapAttributeValue{ +return ExampleValue{ l, }, diags } diff --git a/internal/schema/to_from_set_test.go b/internal/schema/to_from_set_test.go index ef529d2a..46f8a193 100644 --- a/internal/schema/to_from_set_test.go +++ b/internal/schema/to_from_set_test.go @@ -57,12 +57,12 @@ l, d := basetypes.NewSetValueFrom(ctx, types.BoolType, elems) diags.Append(d...) if diags.HasError() { -return SetAttributeValue{ +return ExampleValue{ types.SetNull(types.BoolType), }, diags } -return SetAttributeValue{ +return ExampleValue{ l, }, diags } From 97e6a50530f5b8d547cb24cd03da8828fa33b1f0 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 23 Oct 2023 18:00:24 +0100 Subject: [PATCH 09/13] Adding changelog entry (#74) --- .changes/unreleased/ENHANCEMENTS-20231023-180006.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/unreleased/ENHANCEMENTS-20231023-180006.yaml diff --git a/.changes/unreleased/ENHANCEMENTS-20231023-180006.yaml b/.changes/unreleased/ENHANCEMENTS-20231023-180006.yaml new file mode 100644 index 00000000..8be7f4c9 --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20231023-180006.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: Adds code generation for List, Map, Object, and Set attributes that have an + associated external type +time: 2023-10-23T18:00:06.752758+01:00 +custom: + Issue: "75" From 10c7d3acee6716373c7418ff1467c88e6cd4f51a Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 24 Oct 2023 08:19:58 +0100 Subject: [PATCH 10/13] Fixing value types (#74) --- internal/schema/elements.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/schema/elements.go b/internal/schema/elements.go index 71b1e9ef..7e841e7a 100644 --- a/internal/schema/elements.go +++ b/internal/schema/elements.go @@ -86,12 +86,12 @@ func GetElementValueType(e specschema.ElementType) string { if e.List.CustomType != nil { return e.List.CustomType.ValueType } - return "types.ListType" + return "types.List" case e.Map != nil: if e.Map.CustomType != nil { return e.Map.CustomType.ValueType } - return "types.MapType" + return "types.Map" case e.Number != nil: if e.Number.CustomType != nil { return e.Number.CustomType.ValueType @@ -101,12 +101,12 @@ func GetElementValueType(e specschema.ElementType) string { if e.Object.CustomType != nil { return e.Object.CustomType.ValueType } - return "types.ObjectType" + return "types.Object" case e.Set != nil: if e.Set.CustomType != nil { return e.Set.CustomType.ValueType } - return "types.SetType" + return "types.Set" case e.String != nil: if e.String.CustomType != nil { return e.String.CustomType.ValueType From bc14d44d597587db4a5c6ddc66dec6207ba795a5 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 24 Oct 2023 08:38:36 +0100 Subject: [PATCH 11/13] Return unknown if error diagnostic is raised during From() method (#74) --- internal/schema/templates/list_from.gotmpl | 2 +- internal/schema/templates/map_from.gotmpl | 2 +- internal/schema/templates/nested_object_from.gotmpl | 2 +- internal/schema/templates/object_from.gotmpl | 2 +- internal/schema/templates/set_from.gotmpl | 2 +- internal/schema/to_from_list_test.go | 2 +- internal/schema/to_from_map_test.go | 2 +- internal/schema/to_from_nested_object_test.go | 2 +- internal/schema/to_from_object_test.go | 2 +- internal/schema/to_from_set_test.go | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/schema/templates/list_from.gotmpl b/internal/schema/templates/list_from.gotmpl index 668c058f..4c3bf517 100644 --- a/internal/schema/templates/list_from.gotmpl +++ b/internal/schema/templates/list_from.gotmpl @@ -20,7 +20,7 @@ diags.Append(d...) if diags.HasError() { return {{.Name}}Value{ -types.ListNull({{.ElementTypeType}}), +types.ListUnknown({{.ElementTypeType}}), }, diags } diff --git a/internal/schema/templates/map_from.gotmpl b/internal/schema/templates/map_from.gotmpl index f522e226..9dbf745a 100644 --- a/internal/schema/templates/map_from.gotmpl +++ b/internal/schema/templates/map_from.gotmpl @@ -20,7 +20,7 @@ diags.Append(d...) if diags.HasError() { return {{.Name}}Value{ -types.MapNull({{.ElementTypeType}}), +types.MapUnknown({{.ElementTypeType}}), }, diags } diff --git a/internal/schema/templates/nested_object_from.gotmpl b/internal/schema/templates/nested_object_from.gotmpl index 39ff2b92..027eff49 100644 --- a/internal/schema/templates/nested_object_from.gotmpl +++ b/internal/schema/templates/nested_object_from.gotmpl @@ -13,7 +13,7 @@ return New{{.Name}}ValueNull(), diags diags.Append(d...) if diags.HasError() { -return New{{$.Name}}ValueNull(), diags +return New{{$.Name}}ValueUnknown(), diags } {{- end}} {{- end}} diff --git a/internal/schema/templates/object_from.gotmpl b/internal/schema/templates/object_from.gotmpl index dc151b8d..f37ceab6 100644 --- a/internal/schema/templates/object_from.gotmpl +++ b/internal/schema/templates/object_from.gotmpl @@ -18,7 +18,7 @@ diags.Append(d...) if diags.HasError() { return {{.Name}}Value{ -types.ObjectNull(v.AttributeTypes(ctx)), +types.ObjectUnknown(v.AttributeTypes(ctx)), }, diags } diff --git a/internal/schema/templates/set_from.gotmpl b/internal/schema/templates/set_from.gotmpl index 8435fa2d..c6e274b3 100644 --- a/internal/schema/templates/set_from.gotmpl +++ b/internal/schema/templates/set_from.gotmpl @@ -20,7 +20,7 @@ diags.Append(d...) if diags.HasError() { return {{.Name}}Value{ -types.SetNull({{.ElementTypeType}}), +types.SetUnknown({{.ElementTypeType}}), }, diags } diff --git a/internal/schema/to_from_list_test.go b/internal/schema/to_from_list_test.go index c4a92a83..20ab533d 100644 --- a/internal/schema/to_from_list_test.go +++ b/internal/schema/to_from_list_test.go @@ -58,7 +58,7 @@ diags.Append(d...) if diags.HasError() { return ExampleValue{ -types.ListNull(types.BoolType), +types.ListUnknown(types.BoolType), }, diags } diff --git a/internal/schema/to_from_map_test.go b/internal/schema/to_from_map_test.go index 2d57e0c7..0707e2d3 100644 --- a/internal/schema/to_from_map_test.go +++ b/internal/schema/to_from_map_test.go @@ -58,7 +58,7 @@ diags.Append(d...) if diags.HasError() { return ExampleValue{ -types.MapNull(types.BoolType), +types.MapUnknown(types.BoolType), }, diags } diff --git a/internal/schema/to_from_nested_object_test.go b/internal/schema/to_from_nested_object_test.go index ade0ba47..ef716064 100644 --- a/internal/schema/to_from_nested_object_test.go +++ b/internal/schema/to_from_nested_object_test.go @@ -77,7 +77,7 @@ boolAttributeVal, d := BoolAttributeValue{}.FromApiBoolAttribute(ctx, apiObject. diags.Append(d...) if diags.HasError() { -return NewExampleValueNull(), diags +return NewExampleValueUnknown(), diags } return ExampleValue{ diff --git a/internal/schema/to_from_object_test.go b/internal/schema/to_from_object_test.go index 602e7b53..078afb94 100644 --- a/internal/schema/to_from_object_test.go +++ b/internal/schema/to_from_object_test.go @@ -60,7 +60,7 @@ diags.Append(d...) if diags.HasError() { return ExampleValue{ -types.ObjectNull(v.AttributeTypes(ctx)), +types.ObjectUnknown(v.AttributeTypes(ctx)), }, diags } diff --git a/internal/schema/to_from_set_test.go b/internal/schema/to_from_set_test.go index 46f8a193..a4c359f9 100644 --- a/internal/schema/to_from_set_test.go +++ b/internal/schema/to_from_set_test.go @@ -58,7 +58,7 @@ diags.Append(d...) if diags.HasError() { return ExampleValue{ -types.SetNull(types.BoolType), +types.SetUnknown(types.BoolType), }, diags } From c95a2411841c3eea1bcfb7ddf60edfd13adb27d1 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 24 Oct 2023 10:15:18 +0100 Subject: [PATCH 12/13] Bumping to latest version of codegen-spec (#74) --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index b6f40153..a65fc5e0 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.20 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca + github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231024091233-c659ac8a54fc github.com/hashicorp/terraform-plugin-framework v1.4.0 + github.com/hashicorp/terraform-plugin-go v0.19.0 github.com/mattn/go-colorable v0.1.12 github.com/mitchellh/cli v1.1.5 ) @@ -21,7 +22,6 @@ require ( github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-multierror v1.0.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.19.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.11 // indirect diff --git a/go.sum b/go.sum index 9641b81f..53729703 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca h1:HOURkk+e71xtn3nEizi8rOd7aWawoGQ/dZ4kh+ko82M= -github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231020114651-05c8a44239ca/go.mod h1:PQn6bDD8UWoAVJoHXqFk2i/RmLbeQBjbiP38i+E+YIw= +github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231024091233-c659ac8a54fc h1:VmMk5vOSJgpWOuBsI4ZBZkcsrkLq0fKoyKvKFnbBuxk= +github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1-0.20231024091233-c659ac8a54fc/go.mod h1:PQn6bDD8UWoAVJoHXqFk2i/RmLbeQBjbiP38i+E+YIw= github.com/hashicorp/terraform-plugin-framework v1.4.0 h1:WKbtCRtNrjsh10eA7NZvC/Qyr7zp77j+D21aDO5th9c= github.com/hashicorp/terraform-plugin-framework v1.4.0/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0= github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= From 48bfbfa91a396d656f1b562956bdd4cfd4dec235 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 24 Oct 2023 17:55:33 +0100 Subject: [PATCH 13/13] Fixing handling of generated To() method for object attribute (#74) --- .../datasource_generate/object_attribute.go | 4 +- .../provider_generate/object_attribute.go | 4 +- .../resource_generate/object_attribute.go | 4 +- internal/schema/attrtypes.go | 68 ++++++++++++++ internal/schema/elements.go | 24 ----- internal/schema/templates/object_to.gotmpl | 20 ++++- internal/schema/to_from_object.go | 20 +++-- internal/schema/to_from_object_test.go | 88 ++++++++++++++++--- 8 files changed, 186 insertions(+), 46 deletions(-) diff --git a/internal/datasource_generate/object_attribute.go b/internal/datasource_generate/object_attribute.go index e3b150a7..08cc273c 100644 --- a/internal/datasource_generate/object_attribute.go +++ b/internal/datasource_generate/object_attribute.go @@ -174,9 +174,11 @@ func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { return nil, nil } + attrTypesToFuncs := generatorschema.GetAttrTypesToFuncs(g.AttributeTypes) + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesToFuncs, attrTypesFromFuncs) b, err := toFrom.Render() diff --git a/internal/provider_generate/object_attribute.go b/internal/provider_generate/object_attribute.go index 5c7eee1d..e135feee 100644 --- a/internal/provider_generate/object_attribute.go +++ b/internal/provider_generate/object_attribute.go @@ -174,9 +174,11 @@ func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { return nil, nil } + attrTypesToFuncs := generatorschema.GetAttrTypesToFuncs(g.AttributeTypes) + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesToFuncs, attrTypesFromFuncs) b, err := toFrom.Render() diff --git a/internal/resource_generate/object_attribute.go b/internal/resource_generate/object_attribute.go index 350ad101..4c48b25b 100644 --- a/internal/resource_generate/object_attribute.go +++ b/internal/resource_generate/object_attribute.go @@ -207,9 +207,11 @@ func (g GeneratorObjectAttribute) ToFromFunctions(name string) ([]byte, error) { return nil, nil } + attrTypesToFuncs := generatorschema.GetAttrTypesToFuncs(g.AttributeTypes) + attrTypesFromFuncs := generatorschema.GetAttrTypesFromFuncs(g.AttributeTypes) - toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesFromFuncs) + toFrom := generatorschema.NewToFromObject(name, g.AssociatedExternalType, attrTypesToFuncs, attrTypesFromFuncs) b, err := toFrom.Render() diff --git a/internal/schema/attrtypes.go b/internal/schema/attrtypes.go index d918a737..e145db62 100644 --- a/internal/schema/attrtypes.go +++ b/internal/schema/attrtypes.go @@ -84,3 +84,71 @@ func GetAttrTypes(attrTypes specschema.ObjectAttributeTypes) string { return aTypes.String() } + +type AttrTypesToFuncs struct { + AttrValue string + ToFunc string +} + +// GetAttrTypesToFuncs returns string representations of the function that is used +// for converting to an API Go type from a framework type. +// TODO: Handle custom type, and types other than primitives. +func GetAttrTypesToFuncs(a specschema.ObjectAttributeTypes) map[string]AttrTypesToFuncs { + attrTypesFuncs := make(map[string]AttrTypesToFuncs, len(a)) + + for _, v := range a { + switch { + case v.Bool != nil: + attrTypesFuncs[v.Name] = AttrTypesToFuncs{ + AttrValue: "types.Bool", + ToFunc: "ValueBoolPointer", + } + case v.Float64 != nil: + attrTypesFuncs[v.Name] = AttrTypesToFuncs{ + AttrValue: "types.Float64", + ToFunc: "ValueFloat64Pointer", + } + case v.Int64 != nil: + attrTypesFuncs[v.Name] = AttrTypesToFuncs{ + AttrValue: "types.Int64", + ToFunc: "ValueInt64Pointer", + } + case v.Number != nil: + attrTypesFuncs[v.Name] = AttrTypesToFuncs{ + AttrValue: "types.Number", + ToFunc: "ValueBigFloat", + } + case v.String != nil: + attrTypesFuncs[v.Name] = AttrTypesToFuncs{ + AttrValue: "types.String", + ToFunc: "ValueStringPointer", + } + } + } + + return attrTypesFuncs +} + +// GetAttrTypesFromFuncs returns string representations of the function that is used +// for converting from an API Go type to a framework type. +// TODO: Handle custom type, and types other than primitives. +func GetAttrTypesFromFuncs(a specschema.ObjectAttributeTypes) map[string]string { + attrTypesFuncs := make(map[string]string, len(a)) + + for _, v := range a { + switch { + case v.Bool != nil: + attrTypesFuncs[v.Name] = "types.BoolPointerValue" + case v.Float64 != nil: + attrTypesFuncs[v.Name] = "types.Float64PointerValue" + case v.Int64 != nil: + attrTypesFuncs[v.Name] = "types.Int64PointerValue" + case v.Number != nil: + attrTypesFuncs[v.Name] = "types.NumberValue" + case v.String != nil: + attrTypesFuncs[v.Name] = "types.StringPointerValue" + } + } + + return attrTypesFuncs +} diff --git a/internal/schema/elements.go b/internal/schema/elements.go index 7e841e7a..51d58d87 100644 --- a/internal/schema/elements.go +++ b/internal/schema/elements.go @@ -136,27 +136,3 @@ func GetElementFromFunc(e specschema.ElementType) string { return "" } - -// GetAttrTypesFromFuncs returns string representations of the function that is used -// for converting from an API Go type to a framework type. -// TODO: Handle custom type, and types other than primitives. -func GetAttrTypesFromFuncs(a specschema.ObjectAttributeTypes) map[string]string { - attrTypesFuncs := make(map[string]string, len(a)) - - for _, v := range a { - switch { - case v.Bool != nil: - attrTypesFuncs[v.Name] = "types.BoolPointerValue" - case v.Float64 != nil: - attrTypesFuncs[v.Name] = "types.Float64PointerValue" - case v.Int64 != nil: - attrTypesFuncs[v.Name] = "types.Int64PointerValue" - case v.Number != nil: - attrTypesFuncs[v.Name] = "types.NumberValue" - case v.String != nil: - attrTypesFuncs[v.Name] = "types.StringPointerValue" - } - } - - return attrTypesFuncs -} diff --git a/internal/schema/templates/object_to.gotmpl b/internal/schema/templates/object_to.gotmpl index 47cb436f..5b39e16e 100644 --- a/internal/schema/templates/object_to.gotmpl +++ b/internal/schema/templates/object_to.gotmpl @@ -14,15 +14,29 @@ diags.Append(diag.NewErrorDiagnostic( return nil, diags } -var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} +attributes := v.Attributes() -d := v.As(ctx, &{{.AssocExtType.ToCamelCase}}, basetypes.ObjectAsOptions{}) +{{- range $key, $value := .AttrTypesToFuncs}} -diags.Append(d...) +{{$key}}Attribute, ok := attributes["{{$key}}"].({{$value.AttrValue}}) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"{{$.Name}}Value {{$key}} is unexpected type", +fmt.Sprintf(`"{{$.Name}}Value" {{$key}} is type of %T".`, attributes["{{$key}}"]), +)) +} +{{- end}} if diags.HasError() { return nil, diags } +{{.AssocExtType.ToCamelCase}} := {{.AssocExtType.TypeReference}} { +{{- range $key, $value := .AttrTypesToFuncs}} +{{$key.ToPascalCase}}: {{$key}}Attribute.{{$value.ToFunc}}(), +{{- end}} +} + return &{{.AssocExtType.ToCamelCase}}, diags } \ No newline at end of file diff --git a/internal/schema/to_from_object.go b/internal/schema/to_from_object.go index cbafd54a..b3344b35 100644 --- a/internal/schema/to_from_object.go +++ b/internal/schema/to_from_object.go @@ -11,16 +11,23 @@ import ( type ToFromObject struct { Name FrameworkIdentifier AssocExtType *AssocExtType + AttrTypesToFuncs map[FrameworkIdentifier]AttrTypesToFuncs AttrTypesFromFuncs map[FrameworkIdentifier]string templates map[string]string } -func NewToFromObject(name string, assocExtType *AssocExtType, attrTypesFromFuncs map[string]string) ToFromObject { +func NewToFromObject(name string, assocExtType *AssocExtType, attrTypesToFuncs map[string]AttrTypesToFuncs, attrTypesFromFuncs map[string]string) ToFromObject { t := map[string]string{ "from": ObjectFromTemplate, "to": ObjectToTemplate, } + attf := make(map[FrameworkIdentifier]AttrTypesToFuncs, len(attrTypesToFuncs)) + + for k, v := range attrTypesToFuncs { + attf[FrameworkIdentifier(k)] = v + } + atff := make(map[FrameworkIdentifier]string, len(attrTypesFromFuncs)) for k, v := range attrTypesFromFuncs { @@ -30,6 +37,7 @@ func NewToFromObject(name string, assocExtType *AssocExtType, attrTypesFromFuncs return ToFromObject{ Name: FrameworkIdentifier(name), AssocExtType: assocExtType, + AttrTypesToFuncs: attf, AttrTypesFromFuncs: atff, templates: t, } @@ -68,11 +76,13 @@ func (o ToFromObject) renderTo() ([]byte, error) { } err = t.Execute(&buf, struct { - Name string - AssocExtType *AssocExtType + Name string + AssocExtType *AssocExtType + AttrTypesToFuncs map[FrameworkIdentifier]AttrTypesToFuncs }{ - Name: o.Name.ToPascalCase(), - AssocExtType: o.AssocExtType, + Name: o.Name.ToPascalCase(), + AssocExtType: o.AssocExtType, + AttrTypesToFuncs: o.AttrTypesToFuncs, }) if err != nil { diff --git a/internal/schema/to_from_object_test.go b/internal/schema/to_from_object_test.go index 078afb94..dfd13be6 100644 --- a/internal/schema/to_from_object_test.go +++ b/internal/schema/to_from_object_test.go @@ -78,7 +78,7 @@ o, t.Run(name, func(t *testing.T) { t.Parallel() - toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.attrTypesFromFuncs) + toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, nil, testCase.attrTypesFromFuncs) got, err := toFromObject.renderFrom() @@ -99,6 +99,7 @@ func TestToFromObject_renderTo(t *testing.T) { testCases := map[string]struct { name string assocExtType *AssocExtType + attrTypesToFuncs map[string]AttrTypesToFuncs attrTypesFromFuncs map[string]string expected []byte expectedError error @@ -113,12 +114,28 @@ func TestToFromObject_renderTo(t *testing.T) { Type: "*apisdk.Type", }, }, - attrTypesFromFuncs: map[string]string{ - "bool": "types.BoolPointerValue", - "float64": "types.Float64PointerValue", - "int64": "types.Int64PointerValue", - "number": "types.NumberValue", - "string": "types.StringPointerValue", + attrTypesToFuncs: map[string]AttrTypesToFuncs{ + "bool": { + AttrValue: "types.Bool", + ToFunc: "ValueBoolPointer", + }, + "float64": { + AttrValue: "types.Float64", + ToFunc: "ValueFloat64Pointer", + }, + "int64": { + AttrValue: "types.Int64", + ToFunc: "ValueInt64Pointer", + }, + + "number": { + AttrValue: "types.Number", + ToFunc: "ValueBigFloat", + }, + "string": { + AttrValue: "types.String", + ToFunc: "ValueStringPointer", + }, }, expected: []byte(`func (v ExampleValue) ToApisdkType(ctx context.Context) (*apisdk.Type, diag.Diagnostics) { var diags diag.Diagnostics @@ -136,16 +153,65 @@ diags.Append(diag.NewErrorDiagnostic( return nil, diags } -var apisdkType apisdk.Type +attributes := v.Attributes() -d := v.As(ctx, &apisdkType, basetypes.ObjectAsOptions{}) +boolAttribute, ok := attributes["bool"].(types.Bool) -diags.Append(d...) +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue bool is unexpected type", +fmt.Sprintf(` + "`" + `"ExampleValue" bool is type of %T".` + "`" + `, attributes["bool"]), +)) +} + +float64Attribute, ok := attributes["float64"].(types.Float64) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue float64 is unexpected type", +fmt.Sprintf(` + "`" + `"ExampleValue" float64 is type of %T".` + "`" + `, attributes["float64"]), +)) +} + +int64Attribute, ok := attributes["int64"].(types.Int64) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue int64 is unexpected type", +fmt.Sprintf(` + "`" + `"ExampleValue" int64 is type of %T".` + "`" + `, attributes["int64"]), +)) +} + +numberAttribute, ok := attributes["number"].(types.Number) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue number is unexpected type", +fmt.Sprintf(` + "`" + `"ExampleValue" number is type of %T".` + "`" + `, attributes["number"]), +)) +} + +stringAttribute, ok := attributes["string"].(types.String) + +if !ok { +diags.Append(diag.NewErrorDiagnostic( +"ExampleValue string is unexpected type", +fmt.Sprintf(` + "`" + `"ExampleValue" string is type of %T".` + "`" + `, attributes["string"]), +)) +} if diags.HasError() { return nil, diags } +apisdkType := apisdk.Type { +Bool: boolAttribute.ValueBoolPointer(), +Float64: float64Attribute.ValueFloat64Pointer(), +Int64: int64Attribute.ValueInt64Pointer(), +Number: numberAttribute.ValueBigFloat(), +String: stringAttribute.ValueStringPointer(), +} + return &apisdkType, diags }`), }, @@ -157,7 +223,7 @@ return &apisdkType, diags t.Run(name, func(t *testing.T) { t.Parallel() - toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.attrTypesFromFuncs) + toFromObject := NewToFromObject(testCase.name, testCase.assocExtType, testCase.attrTypesToFuncs, nil) got, err := toFromObject.renderTo()