hashicorp / hashicorp/terraform-plugin-framework

Consider exposing Resource "add-on" interfaces as stand-alone interfaces

Open
#1,159 1 comment 2 reactions 0 assignees View on GitHub
enhancement
Dominant language
Go
Stars
384
Forks
107
Avg merge
3m
Merged PRs (30d)
1

Description

### Use-cases

In addition to the core [`resource.Resource` interface](https://github.com/hashicorp/terraform-plugin-framework/blob/8a7303c9a6ca60b8a7282848c910b2024c762eac/resource/resource.go#L28), the Plugin Framework defines many "add-on" interfaces to extend functionality, such as

* [`resource.ResourceWithConfigure`](https://github.com/hashicorp/terraform-plugin-framework/blob/8a7303c9a6ca60b8a7282848c910b2024c762eac/resource/resource.go#L67C6-L67C27)
* [`resource.ResourceWithImportState`](https://github.com/hashicorp/terraform-plugin-framework/blob/8a7303c9a6ca60b8a7282848c910b2024c762eac/resource/resource.go#L95C6-L95C29)
* [`resource.ResourceWithIdentity`](https://github.com/hashicorp/terraform-plugin-framework/blob/8a7303c9a6ca60b8a7282848c910b2024c762eac/resource/resource.go#L220C6-L220C26)

In all cases, these "add-on" interfaces follow the pattern

```go
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

```go
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

```go
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

```go
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

```go
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
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.