diff --git a/.changelog/77.txt b/.changelog/77.txt new file mode 100644 index 000000000..4ec20d04c --- /dev/null +++ b/.changelog/77.txt @@ -0,0 +1,3 @@ +```release-note:breaking-change +The `schema.Attribute` and `schema.Schema` types have been moved to `tfsdk.Attribute` and `tfsdk.Schema`. No changes beyond import names are required. +``` diff --git a/internal/proto6/schema.go b/internal/proto6/schema.go deleted file mode 100644 index fcdf0265c..000000000 --- a/internal/proto6/schema.go +++ /dev/null @@ -1,123 +0,0 @@ -package proto6 - -import ( - "context" - "errors" - "sort" - - "github.com/hashicorp/terraform-plugin-framework/schema" - - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -// Schema returns the *tfprotov6.Schema equivalent of a schema.Schema. At least -// one attribute must be set in the schema, or an error will be returned. -func Schema(ctx context.Context, s schema.Schema) (*tfprotov6.Schema, error) { - result := &tfprotov6.Schema{ - Version: s.Version, - } - var attrs []*tfprotov6.SchemaAttribute - for name, attr := range s.Attributes { - a, err := Attribute(ctx, name, attr, tftypes.NewAttributePath().WithAttributeName(name)) - if err != nil { - return nil, err - } - attrs = append(attrs, a) - } - sort.Slice(attrs, func(i, j int) bool { - if attrs[i] == nil { - return true - } - if attrs[j] == nil { - return false - } - return attrs[i].Name < attrs[j].Name - }) - if len(attrs) < 1 { - return nil, errors.New("must have at least one attribute in the schema") - } - result.Block = &tfprotov6.SchemaBlock{ - // core doesn't do anything with version, as far as I can tell, - // so let's not set it. - Attributes: attrs, - Deprecated: s.DeprecationMessage != "", - } - if s.Description != "" { - result.Block.Description = s.Description - result.Block.DescriptionKind = tfprotov6.StringKindPlain - } - if s.MarkdownDescription != "" { - result.Block.Description = s.MarkdownDescription - result.Block.DescriptionKind = tfprotov6.StringKindMarkdown - } - return result, nil -} - -// Attribute returns the *tfprotov6.SchemaAttribute equivalent of a -// schema.Attribute. Errors will be tftypes.AttributePathErrors based on -// `path`. `name` is the name of the attribute. -func Attribute(ctx context.Context, name string, attr schema.Attribute, path *tftypes.AttributePath) (*tfprotov6.SchemaAttribute, error) { - a := &tfprotov6.SchemaAttribute{ - Name: name, - Required: attr.Required, - Optional: attr.Optional, - Computed: attr.Computed, - Sensitive: attr.Sensitive, - } - if attr.DeprecationMessage != "" { - a.Deprecated = true - } - if attr.Description != "" { - a.Description = attr.Description - a.DescriptionKind = tfprotov6.StringKindPlain - } - if attr.MarkdownDescription != "" { - a.Description = attr.MarkdownDescription - a.DescriptionKind = tfprotov6.StringKindMarkdown - } - if attr.Type != nil && attr.Attributes == nil { - a.Type = attr.Type.TerraformType(ctx) - } else if attr.Attributes != nil && len(attr.Attributes.GetAttributes()) > 0 && attr.Type == nil { - object := &tfprotov6.SchemaObject{ - MinItems: attr.Attributes.GetMinItems(), - MaxItems: attr.Attributes.GetMaxItems(), - } - nm := attr.Attributes.GetNestingMode() - switch nm { - case schema.NestingModeSingle: - object.Nesting = tfprotov6.SchemaObjectNestingModeSingle - case schema.NestingModeList: - object.Nesting = tfprotov6.SchemaObjectNestingModeList - case schema.NestingModeSet: - object.Nesting = tfprotov6.SchemaObjectNestingModeSet - case schema.NestingModeMap: - object.Nesting = tfprotov6.SchemaObjectNestingModeMap - default: - return nil, path.NewErrorf("unrecognized nesting mode %v", nm) - } - attrs := attr.Attributes.GetAttributes() - for nestedName, nestedAttr := range attrs { - nestedA, err := Attribute(ctx, nestedName, nestedAttr, path.WithAttributeName(nestedName)) - if err != nil { - return nil, err - } - object.Attributes = append(object.Attributes, nestedA) - } - sort.Slice(object.Attributes, func(i, j int) bool { - if object.Attributes[i] == nil { - return true - } - if object.Attributes[j] == nil { - return false - } - return object.Attributes[i].Name < object.Attributes[j].Name - }) - a.NestedType = object - } else if attr.Attributes != nil && len(attr.Attributes.GetAttributes()) > 0 && attr.Type != nil { - return nil, path.NewErrorf("can't have both Attributes and Type set") - } else if (attr.Attributes == nil || len(attr.Attributes.GetAttributes()) < 1) && attr.Type == nil { - return nil, path.NewErrorf("must have Attributes or Type set") - } - return a, nil -} diff --git a/schema/schema_test.go b/schema/schema_test.go deleted file mode 100644 index 5e0c2aa35..000000000 --- a/schema/schema_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package schema - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -func TestSchemaAttributeType(t *testing.T) { - testSchema := Schema{ - Attributes: map[string]Attribute{ - "foo": { - Type: types.StringType, - Required: true, - }, - "bar": { - Type: types.ListType{ - ElemType: types.StringType, - }, - Required: true, - }, - "disks": { - Attributes: ListNestedAttributes(map[string]Attribute{ - "id": { - Type: types.StringType, - Required: true, - }, - "delete_with_instance": { - Type: types.BoolType, - Optional: true, - }, - }, ListNestedAttributesOptions{}), - Optional: true, - Computed: true, - }, - "boot_disk": { - Attributes: SingleNestedAttributes(map[string]Attribute{ - "id": { - Type: types.StringType, - Required: true, - }, - "delete_with_instance": { - Type: types.BoolType, - }, - }), - }, - }, - } - - expectedType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "foo": types.StringType, - "bar": types.ListType{ - ElemType: types.StringType, - }, - "disks": types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "id": types.StringType, - "delete_with_instance": types.BoolType, - }, - }, - }, - "boot_disk": types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "id": types.StringType, - "delete_with_instance": types.BoolType, - }, - }, - }, - } - - actualType := testSchema.AttributeType() - - if !expectedType.Equal(actualType) { - t.Fatalf("types not equal (+wanted, -got): %s", cmp.Diff(expectedType, actualType)) - } -} diff --git a/schema/attribute.go b/tfsdk/attribute.go similarity index 61% rename from schema/attribute.go rename to tfsdk/attribute.go index 834ddb54a..5b750949e 100644 --- a/schema/attribute.go +++ b/tfsdk/attribute.go @@ -1,9 +1,12 @@ -package schema +package tfsdk import ( + "context" "errors" + "sort" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) @@ -119,3 +122,88 @@ func (a Attribute) Equal(o Attribute) bool { } return true } + +// tfprotov6 returns the *tfprotov6.SchemaAttribute equivalent of an +// Attribute. Errors will be tftypes.AttributePathErrors based on +// `path`. `name` is the name of the attribute. +func (a Attribute) tfprotov6SchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePath) (*tfprotov6.SchemaAttribute, error) { + schemaAttribute := &tfprotov6.SchemaAttribute{ + Name: name, + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + } + + if a.DeprecationMessage != "" { + schemaAttribute.Deprecated = true + } + + if a.Description != "" { + schemaAttribute.Description = a.Description + schemaAttribute.DescriptionKind = tfprotov6.StringKindPlain + } + + if a.MarkdownDescription != "" { + schemaAttribute.Description = a.MarkdownDescription + schemaAttribute.DescriptionKind = tfprotov6.StringKindMarkdown + } + + if a.Attributes != nil && len(a.Attributes.GetAttributes()) > 0 && a.Type != nil { + return nil, path.NewErrorf("can't have both Attributes and Type set") + } + + if (a.Attributes == nil || len(a.Attributes.GetAttributes()) < 1) && a.Type == nil { + return nil, path.NewErrorf("must have Attributes or Type set") + } + + if a.Type != nil { + schemaAttribute.Type = a.Type.TerraformType(ctx) + + return schemaAttribute, nil + } + + object := &tfprotov6.SchemaObject{ + MinItems: a.Attributes.GetMinItems(), + MaxItems: a.Attributes.GetMaxItems(), + } + nm := a.Attributes.GetNestingMode() + switch nm { + case NestingModeSingle: + object.Nesting = tfprotov6.SchemaObjectNestingModeSingle + case NestingModeList: + object.Nesting = tfprotov6.SchemaObjectNestingModeList + case NestingModeSet: + object.Nesting = tfprotov6.SchemaObjectNestingModeSet + case NestingModeMap: + object.Nesting = tfprotov6.SchemaObjectNestingModeMap + default: + return nil, path.NewErrorf("unrecognized nesting mode %v", nm) + } + + for nestedName, nestedA := range a.Attributes.GetAttributes() { + nestedSchemaAttribute, err := nestedA.tfprotov6SchemaAttribute(ctx, nestedName, path.WithAttributeName(nestedName)) + + if err != nil { + return nil, err + } + + object.Attributes = append(object.Attributes, nestedSchemaAttribute) + } + + sort.Slice(object.Attributes, func(i, j int) bool { + if object.Attributes[i] == nil { + return true + } + + if object.Attributes[j] == nil { + return false + } + + return object.Attributes[i].Name < object.Attributes[j].Name + }) + + schemaAttribute.NestedType = object + + return schemaAttribute, nil +} diff --git a/internal/proto6/schema_test.go b/tfsdk/attribute_test.go similarity index 53% rename from internal/proto6/schema_test.go rename to tfsdk/attribute_test.go index 09a036f0c..b2368442d 100644 --- a/internal/proto6/schema_test.go +++ b/tfsdk/attribute_test.go @@ -1,463 +1,22 @@ -package proto6 +package tfsdk import ( "context" "testing" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - - "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) -func TestSchema(t *testing.T) { - t.Parallel() - - type testCase struct { - input schema.Schema - expected *tfprotov6.Schema - expectedErr string - } - - tests := map[string]testCase{ - "empty-val": { - input: schema.Schema{}, - expectedErr: "must have at least one attribute in the schema", - }, - "basic-attrs": { - input: schema.Schema{ - Version: 1, - Attributes: map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - "number": { - Type: types.NumberType, - Optional: true, - }, - "bool": { - Type: types.BoolType, - Computed: true, - }, - }, - }, - expected: &tfprotov6.Schema{ - Version: 1, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "bool", - Type: tftypes.Bool, - Computed: true, - }, - { - Name: "number", - Type: tftypes.Number, - Optional: true, - }, - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - }, - }, - }, - "complex-attrs": { - input: schema.Schema{ - Version: 2, - Attributes: map[string]schema.Attribute{ - "list": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, - }, - "object": { - Type: types.ObjectType{AttrTypes: map[string]attr.Type{ - "string": types.StringType, - "number": types.NumberType, - "bool": types.BoolType, - }}, - Optional: true, - }, - "map": { - Type: types.MapType{ElemType: types.NumberType}, - Computed: true, - }, - // TODO: add tuple support when it lands - // TODO: add set support when it lands - }, - }, - expected: &tfprotov6.Schema{ - Version: 2, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "list", - Type: tftypes.List{ElementType: tftypes.String}, - Required: true, - }, - { - Name: "map", - Type: tftypes.Map{AttributeType: tftypes.Number}, - Computed: true, - }, - { - Name: "object", - Type: tftypes.Object{AttributeTypes: map[string]tftypes.Type{ - "string": tftypes.String, - "number": tftypes.Number, - "bool": tftypes.Bool, - }}, - Optional: true, - }, - }, - }, - }, - }, - "nested-attrs": { - input: schema.Schema{ - Version: 3, - Attributes: map[string]schema.Attribute{ - "single": { - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - "number": { - Type: types.NumberType, - Optional: true, - }, - "bool": { - Type: types.BoolType, - Computed: true, - }, - "list": { - Type: types.ListType{ElemType: types.StringType}, - Computed: true, - Optional: true, - }, - }), - Required: true, - }, - "list": { - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - "number": { - Type: types.NumberType, - Optional: true, - }, - "bool": { - Type: types.BoolType, - Computed: true, - }, - "list": { - Type: types.ListType{ElemType: types.StringType}, - Computed: true, - Optional: true, - }, - }, schema.ListNestedAttributesOptions{}), - Optional: true, - }, - "set": { - Attributes: schema.SetNestedAttributes(map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - "number": { - Type: types.NumberType, - Optional: true, - }, - "bool": { - Type: types.BoolType, - Computed: true, - }, - "list": { - Type: types.ListType{ElemType: types.StringType}, - Computed: true, - Optional: true, - }, - }, schema.SetNestedAttributesOptions{}), - Computed: true, - }, - "map": { - Attributes: schema.MapNestedAttributes(map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - "number": { - Type: types.NumberType, - Optional: true, - }, - "bool": { - Type: types.BoolType, - Computed: true, - }, - "list": { - Type: types.ListType{ElemType: types.StringType}, - Computed: true, - Optional: true, - }, - }, schema.MapNestedAttributesOptions{}), - Optional: true, - Computed: true, - }, - }, - }, - expected: &tfprotov6.Schema{ - Version: 3, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "list", - NestedType: &tfprotov6.SchemaObject{ - Nesting: tfprotov6.SchemaObjectNestingModeList, - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "bool", - Type: tftypes.Bool, - Computed: true, - }, - { - Name: "list", - Type: tftypes.List{ElementType: tftypes.String}, - Optional: true, - Computed: true, - }, - { - Name: "number", - Type: tftypes.Number, - Optional: true, - }, - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - }, - Optional: true, - }, - { - Name: "map", - NestedType: &tfprotov6.SchemaObject{ - Nesting: tfprotov6.SchemaObjectNestingModeMap, - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "bool", - Type: tftypes.Bool, - Computed: true, - }, - { - Name: "list", - Type: tftypes.List{ElementType: tftypes.String}, - Optional: true, - Computed: true, - }, - { - Name: "number", - Type: tftypes.Number, - Optional: true, - }, - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - }, - Optional: true, - Computed: true, - }, - { - Name: "set", - NestedType: &tfprotov6.SchemaObject{ - Nesting: tfprotov6.SchemaObjectNestingModeSet, - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "bool", - Type: tftypes.Bool, - Computed: true, - }, - { - Name: "list", - Type: tftypes.List{ElementType: tftypes.String}, - Optional: true, - Computed: true, - }, - { - Name: "number", - Type: tftypes.Number, - Optional: true, - }, - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - }, - Computed: true, - }, - { - Name: "single", - NestedType: &tfprotov6.SchemaObject{ - Nesting: tfprotov6.SchemaObjectNestingModeSingle, - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "bool", - Type: tftypes.Bool, - Computed: true, - }, - { - Name: "list", - Type: tftypes.List{ElementType: tftypes.String}, - Optional: true, - Computed: true, - }, - { - Name: "number", - Type: tftypes.Number, - Optional: true, - }, - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - }, - Required: true, - }, - }, - }, - }, - }, - "markdown-description": { - input: schema.Schema{ - Version: 1, - Attributes: map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - }, - MarkdownDescription: "a test resource", - }, - expected: &tfprotov6.Schema{ - Version: 1, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - Description: "a test resource", - DescriptionKind: tfprotov6.StringKindMarkdown, - }, - }, - }, - "plaintext-description": { - input: schema.Schema{ - Version: 1, - Attributes: map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - }, - Description: "a test resource", - }, - expected: &tfprotov6.Schema{ - Version: 1, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - Description: "a test resource", - DescriptionKind: tfprotov6.StringKindPlain, - }, - }, - }, - "deprecated": { - input: schema.Schema{ - Version: 1, - Attributes: map[string]schema.Attribute{ - "string": { - Type: types.StringType, - Required: true, - }, - }, - DeprecationMessage: "deprecated, use other_resource instead", - }, - expected: &tfprotov6.Schema{ - Version: 1, - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "string", - Type: tftypes.String, - Required: true, - }, - }, - Deprecated: true, - }, - }, - }, - } - - for name, tc := range tests { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - - got, err := Schema(context.Background(), tc.input) - if err != nil { - if tc.expectedErr == "" { - t.Errorf("Unexpected error: %s", err) - return - } - if err.Error() != tc.expectedErr { - t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) - return - } - // got expected error - return - } - if err == nil && tc.expectedErr != "" { - t.Errorf("Expected error to be %q, got nil", tc.expectedErr) - return - } - if diff := cmp.Diff(got, tc.expected); diff != "" { - t.Errorf("Unexpected diff (+wanted, -got): %s", diff) - return - } - }) - } -} - -func TestAttribute(t *testing.T) { +func TestAttributeTfprotov6SchemaAttribute(t *testing.T) { t.Parallel() type testCase struct { name string - attr schema.Attribute + attr Attribute path *tftypes.AttributePath expected *tfprotov6.SchemaAttribute expectedErr string @@ -466,7 +25,7 @@ func TestAttribute(t *testing.T) { tests := map[string]testCase{ "deprecated": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, DeprecationMessage: "deprecated, use new_string instead", @@ -481,7 +40,7 @@ func TestAttribute(t *testing.T) { }, "description-plain": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, Description: "A string attribute", @@ -497,7 +56,7 @@ func TestAttribute(t *testing.T) { }, "description-markdown": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, MarkdownDescription: "A string attribute", @@ -513,7 +72,7 @@ func TestAttribute(t *testing.T) { }, "description-both": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, Description: "A string attribute", @@ -530,7 +89,7 @@ func TestAttribute(t *testing.T) { }, "attr-string": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, }, @@ -543,7 +102,7 @@ func TestAttribute(t *testing.T) { }, "attr-bool": { name: "bool", - attr: schema.Attribute{ + attr: Attribute{ Type: types.BoolType, Optional: true, }, @@ -556,7 +115,7 @@ func TestAttribute(t *testing.T) { }, "attr-number": { name: "number", - attr: schema.Attribute{ + attr: Attribute{ Type: types.NumberType, Optional: true, }, @@ -569,7 +128,7 @@ func TestAttribute(t *testing.T) { }, "attr-list": { name: "list", - attr: schema.Attribute{ + attr: Attribute{ Type: types.ListType{ElemType: types.NumberType}, Optional: true, }, @@ -582,7 +141,7 @@ func TestAttribute(t *testing.T) { }, "attr-map": { name: "map", - attr: schema.Attribute{ + attr: Attribute{ Type: types.MapType{ElemType: types.StringType}, Optional: true, }, @@ -595,7 +154,7 @@ func TestAttribute(t *testing.T) { }, "attr-object": { name: "object", - attr: schema.Attribute{ + attr: Attribute{ Type: types.ObjectType{AttrTypes: map[string]attr.Type{ "foo": types.StringType, "bar": types.NumberType, @@ -618,7 +177,7 @@ func TestAttribute(t *testing.T) { // TODO: add tuple attribute when we support it "required": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Required: true, }, @@ -631,7 +190,7 @@ func TestAttribute(t *testing.T) { }, "optional": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, }, @@ -644,7 +203,7 @@ func TestAttribute(t *testing.T) { }, "computed": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Computed: true, }, @@ -657,7 +216,7 @@ func TestAttribute(t *testing.T) { }, "optional-computed": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Computed: true, Optional: true, @@ -672,7 +231,7 @@ func TestAttribute(t *testing.T) { }, "sensitive": { name: "string", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, Optional: true, Sensitive: true, @@ -687,8 +246,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-single": { name: "single_nested", - attr: schema.Attribute{ - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -725,8 +284,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-list": { name: "list_nested", - attr: schema.Attribute{ - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -736,7 +295,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.ListNestedAttributesOptions{}), + }, ListNestedAttributesOptions{}), Optional: true, }, path: tftypes.NewAttributePath(), @@ -763,8 +322,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-list-min": { name: "list_nested", - attr: schema.Attribute{ - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -774,7 +333,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.ListNestedAttributesOptions{ + }, ListNestedAttributesOptions{ MinItems: 1, }), Optional: true, @@ -804,8 +363,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-list-max": { name: "list_nested", - attr: schema.Attribute{ - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -815,7 +374,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.ListNestedAttributesOptions{ + }, ListNestedAttributesOptions{ MaxItems: 1, }), Optional: true, @@ -845,8 +404,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-list-minmax": { name: "list_nested", - attr: schema.Attribute{ - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -856,7 +415,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.ListNestedAttributesOptions{ + }, ListNestedAttributesOptions{ MinItems: 1, MaxItems: 10, }), @@ -888,8 +447,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-set": { name: "set_nested", - attr: schema.Attribute{ - Attributes: schema.SetNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: SetNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -899,7 +458,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.SetNestedAttributesOptions{}), + }, SetNestedAttributesOptions{}), Optional: true, }, path: tftypes.NewAttributePath(), @@ -926,8 +485,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-set-min": { name: "set_nested", - attr: schema.Attribute{ - Attributes: schema.SetNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: SetNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -937,7 +496,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.SetNestedAttributesOptions{ + }, SetNestedAttributesOptions{ MinItems: 1, }), Optional: true, @@ -967,8 +526,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-set-max": { name: "set_nested", - attr: schema.Attribute{ - Attributes: schema.SetNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: SetNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -978,7 +537,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.SetNestedAttributesOptions{ + }, SetNestedAttributesOptions{ MaxItems: 1, }), Optional: true, @@ -1008,8 +567,8 @@ func TestAttribute(t *testing.T) { }, "nested-attr-set-minmax": { name: "set_nested", - attr: schema.Attribute{ - Attributes: schema.SetNestedAttributes(map[string]schema.Attribute{ + attr: Attribute{ + Attributes: SetNestedAttributes(map[string]Attribute{ "string": { Type: types.StringType, Optional: true, @@ -1019,7 +578,7 @@ func TestAttribute(t *testing.T) { Computed: true, Sensitive: true, }, - }, schema.SetNestedAttributesOptions{ + }, SetNestedAttributesOptions{ MinItems: 1, MaxItems: 10, }), @@ -1051,9 +610,9 @@ func TestAttribute(t *testing.T) { }, "attr-and-nested-attr-set": { name: "whoops", - attr: schema.Attribute{ + attr: Attribute{ Type: types.StringType, - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ "testing": { Type: types.StringType, Optional: true, @@ -1066,7 +625,7 @@ func TestAttribute(t *testing.T) { }, "attr-and-nested-attr-unset": { name: "whoops", - attr: schema.Attribute{ + attr: Attribute{ Optional: true, }, path: tftypes.NewAttributePath(), @@ -1074,9 +633,9 @@ func TestAttribute(t *testing.T) { }, "attr-and-nested-attr-empty": { name: "whoops", - attr: schema.Attribute{ + attr: Attribute{ Optional: true, - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{}), + Attributes: SingleNestedAttributes(map[string]Attribute{}), }, path: tftypes.NewAttributePath(), expectedErr: "must have Attributes or Type set", @@ -1088,7 +647,7 @@ func TestAttribute(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, err := Attribute(context.Background(), tc.name, tc.attr, tc.path) + got, err := tc.attr.tfprotov6SchemaAttribute(context.Background(), tc.name, tc.path) if err != nil { if tc.expectedErr == "" { t.Errorf("Unexpected error: %s", err) diff --git a/tfsdk/config.go b/tfsdk/config.go index 191c1c769..4ffd4a6ec 100644 --- a/tfsdk/config.go +++ b/tfsdk/config.go @@ -6,14 +6,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/reflect" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Config represents a Terraform config. type Config struct { Raw tftypes.Value - Schema schema.Schema + Schema Schema } // Get populates the struct passed as `target` with the entire config. diff --git a/tfsdk/data_source.go b/tfsdk/data_source.go index fe0c10786..85f57c816 100644 --- a/tfsdk/data_source.go +++ b/tfsdk/data_source.go @@ -3,7 +3,6 @@ package tfsdk import ( "context" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) @@ -12,7 +11,7 @@ import ( // return an instance of it in the map returned by Provider.GetDataSources. type DataSourceType interface { // GetSchema returns the schema for this data source. - GetSchema(context.Context) (schema.Schema, []*tfprotov6.Diagnostic) + GetSchema(context.Context) (Schema, []*tfprotov6.Diagnostic) // NewDataSource instantiates a new DataSource of this DataSourceType. NewDataSource(context.Context, Provider) (DataSource, []*tfprotov6.Diagnostic) diff --git a/schema/nested_attributes.go b/tfsdk/nested_attributes.go similarity index 99% rename from schema/nested_attributes.go rename to tfsdk/nested_attributes.go index 1cf00d953..bac707f73 100644 --- a/schema/nested_attributes.go +++ b/tfsdk/nested_attributes.go @@ -1,4 +1,4 @@ -package schema +package tfsdk import ( "fmt" diff --git a/tfsdk/plan.go b/tfsdk/plan.go index 48e966e26..a8270a1a4 100644 --- a/tfsdk/plan.go +++ b/tfsdk/plan.go @@ -6,14 +6,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/reflect" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Plan represents a Terraform plan. type Plan struct { Raw tftypes.Value - Schema schema.Schema + Schema Schema } // Get populates the struct passed as `target` with the entire plan. diff --git a/tfsdk/provider.go b/tfsdk/provider.go index ab5c2f73d..976166307 100644 --- a/tfsdk/provider.go +++ b/tfsdk/provider.go @@ -3,7 +3,6 @@ package tfsdk import ( "context" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) @@ -11,7 +10,7 @@ import ( type Provider interface { // GetSchema returns the schema for this provider's configuration. If // this provider has no configuration, return an empty schema.Schema. - GetSchema(context.Context) (schema.Schema, []*tfprotov6.Diagnostic) + GetSchema(context.Context) (Schema, []*tfprotov6.Diagnostic) // Configure is called at the beginning of the provider lifecycle, when // Terraform sends to the provider the values the user specified in the @@ -38,5 +37,5 @@ type Provider interface { type ProviderWithProviderMeta interface { Provider // GetMetaSchema returns the provider meta schema. - GetMetaSchema(context.Context) (schema.Schema, []*tfprotov6.Diagnostic) + GetMetaSchema(context.Context) (Schema, []*tfprotov6.Diagnostic) } diff --git a/tfsdk/resource.go b/tfsdk/resource.go index 92872d576..59f6faa56 100644 --- a/tfsdk/resource.go +++ b/tfsdk/resource.go @@ -3,7 +3,6 @@ package tfsdk import ( "context" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) @@ -12,7 +11,7 @@ import ( // instance of it in the map returned by Provider.GeResources. type ResourceType interface { // GetSchema returns the schema for this resource. - GetSchema(context.Context) (schema.Schema, []*tfprotov6.Diagnostic) + GetSchema(context.Context) (Schema, []*tfprotov6.Diagnostic) // NewResource instantiates a new Resource of this ResourceType. NewResource(context.Context, Provider) (Resource, []*tfprotov6.Diagnostic) diff --git a/schema/schema.go b/tfsdk/schema.go similarity index 74% rename from schema/schema.go rename to tfsdk/schema.go index b8ffaae6d..9962fdd85 100644 --- a/schema/schema.go +++ b/tfsdk/schema.go @@ -1,12 +1,14 @@ -package schema +package tfsdk import ( "context" "errors" "fmt" + "sort" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) @@ -126,3 +128,58 @@ func (s Schema) AttributeAtPath(path *tftypes.AttributePath) (Attribute, error) } return a, nil } + +// tfprotov6Schema returns the *tfprotov6.Schema equivalent of a Schema. At least +// one attribute must be set in the schema, or an error will be returned. +func (s Schema) tfprotov6Schema(ctx context.Context) (*tfprotov6.Schema, error) { + result := &tfprotov6.Schema{ + Version: s.Version, + } + + var attrs []*tfprotov6.SchemaAttribute + + for name, attr := range s.Attributes { + a, err := attr.tfprotov6SchemaAttribute(ctx, name, tftypes.NewAttributePath().WithAttributeName(name)) + + if err != nil { + return nil, err + } + + attrs = append(attrs, a) + } + + sort.Slice(attrs, func(i, j int) bool { + if attrs[i] == nil { + return true + } + + if attrs[j] == nil { + return false + } + + return attrs[i].Name < attrs[j].Name + }) + + if len(attrs) < 1 { + return nil, errors.New("must have at least one attribute in the schema") + } + + result.Block = &tfprotov6.SchemaBlock{ + // core doesn't do anything with version, as far as I can tell, + // so let's not set it. + Attributes: attrs, + Deprecated: s.DeprecationMessage != "", + } + + if s.Description != "" { + result.Block.Description = s.Description + result.Block.DescriptionKind = tfprotov6.StringKindPlain + } + + if s.MarkdownDescription != "" { + result.Block.Description = s.MarkdownDescription + result.Block.DescriptionKind = tfprotov6.StringKindMarkdown + } + + return result, nil +} diff --git a/tfsdk/schema_test.go b/tfsdk/schema_test.go new file mode 100644 index 000000000..253fa7caa --- /dev/null +++ b/tfsdk/schema_test.go @@ -0,0 +1,522 @@ +package tfsdk + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestSchemaAttributeType(t *testing.T) { + testSchema := Schema{ + Attributes: map[string]Attribute{ + "foo": { + Type: types.StringType, + Required: true, + }, + "bar": { + Type: types.ListType{ + ElemType: types.StringType, + }, + Required: true, + }, + "disks": { + Attributes: ListNestedAttributes(map[string]Attribute{ + "id": { + Type: types.StringType, + Required: true, + }, + "delete_with_instance": { + Type: types.BoolType, + Optional: true, + }, + }, ListNestedAttributesOptions{}), + Optional: true, + Computed: true, + }, + "boot_disk": { + Attributes: SingleNestedAttributes(map[string]Attribute{ + "id": { + Type: types.StringType, + Required: true, + }, + "delete_with_instance": { + Type: types.BoolType, + }, + }), + }, + }, + } + + expectedType := types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "foo": types.StringType, + "bar": types.ListType{ + ElemType: types.StringType, + }, + "disks": types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "delete_with_instance": types.BoolType, + }, + }, + }, + "boot_disk": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "delete_with_instance": types.BoolType, + }, + }, + }, + } + + actualType := testSchema.AttributeType() + + if !expectedType.Equal(actualType) { + t.Fatalf("types not equal (+wanted, -got): %s", cmp.Diff(expectedType, actualType)) + } +} + +func TestSchemaTfprotov6Schema(t *testing.T) { + t.Parallel() + + type testCase struct { + input Schema + expected *tfprotov6.Schema + expectedErr string + } + + tests := map[string]testCase{ + "empty-val": { + input: Schema{}, + expectedErr: "must have at least one attribute in the schema", + }, + "basic-attrs": { + input: Schema{ + Version: 1, + Attributes: map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + "number": { + Type: types.NumberType, + Optional: true, + }, + "bool": { + Type: types.BoolType, + Computed: true, + }, + }, + }, + expected: &tfprotov6.Schema{ + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + Computed: true, + }, + { + Name: "number", + Type: tftypes.Number, + Optional: true, + }, + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + }, + }, + }, + "complex-attrs": { + input: Schema{ + Version: 2, + Attributes: map[string]Attribute{ + "list": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + "object": { + Type: types.ObjectType{AttrTypes: map[string]attr.Type{ + "string": types.StringType, + "number": types.NumberType, + "bool": types.BoolType, + }}, + Optional: true, + }, + "map": { + Type: types.MapType{ElemType: types.NumberType}, + Computed: true, + }, + // TODO: add tuple support when it lands + // TODO: add set support when it lands + }, + }, + expected: &tfprotov6.Schema{ + Version: 2, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "list", + Type: tftypes.List{ElementType: tftypes.String}, + Required: true, + }, + { + Name: "map", + Type: tftypes.Map{AttributeType: tftypes.Number}, + Computed: true, + }, + { + Name: "object", + Type: tftypes.Object{AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + "number": tftypes.Number, + "bool": tftypes.Bool, + }}, + Optional: true, + }, + }, + }, + }, + }, + "nested-attrs": { + input: Schema{ + Version: 3, + Attributes: map[string]Attribute{ + "single": { + Attributes: SingleNestedAttributes(map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + "number": { + Type: types.NumberType, + Optional: true, + }, + "bool": { + Type: types.BoolType, + Computed: true, + }, + "list": { + Type: types.ListType{ElemType: types.StringType}, + Computed: true, + Optional: true, + }, + }), + Required: true, + }, + "list": { + Attributes: ListNestedAttributes(map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + "number": { + Type: types.NumberType, + Optional: true, + }, + "bool": { + Type: types.BoolType, + Computed: true, + }, + "list": { + Type: types.ListType{ElemType: types.StringType}, + Computed: true, + Optional: true, + }, + }, ListNestedAttributesOptions{}), + Optional: true, + }, + "set": { + Attributes: SetNestedAttributes(map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + "number": { + Type: types.NumberType, + Optional: true, + }, + "bool": { + Type: types.BoolType, + Computed: true, + }, + "list": { + Type: types.ListType{ElemType: types.StringType}, + Computed: true, + Optional: true, + }, + }, SetNestedAttributesOptions{}), + Computed: true, + }, + "map": { + Attributes: MapNestedAttributes(map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + "number": { + Type: types.NumberType, + Optional: true, + }, + "bool": { + Type: types.BoolType, + Computed: true, + }, + "list": { + Type: types.ListType{ElemType: types.StringType}, + Computed: true, + Optional: true, + }, + }, MapNestedAttributesOptions{}), + Optional: true, + Computed: true, + }, + }, + }, + expected: &tfprotov6.Schema{ + Version: 3, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "list", + NestedType: &tfprotov6.SchemaObject{ + Nesting: tfprotov6.SchemaObjectNestingModeList, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + Computed: true, + }, + { + Name: "list", + Type: tftypes.List{ElementType: tftypes.String}, + Optional: true, + Computed: true, + }, + { + Name: "number", + Type: tftypes.Number, + Optional: true, + }, + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + }, + Optional: true, + }, + { + Name: "map", + NestedType: &tfprotov6.SchemaObject{ + Nesting: tfprotov6.SchemaObjectNestingModeMap, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + Computed: true, + }, + { + Name: "list", + Type: tftypes.List{ElementType: tftypes.String}, + Optional: true, + Computed: true, + }, + { + Name: "number", + Type: tftypes.Number, + Optional: true, + }, + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + }, + Optional: true, + Computed: true, + }, + { + Name: "set", + NestedType: &tfprotov6.SchemaObject{ + Nesting: tfprotov6.SchemaObjectNestingModeSet, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + Computed: true, + }, + { + Name: "list", + Type: tftypes.List{ElementType: tftypes.String}, + Optional: true, + Computed: true, + }, + { + Name: "number", + Type: tftypes.Number, + Optional: true, + }, + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + }, + Computed: true, + }, + { + Name: "single", + NestedType: &tfprotov6.SchemaObject{ + Nesting: tfprotov6.SchemaObjectNestingModeSingle, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + Computed: true, + }, + { + Name: "list", + Type: tftypes.List{ElementType: tftypes.String}, + Optional: true, + Computed: true, + }, + { + Name: "number", + Type: tftypes.Number, + Optional: true, + }, + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + }, + }, + "markdown-description": { + input: Schema{ + Version: 1, + Attributes: map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + }, + MarkdownDescription: "a test resource", + }, + expected: &tfprotov6.Schema{ + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + Description: "a test resource", + DescriptionKind: tfprotov6.StringKindMarkdown, + }, + }, + }, + "plaintext-description": { + input: Schema{ + Version: 1, + Attributes: map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + }, + Description: "a test resource", + }, + expected: &tfprotov6.Schema{ + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + Description: "a test resource", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + "deprecated": { + input: Schema{ + Version: 1, + Attributes: map[string]Attribute{ + "string": { + Type: types.StringType, + Required: true, + }, + }, + DeprecationMessage: "deprecated, use other_resource instead", + }, + expected: &tfprotov6.Schema{ + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + Required: true, + }, + }, + Deprecated: true, + }, + }, + }, + } + + for name, tc := range tests { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := tc.input.tfprotov6Schema(context.Background()) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } + }) + } +} diff --git a/tfsdk/serve.go b/tfsdk/serve.go index 7e058c8b7..4d3b413fd 100644 --- a/tfsdk/serve.go +++ b/tfsdk/serve.go @@ -7,8 +7,6 @@ import ( "sync" "github.com/hashicorp/terraform-plugin-framework/internal/proto6" - "github.com/hashicorp/terraform-plugin-framework/schema" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" tf6server "github.com/hashicorp/terraform-plugin-go/tfprotov6/server" "github.com/hashicorp/terraform-plugin-go/tftypes" @@ -119,7 +117,7 @@ func (s *server) GetProviderSchema(ctx context.Context, _ *tfprotov6.GetProvider } } // convert the provider schema to a *tfprotov6.Schema - provider6Schema, err := proto6.Schema(ctx, providerSchema) + provider6Schema, err := providerSchema.tfprotov6Schema(ctx) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov6.Diagnostic{ Severity: tfprotov6.DiagnosticSeverityError, @@ -144,7 +142,7 @@ func (s *server) GetProviderSchema(ctx context.Context, _ *tfprotov6.GetProvider return resp, nil } } - pm6Schema, err := proto6.Schema(ctx, providerMetaSchema) + pm6Schema, err := providerMetaSchema.tfprotov6Schema(ctx) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov6.Diagnostic{ Severity: tfprotov6.DiagnosticSeverityError, @@ -173,7 +171,7 @@ func (s *server) GetProviderSchema(ctx context.Context, _ *tfprotov6.GetProvider return resp, nil } } - schema6, err := proto6.Schema(ctx, schema) + schema6, err := schema.tfprotov6Schema(ctx) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov6.Diagnostic{ Severity: tfprotov6.DiagnosticSeverityError, @@ -202,7 +200,7 @@ func (s *server) GetProviderSchema(ctx context.Context, _ *tfprotov6.GetProvider return resp, nil } } - schema6, err := proto6.Schema(ctx, schema) + schema6, err := schema.tfprotov6Schema(ctx) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov6.Diagnostic{ Severity: tfprotov6.DiagnosticSeverityError, @@ -379,14 +377,14 @@ func (s *server) ReadResource(ctx context.Context, req *tfprotov6.ReadResourceRe return resp, nil } -func markComputedNilsAsUnknown(ctx context.Context, resourceSchema schema.Schema) func(*tftypes.AttributePath, tftypes.Value) (tftypes.Value, error) { +func markComputedNilsAsUnknown(ctx context.Context, resourceSchema Schema) func(*tftypes.AttributePath, tftypes.Value) (tftypes.Value, error) { return func(path *tftypes.AttributePath, val tftypes.Value) (tftypes.Value, error) { if !val.IsNull() { return val, nil } attribute, err := resourceSchema.AttributeAtPath(path) if err != nil { - if errors.Is(err, schema.ErrPathInsideAtomicAttribute) { + if errors.Is(err, ErrPathInsideAtomicAttribute) { // ignore attributes/elements inside schema.Attributes, they have no schema of their own return val, nil } diff --git a/tfsdk/serve_data_source_one_test.go b/tfsdk/serve_data_source_one_test.go index a5cfe636d..7000c8d8e 100644 --- a/tfsdk/serve_data_source_one_test.go +++ b/tfsdk/serve_data_source_one_test.go @@ -4,18 +4,16 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) type testServeDataSourceTypeOne struct{} -func (dt testServeDataSourceTypeOne) GetSchema(_ context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ +func (dt testServeDataSourceTypeOne) GetSchema(_ context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ + Attributes: map[string]Attribute{ "current_time": { Type: types.StringType, Computed: true, diff --git a/tfsdk/serve_data_source_two_test.go b/tfsdk/serve_data_source_two_test.go index d82b925bc..a3cc8836c 100644 --- a/tfsdk/serve_data_source_two_test.go +++ b/tfsdk/serve_data_source_two_test.go @@ -4,18 +4,16 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) type testServeDataSourceTypeTwo struct{} -func (dt testServeDataSourceTypeTwo) GetSchema(_ context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ +func (dt testServeDataSourceTypeTwo) GetSchema(_ context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ + Attributes: map[string]Attribute{ "family": { Type: types.StringType, Optional: true, diff --git a/tfsdk/serve_provider_test.go b/tfsdk/serve_provider_test.go index 1c2a3afe0..25057d097 100644 --- a/tfsdk/serve_provider_test.go +++ b/tfsdk/serve_provider_test.go @@ -4,9 +4,7 @@ import ( "context" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) @@ -14,14 +12,14 @@ import ( type testServeProvider struct { // configure configuredVal tftypes.Value - configuredSchema schema.Schema + configuredSchema Schema configuredTFVersion string // read resource request readResourceCurrentStateValue tftypes.Value - readResourceCurrentStateSchema schema.Schema + readResourceCurrentStateSchema Schema readResourceProviderMetaValue tftypes.Value - readResourceProviderMetaSchema schema.Schema + readResourceProviderMetaSchema Schema readResourceImpl func(context.Context, ReadResourceRequest, *ReadResourceResponse) readResourceCalledResourceType string @@ -29,31 +27,31 @@ type testServeProvider struct { applyResourceChangeCalledResourceType string applyResourceChangeCalledAction string applyResourceChangePriorStateValue tftypes.Value - applyResourceChangePriorStateSchema schema.Schema + applyResourceChangePriorStateSchema Schema applyResourceChangePlannedStateValue tftypes.Value - applyResourceChangePlannedStateSchema schema.Schema + applyResourceChangePlannedStateSchema Schema applyResourceChangeConfigValue tftypes.Value - applyResourceChangeConfigSchema schema.Schema + applyResourceChangeConfigSchema Schema applyResourceChangeProviderMetaValue tftypes.Value - applyResourceChangeProviderMetaSchema schema.Schema + applyResourceChangeProviderMetaSchema Schema createFunc func(context.Context, CreateResourceRequest, *CreateResourceResponse) updateFunc func(context.Context, UpdateResourceRequest, *UpdateResourceResponse) deleteFunc func(context.Context, DeleteResourceRequest, *DeleteResourceResponse) // read data source request readDataSourceConfigValue tftypes.Value - readDataSourceConfigSchema schema.Schema + readDataSourceConfigSchema Schema readDataSourceProviderMetaValue tftypes.Value - readDataSourceProviderMetaSchema schema.Schema + readDataSourceProviderMetaSchema Schema readDataSourceImpl func(context.Context, ReadDataSourceRequest, *ReadDataSourceResponse) readDataSourceCalledDataSourceType string } -func (t *testServeProvider) GetSchema(_ context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ +func (t *testServeProvider) GetSchema(_ context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ Version: 1, DeprecationMessage: "Deprecated in favor of other_resource", - Attributes: map[string]schema.Attribute{ + Attributes: map[string]Attribute{ "required": { Type: types.StringType, Required: true, @@ -143,7 +141,7 @@ func (t *testServeProvider) GetSchema(_ context.Context) (schema.Schema, []*tfpr // TODO: add sets when we support them // TODO: add tuples when we support them "single-nested-attributes": { - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ "foo": { Type: types.StringType, Optional: true, @@ -157,7 +155,7 @@ func (t *testServeProvider) GetSchema(_ context.Context) (schema.Schema, []*tfpr Optional: true, }, "list-nested-attributes": { - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "foo": { Type: types.StringType, Optional: true, @@ -167,11 +165,11 @@ func (t *testServeProvider) GetSchema(_ context.Context) (schema.Schema, []*tfpr Type: types.NumberType, Required: true, }, - }, schema.ListNestedAttributesOptions{}), + }, ListNestedAttributesOptions{}), Optional: true, }, "map-nested-attributes": { - Attributes: schema.MapNestedAttributes(map[string]schema.Attribute{ + Attributes: MapNestedAttributes(map[string]Attribute{ "foo": { Type: types.StringType, Optional: true, @@ -181,7 +179,7 @@ func (t *testServeProvider) GetSchema(_ context.Context) (schema.Schema, []*tfpr Type: types.NumberType, Required: true, }, - }, schema.MapNestedAttributesOptions{}), + }, MapNestedAttributesOptions{}), Optional: true, }, }, @@ -429,10 +427,10 @@ type testServeProviderWithMetaSchema struct { *testServeProvider } -func (t *testServeProviderWithMetaSchema) GetMetaSchema(context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ +func (t *testServeProviderWithMetaSchema) GetMetaSchema(context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ Version: 2, - Attributes: map[string]schema.Attribute{ + Attributes: map[string]Attribute{ "foo": { Type: types.StringType, Required: true, diff --git a/tfsdk/serve_resource_one_test.go b/tfsdk/serve_resource_one_test.go index 961afd74d..967a3c542 100644 --- a/tfsdk/serve_resource_one_test.go +++ b/tfsdk/serve_resource_one_test.go @@ -4,19 +4,17 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) type testServeResourceTypeOne struct{} -func (rt testServeResourceTypeOne) GetSchema(_ context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ +func (rt testServeResourceTypeOne) GetSchema(_ context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ Version: 1, - Attributes: map[string]schema.Attribute{ + Attributes: map[string]Attribute{ "name": { Required: true, Type: types.StringType, diff --git a/tfsdk/serve_resource_two_test.go b/tfsdk/serve_resource_two_test.go index 99a8b797b..03a6892d0 100644 --- a/tfsdk/serve_resource_two_test.go +++ b/tfsdk/serve_resource_two_test.go @@ -4,18 +4,16 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) type testServeResourceTypeTwo struct{} -func (rt testServeResourceTypeTwo) GetSchema(_ context.Context) (schema.Schema, []*tfprotov6.Diagnostic) { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ +func (rt testServeResourceTypeTwo) GetSchema(_ context.Context) (Schema, []*tfprotov6.Diagnostic) { + return Schema{ + Attributes: map[string]Attribute{ "id": { Optional: true, Computed: true, @@ -24,7 +22,7 @@ func (rt testServeResourceTypeTwo) GetSchema(_ context.Context) (schema.Schema, "disks": { Optional: true, Computed: true, - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "name": { Required: true, Type: types.StringType, @@ -37,7 +35,7 @@ func (rt testServeResourceTypeTwo) GetSchema(_ context.Context) (schema.Schema, Required: true, Type: types.BoolType, }, - }, schema.ListNestedAttributesOptions{}), + }, ListNestedAttributesOptions{}), }, }, }, nil diff --git a/tfsdk/serve_test.go b/tfsdk/serve_test.go index 6f517c3a3..98ec08ece 100644 --- a/tfsdk/serve_test.go +++ b/tfsdk/serve_test.go @@ -8,7 +8,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" @@ -57,8 +56,8 @@ func TestServerCancelInFlightContexts(t *testing.T) { func TestMarkComputedNilsAsUnknown(t *testing.T) { t.Parallel() - s := schema.Schema{ - Attributes: map[string]schema.Attribute{ + s := Schema{ + Attributes: map[string]Attribute{ // values should be left alone "string-value": { Type: types.StringType, @@ -114,7 +113,7 @@ func TestMarkComputedNilsAsUnknown(t *testing.T) { }, // nil nested attributes should be unknown "nested-nil-optional-computed": { - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ "string-nil": { Type: types.StringType, Optional: true, @@ -131,7 +130,7 @@ func TestMarkComputedNilsAsUnknown(t *testing.T) { }, // non-nil nested attributes should be left alone on the top level "nested-value-optional-computed": { - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ // nested computed attributes should be unknown "string-nil": { Type: types.StringType, @@ -801,7 +800,7 @@ func TestServerReadResource(t *testing.T) { testServer := &server{ p: s, } - var pmSchema schema.Schema + var pmSchema Schema if tc.providerMeta.Type() != nil { sWithMeta := &testServeProviderWithMetaSchema{s} testServer.p = sWithMeta @@ -2113,7 +2112,7 @@ func TestServerApplyResourceChange(t *testing.T) { testServer := &server{ p: s, } - var pmSchema schema.Schema + var pmSchema Schema if tc.providerMeta.Type() != nil { sWithMeta := &testServeProviderWithMetaSchema{s} testServer.p = sWithMeta @@ -2409,7 +2408,7 @@ func TestServerReadDataSource(t *testing.T) { testServer := &server{ p: s, } - var pmSchema schema.Schema + var pmSchema Schema if tc.providerMeta.Type() != nil { sWithMeta := &testServeProviderWithMetaSchema{s} testServer.p = sWithMeta diff --git a/tfsdk/state.go b/tfsdk/state.go index 942fe5eda..8f8703a43 100644 --- a/tfsdk/state.go +++ b/tfsdk/state.go @@ -6,14 +6,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/reflect" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-go/tftypes" ) // State represents a Terraform state. type State struct { Raw tftypes.Value - Schema schema.Schema + Schema Schema } // Get populates the struct passed as `target` with the entire state. diff --git a/tfsdk/state_test.go b/tfsdk/state_test.go index d8dfc86d2..301908e10 100644 --- a/tfsdk/state_test.go +++ b/tfsdk/state_test.go @@ -7,7 +7,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-go/tftypes" ) @@ -15,8 +14,8 @@ import ( var allowAllUnexported = cmp.Exporter(func(reflect.Type) bool { return true }) // schema used for all tests -var testSchema = schema.Schema{ - Attributes: map[string]schema.Attribute{ +var testSchema = Schema{ + Attributes: map[string]Attribute{ "name": { Type: types.StringType, Required: true, @@ -31,7 +30,7 @@ var testSchema = schema.Schema{ Required: true, }, "disks": { - Attributes: schema.ListNestedAttributes(map[string]schema.Attribute{ + Attributes: ListNestedAttributes(map[string]Attribute{ "id": { Type: types.StringType, Required: true, @@ -40,12 +39,12 @@ var testSchema = schema.Schema{ Type: types.BoolType, Optional: true, }, - }, schema.ListNestedAttributesOptions{}), + }, ListNestedAttributesOptions{}), Optional: true, Computed: true, }, "boot_disk": { - Attributes: schema.SingleNestedAttributes(map[string]schema.Attribute{ + Attributes: SingleNestedAttributes(map[string]Attribute{ "id": { Type: types.StringType, Required: true,