Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

provider/metaschema: Initial package #562

Merged
merged 3 commits into from Nov 29, 2022
Merged

provider/metaschema: Initial package #562

merged 3 commits into from Nov 29, 2022

Conversation

bflad
Copy link
Member

@bflad bflad commented Nov 29, 2022

Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #508
Reference: #532

NOTE: Almost all providers will not use this functionality and it is already marked as experimental.

This change introduces a new provider/metaschema package, which contains schema interfaces and types relevant to provider meta schemas, such as omitting Computed, DeprecatedMessage, Sensitive, plan modifiers, validators, blocks, schema descriptions, and schema versioning. This new schema implementation also provides strongly typed attributes and nested attributes with customizable types. Nested attributes are exposed with a separate nested object for customization.

The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for provider meta schemas.

No changes are required for data handling during requests.

Example definition:

package test

import (
	"context"

	"github.com/bflad/terraform-plugin-framework-type-time/timetypes"
	"github.com/hashicorp/terraform-plugin-framework-validators/float64validator"
	"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
)

type ExampleCloudProvider struct{}

func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) {
	resp.Schema = metaschema.Schema{
		Attributes: map[string]metaschema.Attribute{
			"string_attribute": metaschema.StringAttribute{
				Required: true,
			},
			"custom_string_attribute": metaschema.StringAttribute{
				CustomType: timetypes.RFC3339Type,
				Optional:   true,
			},
			"list_attribute": metaschema.ListAttribute{
				ElementType: types.StringType,
				Optional:    true,
			},
			"list_nested_attribute": metaschema.ListNestedAttribute{
				NestedObject: metaschema.NestedAttributeObject{
					Attributes: map[string]metaschema.Attribute{
						"bool_attribute": metaschema.BoolAttribute{
							Optional: true,
						},
					},
				},
				Optional: true,
			},
			"single_nested_attribute": metaschema.SingleNestedAttribute{
				Attributes: map[string]metaschema.Attribute{
					"int64_attribute": metaschema.Int64Attribute{
						Optional: true,
					},
				},
				Optional: true,
			},
		},
	}
}

To migrate a provider meta schema:

  • Add github.com/hashicorp/terraform-plugin-framework/provider/metaschema to the import statement
  • Switch the provider.ProviderWithMetaSchema implementation GetMetaSchema method to MetaSchema whose response includes a metaschema.Schema from the new package.

Prior implementation:

func (p ExampleCloudProvider) GetMetaSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
  return tfsdk.Schema{/* ... */}, nil
}

Migrated implementation:

func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) {
  resp.Schema =  metaschema.Schema{/*...*/}
}

If the provider requires no meta schema, the method can be removed entirely.

  • Switch map[string]tfsdk.Attribute with map[string]metaschema.Attribute
  • Switch individual attribute definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the Type field will be removed and the map entries must declare the typed implementation, e.g. a tfsdk.Attribute with Type: types.StringType is equivalent to metaschema.StringAttribute. Custom attribute types can be specified via the CustomType field in each of the implementations.

Prior primitive type (types.BoolType, types.Float64Type, types.Int64Type, types.NumberType, types.StringType) attribute implementation:

// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Required: true,
  Type: types.StringType,
}

Migrated implementation:

// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.StringAttribute{
  Required: true,
}

Prior collection type (types.ListType, types.MapType, types.SetType) attribute implementation:

// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Required: true,
  Type: types.ListType{
    ElemType: types.StringType,
  },
}

Migrated implementation:

// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.ListAttribute{
  ElementType: types.StringType,
  Required: true,
}

Prior single nested attributes type (tfsdk.SingleNestedAttributes()) attribute implementation:

// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}),
  Required: true,
},

Migrated implementation:

// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.SingleNestedAttribute{
  Attributes: map[string]metaschema.Attribute{/*...*/},
  Required: true,
}

Prior collection nested attributes type (tfsdk.ListNestedAttributes(), tfsdk.MapNestedAttributes(), tfsdk.SetNestedAttributes()) attribute implementation:

// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}),
  Required: true,
},

Migrated implementation:

// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.ListNestedAttribute{
  NestedObject: metaschema.NestedAttributeObject{
    Attributes: map[string]metaschema.Attribute{/*...*/},
  },
  Required: true,
}

Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #508
Reference: #532

This change introduces a new `provider/metaschema` package, which contains schema interfaces and types relevant to provider meta schemas, such as omitting `Computed`, `DeprecatedMessage`, `Sensitive`, plan modifiers, validators, blocks, schema descriptions, and schema versioning. This new schema implementation also provides strongly typed attributes and nested attributes with customizable types. Nested attributes are exposed with a separate nested object for customization.

The implementation leans heavily on the design choice of the framework being responsible for preventing provider developer runtime errors. The tailored fields no longer expose functionality that is not available for provider meta schemas.

No changes are required for data handling during requests.

Example definition:

```go
package test

import (
	"context"

	"github.com/bflad/terraform-plugin-framework-type-time/timetypes"
	"github.com/hashicorp/terraform-plugin-framework-validators/float64validator"
	"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
)

type ExampleCloudProvider struct{}

func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) {
	resp.Schema = metaschema.Schema{
		Attributes: map[string]schema.Attribute{
			"string_attribute": schema.StringAttribute{
				Required: true,
			},
			"custom_string_attribute": schema.StringAttribute{
				CustomType: timetypes.RFC3339Type,
				Optional:   true,
			},
			"list_attribute": schema.ListAttribute{
				ElementType: types.StringType,
				Optional:    true,
			},
			"list_nested_attribute": schema.ListNestedAttribute{
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"bool_attribute": schema.BoolAttribute{
							Optional: true,
						},
					},
				},
				Optional: true,
			},
			"single_nested_attribute": schema.SingleNestedAttribute{
				Attributes: map[string]schema.Attribute{
					"int64_attribute": schema.Int64Attribute{
						Optional: true,
					},
				},
				Optional: true,
			},
		},
	}
}
```

To migrate a provider meta schema:

- Add `github.com/hashicorp/terraform-plugin-framework/provider/metaschema` to the `import` statement
- Switch the `provider.ProviderWithMetaSchema` implementation `GetMetaSchema` method to `MetaSchema` whose response includes a `metaschema.Schema` from the new package.

Prior implementation:

```go
func (p ExampleCloudProvider) GetMetaSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
  return tfsdk.Schema{/* ... */}, nil
}
```

Migrated implementation:

```go
func (p ExampleCloudProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) {
  resp.Schema =  metaschema.Schema{/*...*/}
}
```

If the provider requires no meta schema, the method can be removed entirely.

- Switch `map[string]tfsdk.Attribute` with `map[string]metaschema.Attribute`
- Switch individual attribute  definitions. Unless the code was already taking advantage of custom attribute types (uncommon so far), the `Type` field will be removed and the map entries must declare the typed implementation, e.g. a `tfsdk.Attribute` with `Type: types.StringType` is equivalent to `metaschema.StringAttribute`. Custom attribute types can be specified via the `CustomType` field in each of the implementations.

Prior primitive type (`types.BoolType`, `types.Float64Type`, `types.Int64Type`, `types.NumberType`, `types.StringType`) attribute implementation:

```go
// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Required: true,
  Type: types.StringType,
}
```

Migrated implementation:

```go
// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
schema.StringAttribute{
  Required: true,
}
```

Prior collection type (`types.ListType`, `types.MapType`, `types.SetType`) attribute implementation:

```go
// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Required: true,
  Type: types.ListType{
    ElemType: types.StringType,
  },
}
```

Migrated implementation:

```go
// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.ListAttribute{
  ElementType: types.StringType,
  Required: true,
}
```

Prior single nested attributes type (`tfsdk.SingleNestedAttributes()`) attribute implementation:

```go
// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{/*...*/}),
  Required: true,
},
```

Migrated implementation:

```go
// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.SingleNestedAttribute{
  Attributes: map[string]metaschema.Attribute{/*...*/},
  Required: true,
}
```

Prior collection nested attributes type (`tfsdk.ListNestedAttributes()`, `tfsdk.MapNestedAttributes()`, `tfsdk.SetNestedAttributes()`) attribute implementation:

```go
// The "tfsdk.Attribute" could be omitted inside a map[string]tfsdk.Attribute
tfsdk.Attribute{
  Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{/*...*/}),
  Required: true,
},
```

Migrated implementation:

```go
// The metaschema.XXXAttribute must be declared inside map[string]metaschema.Attribute
metaschema.ListNestedAttribute{
  NestedObject: metaschema.NestedAttributeObject{
    Attributes: map[string]metaschema.Attribute{/*...*/},
  },
  Required: true,
}
```
@bflad bflad added the enhancement New feature or request label Nov 29, 2022
@bflad bflad added this to the v0.17.0 milestone Nov 29, 2022
@bflad bflad requested a review from a team as a code owner November 29, 2022 16:10
bflad added a commit that referenced this pull request Nov 29, 2022
Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #546
Reference: #553
Reference: #558
Reference: #562
Copy link
Contributor

@bendbennett bendbennett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@bflad bflad merged commit 9353b7c into main Nov 29, 2022
@bflad bflad deleted the bflad/provider-metaschema branch November 29, 2022 18:55
bflad added a commit that referenced this pull request Nov 29, 2022
Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #546
Reference: #553
Reference: #558
Reference: #562
Reference: #563
bflad added a commit that referenced this pull request Nov 29, 2022
Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #546
Reference: #553
Reference: #558
Reference: #562
bflad added a commit that referenced this pull request Nov 30, 2022
…564)

Reference: #132
Reference: #326
Reference: #437
Reference: #491
Reference: #546
Reference: #553
Reference: #558
Reference: #562
Reference: #563
bflad added a commit that referenced this pull request Dec 13, 2022
Reference: #132
Reference: #326
Reference: #491

Refer to the following pull request descriptions for migration information about migrating to the split schema packages:

- `datasource/schema`: #546
- `provider/schema`: #553
- `resource/schema`: #558
- `provider/metaschema`: #562
bflad added a commit that referenced this pull request Dec 13, 2022
Reference: #132
Reference: #326
Reference: #491

Refer to the following pull request descriptions for migration information about migrating to the split schema packages:

- `datasource/schema`: #546
- `provider/schema`: #553
- `resource/schema`: #558
- `provider/metaschema`: #562
bflad added a commit that referenced this pull request Dec 13, 2022
Reference: #132
Reference: #326
Reference: #491

Refer to the following pull request descriptions for migration information about migrating to the split schema packages:

- `datasource/schema`: #546
- `provider/schema`: #553
- `resource/schema`: #558
- `provider/metaschema`: #562
bflad added a commit that referenced this pull request Dec 13, 2022
Reference: #132
Reference: #326
Reference: #491

Refer to the following pull request descriptions for migration information about migrating to the split schema packages:

- `datasource/schema`: #546
- `provider/schema`: #553
- `resource/schema`: #558
- `provider/metaschema`: #562
@github-actions
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants