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: Deprecate DataSourceType
, ResourceType
, Provider
type GetDataSources
, and Provider
type GetResources
#472
Conversation
… `GetDataSources`, and `Provider` type `GetResources` Reference: #441 Reference: #442 The goal of this change is twofold: - Deprecate the separate `DataSourceType` and `ResourceType` types. - Enable `DataSource` and `Resource` to be self-documenting with respects to their type name. Removing `DataSourceType` and `ResourceType` simplifies provider implementations and gives developers the choice of whether or not to configure extra data in their `DataSource` and `Resource` implementations by adding the optional `Configure` method. This data is no longer required to be the `provider.Provider` itself, but rather can be any type such as a provider-defined structure or vendor-supplied client type. Enabling `DataSource` and `Resource` type name handling within the implementation allows provider developers to further split out their data source and resource code across package boundaries without repeated map addition logic. Given this framework version 0.11.1 code (which generally is split across Go files/packages, but shown altogether here for brevity): ```go var _ provider.Provider = &ExampleProvider{} type ExampleProvider struct{ configured bool // ... additional remote client fields, etc. } func (p ExampleProvider) GetDataSources(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) { return map[string]provider.DataSourceType{ "example_thing": ThingDataSourceType{}, }, nil } func (p ExampleProvider) GetResources(ctx context.Context) (map[string]provider.ResourceType, diag.Diagnostics) { return map[string]provider.ResourceType{ "example_thing": ThingResourceType{}, }, nil } func (p ExampleProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { /* ... */ } func (p *ExampleProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { // ... remote client setup, etc. p.configured = true } var _ provider.DataSourceType = ThingDataSourceType{} var _ datasource.DataSource = ThingDataSource{} type ThingDataSourceType struct{} type ThingDataSource struct{ provider ExampleProvider } func (t ThingDataSourceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { /* ... */ } func (t ThingDataSourceType) NewDataSource(ctx context.Context, p provider.Provider) (datasource.DataSource, diag.Diagnostics) { return ThingDataSource{ provider: p.(ExampleProvider), // unchecked type assertion shown for brevity } } func (d ThingDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // e.g. d.provider.Client.ReadThing() } var _ provider.ResourceType = ThingResourceType{} var _ resource.Resource = ThingResource{} type ThingResourceType struct{} type ThingResource struct{ provider ExampleProvider } func (t ThingResourceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { /* ... */ } func (t ThingResourceType) NewResource(ctx context.Context, p provider.Provider) (resource.Resource, diag.Diagnostics) { return ThingResource{ provider: p.(ExampleProvider), // unchecked type assertion shown for brevity } } func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // e.g. r.provider.Client.CreateThing() } func (r ThingResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // e.g. r.provider.Client.DeleteThing() } func (r ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // e.g. r.provider.Client.ReadThing() } func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // e.g. r.provider.Client.UpdateThing() } ``` Could be migrated as: ```go type ExampleClient struct { // client/data fields OR just use the vendor client type directly instead of this new type } var _ provider.Provider = &ExampleProvider{} type ExampleProvider struct{ // setting client, etc. fields here is now completely optional, // if you want to configure data sources and resource with _this_ type } func (p ExampleProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { // make available in DataSource and Resource type TypeName methods resp.TypeName = "example" } func (p ExampleProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewThingDataSource, } } func (p ExampleProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewThingResource, } } func (p ExampleProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { /* ... */ } func (p *ExampleProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { client := &ExampleClient{} // OR existing vendor type OR p itself resp.DataSourceData = client resp.ResourceData = client } var _ datasource.DataSource = ThingDataSource{} func NewThingDataSource() datasource.DataSource { return ThingDataSource{} } type ThingDataSource struct{ client *ExampleClient } func (d ThingDataSource) TypeName(ctx context.Context, req datasource.TypeNameRequest, resp *datasource.TypeNameResponse) { resp.TypeName = req.ProviderTypeName + "_thing" } func (d ThingDataSource) GetSchema(ctx context.Context) (fwschema.Schema, diag.Diagnostics) { /* ... */ } func (d ThingDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { d.client = req.ProviderData.(*ExampleClient) // unchecked type assertion shown for brevity } func (d ThingDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // e.g. d.client.ReadThing() } var _ resource.Resource = ThingResource{} func NewThingResource() resource.Resource { return ThingResource{} } type ThingResource struct{ client *ExampleClient } func (d ThingResource) TypeName(ctx context.Context, req resource.TypeNameRequest, resp *resource.TypeNameResponse) { resp.TypeName = req.ProviderTypeName + "_thing" } func (r ThingResource) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { /* ... */ } func (r ThingResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { r.client = req.ProviderConfigureData.(*ExampleClient) // unchecked type assertion shown for brevity } func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // e.g. r.client.CreateThing() } func (r ThingResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // e.g. r.client.DeleteThing() } func (r ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // e.g. r.client.ReadThing() } func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // e.g. r.client.UpdateThing() } ``` Some additional implementation notes: - The new `Provider` type `Metadata` method is called at the beginning of the `GetProviderSchema` RPC and populates the provider type name for data source and resource type name requests. - The framework server will automatically validate against duplicate type names, missing type names, and missing schemas when using the new `Provider` type `DataSources` and `Resources` methods. - The new `DataSource` and `Resource` type `Configure` methods are automatically called where the framework server previously called the `DataSourceType` type `NewDataSource` method and `ResourceType` type `NewDataSource` method. - The new `DataSource` and `Resource` type `TypeName` methods could potentially be renamed `Metadata` to match the `Provider` implementation and potentially help futureproof it, however it could be unclear to provider developers where to set the type name at first and there are no apparent use cases which would extend that type of metadata yet.
… Resource implementations
hashicorp/terraform-provider-scaffolding-framework#86 shows an example migration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I read the description of the refactoring and the first couple of examples, but have to confess that I didn't look at every line.
…ider.ResourceType deprecation Reference: #472
…ider.ResourceType deprecation Reference: #472
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. |
Reference: #441
Reference: #442
The goal of this change is twofold:
DataSourceType
andResourceType
types.DataSource
andResource
to be self-documenting with respects to their type name.Removing
DataSourceType
andResourceType
simplifies provider implementations and gives developers the choice of whether or not to configure extra data in theirDataSource
andResource
implementations by adding the optionalConfigure
method. This data is no longer required to be theprovider.Provider
itself, but rather can be any type such as a provider-defined structure or vendor-supplied client type.Enabling
DataSource
andResource
type name handling within the implementation allows provider developers to further split out their data source and resource code across package boundaries without repeated map addition logic.Given this framework version 0.11.1 code (which generally is split across Go files/packages, but shown altogether here for brevity):
Could be migrated as:
Some additional implementation notes:
Provider
typeMetadata
method is called at the beginning of theGetProviderSchema
RPC and populates the provider type name for data source and resource type name requests.Provider
typeDataSources
andResources
methods.DataSource
andResource
typeConfigure
methods are automatically called where the framework server previously called theDataSourceType
typeNewDataSource
method andResourceType
typeNewResource
method.