Merged
Conversation
Reference: #132 As part of upcoming effort to split schema functionality into the `datasource`, `provider`, and `resource` packages, there are some improvements that will land in the new implementations rather than breaking the existing `tfsdk` package schema functionality. One area which has caused developer burden is that "attribute" plan modifiers, currently implementations of the `tfsdk.AttributePlanModifier` interface, receive generic `attr.Value` as the configuration, plan, and state values to perform modification logic. This means that implementors must currently handle validating and converting the value into the concrete type they expect. The upcoming split schemas handling will introduce separate attribute/block types that will enable to framework to strongly type validators and other future schema enhancements. This change prepares the exported interfaces and internal validation logic for those enhancements. Plan modifiers are only available for resources, so this package is explicitly placed under that structure to further reduce usage confusion.
Reference: #132 Reference: #326 Reference: #437 Reference: #491 Reference: #508 Reference: #532 This change introduces a new `resource/schema` package, which contains schema interfaces and types relevant to resources. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization, plan modification, and validation. The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for resources. The framework design will also raise compiler-time errors for errant typing of validators. No changes are required for data handling in any other `resource.Resource` methods. Example definition: ```go package test import ( "context" "github.com/bflad/terraform-plugin-framework-type-time/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) type ThingResource struct{} func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 256), }, }, "custom_string_attribute": schema.StringAttribute{ CustomType: timetypes.RFC3339Type, Optional: true, }, "list_attribute": schema.ListAttribute{ ElementType: types.StringType, Optional: true, }, "list_nested_attribute": schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "bool_attribute": schema.BoolAttribute{ Optional: true, }, }, Validators: []validator.Object{ /*...*/ }, }, Optional: true, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, "single_nested_attribute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ "int64_attribute": schema.Int64Attribute{ Optional: true, }, }, Optional: true, }, }, Blocks: map[string]schema.Block{ "list_block": schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "float64_attribute": schema.Float64Attribute{ Optional: true, Validators: []validator.Float64{ float64validator.OneOf(1.2, 2.4), }, }, }, Validators: []validator.Object{ /*...*/ }, }, Validators: []validator.List{ listvalidator.SizeAtMost(2), }, }, }, } } ``` To migrate a resource schema: - Add `github.com/hashicorp/terraform-plugin-framework/resource/schema` to the `import` statement - Switch the `resource.Resource` implementation `GetSchema` method to `Schema` whose response includes a `schema.Schema` from the new package. Prior implementation: ```go func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{/* ... */}, nil } ``` Migrated implementation: ```go func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{/*...*/} } ``` If the resource requires no schema, the method can be entirely empty. - Switch `map[string]tfsdk.Attribute` with `map[string]schema.Attribute` - Switch `map[string]tfsdk.Block` with `map[string]schema.Block` - Switch individual attribute and block definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `schema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations. Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.StringType, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.StringAttribute{ Required: true, } ``` Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Required: true, Type: types.ListType{ ElemType: types.StringType, }, } ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListAttribute{ ElementType: types.StringType, Required: true, } ``` Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{/*...*/}, Required: true, } ``` Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation: ```go // The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute tfsdk.Attribute{ Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}), Required: true, }, ``` Migrated implementation: ```go // The schema.XXXAttribute must be declared inside map[string]schema.Attribute schema.ListNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{/*...*/}, }, Required: true, } ``` Prior collection blocks type (`tfsdk.Block`) attribute implementation: ```go // The "tfsdk.Block" could be omitted inside a map[string]tfsdk.Block tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{/*...*/}, Blocks: map[string]tfsdk.Block{/*...*/}, NestingMode: tfsdk.BlockNestingModeList, }, ``` Migrated implementation: ```go // The schema.XXXBlock must be declared inside map[string]schema.Block schema.ListNestedBlock{ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{/*...*/}, Blocks: map[string]schema.Block{/*...*/}, }, } ```
Base automatically changed from
bflad/resource-schema-planmodifier-package
to
main
November 29, 2022 13:55
This was referenced Nov 30, 2022
|
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #508
Reference: #532
This change introduces a new
resource/schemapackage, which contains schema interfaces and types relevant to resources. This new schema implementation also provides strongly typed attributes, nested attributes, and blocks with customizable types. Nested attributes and blocks are exposed with a separate nested object for customization, plan modification, and validation.The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for resources. The framework design will also raise compiler-time errors for errant typing of validators.
No changes are required for data handling in any other
resource.Resourcemethods.Example definition:
To migrate a resource schema:
github.com/hashicorp/terraform-plugin-framework/resource/schemato theimportstatementresource.ResourceimplementationGetSchemamethod toSchemawhose response includes aschema.Schemafrom the new package.Prior implementation:
Migrated implementation:
If the resource requires no schema, the method can be entirely empty.
map[string]tfsdk.Attributewithmap[string]schema.Attributemap[string]tfsdk.Blockwithmap[string]schema.BlockTypefield will be removed and the map entries must declare the typed implementation, e.g. atfsdk.AttributewithType: types.StringTypeis equivalent toschema.StringAttribute. Custom attribute types can be specified via theCustomTypefield in each of the implementations.Prior primitive type (
types.BoolType,types.Float64Type,types.Int64Type,types.NumberType,types.StringType) attribute implementation:Migrated implementation:
Prior collection type (
types.ListType,types.MapType,types.SetType) attribute implementation:Migrated implementation:
Prior single nested attributes type (
tfsdk.SingleNestedAttributes()) attribute implementation:Migrated implementation:
Prior collection nested attributes type (
tfsdk.ListNestedAttributes(),tfsdk.MapNestedAttributes(),tfsdk.SetNestedAttributes()) attribute implementation:Migrated implementation:
Prior collection blocks type (
tfsdk.Block) attribute implementation:Migrated implementation: