From 5f9e61a621903fe31e0b1a8d331a4f3ec26f9d60 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 10 Aug 2023 15:34:17 +0100 Subject: [PATCH] Add handling for set nested attribute/block with associated external type (#31) --- .../all_output/datasource_data_source_gen.go | 245 ++++++- .../datasource_data_source_gen.go | 245 ++++++- .../cmd/testdata/custom_and_external/ir.json | 91 +++ .../set_nested_attribute.go | 7 +- .../datasource_convert/set_nested_block.go | 9 +- .../set_nested_attribute.go | 7 + .../set_nested_attribute_test.go | 573 +++++++++++++++++ .../datasource_generate/set_nested_block.go | 7 + .../set_nested_block_test.go | 604 ++++++++++++++++++ internal/schema/schema.go | 19 + internal/templates/embed.go | 3 + .../templates/model_object_helpers.gotmpl | 2 +- .../set_nested_object_to_from.gotmpl | 51 ++ 13 files changed, 1827 insertions(+), 36 deletions(-) create mode 100644 internal/templates/set_nested_object_to_from.gotmpl diff --git a/internal/cmd/testdata/custom_and_external/all_output/datasource_data_source_gen.go b/internal/cmd/testdata/custom_and_external/all_output/datasource_data_source_gen.go index 7a88cf70..3605d93b 100644 --- a/internal/cmd/testdata/custom_and_external/all_output/datasource_data_source_gen.go +++ b/internal/cmd/testdata/custom_and_external/all_output/datasource_data_source_gen.go @@ -172,6 +172,32 @@ var datasourceDataSourceSchema = schema.Schema{ }, Computed: true, }, + "set_nested_attribute_assoc_ext_type": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "bool_attribute": schema.BoolAttribute{ + Computed: true, + }, + "float64_attribute": schema.Float64Attribute{ + Optional: true, + Computed: true, + }, + "int64_attribute": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "number_attribute": schema.NumberAttribute{ + Optional: true, + Computed: true, + }, + "string_attribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + }, + }, + Optional: true, + }, "single_nested_attribute_assoc_ext_type": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ @@ -306,6 +332,31 @@ var datasourceDataSourceSchema = schema.Schema{ }, }, }, + "set_nested_block_assoc_ext_type": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "bool_attribute": schema.BoolAttribute{ + Computed: true, + }, + "float64_attribute": schema.Float64Attribute{ + Optional: true, + Computed: true, + }, + "int64_attribute": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "number_attribute": schema.NumberAttribute{ + Optional: true, + Computed: true, + }, + "string_attribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + }, + }, + }, "single_nested_block_assoc_ext_type": schema.SingleNestedBlock{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ @@ -386,6 +437,7 @@ type DatasourceModel struct { ObjectAttribute types.Object `tfsdk:"object_attribute"` ObjectListAttribute types.Object `tfsdk:"object_list_attribute"` ObjectListObjectAttribute types.Object `tfsdk:"object_list_object_attribute"` + SetNestedAttributeAssocExtType types.Set `tfsdk:"set_nested_attribute_assoc_ext_type"` SingleNestedAttributeAssocExtType types.Object `tfsdk:"single_nested_attribute_assoc_ext_type"` SingleNestedAttributeOne types.Object `tfsdk:"single_nested_attribute_one"` SingleNestedAttributeThree types.Object `tfsdk:"single_nested_attribute_three"` @@ -394,6 +446,7 @@ type DatasourceModel struct { ListNestedBlockOne types.List `tfsdk:"list_nested_block_one"` ListNestedBlockThree types.List `tfsdk:"list_nested_block_three"` ListNestedBlockTwo types.List `tfsdk:"list_nested_block_two"` + SetNestedBlockAssocExtType types.Set `tfsdk:"set_nested_block_assoc_ext_type"` SingleNestedBlockAssocExtType types.Object `tfsdk:"single_nested_block_assoc_ext_type"` SingleNestedBlockOne types.Object `tfsdk:"single_nested_block_one"` SingleNestedBlockThree types.Object `tfsdk:"single_nested_block_three"` @@ -436,6 +489,14 @@ type MapNestedAttributeAssocExtTypeModel struct { StringAttribute types.String `tfsdk:"string_attribute"` } +type SetNestedAttributeAssocExtTypeModel struct { + BoolAttribute types.Bool `tfsdk:"bool_attribute"` + Float64Attribute types.Float64 `tfsdk:"float64_attribute"` + Int64Attribute types.Int64 `tfsdk:"int64_attribute"` + NumberAttribute types.Number `tfsdk:"number_attribute"` + StringAttribute types.String `tfsdk:"string_attribute"` +} + type SingleNestedAttributeAssocExtTypeModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` Float64Attribute types.Float64 `tfsdk:"float64_attribute"` @@ -493,6 +554,14 @@ type ListNestedBlockTwoListNestedBlockOneModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` } +type SetNestedBlockAssocExtTypeModel struct { + BoolAttribute types.Bool `tfsdk:"bool_attribute"` + Float64Attribute types.Float64 `tfsdk:"float64_attribute"` + Int64Attribute types.Int64 `tfsdk:"int64_attribute"` + NumberAttribute types.Number `tfsdk:"number_attribute"` + StringAttribute types.String `tfsdk:"string_attribute"` +} + type SingleNestedBlockAssocExtTypeModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` Float64Attribute types.Float64 `tfsdk:"float64_attribute"` @@ -549,7 +618,6 @@ func (m ListNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Contex data, ) } - func (m ListNestedAttributeOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -573,7 +641,6 @@ func (m ListNestedAttributeOneModel) ObjectValueFrom(ctx context.Context, data a data, ) } - func (m ListNestedAttributeThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -625,7 +692,6 @@ func (m ListNestedAttributeThreeListNestedAttributeOneModel) ObjectValueFrom(ctx data, ) } - func (m ListNestedAttributeTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -675,7 +741,6 @@ func (m ListNestedAttributeTwoListNestedAttributeOneModel) ObjectValueFrom(ctx c data, ) } - func (m MapNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -703,7 +768,33 @@ func (m MapNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Context data, ) } +func (m SetNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { + return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} +} +func (m SetNestedAttributeAssocExtTypeModel) ObjectAttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "bool_attribute": types.BoolType, + "float64_attribute": types.Float64Type, + "int64_attribute": types.Int64Type, + "number_attribute": types.NumberType, + "string_attribute": types.StringType, + } +} + +func (m SetNestedAttributeAssocExtTypeModel) ObjectNull(ctx context.Context) types.Object { + return types.ObjectNull( + m.ObjectAttributeTypes(ctx), + ) +} + +func (m SetNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data any) (types.Object, diag.Diagnostics) { + return types.ObjectValueFrom( + ctx, + m.ObjectAttributeTypes(ctx), + data, + ) +} func (m SingleNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -731,7 +822,6 @@ func (m SingleNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Cont data, ) } - func (m SingleNestedAttributeOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -755,7 +845,6 @@ func (m SingleNestedAttributeOneModel) ObjectValueFrom(ctx context.Context, data data, ) } - func (m SingleNestedAttributeThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -807,7 +896,6 @@ func (m SingleNestedAttributeThreeSingleNestedAttributeOneModel) ObjectValueFrom data, ) } - func (m SingleNestedAttributeTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -857,7 +945,6 @@ func (m SingleNestedAttributeTwoSingleNestedAttributeOneModel) ObjectValueFrom(c data, ) } - func (m ListNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -885,7 +972,6 @@ func (m ListNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, d data, ) } - func (m ListNestedBlockOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -909,7 +995,6 @@ func (m ListNestedBlockOneModel) ObjectValueFrom(ctx context.Context, data any) data, ) } - func (m ListNestedBlockThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -966,7 +1051,6 @@ func (m ListNestedBlockThreeListNestedBlockOneModel) ObjectValueFrom(ctx context data, ) } - func (m ListNestedBlockTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1016,7 +1100,33 @@ func (m ListNestedBlockTwoListNestedBlockOneModel) ObjectValueFrom(ctx context.C data, ) } +func (m SetNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { + return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} +} +func (m SetNestedBlockAssocExtTypeModel) ObjectAttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "bool_attribute": types.BoolType, + "float64_attribute": types.Float64Type, + "int64_attribute": types.Int64Type, + "number_attribute": types.NumberType, + "string_attribute": types.StringType, + } +} + +func (m SetNestedBlockAssocExtTypeModel) ObjectNull(ctx context.Context) types.Object { + return types.ObjectNull( + m.ObjectAttributeTypes(ctx), + ) +} + +func (m SetNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data any) (types.Object, diag.Diagnostics) { + return types.ObjectValueFrom( + ctx, + m.ObjectAttributeTypes(ctx), + data, + ) +} func (m SingleNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1044,7 +1154,6 @@ func (m SingleNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data, ) } - func (m SingleNestedBlockOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1068,7 +1177,6 @@ func (m SingleNestedBlockOneModel) ObjectValueFrom(ctx context.Context, data any data, ) } - func (m SingleNestedBlockThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1125,7 +1233,6 @@ func (m SingleNestedBlockThreeListNestedBlockOneModel) ObjectValueFrom(ctx conte data, ) } - func (m SingleNestedBlockTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1286,6 +1393,61 @@ func FromMapNestedAttributeAssocExtType(ctx context.Context, apiObjects map[stri return types.MapValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) } +func ToSetNestedAttributeAssocExtType(ctx context.Context, tfSet types.Set) ([]*apisdk.Type, diag.Diagnostics) { + var diags diag.Diagnostics + + if tfSet.IsNull() || tfSet.IsUnknown() { + return nil, diags + } + + var tfModels []SetNestedAttributeAssocExtTypeModel + + diags.Append(tfSet.ElementsAs(ctx, &tfModels, false)...) + + if diags.HasError() { + return nil, diags + } + + var apiObjects []*apisdk.Type + + for _, tfModel := range tfModels { + apiObjects = append(apiObjects, &apisdk.Type{ + BoolAttribute: tfModel.BoolAttribute.ValueBoolPointer(), + Float64Attribute: tfModel.Float64Attribute.ValueFloat64Pointer(), + Int64Attribute: tfModel.Int64Attribute.ValueInt64Pointer(), + NumberAttribute: tfModel.NumberAttribute.ValueBigFloat(), + StringAttribute: tfModel.StringAttribute.ValueStringPointer(), + }) + } + + return apiObjects, diags +} + +func FromSetNestedAttributeAssocExtType(ctx context.Context, apiObjects []*apisdk.Type) (types.Set, diag.Diagnostics) { + var diags diag.Diagnostics + var tfModel SetNestedAttributeAssocExtTypeModel + + if apiObjects == nil { + return types.SetNull( + tfModel.ObjectType(ctx), + ), diags + } + + var tfModels []SetNestedAttributeAssocExtTypeModel + + for _, apiObject := range apiObjects { + tfModels = append(tfModels, SetNestedAttributeAssocExtTypeModel{ + BoolAttribute: types.BoolPointerValue(apiObject.BoolAttribute), + Float64Attribute: types.Float64PointerValue(apiObject.Float64Attribute), + Int64Attribute: types.Int64PointerValue(apiObject.Int64Attribute), + NumberAttribute: types.NumberValue(apiObject.NumberAttribute), + StringAttribute: types.StringPointerValue(apiObject.StringAttribute), + }) + } + + return types.SetValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) +} + func ToSingleNestedAttributeAssocExtType(ctx context.Context, tfObject types.Object) (*apisdk.Type, diag.Diagnostics) { var diags diag.Diagnostics @@ -1384,6 +1546,61 @@ func FromListNestedBlockAssocExtType(ctx context.Context, apiObjects []*apisdk.T return types.ListValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) } +func ToSetNestedBlockAssocExtType(ctx context.Context, tfSet types.Set) ([]*apisdk.Type, diag.Diagnostics) { + var diags diag.Diagnostics + + if tfSet.IsNull() || tfSet.IsUnknown() { + return nil, diags + } + + var tfModels []SetNestedBlockAssocExtTypeModel + + diags.Append(tfSet.ElementsAs(ctx, &tfModels, false)...) + + if diags.HasError() { + return nil, diags + } + + var apiObjects []*apisdk.Type + + for _, tfModel := range tfModels { + apiObjects = append(apiObjects, &apisdk.Type{ + BoolAttribute: tfModel.BoolAttribute.ValueBoolPointer(), + Float64Attribute: tfModel.Float64Attribute.ValueFloat64Pointer(), + Int64Attribute: tfModel.Int64Attribute.ValueInt64Pointer(), + NumberAttribute: tfModel.NumberAttribute.ValueBigFloat(), + StringAttribute: tfModel.StringAttribute.ValueStringPointer(), + }) + } + + return apiObjects, diags +} + +func FromSetNestedBlockAssocExtType(ctx context.Context, apiObjects []*apisdk.Type) (types.Set, diag.Diagnostics) { + var diags diag.Diagnostics + var tfModel SetNestedBlockAssocExtTypeModel + + if apiObjects == nil { + return types.SetNull( + tfModel.ObjectType(ctx), + ), diags + } + + var tfModels []SetNestedBlockAssocExtTypeModel + + for _, apiObject := range apiObjects { + tfModels = append(tfModels, SetNestedBlockAssocExtTypeModel{ + BoolAttribute: types.BoolPointerValue(apiObject.BoolAttribute), + Float64Attribute: types.Float64PointerValue(apiObject.Float64Attribute), + Int64Attribute: types.Int64PointerValue(apiObject.Int64Attribute), + NumberAttribute: types.NumberValue(apiObject.NumberAttribute), + StringAttribute: types.StringPointerValue(apiObject.StringAttribute), + }) + } + + return types.SetValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) +} + func ToSingleNestedBlockAssocExtType(ctx context.Context, tfObject types.Object) (*apisdk.Type, diag.Diagnostics) { var diags diag.Diagnostics diff --git a/internal/cmd/testdata/custom_and_external/data_sources_output/datasource_data_source_gen.go b/internal/cmd/testdata/custom_and_external/data_sources_output/datasource_data_source_gen.go index 7a88cf70..3605d93b 100644 --- a/internal/cmd/testdata/custom_and_external/data_sources_output/datasource_data_source_gen.go +++ b/internal/cmd/testdata/custom_and_external/data_sources_output/datasource_data_source_gen.go @@ -172,6 +172,32 @@ var datasourceDataSourceSchema = schema.Schema{ }, Computed: true, }, + "set_nested_attribute_assoc_ext_type": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "bool_attribute": schema.BoolAttribute{ + Computed: true, + }, + "float64_attribute": schema.Float64Attribute{ + Optional: true, + Computed: true, + }, + "int64_attribute": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "number_attribute": schema.NumberAttribute{ + Optional: true, + Computed: true, + }, + "string_attribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + }, + }, + Optional: true, + }, "single_nested_attribute_assoc_ext_type": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ @@ -306,6 +332,31 @@ var datasourceDataSourceSchema = schema.Schema{ }, }, }, + "set_nested_block_assoc_ext_type": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "bool_attribute": schema.BoolAttribute{ + Computed: true, + }, + "float64_attribute": schema.Float64Attribute{ + Optional: true, + Computed: true, + }, + "int64_attribute": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "number_attribute": schema.NumberAttribute{ + Optional: true, + Computed: true, + }, + "string_attribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + }, + }, + }, "single_nested_block_assoc_ext_type": schema.SingleNestedBlock{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ @@ -386,6 +437,7 @@ type DatasourceModel struct { ObjectAttribute types.Object `tfsdk:"object_attribute"` ObjectListAttribute types.Object `tfsdk:"object_list_attribute"` ObjectListObjectAttribute types.Object `tfsdk:"object_list_object_attribute"` + SetNestedAttributeAssocExtType types.Set `tfsdk:"set_nested_attribute_assoc_ext_type"` SingleNestedAttributeAssocExtType types.Object `tfsdk:"single_nested_attribute_assoc_ext_type"` SingleNestedAttributeOne types.Object `tfsdk:"single_nested_attribute_one"` SingleNestedAttributeThree types.Object `tfsdk:"single_nested_attribute_three"` @@ -394,6 +446,7 @@ type DatasourceModel struct { ListNestedBlockOne types.List `tfsdk:"list_nested_block_one"` ListNestedBlockThree types.List `tfsdk:"list_nested_block_three"` ListNestedBlockTwo types.List `tfsdk:"list_nested_block_two"` + SetNestedBlockAssocExtType types.Set `tfsdk:"set_nested_block_assoc_ext_type"` SingleNestedBlockAssocExtType types.Object `tfsdk:"single_nested_block_assoc_ext_type"` SingleNestedBlockOne types.Object `tfsdk:"single_nested_block_one"` SingleNestedBlockThree types.Object `tfsdk:"single_nested_block_three"` @@ -436,6 +489,14 @@ type MapNestedAttributeAssocExtTypeModel struct { StringAttribute types.String `tfsdk:"string_attribute"` } +type SetNestedAttributeAssocExtTypeModel struct { + BoolAttribute types.Bool `tfsdk:"bool_attribute"` + Float64Attribute types.Float64 `tfsdk:"float64_attribute"` + Int64Attribute types.Int64 `tfsdk:"int64_attribute"` + NumberAttribute types.Number `tfsdk:"number_attribute"` + StringAttribute types.String `tfsdk:"string_attribute"` +} + type SingleNestedAttributeAssocExtTypeModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` Float64Attribute types.Float64 `tfsdk:"float64_attribute"` @@ -493,6 +554,14 @@ type ListNestedBlockTwoListNestedBlockOneModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` } +type SetNestedBlockAssocExtTypeModel struct { + BoolAttribute types.Bool `tfsdk:"bool_attribute"` + Float64Attribute types.Float64 `tfsdk:"float64_attribute"` + Int64Attribute types.Int64 `tfsdk:"int64_attribute"` + NumberAttribute types.Number `tfsdk:"number_attribute"` + StringAttribute types.String `tfsdk:"string_attribute"` +} + type SingleNestedBlockAssocExtTypeModel struct { BoolAttribute types.Bool `tfsdk:"bool_attribute"` Float64Attribute types.Float64 `tfsdk:"float64_attribute"` @@ -549,7 +618,6 @@ func (m ListNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Contex data, ) } - func (m ListNestedAttributeOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -573,7 +641,6 @@ func (m ListNestedAttributeOneModel) ObjectValueFrom(ctx context.Context, data a data, ) } - func (m ListNestedAttributeThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -625,7 +692,6 @@ func (m ListNestedAttributeThreeListNestedAttributeOneModel) ObjectValueFrom(ctx data, ) } - func (m ListNestedAttributeTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -675,7 +741,6 @@ func (m ListNestedAttributeTwoListNestedAttributeOneModel) ObjectValueFrom(ctx c data, ) } - func (m MapNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -703,7 +768,33 @@ func (m MapNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Context data, ) } +func (m SetNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { + return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} +} +func (m SetNestedAttributeAssocExtTypeModel) ObjectAttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "bool_attribute": types.BoolType, + "float64_attribute": types.Float64Type, + "int64_attribute": types.Int64Type, + "number_attribute": types.NumberType, + "string_attribute": types.StringType, + } +} + +func (m SetNestedAttributeAssocExtTypeModel) ObjectNull(ctx context.Context) types.Object { + return types.ObjectNull( + m.ObjectAttributeTypes(ctx), + ) +} + +func (m SetNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data any) (types.Object, diag.Diagnostics) { + return types.ObjectValueFrom( + ctx, + m.ObjectAttributeTypes(ctx), + data, + ) +} func (m SingleNestedAttributeAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -731,7 +822,6 @@ func (m SingleNestedAttributeAssocExtTypeModel) ObjectValueFrom(ctx context.Cont data, ) } - func (m SingleNestedAttributeOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -755,7 +845,6 @@ func (m SingleNestedAttributeOneModel) ObjectValueFrom(ctx context.Context, data data, ) } - func (m SingleNestedAttributeThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -807,7 +896,6 @@ func (m SingleNestedAttributeThreeSingleNestedAttributeOneModel) ObjectValueFrom data, ) } - func (m SingleNestedAttributeTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -857,7 +945,6 @@ func (m SingleNestedAttributeTwoSingleNestedAttributeOneModel) ObjectValueFrom(c data, ) } - func (m ListNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -885,7 +972,6 @@ func (m ListNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, d data, ) } - func (m ListNestedBlockOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -909,7 +995,6 @@ func (m ListNestedBlockOneModel) ObjectValueFrom(ctx context.Context, data any) data, ) } - func (m ListNestedBlockThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -966,7 +1051,6 @@ func (m ListNestedBlockThreeListNestedBlockOneModel) ObjectValueFrom(ctx context data, ) } - func (m ListNestedBlockTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1016,7 +1100,33 @@ func (m ListNestedBlockTwoListNestedBlockOneModel) ObjectValueFrom(ctx context.C data, ) } +func (m SetNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { + return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} +} +func (m SetNestedBlockAssocExtTypeModel) ObjectAttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "bool_attribute": types.BoolType, + "float64_attribute": types.Float64Type, + "int64_attribute": types.Int64Type, + "number_attribute": types.NumberType, + "string_attribute": types.StringType, + } +} + +func (m SetNestedBlockAssocExtTypeModel) ObjectNull(ctx context.Context) types.Object { + return types.ObjectNull( + m.ObjectAttributeTypes(ctx), + ) +} + +func (m SetNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data any) (types.Object, diag.Diagnostics) { + return types.ObjectValueFrom( + ctx, + m.ObjectAttributeTypes(ctx), + data, + ) +} func (m SingleNestedBlockAssocExtTypeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1044,7 +1154,6 @@ func (m SingleNestedBlockAssocExtTypeModel) ObjectValueFrom(ctx context.Context, data, ) } - func (m SingleNestedBlockOneModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1068,7 +1177,6 @@ func (m SingleNestedBlockOneModel) ObjectValueFrom(ctx context.Context, data any data, ) } - func (m SingleNestedBlockThreeModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1125,7 +1233,6 @@ func (m SingleNestedBlockThreeListNestedBlockOneModel) ObjectValueFrom(ctx conte data, ) } - func (m SingleNestedBlockTwoModel) ObjectType(ctx context.Context) types.ObjectType { return types.ObjectType{AttrTypes: m.ObjectAttributeTypes(ctx)} } @@ -1286,6 +1393,61 @@ func FromMapNestedAttributeAssocExtType(ctx context.Context, apiObjects map[stri return types.MapValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) } +func ToSetNestedAttributeAssocExtType(ctx context.Context, tfSet types.Set) ([]*apisdk.Type, diag.Diagnostics) { + var diags diag.Diagnostics + + if tfSet.IsNull() || tfSet.IsUnknown() { + return nil, diags + } + + var tfModels []SetNestedAttributeAssocExtTypeModel + + diags.Append(tfSet.ElementsAs(ctx, &tfModels, false)...) + + if diags.HasError() { + return nil, diags + } + + var apiObjects []*apisdk.Type + + for _, tfModel := range tfModels { + apiObjects = append(apiObjects, &apisdk.Type{ + BoolAttribute: tfModel.BoolAttribute.ValueBoolPointer(), + Float64Attribute: tfModel.Float64Attribute.ValueFloat64Pointer(), + Int64Attribute: tfModel.Int64Attribute.ValueInt64Pointer(), + NumberAttribute: tfModel.NumberAttribute.ValueBigFloat(), + StringAttribute: tfModel.StringAttribute.ValueStringPointer(), + }) + } + + return apiObjects, diags +} + +func FromSetNestedAttributeAssocExtType(ctx context.Context, apiObjects []*apisdk.Type) (types.Set, diag.Diagnostics) { + var diags diag.Diagnostics + var tfModel SetNestedAttributeAssocExtTypeModel + + if apiObjects == nil { + return types.SetNull( + tfModel.ObjectType(ctx), + ), diags + } + + var tfModels []SetNestedAttributeAssocExtTypeModel + + for _, apiObject := range apiObjects { + tfModels = append(tfModels, SetNestedAttributeAssocExtTypeModel{ + BoolAttribute: types.BoolPointerValue(apiObject.BoolAttribute), + Float64Attribute: types.Float64PointerValue(apiObject.Float64Attribute), + Int64Attribute: types.Int64PointerValue(apiObject.Int64Attribute), + NumberAttribute: types.NumberValue(apiObject.NumberAttribute), + StringAttribute: types.StringPointerValue(apiObject.StringAttribute), + }) + } + + return types.SetValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) +} + func ToSingleNestedAttributeAssocExtType(ctx context.Context, tfObject types.Object) (*apisdk.Type, diag.Diagnostics) { var diags diag.Diagnostics @@ -1384,6 +1546,61 @@ func FromListNestedBlockAssocExtType(ctx context.Context, apiObjects []*apisdk.T return types.ListValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) } +func ToSetNestedBlockAssocExtType(ctx context.Context, tfSet types.Set) ([]*apisdk.Type, diag.Diagnostics) { + var diags diag.Diagnostics + + if tfSet.IsNull() || tfSet.IsUnknown() { + return nil, diags + } + + var tfModels []SetNestedBlockAssocExtTypeModel + + diags.Append(tfSet.ElementsAs(ctx, &tfModels, false)...) + + if diags.HasError() { + return nil, diags + } + + var apiObjects []*apisdk.Type + + for _, tfModel := range tfModels { + apiObjects = append(apiObjects, &apisdk.Type{ + BoolAttribute: tfModel.BoolAttribute.ValueBoolPointer(), + Float64Attribute: tfModel.Float64Attribute.ValueFloat64Pointer(), + Int64Attribute: tfModel.Int64Attribute.ValueInt64Pointer(), + NumberAttribute: tfModel.NumberAttribute.ValueBigFloat(), + StringAttribute: tfModel.StringAttribute.ValueStringPointer(), + }) + } + + return apiObjects, diags +} + +func FromSetNestedBlockAssocExtType(ctx context.Context, apiObjects []*apisdk.Type) (types.Set, diag.Diagnostics) { + var diags diag.Diagnostics + var tfModel SetNestedBlockAssocExtTypeModel + + if apiObjects == nil { + return types.SetNull( + tfModel.ObjectType(ctx), + ), diags + } + + var tfModels []SetNestedBlockAssocExtTypeModel + + for _, apiObject := range apiObjects { + tfModels = append(tfModels, SetNestedBlockAssocExtTypeModel{ + BoolAttribute: types.BoolPointerValue(apiObject.BoolAttribute), + Float64Attribute: types.Float64PointerValue(apiObject.Float64Attribute), + Int64Attribute: types.Int64PointerValue(apiObject.Int64Attribute), + NumberAttribute: types.NumberValue(apiObject.NumberAttribute), + StringAttribute: types.StringPointerValue(apiObject.StringAttribute), + }) + } + + return types.SetValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) +} + func ToSingleNestedBlockAssocExtType(ctx context.Context, tfObject types.Object) (*apisdk.Type, diag.Diagnostics) { var diags diag.Diagnostics diff --git a/internal/cmd/testdata/custom_and_external/ir.json b/internal/cmd/testdata/custom_and_external/ir.json index 6efef171..7c103629 100644 --- a/internal/cmd/testdata/custom_and_external/ir.json +++ b/internal/cmd/testdata/custom_and_external/ir.json @@ -289,6 +289,52 @@ "computed_optional_required": "optional" } }, + { + "name": "set_nested_attribute_assoc_ext_type", + "set_nested": { + "nested_object": { + "associated_external_type": { + "import": { + "path": "example.com/apisdk" + }, + "type": "*apisdk.Type" + }, + "attributes": [ + { + "name": "bool_attribute", + "bool": { + "computed_optional_required": "computed" + } + }, + { + "name": "float64_attribute", + "float64": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "int64_attribute", + "int64": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "number_attribute", + "number": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "string_attribute", + "string": { + "computed_optional_required": "computed_optional" + } + } + ] + }, + "computed_optional_required": "optional" + } + }, { "name": "single_nested_attribute_one", "single_nested": { @@ -521,6 +567,51 @@ } } }, + { + "name": "set_nested_block_assoc_ext_type", + "set_nested": { + "nested_object": { + "associated_external_type": { + "import": { + "path": "example.com/apisdk" + }, + "type": "*apisdk.Type" + }, + "attributes": [ + { + "name": "bool_attribute", + "bool": { + "computed_optional_required": "computed" + } + }, + { + "name": "float64_attribute", + "float64": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "int64_attribute", + "int64": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "number_attribute", + "number": { + "computed_optional_required": "computed_optional" + } + }, + { + "name": "string_attribute", + "string": { + "computed_optional_required": "computed_optional" + } + } + ] + } + } + }, { "name": "single_nested_block_one", "single_nested": { diff --git a/internal/datasource_convert/set_nested_attribute.go b/internal/datasource_convert/set_nested_attribute.go index 3ac55632..c9a8c298 100644 --- a/internal/datasource_convert/set_nested_attribute.go +++ b/internal/datasource_convert/set_nested_attribute.go @@ -75,9 +75,10 @@ func convertSetNestedAttribute(a *datasource.SetNestedAttribute) (datasource_gen CustomType: a.CustomType, NestedObject: datasource_generate.GeneratorNestedAttributeObject{ - Attributes: attributes, - CustomType: a.NestedObject.CustomType, - Validators: a.NestedObject.Validators, + AssociatedExternalType: a.NestedObject.AssociatedExternalType, + Attributes: attributes, + CustomType: a.NestedObject.CustomType, + Validators: a.NestedObject.Validators, }, Validators: a.Validators, }, nil diff --git a/internal/datasource_convert/set_nested_block.go b/internal/datasource_convert/set_nested_block.go index e0bc7387..9ad8ecf8 100644 --- a/internal/datasource_convert/set_nested_block.go +++ b/internal/datasource_convert/set_nested_block.go @@ -95,10 +95,11 @@ func convertSetNestedBlock(b *datasource.SetNestedBlock) (datasource_generate.Ge CustomType: b.CustomType, NestedObject: datasource_generate.GeneratorNestedBlockObject{ - Attributes: attributes, - Blocks: blocks, - CustomType: b.NestedObject.CustomType, - Validators: b.NestedObject.Validators, + AssociatedExternalType: b.NestedObject.AssociatedExternalType, + Attributes: attributes, + Blocks: blocks, + CustomType: b.NestedObject.CustomType, + Validators: b.NestedObject.Validators, }, Validators: b.Validators, }, nil diff --git a/internal/datasource_generate/set_nested_attribute.go b/internal/datasource_generate/set_nested_attribute.go index 0f595633..7d0398b2 100644 --- a/internal/datasource_generate/set_nested_attribute.go +++ b/internal/datasource_generate/set_nested_attribute.go @@ -26,6 +26,10 @@ type GeneratorSetNestedAttribute struct { Validators []specschema.SetValidator } +func (g GeneratorSetNestedAttribute) AssocExtType() *generatorschema.AssocExtType { + return generatorschema.NewAssocExtType(g.NestedObject.AssociatedExternalType) +} + func (g GeneratorSetNestedAttribute) AttrType() attr.Type { return types.SetType{ //TODO: Add ElemType? @@ -55,6 +59,9 @@ func (g GeneratorSetNestedAttribute) Imports() *generatorschema.Imports { imports.Append(v.Imports()) } + // TODO: This should only be added if model object helper functions are being generated. + imports.Append(generatorschema.AttrImports()) + return imports } diff --git a/internal/datasource_generate/set_nested_attribute_test.go b/internal/datasource_generate/set_nested_attribute_test.go index 8f236c97..8954fe7d 100644 --- a/internal/datasource_generate/set_nested_attribute_test.go +++ b/internal/datasource_generate/set_nested_attribute_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -14,6 +15,578 @@ import ( generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) +func TestGeneratorSetNestedAttribute_Imports(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input GeneratorSetNestedAttribute + expected []code.Import + }{ + "default": { + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-without-import": { + input: GeneratorSetNestedAttribute{ + CustomType: &specschema.CustomType{}, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-without-import": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{}, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-and-nested-object-custom-type-without-import": { + input: GeneratorSetNestedAttribute{ + CustomType: &specschema.CustomType{}, + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{}, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import-empty-string": { + input: GeneratorSetNestedAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-with-import-empty-string": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-and-nested-object-custom-type-with-import-empty-string": { + input: GeneratorSetNestedAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import": { + input: GeneratorSetNestedAttribute{ + 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: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-with-import": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import-with-nested-object-custom-type-with-import": { + input: GeneratorSetNestedAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + NestedObject: GeneratorNestedAttributeObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + ElementType: specschema.ElementType{ + Bool: &specschema.BoolType{}, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set-with-custom-type": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_list", + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_list", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set-with-custom-type-with-element-with-custom-type": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_list", + }, + }, + ElementType: specschema.ElementType{ + Bool: &specschema.BoolType{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/bool", + }, + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_list", + }, + { + Path: "github.com/my_account/my_project/bool", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + AttributeTypes: []specschema.ObjectAttributeType{ + { + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-with-custom-type": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-with-custom-type-with-attribute-with-custom-type": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + AttributeTypes: []specschema.ObjectAttributeType{ + { + Name: "bool", + Bool: &specschema.BoolType{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/bool", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, + { + Path: "github.com/my_account/my_project/bool", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-nil": { + input: GeneratorSetNestedAttribute{ + Validators: []specschema.SetValidator{ + { + Custom: nil, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import-nil": { + input: GeneratorSetNestedAttribute{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{}, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import-empty-string": { + input: GeneratorSetNestedAttribute{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "", + }, + }, + }, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import": { + input: GeneratorSetNestedAttribute{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + }, + }, + }, + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myproject/myvalidators/validator", + }, + }, + }, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.ValidatorImport, + }, + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + { + Path: "github.com/myproject/myvalidators/validator", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-nil": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: nil, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import-nil": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{}, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import-empty-string": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "", + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import": { + input: GeneratorSetNestedAttribute{ + NestedObject: GeneratorNestedAttributeObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + }, + }, + }, + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myproject/myvalidators/validator", + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.ValidatorImport, + }, + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + { + Path: "github.com/myproject/myvalidators/validator", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.Imports().All() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestGeneratorSetNestedAttribute_ToString(t *testing.T) { t.Parallel() diff --git a/internal/datasource_generate/set_nested_block.go b/internal/datasource_generate/set_nested_block.go index cb42858e..182824bf 100644 --- a/internal/datasource_generate/set_nested_block.go +++ b/internal/datasource_generate/set_nested_block.go @@ -26,6 +26,10 @@ type GeneratorSetNestedBlock struct { Validators []specschema.SetValidator } +func (g GeneratorSetNestedBlock) AssocExtType() *generatorschema.AssocExtType { + return generatorschema.NewAssocExtType(g.NestedObject.AssociatedExternalType) +} + func (g GeneratorSetNestedBlock) AttrType() attr.Type { return types.SetType{ //TODO: Add ElemType? @@ -59,6 +63,9 @@ func (g GeneratorSetNestedBlock) Imports() *generatorschema.Imports { imports.Append(v.Imports()) } + // TODO: This should only be added if model object helper functions are being generated. + imports.Append(generatorschema.AttrImports()) + return imports } diff --git a/internal/datasource_generate/set_nested_block_test.go b/internal/datasource_generate/set_nested_block_test.go index fcaf752a..4443d779 100644 --- a/internal/datasource_generate/set_nested_block_test.go +++ b/internal/datasource_generate/set_nested_block_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-codegen-spec/code" specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -14,6 +15,609 @@ import ( generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" ) +func TestGeneratorSetNestedBlock_Imports(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input GeneratorSetNestedBlock + expected []code.Import + }{ + "default": { + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-without-import": { + input: GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{}, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-without-import": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{}, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-and-nested-object-custom-type-without-import": { + input: GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{}, + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{}, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import-empty-string": { + input: GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-with-import-empty-string": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-and-nested-object-custom-type-with-import-empty-string": { + input: GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import": { + input: GeneratorSetNestedBlock{ + 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: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-custom-type-with-import": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "custom-type-with-import-with-nested-object-custom-type-with-import": { + input: GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/attribute", + }, + }, + NestedObject: GeneratorNestedBlockObject{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + }, + }, + expected: []code.Import{ + { + Path: "github.com/my_account/my_project/attribute", + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + ElementType: specschema.ElementType{ + Bool: &specschema.BoolType{}, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set-with-custom-type": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_list", + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_list", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-set-with-custom-type-with-element-with-custom-type": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "set": GeneratorSetAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_list", + }, + }, + ElementType: specschema.ElementType{ + Bool: &specschema.BoolType{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/bool", + }, + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_list", + }, + { + Path: "github.com/my_account/my_project/bool", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + AttributeTypes: []specschema.ObjectAttributeType{ + { + Name: "bool", + Bool: &specschema.BoolType{}, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-with-custom-type": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-with-custom-type-with-attribute-with-custom-type": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Attributes: generatorschema.GeneratorAttributes{ + "obj": GeneratorObjectAttribute{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_object", + }, + }, + AttributeTypes: []specschema.ObjectAttributeType{ + { + Name: "bool", + Bool: &specschema.BoolType{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/bool", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_object", + }, + { + Path: "github.com/my_account/my_project/bool", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-block-with-custom-type": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Blocks: generatorschema.GeneratorBlocks{ + "list-nested-block": GeneratorSetNestedBlock{ + CustomType: &specschema.CustomType{ + Import: &code.Import{ + Path: "github.com/my_account/my_project/nested_block", + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: "github.com/my_account/my_project/nested_block", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-nil": { + input: GeneratorSetNestedBlock{ + Validators: []specschema.SetValidator{ + { + Custom: nil, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import-nil": { + input: GeneratorSetNestedBlock{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{}, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import-empty-string": { + input: GeneratorSetNestedBlock{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "", + }, + }, + }, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "validator-custom-import": { + input: GeneratorSetNestedBlock{ + Validators: []specschema.SetValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + }, + }, + }, + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myproject/myvalidators/validator", + }, + }, + }, + }, + }}, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.ValidatorImport, + }, + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + { + Path: "github.com/myproject/myvalidators/validator", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-nil": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: nil, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import-nil": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{}, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import-empty-string": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "", + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + "nested-object-validator-custom-import": { + input: GeneratorSetNestedBlock{ + NestedObject: GeneratorNestedBlockObject{ + Validators: []specschema.ObjectValidator{ + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + }, + }, + }, + { + Custom: &specschema.CustomValidator{ + Imports: []code.Import{ + { + Path: "github.com/myproject/myvalidators/validator", + }, + }, + }, + }, + }, + }, + }, + expected: []code.Import{ + { + Path: generatorschema.TypesImport, + }, + { + Path: generatorschema.ValidatorImport, + }, + { + Path: "github.com/myotherproject/myvalidators/validator", + }, + { + Path: "github.com/myproject/myvalidators/validator", + }, + { + Path: generatorschema.AttrImport, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.Imports().All() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestGeneratorSetNestedBlock_ToString(t *testing.T) { t.Parallel() diff --git a/internal/schema/schema.go b/internal/schema/schema.go index d7e8ea14..7b3da4d9 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -606,6 +606,15 @@ func (g GeneratorSchema) ModelsToFromBytes() ([]byte, error) { if err != nil { return nil, err } + case basetypes.SetTypable: + t, err = template.New("set_nested_object_to_from").Parse(templates.SetNestedObjectToFromTemplate) + if err != nil { + return nil, err + } + } + + if t == nil { + return nil, fmt.Errorf("no matching template for type: %T", attributeAssocExtType.AttrType()) } var templateBuf bytes.Buffer @@ -709,7 +718,17 @@ func (g GeneratorSchema) ModelsToFromBytes() ([]byte, error) { if err != nil { return nil, err } + case basetypes.SetTypable: + t, err = template.New("set_nested_object_to_from").Parse(templates.SetNestedObjectToFromTemplate) + if err != nil { + return nil, err + } } + + if t == nil { + return nil, fmt.Errorf("no matching template for type: %T", blockAssocExtType.AttrType()) + } + var templateBuf bytes.Buffer templateData := struct { diff --git a/internal/templates/embed.go b/internal/templates/embed.go index 8f891a67..bef8e216 100644 --- a/internal/templates/embed.go +++ b/internal/templates/embed.go @@ -19,5 +19,8 @@ var ModelObjectHelpersTemplate string //go:embed schema.gotmpl var SchemaGoTemplate string +//go:embed set_nested_object_to_from.gotmpl +var SetNestedObjectToFromTemplate string + //go:embed single_nested_object_to_from.gotmpl var SingleNestedObjectToFromTemplate string diff --git a/internal/templates/model_object_helpers.gotmpl b/internal/templates/model_object_helpers.gotmpl index ea1b1150..392e796c 100644 --- a/internal/templates/model_object_helpers.gotmpl +++ b/internal/templates/model_object_helpers.gotmpl @@ -23,4 +23,4 @@ ctx, m.ObjectAttributeTypes(ctx), data, ) -} +} \ No newline at end of file diff --git a/internal/templates/set_nested_object_to_from.gotmpl b/internal/templates/set_nested_object_to_from.gotmpl new file mode 100644 index 00000000..60c5c279 --- /dev/null +++ b/internal/templates/set_nested_object_to_from.gotmpl @@ -0,0 +1,51 @@ + +func To{{.Name}}(ctx context.Context, tfSet types.Set) ([]{{.Type}}, diag.Diagnostics) { +var diags diag.Diagnostics + +if tfSet.IsNull() || tfSet.IsUnknown() { +return nil, diags +} + +var tfModels []{{.Name}}Model + +diags.Append(tfSet.ElementsAs(ctx, &tfModels, false)...) + +if diags.HasError() { +return nil, diags +} + +var apiObjects []{{.Type}} + +for _, tfModel := range tfModels { +apiObjects = append(apiObjects, &{{.TypeReference}}{ +{{- range $field := .Fields }} +{{$field.Name}}: tfModel.{{$field.Name}}.{{$field.DefaultTo}}(), +{{- end}} +}) +} + +return apiObjects, diags +} + +func From{{.Name}}(ctx context.Context, apiObjects []{{.Type}}) (types.Set, diag.Diagnostics) { +var diags diag.Diagnostics +var tfModel {{.Name}}Model + +if apiObjects == nil { +return types.SetNull( +tfModel.ObjectType(ctx), +), diags +} + +var tfModels []{{.Name}}Model + +for _, apiObject := range apiObjects { +tfModels = append(tfModels, {{.Name}}Model{ +{{- range $field := .Fields }} +{{$field.Name}}: types.{{$field.DefaultFrom}}(apiObject.{{$field.Name}}), +{{- end}} +}) +} + +return types.SetValueFrom(ctx, tfModel.ObjectType(ctx), tfModels) +}