diff --git a/website/docs/plugin/framework/acctests.mdx b/website/docs/plugin/framework/acctests.mdx index 2c89270a6..0ea910d7b 100644 --- a/website/docs/plugin/framework/acctests.mdx +++ b/website/docs/plugin/framework/acctests.mdx @@ -61,19 +61,18 @@ testing_new.go:53: no "id" found in attributes To avoid this, add a root level `id` attribute to resource and data source schemas. Ensure the attribute value is appropriately [written to state](/plugin/framework/writing-state). Conventionally, `id` is a computed attribute that contains the identifier for the resource. -For example, in the `GetSchema` method implementation of a [`datasource.DataSource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#DataSource) or [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource): +For example, in the `Schema` method implementation of a [`datasource.DataSource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#DataSource) or [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource): ```go -func (r *ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ // ... potentially other schema configuration ... - Attributes: map[string]tfsdk.Attribute{ + Attributes: map[string]schema.Attribute{ // ... potentially other schema attributes ... - "id": { - Type: types.StringType, + "id": schema.StringAttribute{ Computed: true, }, }, - }, nil + } } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index d057e2154..c07cf3dc6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -39,42 +39,24 @@ func resourceExample() *schema.Resource { ## Framework In the Framework, you define attributes by setting the `Attributes` field on your provider, resource, or data type's -schema, as returned by the `GetSchema` function. The `tfsdk.Schema` type returned by `GetSchema` includes an -`Attributes` field that maps each attribute name (string) to the attribute's `tfsdk.Attribute` struct. +schema, as returned by the `Schema` method. The `schema.Schema` type returned by `SchemaResponse` includes an +`Attributes` field that maps each attribute name (string) to the attribute's definition. The following code shows how to define an attribute for a resource with the Framework. ```go -func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "example": { - /* ... */ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example": /* ... */ ``` -The Framework defines the `tfsdk.Attribute` struct as follows. - -```go -type Attribute struct { - Type attr.Type - Attributes NestedAttributes - Description string - MarkdownDescription string - Required bool - Optional bool - Computed bool - Sensitive bool - DeprecationMessage string - Validators []AttributeValidator - PlanModifiers AttributePlanModifiers -} -``` ## Migration Notes Remember the following differences between SDKv2 and the Framework when completing the migration. - In SDKv2, attributes are defined by a map from attribute names to `schema.Schema` structs in the `Schema` field of -your resource's schema. In the Framework, attributes are defined by a map from attribute names to `tfsdk.Attribute` -structs in your resource's schema, which is returned by the `GetSchema` function. +your resource's schema. In the Framework, attributes are defined by a map from attribute names to `schema.Attribute` +implementations in your resource's schema, which is returned by the resource `Schema` method. - There are several differences between the implementation of attributes in SDKv2 and the Framework. Refer to the other pages in the Attributes & Blocks section of this migration guide for more details. @@ -115,13 +97,12 @@ The following shows the same section of provider code after the migration. This code implements the `url` attribute for the `http` data source with the Framework. ```go -func (d *httpDataSource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (d *httpDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + return schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "url": { + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ Description: "The URL for the request. Supported schemes are `http` and `https`.", - Type: types.StringType, Required: true, }, /* ... */ diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 64660497c..3a5110c29 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -36,41 +36,39 @@ map[string]*schema.Schema{ ## Framework -In the Framework, when working with protocol version 5, computed blocks are implemented using an attribute with a `Type` -of `types.ListType` which has an `ElemType` of `types.ObjectType`. +In the Framework, when working with protocol version 5, computed blocks are implemented using a `ListAttribute` which has an `ElementType` of `types.ObjectType`. ```go -map[string]tfsdk.Attribute{ -"example": { +map[string]schema.Attribute{ +"example": schema.ListAttribute{ Computed: true, - Type: types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_example": types.StringType, - /* ... */ + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_example": types.StringType, + /* ... */ ``` In the Framework, when working with protocol version 6, we recommend that you define computed blocks using nested -attributes. This example shows usage of `ListNestedAttributes` as this provides configuration references with list index -syntax as is the case when using `schema.TypeList` in SDKv2. `SingleNestedAttributes` is a good choice for single +attributes. This example shows usage of `ListNestedAttribute` as this provides configuration references with list index +syntax as is the case when using `schema.TypeList` in SDKv2. `SingleNestedAttribute` is a good choice for single underlying objects which results in a breaking change but also allows dropping `[0]` in configuration references. ```go -map[string]tfsdk.Attribute{ -"example": { - Computed: true, - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_example": { - Computed: true, - Type: types.StringType, - /* ... */ +map[string]schema.Attribute{ +"example": schema.ListNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_example": schema.StringAttribute{ + Computed: true, + /* ... */ ``` ## Migration Notes -- When using protocol version 5 specify `ElemType` as `types.ObjectType` when migrating blocks that are computed from SDKv2 to Framework. +- When using protocol version 5 specify `ElementType` as `types.ObjectType` when migrating blocks that are computed from SDKv2 to Framework. - When using protocol version 6, use [nested attributes](/plugin/framework/schemas#attributes-1) when migrating blocks that are computed from SDKv2 to Framework. ## Example @@ -111,12 +109,11 @@ This code defines the `certificates` block as an attribute on the `certificate` `types.ObjectType` is being used to define the nested block. ```go -map[string]tfsdk.Attribute{ - "certificates": { - Type: types.ListType{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "signature_algorithm": types.StringType, +map[string]schema.Attribute{ + "certificates": schema.ListAttribute{ + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "signature_algorithm": types.StringType, }, }, Computed: true, diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index ee39735bd..fd3d3f390 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -59,22 +59,20 @@ func resourceExample() *schema.Resource { ## Framework In the Framework, you implement nested blocks with the `Blocks` field of your provider, resource, or data source's -`tfsdk.Schema`, as returned by the `GetSchema` function. The `Blocks` field maps the name of each block to a -`tfsdk.Block` struct which defines the block's behavior. +schema, as returned by the `Schema` method. The `Blocks` field maps the name of each block to a +`schema.Block` definition. ```go -func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + return schema.Schema{ /* ... */ - Blocks: map[string]tfsdk.Block{ - "example": { - NestingMode: tfsdk.BlockNestingModeList, - MaxItems: int, - Attributes: map[string]tfsdk.Attribute{ - "nested_example": { - Type: types.StringType, - Optional: bool - /* ... */ + Blocks: map[string]schema.Block{ + "example": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "nested_example": schema.StringAttribute{ + Optional: bool + /* ... */ ``` @@ -122,24 +120,22 @@ The following example from the `resource_cert_request.go` file shows how the nes `cert_request` resource is defined with the Framework after the migration. ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "private_key_pem": { - Type: types.StringType, - /* ... */ - - Blocks: map[string]tfsdk.Block{ - "subject": { - NestingMode: tfsdk.BlockNestingModeList, - MinItems: 0, - MaxItems: 1, - Attributes: map[string]tfsdk.Attribute{ - "organization": { - Type: types.StringType, - /* ... */ - }, - "common_name": { - Type: types.StringType, - /* ... */ - }, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "private_key_pem": schema.StringAttribute{ + /* ... */ + + Blocks: map[string]schema.Block{ + "subject": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "organization": schema.StringAttribute{ + /* ... */ + }, + "common_name": schema.StringAttribute{ + /* ... */ + }, + Validators: validator.List{ + listvalidator.SizeAtMost(1), + }, ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index 724dc1387..a1ee6b465 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -39,16 +39,15 @@ func resourceExample() *schema.Resource { ## Framework -In the Framework, you set default values with the `PlanModifiers` field on your attribute's `tfsdk.Attribute` -struct. +In the Framework, you set default values with the `PlanModifiers` field on your attribute's definition. ```go -func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - PlanModifiers: []tfsdk.AttributePlanModifier{ + Attributes: map[string]schema.Attribute{ + "attribute_example": schema.BoolAttribute{ + PlanModifiers: []planmodifier.Bool{ defaultValue(types.BoolValue(true)), /* ... */ ``` @@ -93,12 +92,12 @@ The following shows the same section of code after the migration. This code implements the `PlanModifiers` field for the `rsa_bits` attribute with the Framework. ```go -func (r *privateKeyResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "rsa_bits": { - PlanModifiers: []tfsdk.AttributePlanModifier{ - attribute_plan_modifier.DefaultValue(types.Int64Value(2048)), +func (r *privateKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "rsa_bits": schema.Int64Attribute{ + PlanModifiers: []planmodifier.Int64{ + attribute_plan_modifier.Int64DefaultValue(types.Int64Value(2048)), /* ... */ }, /* ... */ @@ -113,36 +112,36 @@ The following example from the `attribute_plan_modifiers.go` file implements the that the `rsa_bits` attribute uses. ```go -func DefaultValue(v attr.Value) tfsdk.AttributePlanModifier { - return &defaultValueAttributePlanModifier{v} +func Int64DefaultValue(v types.Int64) planmodifier.Int64 { + return &int64DefaultValuePlanModifier{v} } -type defaultValueAttributePlanModifier struct { - DefaultValue attr.Value +type int64DefaultValuePlanModifier struct { + DefaultValue types.Int64 } -var _ tfsdk.AttributePlanModifier = (*defaultValueAttributePlanModifier)(nil) +var _ planmodifier.Int64 = (*int64DefaultValuePlanModifier)(nil) -func (apm *defaultValueAttributePlanModifier) Description(ctx context.Context) string { +func (apm *int64DefaultValuePlanModifier) Description(ctx context.Context) string { /* ... */ } -func (apm *defaultValueAttributePlanModifier) MarkdownDescription(ctx context.Context) string { +func (apm *int64DefaultValuePlanModifier) MarkdownDescription(ctx context.Context) string { /* ... */ } -func (apm *defaultValueAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) { +func (apm *int64DefaultValuePlanModifier) PlanModifyInt64(ctx context.Context, req planmodifier.Int64Request, res *planmodifier.Int64Response) { // If the attribute configuration is not null, we are done here - if !req.AttributeConfig.IsNull() { + if !req.ConfigValue.IsNull() { return } // If the attribute plan is "known" and "not null", then a previous plan modifier in the sequence // has already been applied, and we don't want to interfere. - if !req.AttributePlan.IsUnknown() && !req.AttributePlan.IsNull() { + if !req.PlanValue.IsUnknown() && !req.PlanValue.IsNull() { return } - res.AttributePlan = apm.DefaultValue + res.PlanValue = apm.DefaultValue } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index 9ddcef1f1..a5f22c0e2 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -33,14 +33,14 @@ func resourceExample() *schema.Resource { ## Framework -In the Framework, you set the same fields on the `tfsdk.Attribute` struct, with the same behavior. +In the Framework, you set the same fields on the `schema.Attribute` implementation, with the same behavior. ```go -func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { + Attributes: map[string]schema.Attribute{ + "attribute_example": schema.XXXAttribute{ Required: bool Optional: bool Computed: bool @@ -81,10 +81,10 @@ The following example from the `data_source_http.go` file shows how the `url` at to be required with the Framework. ```go -func (d *httpDataSource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "url": { +func (d *httpDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ Required: true, /* ... */ }, diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index 228bb1723..592586cec 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -34,16 +34,16 @@ func resourceExample() *schema.Resource { ## Framework In the Framework, you implement the same behavior by using the `resource.RequiresReplace` plan modifier on your -attribute's `tfsdk.Attribute` struct. +attribute's `schema.Attribute` implementation. ```go -func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + return schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), + Attributes: map[string]schema.Attribute{ + "attribute_example": schema.StringAttribute{ + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), /* ... */ ``` @@ -96,17 +96,17 @@ This code forces the replacement of a `random_password` resource when the value The example does this using the `PlanModifiers` field within the `random_password` attribute's schema. ```go -func (r *passwordResource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "keepers": { - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), +func (r *passwordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "keepers": schema.MapAttribute{ + PlanModifiers: []planmodifier.Map{ + mapplanmodifier.RequiresReplace(), }, /* ... */ }, /* ... */ }, - }, nil + } } ``` diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 0f7b07cff..9c0863753 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -9,8 +9,7 @@ description: >- -> **Note:** The Plugin Framework is in beta. An attribute either contains a primitive type, such as an integer or a string, or contains other attributes. Attributes -that contain other attributes are referred to as nested attributes, and are implemented by populating the -`NestedAttributes` field on the `tfsdk.Attribute` struct. Refer to +that contain other attributes are referred to as nested attributes. Refer to [Schemas - Attributes](/plugin/framework/schemas#type) in the Framework documentation for details. This page explains how to migrate a primitive attribute from SDKv2 to the plugin Framework. For an example of @@ -67,39 +66,35 @@ func resourceExample() *schema.Resource { ``` ## Framework -In the Framework, you set your attribute's type with the `Type` field on your attribute's `tfsdk.Attribute` struct. +In the Framework, you set your attribute's type with the attribute's `schema.Attribute` implementation. ```go -func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schea.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "bool_example": { - Type: types.BoolType, + Attributes: map[string]schema.Attribute{ + "bool_example": schema.BoolAttribute{ /* ... */ }, - "float64_example": { - Type: types.Float64Type, + "float64_example": schema.Float64Attribute{ /* ... */ }, - "int64_example": { - Type: types.Int64Type, + "int64_example": schema.Int64Attribute{ /* ... */ }, - "list_example": { - Type: types.ListType{ElemType: types.BoolType}, + "list_example": schema.ListAttribute{ + ElementType: types.BoolType, /* ... */ }, - "map_example": { - Type: types.MapType{ElemType: types.Float64Type}, + "map_example": schema.MapAttribute{ + ElementType: types.Float64Type, /* ... */ }, - "set_example": { - Type: types.SetType{ElemType: types.Int64Type}, + "set_example": schema.SetAttribute{ + ElementType: types.Int64Type, /* ... */ }, - "string_example": { - Type: types.StringType, + "string_example": schema.StringAttribute{ /* ... */ }, /* ... */ @@ -109,11 +104,7 @@ func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnos Remember the following differences between SDKv2 and the Framework when completing the migration. -- In the Framework, an `Attribute` struct has either the `Type` or `Attributes` field defined. The `Type` field is set to -a primitive type such as an integer or string, and you use the `Attributes` field for `NestedAttributes`. Refer to -[Providers](/plugin/framework/migrating/providers#example-1) for an example of using a single -nested attribute. Nested attributes are also described in more detail on the -[Schemas](/plugin/framework/schemas#attributes-1) page in the Framework documentation. +- In the Framework, the `schema.Attribute` implementation determines the required details. ## Example @@ -148,11 +139,10 @@ The following example from the `data_source_http.go` file shows how the type of source is defined with the Framework after the migration. ```go -func (d *httpDataSource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "url": { - Type: types.StringType, +func (d *httpDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ /* ... */ }, /* ... */ @@ -192,13 +182,11 @@ The following example from the `data_source_http.go` file shows how the type of source is defined with the Framework after the migration. ```go -func (r *certRequestResource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "dns_names": { - Type: types.ListType{ - ElemType: types.StringType - }, +func (r *certRequestResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "dns_names": schema.ListAttribute{ + ElementType: types.StringType, /* ... */ }, /* ... */ diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 419b35722..d0d85be17 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -35,19 +35,19 @@ func resourceExample() *schema.Resource { ## Framework -In the Framework, you implement either type of validation by setting the `Validators` field on the `tfsdk.Attribute` -struct with types that satisfy the `tfsdk.AttributeValidator` interface. +In the Framework, you implement either type of validation by setting the `Validators` field on the `schema.Attribute` +implementation. The following example shows how to implement a validation that ensures that an integer attribute has a value greater than one. ```go -func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - Validators: []tfsdk.AttributeValidator{ + Attributes: map[string]schema.Attribute{ + "attribute_example": schema.Int64Attribute{ + Validators: []validator.Int64{ int64validator.AtLeast(1), /* ... */ ``` @@ -57,7 +57,7 @@ func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Dia Remember the following details when migrating from SDKv2 to the Framework. - In SDKv2, `ValidateDiagFunc` is a field on `schema.Schema` that you can use to define custom validation functions. In -the Framework, `Validators` is a field on each `tfsdk.Attribute` struct that can be used for custom validations. +the Framework, `Validators` is a field on each `schema.Attribute` implementation that can be used for custom validations. - Use [predefined validators](/plugin/framework/migrating/attributes-blocks/validators-predefined) when there is a validator that meets your requirements. @@ -96,16 +96,17 @@ This code validates that the `random_password`'s `length` attribute is greater t validator. ```go -func (r *passwordResource) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "length": { - Validators: []tfsdk.AttributeValidator{ +func (r *passwordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "length": schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ int64validator.AtLeast(1), }, }, }, - }, nil + } } ``` @@ -114,7 +115,7 @@ This example code is taken from to illustrate how you can implement your own validators. ```go -var _ tfsdk.AttributeValidator = atLeastValidator{} +var _ validator.Int64 = atLeastValidator{} // atLeastValidator validates that an integer Attribute's value is at least a certain value. type atLeastValidator struct { @@ -122,31 +123,23 @@ type atLeastValidator struct { } // Description describes the validation in plain text formatting. -func (validator atLeastValidator) Description(_ context.Context) string { +func (v atLeastValidator) Description(_ context.Context) string { return fmt.Sprintf("value must be at least %d", validator.min) } // MarkdownDescription describes the validation in Markdown formatting. -func (validator atLeastValidator) MarkdownDescription(ctx context.Context) string { +func (v atLeastValidator) MarkdownDescription(ctx context.Context) string { return validator.Description(ctx) } // Validate performs the validation. -func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) { - i, ok := validateInt(ctx, request, response) - - if !ok { - return - } - - if i < validator.min { +func (v atLeastValidator) ValidateInt64(ctx context.Context, request validator.Int64Request, response *validator.Int64Response) { + if req.ConfigValue.Int64Value() < validator.min { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.AttributePath, - validator.Description(ctx), - fmt.Sprintf("%d", i), + request.Path, + v.Description(ctx), + fmt.Sprintf("%d", req.ConfigValue.Int64Value()), )) - - return } } @@ -157,7 +150,7 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va // - Is exclusively greater than the given minimum. // // Null (unconfigured) and unknown (known after apply) values are skipped. -func AtLeast(min int64) tfsdk.AttributeValidator { +func AtLeast(min int64) validator.Int64 { return atLeastValidator{ min: min, } diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index efa5245c9..1a98af6e0 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -36,21 +36,21 @@ func resourceExample() *schema.Resource { ``` ## Framework -In the Framework, you implement either type of validation by setting the `Validators` field on the `tfsdk.Attribute` -struct with types that satisfy the `tfsdk.AttributeValidator` interface. Validators that perform the same checks as the +In the Framework, you implement either type of validation by setting the `Validators` field on the `schema.Attribute` +implementation. Validators that perform the same checks as the predefined validators in SDKv2 are [available for the Framework](https://github.com/hashicorp/terraform-plugin-framework-validators). If the predefined validators do not meet your needs, you must define [custom validators](/plugin/framework/migrating/attributes-blocks/validators-custom). ```go -func (d *resourceTypeExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ - "attribute_example": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith( /* ... */ ), + Attributes: map[string]schema.Attribute{ + "attribute_example": schema.StringAttribute{ + Validators: []validator.String{ + stringvalidator.ConflictsWith( /* ... */ ), /* ... */ ``` @@ -76,13 +76,10 @@ Remember the following details when migrating from SDKv2 to the Framework. - In SDKv2, `ValidateDiagFunc` is a field on `schema.Schema` that you can use to define validation functions. In SDKv2, there are also built-in validations. For example, `ConflictsWith` is a field on the `schema.Schema` struct in SDKv2. In -the Framework, `Validators` is a field on each `tfsdk.Attribute` struct. +the Framework, `Validators` is a field on each `schema.Attribute` implementation. - Validators replicating the behavior of `ConflictsWith`, `ExactlyOneOf`, `AtLeastOneOf`, and `RequiredWith` in SDKv2 are -available for the Framework: - - [ConflictsWith](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#ConflictsWith) - - [ExactlyOneOf](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#ExactlyOneOf) - - [AtLeastOneOf](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#AtLeastOneOf) - - [AlsoRequires](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator#AlsoRequires) +available for the Framework in each of the type-specific packages of +[terraform-plugin-framework-validators](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators). - Define [custom validators](/plugin/framework/migrating/attributes-blocks/validators-custom) when the predefined validators do not meet your requirements. @@ -142,41 +139,43 @@ This code implements the `ConflictsWith` and `AlsoRequires` validators with the via the `Validators` field of the provider's `proxy` block's attribute schema. ```go -func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "proxy": { - Attributes: map[string]tfsdk.Attribute{ - "url": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), +func (p *TlsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Blocks: map[string]schema.Block{ + "proxy": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + }, + /* ... */ }, - /* ... */ - }, - "username": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), + "username": schema.StringAttribute{ + Validators: []validator.String{ + stringvalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")), + }, + /* ... */ }, - /* ... */ - }, - "password": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), + "password": schema.StringAttribute{ + Validators: []validator.String{ + stringvalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")), + }, + /* ... */ }, - /* ... */ - }, - "from_env": { - Validators: []tfsdk.AttributeValidator{ - schemavalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("url"), - path.MatchRelative().AtParent().AtName("username"), - path.MatchRelative().AtParent().AtName("password"), - ), + "from_env": schema.BoolAttribute{ + Validators: []validator.Bool{ + boolvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("url"), + path.MatchRelative().AtParent().AtName("username"), + path.MatchRelative().AtParent().AtName("password"), + ), + }, + /* ... */ }, - /* ... */ }, }, }, - }, nil + } } ``` diff --git a/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx b/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx index 583006db1..8a47edfad 100644 --- a/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx @@ -69,7 +69,7 @@ You can use this module to mutate the `schema.Schema` as follows: ```go func (d *ThingDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: map[string]tfsdk.Attribute{ + Attributes: map[string]schema.Attribute{ /* ... */ "timeouts": timeouts.Attributes(ctx, timeouts.Opts{ Read: true, diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index 010c11a51..f11e28fee 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -150,21 +150,21 @@ Refer to the [Resources](/plugin/framework/migrating/resources) and provider. ```go -type provider struct { +type ExampleCloudProvider struct { } func New() provider.Provider { - return &provider{} + return &ExampleCloudProvider{} } -func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{} } -func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { +func (p *ExampleCloudProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { } -func (p *provider) Resources(ctx context.Context) []func() resource.Resource { +func (p *ExampleCloudProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource { func() resource.Resource { return resourceExample{}, @@ -172,7 +172,7 @@ func (p *provider) Resources(ctx context.Context) []func() resource.Resource { } } -func (p *provider) GetDataSources(ctx context.Context) []func() datasource.DataSource { +func (p *ExampleCloudProvider) GetDataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource { func() datasource.DataSource { return dataSourceExample{}, @@ -188,8 +188,8 @@ Remember the following differences between SDKv2 and the Framework when completi - In SDKv2, your provider's `New` function returns a `schema.Provider` struct. In the Framework, `New` returns a type that you define which satisfies the `provider.Provider` interface. - In SDKv2, `Schema` is a field on `schema.Provider` that contains `map[string]*schema.Schema`, which maps attribute -names to `schema.Schema` structs. In the Framework, `GetSchema` is a function you define on your provider's -`provider.Provider` interface that returns your provider's `tfsdk.Schema` struct. +names to `schema.Schema` structs. In the Framework, `Schema` is a method you define on your provider's +`provider.Provider` interface that returns your provider's `schema.Schema` struct. - In SDKv2, `ConfigureContextFunc` is a field on `schema.Provider` containing a function that configures the provider. In the Framework, `Configure` is a function you define on your provider that configures your provider. - In SDKv2, `ResourcesMap` is a field on `schema.Provider` containing `map[string]*schema.Resource`, which maps resource @@ -241,13 +241,13 @@ func New() (*schema.Provider, error) { The following shows the same section of provider code after the migration. ```go -var _ provider.Provider = (*provider)(nil) +var _ provider.Provider = (*TlsProvider)(nil) func New() provider.Provider { - return &provider{} + return &TlsProvider{} } -func (p *provider) Resources(_ context.Context) []func() resource.Resource { +func (p *TlsProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ func() resource.Resource { return &privateKeyResource{} @@ -256,7 +256,7 @@ func (p *provider) Resources(_ context.Context) []func() resource.Resource { } } -func (p *provider) DataSources(_ context.Context) []func() datasource.DataSource { +func (p *TlsProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ func() datasource.DataSource { return &publicKeyDataSource{}, @@ -265,17 +265,17 @@ func (p *provider) DataSources(_ context.Context) []func() datasource.DataSource } } -func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "proxy": { +func (p *TlsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "proxy": schema.SingleNestedBlock{ /* ... */ }, }, - }, nil + } } -func (p *provider) Configure(ctx context.Context, req provider.ConfigureRequest, res *provider.ConfigureResponse) { +func (p *TlsProvider) Configure(ctx context.Context, req provider.ConfigureRequest, res *provider.ConfigureResponse) { /* ... */ } ``` @@ -303,19 +303,19 @@ func New() *schema.Provider { ### Framework -In the Framework, the `GetSchema` function returns the provider schema. The `GetSchema` function is part of the -`provider.Provider` interface that your provider must implement. `GetSchema` returns a struct containing fields for -`Attributes` and `Blocks`. These `Attributes` and `Blocks` contain `map[string]tfsdk.Attribute` and -`map[string]tfsdk.Block`, respectively. Refer to [Providers - GetSchema](/plugin/framework/providers#getschema) in the +In the Framework, the `Schema` method returns the provider schema. The `Schema` method is part of the +`provider.Provider` interface that your provider must implement. `Schema` returns a struct containing fields for +`Attributes` and `Blocks`. These `Attributes` and `Blocks` contain `map[string]schema.Attribute` and +`map[string]schema.Block`, respectively. Refer to [Providers - Schema](/plugin/framework/providers#schema) in the Framework documentation for details. -The following code shows the `GetSchema` function, which returns the provider schema. +The following code shows the `Schema` method, which returns the provider schema. ```go -func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - }, nil + } } ``` @@ -328,8 +328,7 @@ those fields to the Framework. Remember the following differences between SDKv2 and the Framework when completing the migration. - In SDKv2, `schema.Schema` is a struct that defines attributes and behaviors (e.g., `Type`, `Optional`). In the -Framework `tfsdk.Schema` is a struct that includes `tfsdk.Attributes`, which are structs that define attributes and behaviors -(e.g., `Type`, `Optional`). +Framework `schema.Schema` is a struct that includes attributes and blocks. ### Example @@ -376,18 +375,17 @@ The following shows the same section of provider code after the migration. This code implements the `url` attribute for the `proxy` block with the Framework. ```go -func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "proxy": { +func (p *TlsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "proxy": schema.SingleNestedBlock{ Optional: true, - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "url": { - Type: types.StringType, + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ Optional: true, - Validators: []tfsdk.AttributeValidator{ + Validators: []validator.String{ attribute_validator.UrlWithScheme(supportedProxySchemesStr()...), - schemavalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")), }, MarkdownDescription: "URL used to connect to the Proxy. " + fmt.Sprintf("Accepted schemes are: `%s`. ", strings.Join(supportedProxySchemesStr(), "`, `")), diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index faf8d8e20..eb5ff8b48 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -57,7 +57,7 @@ Remember the following differences between SDKv2 and the Framework when completi functions can only access the value (e.g., the resource's `ID`) supplied to the `terraform import` command. - In SDKv2, you implement resource importing by populating the `Importer` field on the `schema.Resource` struct. In the Framework, you define an `ImportState` function on the type which implements `resource.Resource`. This implementation -satisfies the `tfsdk.ResourceWithImportState` interface. +satisfies the `resource.ResourceWithImportState` interface. ## Example diff --git a/website/docs/plugin/framework/migrating/resources/index.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx index 0f6589ddc..055e69d4b 100644 --- a/website/docs/plugin/framework/migrating/resources/index.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -92,10 +92,9 @@ func (p *provider) Resources(ctx context.Context) []func() resource.Resource { } ``` -The `resource.Resource` interface requires `Metadata`, `GetSchema`, `Create`, `Read`, `Update`, and `Delete` methods. +The `resource.Resource` interface requires `Metadata`, `Schema`, `Create`, `Read`, `Update`, and `Delete` methods. -The `GetSchema` method returns a `tfsdk.Schema` struct which defines your resource's attributes. This is the same -struct you use to define provider and data source attributes. +The `Schema` method returns a `schema.Schema` struct which defines your resource's attributes. The `Metadata` method returns a type name that you define. @@ -109,7 +108,7 @@ func (r *resourceExample) Metadata(ctx context.Context, req resource.MetadataReq /* ... */ } -func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { +func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { /* ... */ } @@ -130,20 +129,6 @@ func (r *resourceExample) Delete(ctx context.Context, req resource.DeleteRequest } ``` -The `tfsdk.Schema` is used for defining the schema for providers, resources and data sources. The Framework defines the -`tfsdk.Schema` struct as follows. - -```go -type Schema struct { - Attributes map[string]Attribute - Blocks map[string]Block - Version int64 - DeprecationMessage string - Description string - MarkdownDescription string -} -``` - Refer to the [Resources - CRUD functions](/plugin/framework/migrating/resources/crud) page in this guide to learn how to implement the `resource.Resource` interface. @@ -153,7 +138,7 @@ Remember the following differences between SDKv2 and the Framework when completi - SDKv2 uses `schema.Resource` structs to define resources. These structs have a `Schema` field which holds a `schema.Schema` to define the resource's attributes and behavior. In the Framework, you define a type that implements -the `resource.Resource` interface, which includes a `GetSchema` method that returns your resource's schema. +the `resource.Resource` interface, which includes a `Schema` method that returns your resource's schema. - SDKv2 implements a resource's CRUD operations as functions on the `schema.Resource`. In the Framework, you define a type that implements the `resource.Resource` interface. The resource interface contains the functions that define your resource's CRUD operations. @@ -213,7 +198,7 @@ The following shows the same section of provider code after the migration. This code defines the `tls_private_key` resource by mapping the resource name to the `privateKeyResourceType` struct. ```go -func (p *provider) Resources(_ context.Context) []func() resource.Resource { +func (p *TlsProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ func() resource.Resource { return &privateKeyResource{} @@ -223,31 +208,30 @@ func (p *provider) Resources(_ context.Context) []func() resource.Resource { } ``` -This code defines the `GetSchema` and `Metadata` methods for the `privateKeyResource`. +This code defines the `Schema` and `Metadata` methods for the `privateKeyResource`. ```go func (r *privateKeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = "tls_private_key" } -func (r *privateKeyResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ +func (r *privateKeyResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ // Required attributes - "algorithm": { - Type: types.StringType, + "algorithm": schema.StringAttribute{ Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, - Validators: []tfsdk.AttributeValidator{ - attribute_validation.OneOf(supportedAlgorithmsAttrValue()...), + Validators: []validator.String{ + stringvalidator.OneOf(supportedAlgorithmsAttrValue()...), }, Description: "Name of the algorithm to use when generating the private key. " + fmt.Sprintf("Currently-supported values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")), }, /* ... */ }, - }, nil + } } ``` diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index 23a238b68..b418980ce 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -108,22 +108,22 @@ Many existing `CustomizeDiff` implementations would be better suited to migratio Framework. This code shows the implementation using attribute plan modifiers with the Framework. ```go -func passwordSchemaV2() tfsdk.Schema { - return tfsdk.Schema{ +func passwordSchemaV2() schema.Schema { + return schema.Schema{ /* ... */ - Attributes: map[string]tfsdk.Attribute{ + Attributes: map[string]schema.Attribute{ /* ... */ - "number": { + "number": schema.BoolAttribute{ /* ... */ - PlanModifiers: []tfsdk.AttributePlanModifier{ + PlanModifiers: []planmodifier.Bool{ planmodifiers.NumberNumericAttributePlanModifier(), /* ... */ }, }, - "numeric": { + "numeric": schema.BoolAttribute{ /* ... */ - PlanModifiers: []tfsdk.AttributePlanModifier{ + PlanModifiers: []planmodifier.Bool{ planmodifiers.NumberNumericAttributePlanModifier(), /* ... */ }, @@ -133,7 +133,7 @@ func passwordSchemaV2() tfsdk.Schema { The following shows an implementation of `NumberNumericAttributePlanModifier` in the Framework. ```go -func NumberNumericAttributePlanModifier() tfsdk.AttributePlanModifier { +func NumberNumericAttributePlanModifier() planmodifier.Bool { return &numberNumericAttributePlanModifier{} } @@ -148,7 +148,7 @@ func (d *numberNumericAttributePlanModifier) MarkdownDescription(ctx context.Con return d.Description(ctx) } -func (d *numberNumericAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { +func (d *numberNumericAttributePlanModifier) PlanModifyBool(ctx context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) { var numberConfig types.Bool diags := req.Config.GetAttribute(ctx, path.Root("number"), &numberConfig) resp.Diagnostics.Append(diags...) @@ -173,19 +173,19 @@ func (d *numberNumericAttributePlanModifier) Modify(ctx context.Context, req tfs // Default to true for both number and numeric when both are null. if numberConfig.IsNull() && numericConfig.IsNull() { - resp.AttributePlan = types.BoolValue(true) + resp.PlanValue = types.BoolValue(true) return } // Default to using value for numeric if number is null if numberConfig.IsNull() && !numericConfig.IsNull() { - resp.AttributePlan = numericConfig + resp.PlanValue = numericConfig return } // Default to using value for number if numeric is null if !numberConfig.IsNull() && numericConfig.IsNull() { - resp.AttributePlan = numberConfig + resp.PlanValue = numberConfig return } } diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 1e74df312..c73cad1be 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -45,7 +45,7 @@ The following code shows how you define an `UpgradeState` function with the Fram func (r *resourceExample) UpgradeState(context.Context) map[int64]resource.StateUpgrader { return map[int64]resource.StateUpgrader{ 0: { - PriorSchema: *tfsdk.Schema, + PriorSchema: *schema.Schema, StateUpgrader: func(context.Context, UpgradeStateRequest, *UpgradeStateResponse), }, /* ... */ diff --git a/website/docs/plugin/framework/migrating/resources/timeouts.mdx b/website/docs/plugin/framework/migrating/resources/timeouts.mdx index 6966df1b0..eadc4204a 100644 --- a/website/docs/plugin/framework/migrating/resources/timeouts.mdx +++ b/website/docs/plugin/framework/migrating/resources/timeouts.mdx @@ -36,14 +36,14 @@ resource "timeouts_example" "example" { } ``` -You can use this module to mutate the `tfsdk.Schema` as follows: +You can use this module to mutate the `schema.Schema` as follows: ```go -func (t *exampleResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Blocks: map[string]tfsdk.Block{ + Blocks: map[string]schema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{ Create: true, }), @@ -64,12 +64,12 @@ resource "timeouts_example" "example" { } ``` -You can use this module to mutate the `tfsdk.Schema` as follows: +You can use this module to mutate the `schema.Schema` as follows: ```go -func (t *exampleResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ +func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ /* ... */ "timeouts": timeouts.Attributes(ctx, timeouts.Opts{ Create: true, diff --git a/website/docs/plugin/framework/migrating/schema/index.mdx b/website/docs/plugin/framework/migrating/schema/index.mdx index b69e7a75e..541ccbd39 100644 --- a/website/docs/plugin/framework/migrating/schema/index.mdx +++ b/website/docs/plugin/framework/migrating/schema/index.mdx @@ -29,8 +29,8 @@ SDKv2 uses `schema.Schema` structs to define the structure, type, and behavior o state, or plan data. The same `schema.Schema` struct type is used for providers, resources, and data sources. The schema struct is returned by the function that creates the provider, resource, or data source in question. -The Framework uses `tfsdk.Schema` structs for providers, resources, and data sources. The schema struct is returned by -a `GetSchema` function you define for the provider and each resource type and data source type. Refer to +The Framework uses `schema.Schema` structs for providers, resources, and data sources. The schema struct is returned by +a `Schema` method you define for the provider and each resource type and data source type. Refer to [Framework](#framework) for details. ## SDKv2 @@ -98,39 +98,26 @@ type Schema struct { ## Framework -In the Framework, you implement `GetSchema` method for your provider, resources, and data sources. This function is +In the Framework, you implement `Schema` method for your provider, resources, and data sources. This function is required by the `provider.Provider`, `resource.Resource`, and `datasource.DataSource` interfaces, respectively. -The following code shows how you define the `GetSchema` function for your provider, resources, and data sources. +The following code shows how you define the `Schema` method for your provider, resources, and data sources. ```go -func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{/* ... */} } ``` ```go -func (r *resourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil +func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{/* ... */} } ``` ```go -func (r *dataSourceExample) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{}, nil -} -``` - -The Framework defines the `schema.Schema` struct as follows. - -```go -type Schema struct { - Attributes map[string]Attribute - Blocks map[string]Block - Version int64 - DeprecationMessage string - Description string - MarkdownDescription string +func (r *dataSourceExample) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{/* ... */} } ``` @@ -141,15 +128,15 @@ You use the `Attributes` field to define attributes for your provider, resources Remember the following differences between SDKv2 and the Framework when completing the migration. -- SDKv2 uses `schema.Schema` structs to define the provider, resources, and data sources. The Framework uses -`tfsdk.Schema` structs instead. +- SDKv2 uses `schema.Schema` structs to define the provider, resources, and data sources. The Framework uses concept-specific +`schema.Schema` structs instead. - In SDKv2, schema structs are returned when a provider, resource, or data type is created. In the Framework, the -provider and each resource and data type have a `GetSchema` method that returns the schema. +provider and each resource and data type have a `Schema` method that returns the schema. - In SDKv2, schema structs have a `Set` field which can be populated with a `SchemaSetFunc` which is used for hashing. In the Framework, this is not required and does not need to be migrated. -- The `tfsdk.Schema` struct includes fields that you use to define +- The `schema.Schema` struct includes fields that you use to define [attributes](/plugin/framework/migrating/attributes-and-blocks/attribute-schema) and [blocks](/plugin/framework/migrating/attributes-and-blocks/blocks) for your provider and each resource and data source. -- When you populate the `Version` field in `tfsdk.Schema` for a resource in the Framework, copy the `Version` +- When you populate the `Version` field in `schema.Schema` for a resource in the Framework, copy the `Version` field in `schema.Schema` from the SDKv2 version of that resource. diff --git a/website/docs/plugin/framework/path-expressions.mdx b/website/docs/plugin/framework/path-expressions.mdx index 09883d426..83b1bd1fe 100644 --- a/website/docs/plugin/framework/path-expressions.mdx +++ b/website/docs/plugin/framework/path-expressions.mdx @@ -47,11 +47,10 @@ Call the [`path.MatchRoot()` function](https://pkg.go.dev/github.com/hashicorp/t Given this example schema with a root attribute named `example_root_attribute`: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "example_root_attribute": { +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_root_attribute": schema.StringAttribute{ Required: true, - Type: types.StringType, }, }, } @@ -68,11 +67,10 @@ For blocks, the beginning of a path expression is similarly defined. Attribute a Given this example schema with a root block named `example_root_block`: ```go -tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "example_root_block": { - Attributes: map[string]tfsdk.Attribute{/* ... */}, - NestingMode: tfsdk.BlockNestingModeList, +schema.Schema{ + Blocks: map[string]schema.Block{ + "example_root_block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{/* ... */}, }, }, } @@ -101,29 +99,31 @@ Relative path expressions are, by nature, contextual to the actual path where th This example shows a relative path expression which references a child attribute: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_list_attribute": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_list_attribute": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "deeply_nested_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_list_attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_list_attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "deeply_nested_string_attribute": schema.StringAttribute{ + Required: true, + }, + }, + }, + Required: true, + Validators: []validator.List{ + exampleValidatorThatAcceptsExpressions( + path.MatchRelative().AtAnyListIndex().AtName("deeply_nested_string_attribute"), + ), }, - }), - Required: true, - Validators: []tfsdk.AttributeValidator{ - exampleValidatorThatAcceptsExpressions( - path.MatchRelative().AtAnyListIndex().AtName("deeply_nested_string_attribute"), - ), + }, + "nested_string_attribute": schema.StringAttribute{ + Required: true, }, }, - "nested_string_attribute": { - Required: true, - Type: types.StringType, - }, - }), + }, Required: true, }, }, @@ -133,29 +133,31 @@ tfsdk.Schema{ This example shows a relative path expression which references a different attribute within the same list index: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_list_attribute": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_list_attribute": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "deeply_nested_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_list_attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_list_attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "deeply_nested_string_attribute": schema.StringAttribute{ + Required: true, + }, + }, + }, + Required: true, + Validators: []validator.List{ + exampleValidatorThatAcceptsExpressions( + path.MatchRelative().AtParent().AtName("nested_string_attribute"), + ), }, - }), - Required: true, - Validators: []tfsdk.AttributeValidator{ - exampleValidatorThatAcceptsExpressions( - path.MatchRelative().AtParent().AtName("nested_string_attribute"), - ), + }, + "nested_string_attribute": schema.StringAttribute{ + Required: true, }, }, - "nested_string_attribute": { - Required: true, - Type: types.StringType, - }, - }), + }, Required: true, }, }, diff --git a/website/docs/plugin/framework/paths.mdx b/website/docs/plugin/framework/paths.mdx index b76113f4a..fa9cd37e9 100644 --- a/website/docs/plugin/framework/paths.mdx +++ b/website/docs/plugin/framework/paths.mdx @@ -38,11 +38,10 @@ The framework implementation for paths is in the [`path` package](https://pkg.go Given this example schema with a root attribute named `example_root_attribute`: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "example_root_attribute": { +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_root_attribute": schema.StringAttribute{ Required: true, - Type: types.StringType, }, }, } @@ -59,11 +58,10 @@ For blocks, the beginning of a path is similarly defined. Attribute and block na Given this example schema with a root block named `example_root_block`: ```go -tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "example_root_block": { - Attributes: map[string]tfsdk.Attribute{/* ... */}, - NestingMode: tfsdk.BlockNestingModeList, +schema.Schema{ + Blocks: map[string]schema.Block{ + "example_root_block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{/* ... */}, }, }, } @@ -87,30 +85,28 @@ This pattern can be extended to as many calls as necessary. The different framew ### Building Attribute Paths -The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for [`tfsdk.Attribute` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Attribute) implementations using the `Type` field. Attribute types that cannot be traversed further are shown with N/A (not applicable). +The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for attribute implementations. Attribute types that cannot be traversed further are shown with N/A (not applicable). -| Attribute Type | Child Path Method | -| ------------------- | ----------------- | -| `types.BoolType` | N/A | -| `types.Float64Type` | N/A | -| `types.Int64Type` | N/A | -| `types.ListType` | `AtListIndex()` | -| `types.MapType` | `AtMapKey()` | -| `types.NumberType` | N/A | -| `types.ObjectType` | `AtName()` | -| `types.SetType` | `AtSetValue()` | -| `types.StringType` | N/A | +| Attribute Type | Child Path Method | +| ------------------------- | ----------------- | +| `schema.BoolAttribute` | N/A | +| `schema.Float64Attribute` | N/A | +| `schema.Int64Attribute` | N/A | +| `schema.ListAttribute` | `AtListIndex()` | +| `schema.MapAttribute` | `AtMapKey()` | +| `schema.NumberAttribute` | N/A | +| `schema.ObjectAttribute` | `AtName()` | +| `schema.SetAttribute` | `AtSetValue()` | +| `schema.StringAttribute` | N/A | Given following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_map_attribute": { +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_map_attribute": schema.MapAttribute{ + ElementType: types.StringType, Required: true, - Type: types.MapType{ - ElemType: types.StringType, - }, }, }, } @@ -127,15 +123,13 @@ Any type that supports a child path may define an element type that also support Given following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_map_attribute": { - Required: true, - Type: types.MapType{ - ElemType: types.ListType{ - ElemType: types.StringType, - }, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_map_attribute": schema.MapAttribute{ + ElementType: types.ListType{ + ElemType: types.StringType, }, + Required: true, }, }, } @@ -147,7 +141,7 @@ The path which matches the third string value associated with the list value of path.Root("root_map_attribute").AtMapKey("example-key").AtListIndex(2) ``` -Unless there is a very well defined data structure involved, the level of path specificity in the example above is fairly uncommon to manually build in provider logic though. Most provider logic will typically build a path to the value of an attribute (e.g. its first `Type`) and work with that data, or potentially one level deeper in well known cases, since each level introduces additional complexity associated with potentially null or unknown values. Provider logic can instead use an iterative path building approach when dealing with attributes that have multiple levels. +Unless there is a very well defined data structure involved, the level of path specificity in the example above is fairly uncommon to manually build in provider logic though. Most provider logic will typically build a path to the value of an attribute (e.g. its first type) and work with that data, or potentially one level deeper in well known cases, since each level introduces additional complexity associated with potentially null or unknown values. Provider logic can instead use an iterative path building approach when dealing with attributes that have multiple levels. This example shows an iterative path building approach to handle any map keys and list indices in the above schema: @@ -156,12 +150,12 @@ attributePath := path.Root("root_map_attribute") // attributeValue is an example types.Map value which was previously fetched, // potentially using the path above. -for mapKey, mapValue := range attributeValue.Value { +for mapKey, mapValue := range attributeValue.Elements() { mapKeyPath := attributePath.AtMapKey(mapKey) // ... - for listIndex, listValue := range mapValue.Value { + for listIndex, listValue := range mapValue.Elements() { listIndexPath := mapKeyPath.AtListIndex(listIndex) // ... @@ -171,33 +165,34 @@ for mapKey, mapValue := range attributeValue.Value { ### Building Nested Attribute Paths -The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for [`tfsdk.Attribute` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Attribute) implementations using the `Attributes` field ([`tfsdk.NestedAttibutes` interface](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#NestedAttributes) implementations). +The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for nested attributes. | Nested Attribute Type | Child Path Method | | ------------------------------ | ----------------- | -| `tfsdk.ListNestedAttributes` | `AtListIndex()` | -| `tfsdk.MapNestedAttributes` | `AtMapKey()` | -| `tfsdk.SetNestedAttributes` | `AtSetValue()` | -| `tfsdk.SingleNestedAttributes` | `AtName()` | +| `schema.ListNestedAttribute` | `AtListIndex()` | +| `schema.MapNestedAttribute` | `AtMapKey()` | +| `schema.SetNestedAttribute` | `AtSetValue()` | +| `schema.SingleNestedAttribute` | `AtName()` | Nested attributes eventually implement attributes at child paths, which follow the methods shown in the [Building Attribute Paths section](#building-attribute-paths). #### Building List Nested Attributes Paths -An attribute that implements `tfsdk.ListNestedAttributes` conceptually is a list containing objects with attribute names. +An attribute that implements `schema.ListNestedAttribute` conceptually is a list containing objects with attribute names. Given the following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_list_attribute": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_list_attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_string_attribute": schema.StringAttribute{ + Required: true, + }, }, - }), + }, Required: true, }, }, @@ -224,20 +219,21 @@ path.Root("root_list_attribute").AtListIndex(0).AtName("nested_string_attribute" #### Building Map Nested Attributes Paths -An attribute that implements `tfsdk.MapNestedAttributes` conceptually is a map containing values of objects with attribute names. +An attribute that implements `schema.MapNestedAttribute` conceptually is a map containing values of objects with attribute names. Given the following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_map_attribute": { - Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ - "nested_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_map_attribute": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_string_attribute": schema.StringAttribute{ + Required: true, + }, }, - }), + }, Required: true, }, }, @@ -264,20 +260,21 @@ path.Root("root_map_attribute").AtMapKey("example-key").AtName("nested_string_at #### Building Set Nested Attributes Paths -An attribute that implements `tfsdk.SetNestedAttributes` conceptually is a set containing objects with attribute names. Attempting to build set nested attribute paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports [path expressions](/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths. +An attribute that implements `schema.SetNestedAttribute` conceptually is a set containing objects with attribute names. Attempting to build set nested attribute paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports [path expressions](/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths. Given the following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_set_attribute": { - Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ - "nested_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_set_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested_string_attribute": schema.StringAttribute{ + Required: true, + }, }, - }), + }, Required: true, }, }, @@ -320,20 +317,19 @@ path.Root("root_set_attribute").AtSetValue(types.ObjectValueMust( #### Building Single Nested Attributes Paths -An attribute that implements `tfsdk.SingleNestedAttributes` conceptually is an object with attribute names. +An attribute that implements `schema.SingleNestedAttribute` conceptually is an object with attribute names. Given the following schema example: ```go -tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "root_grouped_attributes": { - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "nested_string_attribute": { +schema.Schema{ + Attributes: map[string]schema.Attribute{ + "root_grouped_attributes": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested_string_attribute": schema.StringAttribute{ Required: true, - Type: types.StringType, }, - }), + }, Required: true, }, }, @@ -354,13 +350,13 @@ path.Root("root_grouped_attributes").AtName("nested_string_attribute") ### Building Block Paths -The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for [`tfsdk.Block` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Block) implementations based on the `NestingMode` field. +The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for blocks. -| Block NestingMode | Child Path Method | -| ------------------------------ | ----------------- | -| `tfsdk.BlockNestingModeList` | `AtListIndex()` | -| `tfsdk.BlockNestingModeSet` | `AtSetValue()` | -| `tfsdk.BlockNestingModeSingle` | `AtName()` | +| Block Type | Child Path Method | +| -------------------------- | ----------------- | +| `schema.ListNestedBlock` | `AtListIndex()` | +| `schema.SetNestedBlock` | `AtSetValue()` | +| `schema.SingleNestedBlock` | `AtName()` | Blocks can implement nested blocks. Paths can continue to be built using the associated method with each level of the block type. @@ -368,32 +364,30 @@ Blocks eventually implement attributes at child paths, which follow the methods #### Building List Block Paths -A block defined with `tfsdk.BlockNestingModeList` conceptually is a list containing objects with attribute or block names. +A block defined with `schema.ListNestedBlock` conceptually is a list containing objects with attribute or block names. Given following schema example: ```go -tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "root_list_block": { - Attributes: map[string]tfsdk.Attribute{ - "block_string_attribute": { - Required: true, - Type: types.StringType, +schema.Schema{ + Blocks: map[string]schema.Block{ + "root_list_block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "block_string_attribute": schema.StringAttribute{ + Required: true, + }, }, - }, - Blocks: map[string]tfsdk.Block{ - "nested_list_block": { - Attributes: map[string]tfsdk.Attribute{ - "nested_block_string_attribute": { - Required: true, - Type: types.StringType, + Blocks: map[string]schema.Block{ + "nested_list_block": schema.ListNestedBlock{ + Attributes: map[string]schema.Attribute{ + "nested_block_string_attribute": schema.StringAttribute{ + Required: true, + }, }, }, - NestingMode: tfsdk.BlockNestingModeList, }, }, - NestingMode: tfsdk.BlockNestingModeList, }, }, } @@ -431,21 +425,21 @@ path.Root("root_list_block").AtListIndex(0).AtName("nested_list_block").AtListIn #### Building Set Block Paths -A block defined with `tfsdk.BlockNestingModeSet` conceptually is a set containing objects with attribute or block names. Attempting to build set block paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports [path expressions](/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths. +A block defined with `schema.SetNestedBlock` conceptually is a set containing objects with attribute or block names. Attempting to build set block paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports [path expressions](/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths. Given following schema example: ```go -tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "root_set_block": { - Attributes: map[string]tfsdk.Attribute{ - "block_string_attribute": { - Optional: true, - Type: types.StringType, +schema.Schema{ + Blocks: map[string]schema.Block{ + "root_set_block": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "block_string_attribute": schema.StringAttribute{ + Optional: true, + }, }, }, - NestingMode: tfsdk.BlockNestingModeSet, }, }, } @@ -487,32 +481,28 @@ path.Root("root_set_block").AtSetValue(types.ObjectValueMust( #### Building Single Block Paths -A block defined with `tfsdk.BlockNestingModeSingle` conceptually is an object with attribute or block names. +A block defined with `schema.SingleNestedBlock` conceptually is an object with attribute or block names. Given following schema example: ```go -tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "root_single_block": { - Attributes: map[string]tfsdk.Attribute{ - "block_string_attribute": { +schema.Schema{ + Blocks: map[string]schema.Block{ + "root_single_block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "block_string_attribute": schema.StringAttribute{ Required: true, - Type: types.StringType, }, }, - Blocks: map[string]tfsdk.Block{ - "nested_single_block": { - Attributes: map[string]tfsdk.Attribute{ - "nested_block_string_attribute": { + Blocks: map[string]schema.Block{ + "nested_single_block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "nested_block_string_attribute": schema.StringAttribute{ Required: true, - Type: types.StringType, }, }, - NestingMode: tfsdk.BlockNestingModeSingle, }, }, - NestingMode: tfsdk.BlockNestingModeSingle, }, }, } diff --git a/website/docs/plugin/framework/providers/index.mdx b/website/docs/plugin/framework/providers/index.mdx index e72feb6c6..9ed74ab46 100644 --- a/website/docs/plugin/framework/providers/index.mdx +++ b/website/docs/plugin/framework/providers/index.mdx @@ -36,12 +36,12 @@ type ExampleCloudProvider struct{ } // GetSchema satisfies the provider.Provider interface for exampleProvider. -func (p *ExampleCloudProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ // Provider specific implementation. }, - }, nil + } } // Configure satisfies the provider.Provider interface for ExampleCloudProvider. @@ -78,7 +78,7 @@ func New(version string) func() provider.Provider { ### GetSchema Method -The [`provider.Provider` interface `GetSchema` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.GetSchema) defines a [schema](/plugin/framework/schemas) describing what data is available in the provider's configuration. This configuration block is used to offer practitioners the opportunity to supply values to the provider and configure its behavior, rather than needing to include those values in every resource and data source. It is usually used to gather credentials, endpoints, and the other data used to authenticate with the API, but it is not limited to those uses. +The [`provider.Provider` interface `Schema` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.Schema) defines a [schema](/plugin/framework/schemas) describing what data is available in the provider's configuration. This configuration block is used to offer practitioners the opportunity to supply values to the provider and configure its behavior, rather than needing to include those values in every resource and data source. It is usually used to gather credentials, endpoints, and the other data used to authenticate with the API, but it is not limited to those uses. In this example, a sample configuration and schema definition are provided: @@ -90,23 +90,21 @@ In this example, a sample configuration and schema definition are provided: // endpoint = "https://example.com/" // } -func (p *ExampleCloudProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "api_token": { +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "api_token": schema.StringAttribute{ Optional: true, - Type: types.StringType, }, - "endpoint": { + "endpoint": schema.StringAttribute{ Optional: true, - Type: types.StringType, }, }, - }, nil + } } ``` -If the provider does not accept practitioner Terraform configuration, it must return an empty schema (`tfsdk.Schema{}`). +If the provider does not accept practitioner Terraform configuration, leave the method defined, but empty. ### Configure Method @@ -126,19 +124,17 @@ type ExampleCloudProviderModel struct { Endpoint types.String `tfsdk:"endpoint"` } -func (p *ExampleCloudProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "api_token": { +func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "api_token": schema.StringAttribute{ Optional: true, - Type: types.StringType, }, - "endpoint": { + "endpoint": schema.StringAttribute{ Optional: true, - Type: types.StringType, }, }, - }, nil + } } func (p *ExampleCloudProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { diff --git a/website/docs/plugin/framework/resources/create.mdx b/website/docs/plugin/framework/resources/create.mdx index 11ddd0bd7..47ead3521 100644 --- a/website/docs/plugin/framework/resources/create.mdx +++ b/website/docs/plugin/framework/resources/create.mdx @@ -51,25 +51,23 @@ type ThingResourceAPIModel struct { Id string `json:"id"` } -func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "name": { +func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaReponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ MarkdownDescription: "Name of the thing to be saved in the service.", Required: true, - Type: types.StringType, }, - "id": { + "id": schema.StringAttribute{ Computed: true, MarkdownDescription: "Service generated identifier for the thing.", - PlanModifiers: tfsdk.AttributePlanModifiers{ - resource.UseStateForUnknown(), + PlanModifiers: planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - Type: types.StringType, }, }, MarkdownDescription: "Manages a thing.", - }, nil + } } func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { diff --git a/website/docs/plugin/framework/resources/delete.mdx b/website/docs/plugin/framework/resources/delete.mdx index 77d8ce799..68334c055 100644 --- a/website/docs/plugin/framework/resources/delete.mdx +++ b/website/docs/plugin/framework/resources/delete.mdx @@ -52,25 +52,23 @@ type ThingResourceAPIModel struct { Id string `json:"id"` } -func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "name": { +func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ MarkdownDescription: "Name of the thing to be saved in the service.", Required: true, - Type: types.StringType, }, - "id": { + "id": schema.StringAttribute{ Computed: true, MarkdownDescription: "Service generated identifier for the thing.", - PlanModifiers: tfsdk.AttributePlanModifiers{ - resource.UseStateForUnknown(), + PlanModifiers: planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - Type: types.StringType, }, }, MarkdownDescription: "Manages a thing.", - }, nil + } } func (r ThingResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { diff --git a/website/docs/plugin/framework/resources/import.mdx b/website/docs/plugin/framework/resources/import.mdx index 19916b924..e1222e374 100644 --- a/website/docs/plugin/framework/resources/import.mdx +++ b/website/docs/plugin/framework/resources/import.mdx @@ -42,20 +42,18 @@ The `terraform import` command will need to accept the multiple attribute values In this example, the resource requires two attributes to refresh state and accepts them as an import identifier of `attr_one,attr_two`: ```go -func (r *ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "attr_one": { - Type: types.StringType, +func (r *ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "attr_one": schema.StringAttribute{ Required: true, }, - "attr_two": { - Type: types.StringType, + "attr_two": schema.StringAttribute{ Required: true, }, /* ... */ }, - }, nil + } } func (r *ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { diff --git a/website/docs/plugin/framework/resources/plan-modification.mdx b/website/docs/plugin/framework/resources/plan-modification.mdx index c2ad855fc..3eb458f44 100644 --- a/website/docs/plugin/framework/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/resources/plan-modification.mdx @@ -33,15 +33,15 @@ Refer to the [Resource Instance Change Lifecycle document](https://github.com/ha ## Attribute Plan Modification -You can supply the [`tfsdk.Attribute` type `PlanModifiers` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Attribute.PlanModifiers) with a list of plan modifiers for that attribute. For example: +You can supply the attribute type `PlanModifiers` field with a list of plan modifiers for that attribute. For example: ```go -// Typically within the tfsdk.Schema returned by GetSchema() for a resource. -tfsdk.Attribute{ +// Typically within the schema.Schema returned by Schema() for a resource. +schema.StringAttribute{ // ... other Attribute configuration ... - PlanModifiers: []AttributePlanModifiers{ - resource.RequiresReplace(), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, } ``` @@ -59,7 +59,7 @@ The framework implements some common use case modifiers in the typed packages un ### Creating Attribute Plan Modifiers -To create an attribute plan modifier, you must implement the [`tfsdk.AttributePlanModifier` interface](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#AttributePlanModifier). For example: +To create an attribute plan modifier, you must implement the one of the [`planmodifier` package](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier) interfaces. For example: ```go // stringDefaultModifier is a plan modifier that sets a default value for a @@ -85,20 +85,9 @@ func (m stringDefaultModifier) MarkdownDescription(ctx context.Context) string { // Access to the configuration, plan, and state is available in `req`, while // `resp` contains fields for updating the planned value, triggering resource // replacement, and returning diagnostics. -func (m stringDefaultModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { +func (m stringDefaultModifier) ModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { // If the value is unknown or known, do not set default value. - if !req.AttributePlan.IsNull() { - return - } - - // types.String must be the attr.Value produced by the attr.Type in the schema for this attribute - // for generic plan modifiers, use - // https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#ConvertValue - // to convert into a known type. - var str types.String - diags := tfsdk.ValueAs(ctx, req.AttributePlan, &str) - resp.Diagnostics.Append(diags...) - if diags.HasError() { + if !req.PlanValue.IsNull() { return } @@ -109,7 +98,7 @@ func (m stringDefaultModifier) Modify(ctx context.Context, req tfsdk.ModifyAttri Optionally, you may also want to create a helper function to instantiate the plan modifier. For example: ```go -func stringDefault(defaultValue string) stringDefaultModifier { +func stringDefault(defaultValue string) planmodifier.String { return stringDefaultModifier{ Default: defaultValue, } diff --git a/website/docs/plugin/framework/resources/private-state.mdx b/website/docs/plugin/framework/resources/private-state.mdx index 7733ff986..9b8caa43b 100644 --- a/website/docs/plugin/framework/resources/private-state.mdx +++ b/website/docs/plugin/framework/resources/private-state.mdx @@ -33,7 +33,7 @@ type in the `Private` field present in the _request_ that is passed into: | --- | --- | | Delete | [resource.DeleteRequest.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#DeleteRequest.Private) | | Plan Modification ([resource.ResourceWithModifyPlan](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ResourceWithModifyPlan)) | [resource.ModifyPlanRequest.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ModifyPlanRequest.Private) | -| Plan Modification ([tfsdk.AttributePlanModifier](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#AttributePlanModifier)) | [tfsdk.ModifyAttributePlanRequest.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#ModifyAttributePlanRequest.Private) | +| Plan Modification (`planmodifier` package interfaces) | Request type `Private` fields | | Read | [resource.ReadRequest.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ReadRequest.Private) | | Update | [resource.UpdateRequest.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateRequest.Private) @@ -45,7 +45,7 @@ type in the `Private` field present in the _response_ that is returned from: | Create | [resource.CreateResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#CreateResponse.Private) | | Import | [resource.ImportStateResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ImportStateResponse.Private) | | Plan Modification ([resource.ResourceWithModifyPlan](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ResourceWithModifyPlan)) | [resource.ModifyPlanResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ModifyPlanResponse.Private) | -| Plan Modification ([tfsdk.AttributePlanModifier](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#AttributePlanModifier)) | [tfsdk.ModifyAttributePlanResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#ModifyAttributePlanResponse.Private) | +| Plan Modification (`planmodifier` package interfaces) | Response type `Private` fields | | Read | [resource.ReadResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ReadResponse.Private) | | Update | [resource.UpdateResponse.Private](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateResponse.Private) diff --git a/website/docs/plugin/framework/resources/read.mdx b/website/docs/plugin/framework/resources/read.mdx index bef51d85a..35c88e24f 100644 --- a/website/docs/plugin/framework/resources/read.mdx +++ b/website/docs/plugin/framework/resources/read.mdx @@ -51,25 +51,23 @@ type ThingResourceAPIModel struct { Id string `json:"id"` } -func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "name": { +func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ MarkdownDescription: "Name of the thing to be saved in the service.", Required: true, - Type: types.StringType, }, - "id": { + "id": schema.StringAttribute{ Computed: true, MarkdownDescription: "Service generated identifier for the thing.", - PlanModifiers: tfsdk.AttributePlanModifiers{ - resource.UseStateForUnknown(), + PlanModifiers: planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - Type: types.StringType, }, }, MarkdownDescription: "Manages a thing.", - }, nil + } } func (r *ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { diff --git a/website/docs/plugin/framework/resources/state-upgrade.mdx b/website/docs/plugin/framework/resources/state-upgrade.mdx index 19e610e64..145fffe96 100644 --- a/website/docs/plugin/framework/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/resources/state-upgrade.mdx @@ -24,7 +24,7 @@ A resource schema captures the structure and types of the resource [state](/lang ## Implementing State Upgrade Support -Ensure the [`tfsdk.Schema` type `Version` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Schema.Version) for the [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource) is greater than `0`, then implement the [`resource.ResourceWithStateUpgrade` interface](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ResourceWithStateUpgrade) for the [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource). Conventionally the version is incremented by `1` for each state upgrade. +Ensure the [`schema.Schema` type `Version` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource/schema#Schema.Version) for the [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource) is greater than `0`, then implement the [`resource.ResourceWithStateUpgrade` interface](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#ResourceWithStateUpgrade) for the [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource). Conventionally the version is incremented by `1` for each state upgrade. This example shows a `Resource` with the necessary `StateUpgrade` method to implement the `ResourceWithStateUpgrade` interface: @@ -35,8 +35,8 @@ var _ resource.ResourceWithUpgradeState = &ThingResource{} type ThingResource struct{/* ... */} -func (r *ThingResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (r *ThingResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ // ... other fields ... // This example conventionally declares that the resource has prior @@ -92,19 +92,18 @@ type ThingResourceModelV1 struct { RequiredAttribute string `tfsdk:"required_attribute"` } -func (r *ThingResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]Attribute{ - "id": { - Type: types.StringType, +func (r *ThingResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ Computed: true, }, - "optional_attribute": { - Type: types.StringType, // As compared to prior types.BoolType below + "optional_attribute": schema.StringAttribute{ + // As compared to prior schema.BoolAttribute below Optional: true, }, - "required_attribute": { - Type: types.StringType, // As compared to prior types.BoolType below + "required_attribute": schema.StringAttribute{ + // As compared to prior schema.BoolAttribute below Required: true, }, }, @@ -118,18 +117,17 @@ func (r *ThingResource) UpgradeState(ctx context.Context) map[int64]resource.Sta return map[int64]resource.StateUpgrader{ // State upgrade implementation from 0 (prior state version) to 1 (Schema.Version) 0: { - PriorSchema: &tfsdk.Schema{ - Attributes: map[string]Attribute{ - "id": { - Type: types.StringType, + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ Computed: true, }, - "optional_attribute": { - Type: types.BoolType, // As compared to current types.StringType above + "optional_attribute": schema.BoolAttribute{ + // As compared to current schema.StringAttribute above Optional: true, }, - "required_attribute": { - Type: types.BoolType, // As compared to current types.StringType above + "required_attribute": schema.BoolAttribute{ + // As compared to current schema.StringAttribute above Required: true, }, }, @@ -189,19 +187,18 @@ var ThingResourceTftypesDataV1 = tftypes.Object{ type ThingResource struct{/* ... */} -func (r *ThingResource) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]Attribute{ - "id": { - Type: types.StringType, +func (r *ThingResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ Computed: true, }, - "optional_attribute": { - Type: types.StringType, // As compared to prior types.BoolType below + "optional_attribute": schema.StringAttribute{ + // As compared to prior schema.BoolAttribute below Optional: true, }, - "required_attribute": { - Type: types.StringType, // As compared to prior types.BoolType below + "required_attribute": schema.StringAttribute{ + // As compared to prior schema.BoolAttribute below Required: true, }, }, diff --git a/website/docs/plugin/framework/resources/timeouts.mdx b/website/docs/plugin/framework/resources/timeouts.mdx index ef6f1d1c9..cbe80c91c 100644 --- a/website/docs/plugin/framework/resources/timeouts.mdx +++ b/website/docs/plugin/framework/resources/timeouts.mdx @@ -38,14 +38,14 @@ resource "timeouts_example" "example" { } ``` -You can use this module to mutate the `tfsdk.Schema` as follows: +You can use this module to mutate the `schema.Schema` as follows: ```go -func (t *exampleResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ +func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ /* ... */ - Blocks: map[string]tfsdk.Block{ + Blocks: map[string]schema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{ Create: true, }), @@ -66,12 +66,12 @@ resource "timeouts_example" "example" { } ``` -You can use this module to mutate the `tfsdk.Schema` as follows: +You can use this module to mutate the `schema.Schema` as follows: ```go -func (t *exampleResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ +func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ /* ... */ "timeouts": timeouts.Attributes(ctx, timeouts.Opts{ Create: true, diff --git a/website/docs/plugin/framework/resources/update.mdx b/website/docs/plugin/framework/resources/update.mdx index b17cb909e..ad407112c 100644 --- a/website/docs/plugin/framework/resources/update.mdx +++ b/website/docs/plugin/framework/resources/update.mdx @@ -53,25 +53,23 @@ type ThingResourceAPIModel struct { Id string `json:"id"` } -func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "name": { +func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ MarkdownDescription: "Name of the thing to be saved in the service.", Required: true, - Type: types.StringType, }, - "id": { + "id": schema.StringAttribute{ Computed: true, MarkdownDescription: "Service generated identifier for the thing.", - PlanModifiers: tfsdk.AttributePlanModifiers{ - resource.UseStateForUnknown(), + PlanModifiers: planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - Type: types.StringType, }, }, MarkdownDescription: "Manages a thing.", - }, nil + } } func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { diff --git a/website/docs/plugin/framework/schemas.mdx b/website/docs/plugin/framework/schemas.mdx index 73da26521..554c04d11 100644 --- a/website/docs/plugin/framework/schemas.mdx +++ b/website/docs/plugin/framework/schemas.mdx @@ -61,34 +61,24 @@ used instead of the `Description` property. It is possible that a different stra Attributes are the main point of a schema. They are used to describe the fields of a provider, resource, or data source. Attributes are defined as a map of -[`tfsdk.Attribute`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Attribute)s, -with string keys. +string names to definitions. -The keys are the names of the fields, and should only contain lowercase +The name should only contain lowercase letters, numbers, and underscores. Practitioners will enter these strings in the configuration block of the provider, resource, or data source to set a value for that field. -The values are descriptions of the constraints on that field and the metadata -about it, expressed as a `tfsdk.Attribute` struct value. +The value is the implementation details for the attribute. -### Type +### Nested Attributes -The `Type` property of a `tfsdk.Attribute` is used to specify the [attribute -type](/plugin/framework/types) of the attribute. If the practitioner -enters data of the wrong type, Terraform will automatically return a validation -error to the practitioner, and this type determines what kind of attribute -value is used when [accessing state, config, and plan -values](/plugin/framework/accessing-values). +-> Only supported when using protocol version 6. -You must specify one of the `Type` and `Attributes` properties of -`tfsdk.Attribute`. You cannot specify both. +Nested attributes enable provider developers to define objects of attributes which fully support attribute behaviors and practitioners to configure these directly using [expressions](/language/expressions). -### Attributes +#### SingleNestedAttribute -The `Attributes` property of a `tfsdk.Attribute` is used to specify the -attribute's nested attributes. Nested attributes are attributes that are -grouped beneath another attribute: +With single nested attributes, the attribute behaves like an object. The practitioner can only specify one definition of the nested attributes. ```tf resource "example_foo" "bar" { @@ -99,47 +89,9 @@ resource "example_foo" "bar" { } ``` -In this example, `hello` and `demo` are nested attributes of the -`nested_attribute` attribute. Nested attributes have their own computed, -optional, required, and sensitive flags, and their own properties to hold -descriptions, markdown-formatted descriptions, and deprecation messages. +#### ListNestedAttribute -If an attribute is required and its nested attribute is optional, the attribute -must be specified, and the nested attribute can be omitted inside the curly -braces. If an attribute is optional and its nested attribute is required, the -attribute can be omitted, but if it is specified, the nested attribute must be -specified as well. - -If an attribute is computed, all its nested attributes are considered computed, -as well. Likewise, if an attribute is considered sensitive or deprecated, all -its nested attributes are considered sensitive or deprecated. - -You must specify one of the `Type` and `Attributes` properties of -`tfsdk.Attribute`. You cannot specify both. - --> **Note**: We recommend using an attribute with nested attributes when any of the inner fields should have their own flags or metadata (required, optional, computed, deprecated, sensitive, descriptions, etc.). If the attribute is an atomic unit, we recommend using an object or list of objects instead. - -#### SingleNestedAttributes - -When the `Attributes` property of a `tfsdk.Attribute` is set to the -[`schema.SingleNestedAttributes`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema#SingleNestedAttributes) -type, the nested attributes behave like an object. There can only be one group -of the nested attributes. - -```tf -resource "example_foo" "bar" { - nested_attribute = { - hello = "world" - demo = true - } -} -``` - -#### ListNestedAttributes - -When the `Attributes` property of a `tfsdk.Attribute` is set to the -[`schema.ListNestedAttributes`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema#ListNestedAttributes) -type, the nested attributes behave like a list of objects. The practitioner can +With list nested attributes, the attribute behaves like a list of objects. The practitioner can specify any number of groups of these attributes. ```tf @@ -157,17 +109,9 @@ resource "example_foo" "bar" { } ``` -In addition to a map of the attributes, `ListNestedAttributes` accepts a -[`schema.ListNestedAttributesOptions`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema#ListNestedAttributesOptions) -struct that can be used to specify a minimum or maximum number of instances of -that group of fields. If the user enters fewer than or more than those numbers -of groups, Terraform will automatically return a validation error. - -#### MapNestedAttributes +#### MapNestedAttribute -When the `Attributes` property of a `tfsdk.Attribute` is set to the -[`schema.MapNestedAttributes`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema#MapNestedAttributes) -type, the nested attributes behave like a map of objects. The practitioner can +With map nested attributes, the attribute behaves like a map of objects. The practitioner can specify any number of groups of these attributes, with string keys associated with each group. @@ -186,11 +130,25 @@ resource "example_foo" "bar" { } ``` -In addition to a map of the attributes, `MapNestedAttributes` accepts a -[`schema.MapNestedAttributesOptions`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema#MapNestedAttributesOptions) -struct that can be used to specify a minimum or maximum number of instances of -that group of fields. If the user enters fewer than or more than those numbers -of groups, Terraform will automatically return a validation error. +#### SetNestedAttribute + +With set nested attributes, the attributes behave like a set of objects. The practitioner can +specify any number of groups of these attributes. + +```tf +resource "example_foo" "bar" { + nested_attribute = [ + { + hello = "world" + demo = true + }, + { + hello = "moon" + demo = false + }, + ] +} +``` ### Required diff --git a/website/docs/plugin/framework/validation.mdx b/website/docs/plugin/framework/validation.mdx index 91e480cbd..11c4bc4af 100644 --- a/website/docs/plugin/framework/validation.mdx +++ b/website/docs/plugin/framework/validation.mdx @@ -25,15 +25,15 @@ Terraform CLI syntax and basic schema checks occur during the [`terraform apply` ## Attribute Validation -You can introduce validation on attributes using the generic framework-defined types such as [`types.String`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#String). To do this, supply the [`tfsdk.Attribute` type `Validators` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Attribute.Validators) with a list of validations, and the framework will return diagnostics from all validators. For example: +You can introduce validation on attributes using the generic framework-defined types such as [`types.String`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#String). To do this, supply the `Validators` field with a list of validations, and the framework will return diagnostics from all validators. For example: ```go -// Typically within the tfsdk.Schema returned by GetSchema() for a provider, +// Typically within the schema.Schema returned by Schema() for a provider, // resource, or data source. -tfsdk.Attribute{ +schema.StringAttribute{ // ... other Attribute configuration ... - Validators: []AttributeValidators{ + Validators: []validator.String{ // These are example validators from terraform-plugin-framework-validators stringvalidator.LengthBetween(10, 256), stringvalidator.RegexMatches( @@ -54,7 +54,7 @@ You can implement attribute validators from the [terraform-plugin-framework-vali If there is not an attribute validator in `terraform-plugin-framework-validators` that meets a specific use case, a provider-defined attribute validator can be created. -To create an attribute validator, you must implement the [`tfsdk.AttributeValidator` interface](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#AttributeValidator). For example: +To create an attribute validator, you must implement at least one of the [`validator` package](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/schema/validator) interfaces. For example: ```go type stringLengthBetweenValidator struct { @@ -73,24 +73,13 @@ func (v stringLengthBetweenValidator) MarkdownDescription(ctx context.Context) s } // Validate runs the main validation logic of the validator, reading configuration data out of `req` and updating `resp` with diagnostics. -func (v stringLengthBetweenValidator) Validate(ctx context.Context, req tfsdk.ValidateAttributeRequest, resp *tfsdk.ValidateAttributeResponse) { +func (v stringLengthBetweenValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { // If the value is unknown or null, there is nothing to validate. - if req.AttributeConfig.IsUnknown() || req.AttributeConfig.IsNull() { + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { return } - // types.String must be the attr.Value produced by the attr.Type in the schema for this attribute - // for generic validators, use - // https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#ConvertValue - // to convert into a known type. - var str types.String - diags := tfsdk.ValueAs(ctx, req.AttributeConfig, &str) - resp.Diagnostics.Append(diags...) - if diags.HasError() { - return - } - - strLen := len(str.ValueString()) + strLen := len(req.ConfigValue.ValueString()) if strLen < v.Min || strLen > v.Max { resp.Diagnostics.AddAttributeError( @@ -123,17 +112,17 @@ Path expressions may represent one or more actual paths in the data. To find tho The general structure for working with path expressions in an attribute validator is: -- Merge the given path expression(s) with the current attribute path expression, such as calling the [`tfsdk.ValidateAttributeRequest` type `AttributePathExpression` field `MergeExpressions()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.MergeExpressions). -- Loop through each merged path expression to get the matching paths within the data, such as calling the [`tfsdk.ValidateAttributeRequest` type `Config` field `PathMatches()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config.PathMatches). -- Loop through each matched path to get the generic `attr.Value` value, such as calling the [`tfsdk.ValidateAttributeRequest` type `Config` field `GetAttribute()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config.GetAttribute). +- Merge the given path expression(s) with the current attribute path expression, such as calling the request type `PathExpression` field [`MergeExpressions()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.MergeExpressions). +- Loop through each merged path expression to get the matching paths within the data, such as calling the request type `Config` field [`PathMatches()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config.PathMatches). +- Loop through each matched path to get the generic `attr.Value` value, such as calling the request type `Config` field [`GetAttribute()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config.GetAttribute). - Perform null and unknown value checks on the `attr.Value`, such as the [`IsNull()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/attr#Value.IsNull) and [`IsUnknown()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/attr#Value.IsUnknown). - If the `attr.Value` is not null and not unknown, then use [`tfsdk.ValueAs()`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#ValueAs) using the expected value implementation as the target. The following example shows a generic path based attribute validator that returns an error if `types.Int64` values at the given path expressions are less than the current attribute `types.Int64` value. ```go -// Ensure our implementation satisfies the tfsdk.AttributeValidator interface. -var _ tfsdk.AttributeValidator = &int64IsGreaterThanValidator{} +// Ensure our implementation satisfies the validator.Int64 interface. +var _ validator.Int64 = &int64IsGreaterThanValidator{} // int64IsGreaterThanValidator is the underlying type implementing Int64IsGreaterThan. type int64IsGreaterThanValidator struct { @@ -151,25 +140,10 @@ func (v int64IsGreaterThanValidator) MarkdownDescription(ctx context.Context) st } // Validate performs the validation logic for the validator. -func (v int64IsGreaterThanValidator) Validate(ctx context.Context, req tfsdk.ValidateAttributeRequest, resp *tfsdk.ValidateAttributeResponse) { +func (v int64IsGreaterThanValidator) ValidateInt64(ctx context.Context, req validator.Int64Request, resp *validator.Int64Response) { // If the current attribute configuration is null or unknown, there // cannot be any value comparisons, so exit early without error. - if req.AttributeConfig.IsNull() || req.AttributeConfig.IsUnknown() { - return - } - - // Convert the current attribute configuration to our expected attr.Value - // implementation. This will raise an error if the validator is not put - // on an attribute that is types.Int64Type. - var attributeConfig types.Int64 - - diags := tftypes.ValueAs(ctx, req.AttributeConfig, &attributeConfig) - - resp.Diagnostics.Append(diags...) - - // If the current attribute configuration is not valid, there cannot be - // any value comparisons, so exit early. - if resp.Diagnostics.HasError() { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { return } @@ -244,7 +218,7 @@ func (v int64IsGreaterThanValidator) Validate(ctx context.Context, req tfsdk.Val // Int64IsGreaterThan checks that any Int64 values in the paths described by the // path.Expression are less than the current attribute value. -func Int64IsGreaterThan(expressions ...path.Expression) tfsdk.AttributeValidator { +func Int64IsGreaterThan(expressions ...path.Expression) validator.Int64 { return &int64IsGreaterThanValidator{ expressions: expressions, } @@ -256,17 +230,17 @@ func Int64IsGreaterThan(expressions ...path.Expression) tfsdk.AttributeValidator You may want to create a custom type to simplify schemas if your provider contains common attribute values with consistent validation rules. When you implement validation on a type, you do not need to declare the same validation on the attribute, but you can supply additional validations in that manner. For example: ```go -// Typically within the tfsdk.Schema returned by GetSchema() for a provider, +// Typically within the schema.Schema returned by Schema() for a provider, // resource, or data source. -tfsdk.Attribute{ +schema.StringAttribute{ // ... other Attribute configuration ... // This is an example type which implements its own validation - Type: computeInstanceIdentifierType, + CustomType: computeInstanceIdentifierType, // This is optional, example validation that is checked in addition // to any validation performed by the type - Validators: []AttributeValidators{ + Validators: []validator.String{ stringvalidator.LengthBetween(10, 256), }, }