Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/schema: Initial package #558

Merged
merged 5 commits into from Nov 29, 2022
Merged

resource/schema: Initial package #558

merged 5 commits into from Nov 29, 2022

Commits on Nov 29, 2022

  1. resource/schema/planmodifier: New type-specific plan modifiers package

    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.
    bflad committed Nov 29, 2022
    Copy the full SHA
    00e2251 View commit details
    Browse the repository at this point in the history
  2. Update CHANGELOG for #557

    bflad committed Nov 29, 2022
    Copy the full SHA
    f5ab529 View commit details
    Browse the repository at this point in the history
  3. resource/schema: Initial package

    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{/*...*/},
      },
    }
    ```
    bflad committed Nov 29, 2022
    Copy the full SHA
    782d611 View commit details
    Browse the repository at this point in the history
  4. Copy the full SHA
    6d9b1d8 View commit details
    Browse the repository at this point in the history
  5. Update CHANGELOG for #558

    bflad committed Nov 29, 2022
    Copy the full SHA
    6e00938 View commit details
    Browse the repository at this point in the history