Description
Use-cases
In addition to the core resource.Resource
interface, the Plugin Framework defines many "add-on" interfaces to extend functionality, such as
In all cases, these "add-on" interfaces follow the pattern
type ResourceWithSomethingExtra interface {
Resource
DoSomethingExtra(context.Context, SomethingExtraRequest, *SomethingExtraResponse)
}
Because these interfaces include resource.Resource
, the interface types exported by the resource
package cannot be used directly to create composable types or in testing.
For example, the AWS provider uses various embedded composable types for resource functionality, e.g. the resource type aws_apprunner_default_auto_scaling_configuration_version
is defined as https://github.com/hashicorp/terraform-provider-aws/blob/4e1c9eee98e542dd211fef196e0017799eaf02ef/internal/service/apprunner/default_auto_scaling_configuration_version.go#L31-L35
type defaultAutoScalingConfigurationVersionResource struct {
framework.ResourceWithConfigure
framework.WithNoOpDelete
framework.WithImportByID
}
Where framework.WithImportByID
is defined as https://github.com/hashicorp/terraform-provider-aws/blob/4e1c9eee98e542dd211fef196e0017799eaf02ef/internal/framework/with_import_by_id.go#L16
type WithImportByID struct{}
func (w *WithImportByID) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), request, response)
}
As framework.WithImportByID
is intended for embedding in other types, it only implements the ImportState
function from resource.ResourceWithImportState
.
When referencing full resource type implementations, such as determining if a resource type supports importing, checking if it implements resource.ResourceWithImportState
works. However, when testing or otherwise referencing the embeddable struct, there is no way to validate that the interface is implemented without defining our own interface, such as
type importStater interface {
ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse)
}
Proposal
Consider exporting additional interfaces that define only the "add-on" behaviour and composing the existing "add-on" interfaces from those. For example
type WithImportState interface {
// ImportState is called when the provider must import the state of a
// resource instance. This method must return enough state so the Read
// method can properly refresh the full resource.
//
// If setting an attribute with the import identifier, it is recommended
// to use the ImportStatePassthroughID() call in this method.
/ImportState(context.Context, ImportStateRequest, *ImportStateResponse)
}
type ResourceWithImportState interface {
Resource
WithImportState
}