-
Notifications
You must be signed in to change notification settings - Fork 101
list: add types for ListResource
support
#1150
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
Changes from all commits
d7f3133
35b15f6
012fb27
c4ccf44
16a2973
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list | ||
|
||
import "context" | ||
|
||
// ConfigValidator describes reusable ListResource configuration validation | ||
// functionality. | ||
type ConfigValidator interface { | ||
// Description describes the validation in plain text formatting. | ||
// | ||
// This information may be automatically added to list resource plain text | ||
// descriptions by external tooling. | ||
Description(context.Context) string | ||
|
||
// MarkdownDescription describes the validation in Markdown formatting. | ||
// | ||
// This information may be automatically added to list resource Markdown | ||
// descriptions by external tooling. | ||
MarkdownDescription(context.Context) string | ||
|
||
// ValidateResource performs the validation. | ||
// | ||
// This method name is separate from ConfigValidators in resource and other packages in | ||
// order to allow generic validators. | ||
ValidateListResourceConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list | ||
|
||
import ( | ||
"context" | ||
"iter" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
) | ||
|
||
// ListResource represents an implementation of listing instances of a managed resource | ||
// This is the core interface for all list resource implementations. | ||
// | ||
// ListResource implementations can optionally implement these additional concepts: | ||
// | ||
// - Configure: Include provider-level data or clients. | ||
// - Validation: Schema-based or entire configuration via | ||
// ListResourceWithConfigValidators or ListResourceWithValidateConfig. | ||
type ListResource interface { | ||
// Metadata should return the full name of the list resource such as | ||
// examplecloud_thing. This name should match the full name of the managed | ||
// resource to be listed; otherwise, the GetMetadata RPC will return an | ||
// error diagnostic. | ||
// | ||
// The method signature is intended to be compatible with the Metadata | ||
// method signature in the Resource interface. One implementation of | ||
// Metadata can satisfy both interfaces. | ||
Metadata(context.Context, resource.MetadataRequest, *resource.MetadataResponse) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A trade-off of compatibility: a bit of overloading of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this makes sense, i briefly mentioned that you could share the interfaces internally if you wanted in our 1/1, but that would mean you couldn't write a descriptive package comment for the method, so not really a big deal to duplicate imo. |
||
|
||
// ListResourceConfigSchema should return the schema for list blocks. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
This method is not named |
||
ListResourceConfigSchema(context.Context, ListResourceSchemaRequest, *ListResourceSchemaResponse) | ||
bbasata marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// List is called when the provider must list instances of a managed | ||
// resource type that satisfy a user-provided request. | ||
List(context.Context, ListRequest, *ListResultsStream) | ||
} | ||
|
||
// ListResourceWithConfigure is an interface type that extends ListResource to include a method | ||
// which the framework will automatically call so provider developers have the | ||
// opportunity to setup any necessary provider-level data or clients. | ||
type ListResourceWithConfigure interface { | ||
ListResource | ||
|
||
// Configure enables provider-level data or clients to be set. The method | ||
// signature is intended to be compatible with the Configure method | ||
// signature in the Resource interface. One implementation of Configure can | ||
// satisfy both interfaces. | ||
Configure(context.Context, resource.ConfigureRequest, *resource.ConfigureResponse) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A trade-off of compatibility: a bit of overloading of |
||
} | ||
|
||
// ListResourceWithConfigValidators is an interface type that extends | ||
// ListResource to include declarative validations. | ||
// | ||
// Declaring validation using this methodology simplifies implementation of | ||
// reusable functionality. These also include descriptions, which can be used | ||
// for automating documentation. | ||
// | ||
// Validation will include ListResourceConfigValidators and | ||
// ValidateListResourceConfig, if both are implemented, in addition to any | ||
// Attribute or Type validation. | ||
type ListResourceWithConfigValidators interface { | ||
ListResource | ||
|
||
// ListResourceConfigValidators returns a list of functions which will all be performed during validation. | ||
ListResourceConfigValidators(context.Context) []ConfigValidator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
This method is not named There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it might be worth being a little more specific in what these validators are used for at some point (i.e. Same for the other methods There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 – will revisit the comments in a follow-up PR. |
||
} | ||
|
||
// ListResourceWithValidateConfig is an interface type that extends ListResource to include | ||
// imperative validation. | ||
// | ||
// Declaring validation using this methodology simplifies one-off | ||
// functionality that typically applies to a single resource. Any documentation | ||
// of this functionality must be manually added into schema descriptions. | ||
// | ||
// Validation will include ListResourceConfigValidators and ValidateListResourceConfig, if both | ||
// are implemented, in addition to any Attribute or Type validation. | ||
type ListResourceWithValidateConfig interface { | ||
ListResource | ||
|
||
// ValidateListResourceConfig performs the validation. | ||
ValidateListResourceConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
This method is not named |
||
} | ||
|
||
// ListRequest represents a request for the provider to list instances | ||
// of a managed resource type that satisfy a user-defined request. An instance | ||
// of this reqeuest struct is passed as an argument to the provider's | ||
// ListResource function implementation. | ||
type ListRequest struct { | ||
// Config is the configuration the user supplied for listing resource | ||
// instances. | ||
Config tfsdk.Config | ||
|
||
// IncludeResource indicates whether the provider should populate the | ||
// Resource field in the ListResult struct. | ||
IncludeResource bool | ||
} | ||
|
||
// ListResultsStream represents a streaming response to a ListRequest. | ||
// An instance of this struct is supplied as an argument to the provider's | ||
// ListResource function implementation function. The provider should set a Results | ||
// iterator function that yields zero or more results of type ListResult. | ||
// | ||
// For convenience, a provider implementation may choose to convert a slice of | ||
// results into an iterator using [slices.Values]. | ||
// | ||
// [slices.Values]: https://pkg.go.dev/slices#Values | ||
type ListResultsStream struct { | ||
// Results is a function that emits ListResult values via its yield | ||
// function argument. | ||
Results iter.Seq[ListResult] | ||
} | ||
|
||
// ListResult represents a listed managed resource instance. | ||
type ListResult struct { | ||
// Identity is the identity of the managed resource instance. | ||
// | ||
// A nil value will raise will raise a diagnostic. | ||
Identity *tfsdk.ResourceIdentity | ||
|
||
// Resource is the provider's representation of the attributes of the | ||
// listed managed resource instance. | ||
// | ||
// If ListRequest.IncludeResource is true, a nil value will raise | ||
// a warning diagnostic. | ||
Resource *tfsdk.Resource | ||
|
||
// DisplayName is a provider-defined human-readable description of the | ||
// listed managed resource instance, intended for CLI and browser UIs. | ||
DisplayName string | ||
|
||
// Diagnostics report errors or warnings related to the listed managed | ||
// resource instance. An empty slice indicates a successful operation with | ||
// no warnings or errors generated. | ||
Diagnostics diag.Diagnostics | ||
} | ||
|
||
// ValidateConfigRequest represents a request to validate the configuration of | ||
// a list resource. An instance of this request struct is supplied as an | ||
// argument to the ValidateListResourceConfig receiver method or automatically | ||
// passed through to each ListResourceConfigValidator. | ||
type ValidateConfigRequest struct { | ||
// Config is the configuration the user supplied for the resource. | ||
// | ||
// This configuration may contain unknown values if a user uses | ||
// interpolation or other functionality that would prevent Terraform | ||
// from knowing the value at request time. | ||
Config tfsdk.Config | ||
} | ||
|
||
// ValidateConfigResponse represents a response to a ValidateConfigRequest. An | ||
// instance of this response struct is supplied as an argument to the | ||
// list.ValidateListResourceConfig receiver method or automatically passed | ||
// through to each ConfigValidator. | ||
type ValidateConfigResponse struct { | ||
// Diagnostics report errors or warnings related to validating the list | ||
// configuration. An empty slice indicates success, with no warnings | ||
// or errors generated. | ||
Diagnostics diag.Diagnostics | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/list" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
) | ||
|
||
type ComputeInstanceResource struct { | ||
NoOpListResource | ||
NoOpResource | ||
} | ||
|
||
type ComputeInstanceWithValidateListResourceConfig struct { | ||
ComputeInstanceResource | ||
} | ||
|
||
type ComputeInstanceWithListResourceConfigValidators struct { | ||
ComputeInstanceResource | ||
} | ||
|
||
func (c *ComputeInstanceResource) Configure(_ context.Context, _ resource.ConfigureRequest, _ *resource.ConfigureResponse) { | ||
} | ||
|
||
func (c *ComputeInstanceResource) Metadata(_ context.Context, _ resource.MetadataRequest, _ *resource.MetadataResponse) { | ||
} | ||
|
||
func (c *ComputeInstanceWithValidateListResourceConfig) ValidateListResourceConfig(_ context.Context, _ list.ValidateConfigRequest, _ *list.ValidateConfigResponse) { | ||
} | ||
|
||
func (c *ComputeInstanceWithListResourceConfigValidators) ListResourceConfigValidators(_ context.Context) []list.ConfigValidator { | ||
return nil | ||
} | ||
|
||
// ExampleResource_listable demonstrates a resource.Resource that implements | ||
// list.ListResource interfaces. | ||
func ExampleResource_listable() { | ||
var _ list.ListResource = &ComputeInstanceResource{} | ||
var _ list.ListResourceWithConfigure = &ComputeInstanceResource{} | ||
var _ list.ListResourceWithValidateConfig = &ComputeInstanceWithValidateListResourceConfig{} | ||
var _ list.ListResourceWithConfigValidators = &ComputeInstanceWithListResourceConfigValidators{} | ||
|
||
var _ resource.Resource = &ComputeInstanceResource{} | ||
var _ resource.ResourceWithConfigure = &ComputeInstanceResource{} | ||
|
||
// Output: | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/list" | ||
) | ||
|
||
type NoOpListResource struct{} | ||
|
||
func (*NoOpListResource) ListResourceConfigSchema(_ context.Context, _ list.ListResourceSchemaRequest, _ *list.ListResourceSchemaResponse) { | ||
} | ||
|
||
func (*NoOpListResource) List(_ context.Context, _ list.ListRequest, _ *list.ListResultsStream) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
) | ||
|
||
type NoOpResource struct{} | ||
|
||
func (*NoOpResource) Schema(_ context.Context, _ resource.SchemaRequest, _ *resource.SchemaResponse) { | ||
} | ||
|
||
func (*NoOpResource) Create(_ context.Context, _ resource.CreateRequest, _ *resource.CreateResponse) { | ||
} | ||
|
||
func (*NoOpResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) { | ||
} | ||
|
||
func (*NoOpResource) Update(_ context.Context, _ resource.UpdateRequest, _ *resource.UpdateResponse) { | ||
} | ||
|
||
func (*NoOpResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package list | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A thought about the package location. Since list is coupled to managed resource (i.e. can't list without some managed resource existing in the provider), should we move all of these packages under
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😃 I started from I took a cue from: an instance of a "list resource" corresponds to a "list block" in Terraform. So from a config perspective, And we leave the door open for our understanding of |
||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/list/schema" | ||
) | ||
|
||
// ListResourceSchemaRequest represents a request for the ListResource to | ||
// return its schema. An instance of this request struct is supplied as an | ||
// argument to the ListResource type ListResourceSchema method. | ||
type ListResourceSchemaRequest struct{} | ||
|
||
// ListResourceSchemaResponse represents a response to a | ||
// ListResourceSchemaRequest. An instance of this response struct is supplied | ||
// as an argument to the ListResource type ListResourceResourceSchema method. | ||
type ListResourceSchemaResponse struct { | ||
// Schema is the schema of the list resource. | ||
Schema schema.Schema | ||
|
||
// Diagnostics report errors or warnings related to retrieving the list | ||
// resource schema. An empty slice indicates success, with no warnings | ||
// or errors generated. | ||
Diagnostics diag.Diagnostics | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package schema | ||
|
||
type Schema struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💭 Clearly, this is a (very) partial implementation of a schema type 😃 In the interest of small pull requests, I'd like to save the details for the next PR. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package tfsdk | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
) | ||
|
||
// Resource represents a Terraform resource. | ||
type Resource struct { | ||
Raw tftypes.Value | ||
Schema fwschema.Schema | ||
} | ||
|
||
// Get populates the struct passed as `target` with the resource. | ||
func (c Resource) Get(ctx context.Context, target interface{}) diag.Diagnostics { | ||
return c.data().Get(ctx, target) | ||
} | ||
|
||
// GetAttribute retrieves the attribute or block found at `path` and populates | ||
// the `target` with the value. This method is intended for top level schema | ||
// attributes or blocks. Use `types` package methods or custom types to step | ||
// into collections. | ||
// | ||
// Attributes or elements under null or unknown collections return null | ||
// values, however this behavior is not protected by compatibility promises. | ||
func (c Resource) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { | ||
return c.data().GetAtPath(ctx, path, target) | ||
} | ||
|
||
// PathMatches returns all matching path.Paths from the given path.Expression. | ||
// | ||
// If a parent path is null or unknown, which would prevent a full expression | ||
// from matching, the parent path is returned rather than no match to prevent | ||
// false positives. | ||
func (c Resource) PathMatches(ctx context.Context, pathExpr path.Expression) (path.Paths, diag.Diagnostics) { | ||
return c.data().PathMatches(ctx, pathExpr) | ||
} | ||
|
||
func (c Resource) data() fwschemadata.Data { | ||
return fwschemadata.Data{ | ||
Description: fwschemadata.DataDescriptionConfiguration, | ||
Schema: c.Schema, | ||
TerraformValue: c.Raw, | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.