diff --git a/list/list_resource.go b/list/list_resource.go index def1abfd3..a5d36a533 100644 --- a/list/list_resource.go +++ b/list/list_resource.go @@ -85,35 +85,39 @@ type ListResourceWithValidateConfig interface { ValidateListResourceConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) } -// 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. +// 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 List +// 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. + // [ListResult.Resource] field. 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. +// ListResultsStream represents a streaming response to a [ListRequest]. An +// instance of this struct is supplied as an argument to the provider's +// [ListResource.List] function. The provider should set a Results iterator +// function that pushes 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 + // Results is a function that emits [ListResult] values via its push // function argument. + // + // To indicate a fatal processing error, push a [ListResult] that contains + // a [diag.ErrorDiagnostic]. Results iter.Seq[ListResult] } +// NoListResults is an iterator that pushes zero results. +var NoListResults = func(func(ListResult) bool) {} + // ListResult represents a listed managed resource instance. type ListResult struct { // Identity is the identity of the managed resource instance. @@ -124,7 +128,7 @@ type ListResult struct { // 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 + // If [ListRequest.IncludeResource] is true, a nil value will raise // a warning diagnostic. Resource *tfsdk.Resource @@ -140,8 +144,8 @@ type ListResult struct { // 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. +// argument to the [ListResourceWithValidateConfig.ValidateListResourceConfig] +// receiver method or automatically passed through to each [ConfigValidator]. type ValidateConfigRequest struct { // Config is the configuration the user supplied for the resource. // @@ -151,10 +155,10 @@ type ValidateConfigRequest struct { 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. +// 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 diff --git a/list/schema/schema.go b/list/schema/schema.go index cdf07d662..fad93273c 100644 --- a/list/schema/schema.go +++ b/list/schema/schema.go @@ -17,9 +17,8 @@ import ( // Schema must satify the fwschema.Schema interface. var _ fwschema.Schema = Schema{} -// Schema defines the structure and value types of resource data. This type -// is used as the resource.SchemaResponse type Schema field, which is -// implemented by the resource.DataSource type Schema method. +// Schema defines the structure and value types of a list block. This is +// returned as a ListResourceSchemas map value by the GetProviderSchemas RPC. type Schema struct { // Attributes is the mapping of underlying attribute names to attribute // definitions. @@ -106,7 +105,7 @@ func (s Schema) GetMarkdownDescription() string { return s.MarkdownDescription } -// GetVersion returns zero because list resource schemas do not have a version. +// GetVersion always returns 0 because list resource schemas cannot be versioned. func (s Schema) GetVersion() int64 { return 0 } @@ -126,13 +125,6 @@ func (s Schema) TypeAtTerraformPath(ctx context.Context, p *tftypes.AttributePat return fwschema.SchemaTypeAtTerraformPath(ctx, s, p) } -// Validate verifies that the schema is not using a reserved field name for a top-level attribute. -// -// Deprecated: Use the ValidateImplementation method instead. -func (s Schema) Validate() diag.Diagnostics { - return s.ValidateImplementation(context.Background()) -} - // ValidateImplementation contains logic for validating the provider-defined // implementation of the schema and underlying attributes and blocks to prevent // unexpected errors or panics. This logic runs during the diff --git a/list/schema/schema_test.go b/list/schema/schema_test.go index 14fb2fff0..b556ac7df 100644 --- a/list/schema/schema_test.go +++ b/list/schema/schema_test.go @@ -797,47 +797,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } } -func TestSchemaValidate(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - schema schema.Schema - expectedDiags diag.Diagnostics - }{ - "empty-schema": { - schema: schema.Schema{}, - }, - "validate-implementation-error": { - schema: schema.Schema{ - Attributes: map[string]schema.Attribute{ - "depends_on": schema.StringAttribute{}, - }, - }, - expectedDiags: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Reserved Root Attribute/Block Name", - "When validating the resource or data source schema, an implementation issue was found. "+ - "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ - "\"depends_on\" is a reserved root attribute/block name. "+ - "This is to prevent practitioners from needing special Terraform configuration syntax.", - ), - }, - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - t.Parallel() - - diags := testCase.schema.Validate() - - if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { - t.Errorf("Unexpected diagnostics (+wanted, -got): %s", diff) - } - }) - } -} - func TestSchemaValidateImplementation(t *testing.T) { t.Parallel() diff --git a/list/schema/string_attribute_test.go b/list/schema/string_attribute_test.go index c1dffa2f2..b2d2ff38b 100644 --- a/list/schema/string_attribute_test.go +++ b/list/schema/string_attribute_test.go @@ -4,7 +4,6 @@ package schema_test import ( - "context" "fmt" "strings" "testing" @@ -13,15 +12,10 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + listschema "github.com/hashicorp/terraform-plugin-framework/list/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -30,31 +24,31 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute step tftypes.AttributePathStep expected any expectedError error }{ "AttributeName": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, step: tftypes.AttributeName("test"), expected: nil, expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.StringType"), }, "ElementKeyInt": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, step: tftypes.ElementKeyInt(1), expected: nil, expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.StringType"), }, "ElementKeyString": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, step: tftypes.ElementKeyString("test"), expected: nil, expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.StringType"), }, "ElementKeyValue": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), expected: nil, expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.StringType"), @@ -92,15 +86,15 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected string }{ "no-deprecation-message": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: "", }, "deprecation-message": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ DeprecationMessage: "test deprecation message", }, expected: "test deprecation message", @@ -124,18 +118,18 @@ func TestStringAttributeEqual(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute other fwschema.Attribute expected bool }{ "different-type": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, other: testschema.AttributeWithStringValidators{}, expected: false, }, "equal": { - attribute: schema.StringAttribute{}, - other: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, + other: listschema.StringAttribute{}, expected: true, }, } @@ -157,15 +151,15 @@ func TestStringAttributeGetDescription(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected string }{ "no-description": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: "", }, "description": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ Description: "test description", }, expected: "test description", @@ -189,15 +183,15 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected string }{ "no-markdown-description": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: "", }, "markdown-description": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ MarkdownDescription: "test description", }, expected: "test description", @@ -221,15 +215,15 @@ func TestStringAttributeGetType(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected attr.Type }{ "base": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: types.StringType, }, "custom-type": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ CustomType: testtypes.StringType{}, }, expected: testtypes.StringType{}, @@ -253,19 +247,13 @@ func TestStringAttributeIsComputed(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-computed": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, - "computed": { - attribute: schema.StringAttribute{ - Computed: true, - }, - expected: true, - }, } for name, testCase := range testCases { @@ -285,15 +273,15 @@ func TestStringAttributeIsOptional(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-optional": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, "optional": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ Optional: true, }, expected: true, @@ -317,15 +305,15 @@ func TestStringAttributeIsRequired(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-required": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, "required": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ Required: true, }, expected: true, @@ -349,19 +337,13 @@ func TestStringAttributeIsSensitive(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-sensitive": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, - "sensitive": { - attribute: schema.StringAttribute{ - Sensitive: true, - }, - expected: true, - }, } for name, testCase := range testCases { @@ -381,19 +363,13 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-writeOnly": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, - "writeOnly": { - attribute: schema.StringAttribute{ - WriteOnly: true, - }, - expected: true, - }, } for name, testCase := range testCases { @@ -409,96 +385,19 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } } -func TestStringAttributeStringDefaultValue(t *testing.T) { - t.Parallel() - - opt := cmp.Comparer(func(x, y defaults.String) bool { - ctx := context.Background() - req := defaults.StringRequest{} - - xResp := defaults.StringResponse{} - x.DefaultString(ctx, req, &xResp) - - yResp := defaults.StringResponse{} - y.DefaultString(ctx, req, &yResp) - - return xResp.PlanValue.Equal(yResp.PlanValue) - }) - - testCases := map[string]struct { - attribute schema.StringAttribute - expected defaults.String - }{ - "no-default": { - attribute: schema.StringAttribute{}, - expected: nil, - }, - "default": { - attribute: schema.StringAttribute{ - Default: stringdefault.StaticString("test-value"), - }, - expected: stringdefault.StaticString("test-value"), - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - t.Parallel() - - got := testCase.attribute.StringDefaultValue() - - if diff := cmp.Diff(got, testCase.expected, opt); diff != "" { - t.Errorf("unexpected difference: %s", diff) - } - }) - } -} - -func TestStringAttributeStringPlanModifiers(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - attribute schema.StringAttribute - expected []planmodifier.String - }{ - "no-planmodifiers": { - attribute: schema.StringAttribute{}, - expected: nil, - }, - "planmodifiers": { - attribute: schema.StringAttribute{ - PlanModifiers: []planmodifier.String{}, - }, - expected: []planmodifier.String{}, - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - t.Parallel() - - got := testCase.attribute.StringPlanModifiers() - - if diff := cmp.Diff(got, testCase.expected); diff != "" { - t.Errorf("unexpected difference: %s", diff) - } - }) - } -} - func TestStringAttributeStringValidators(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected []validator.String }{ "no-validators": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: nil, }, "validators": { - attribute: schema.StringAttribute{ + attribute: listschema.StringAttribute{ Validators: []validator.String{}, }, expected: []validator.String{}, @@ -518,78 +417,15 @@ func TestStringAttributeStringValidators(t *testing.T) { } } -func TestStringAttributeValidateImplementation(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - attribute schema.StringAttribute - request fwschema.ValidateImplementationRequest - expected *fwschema.ValidateImplementationResponse - }{ - "computed": { - attribute: schema.StringAttribute{ - Computed: true, - }, - request: fwschema.ValidateImplementationRequest{ - Name: "test", - Path: path.Root("test"), - }, - expected: &fwschema.ValidateImplementationResponse{}, - }, - "default-without-computed": { - attribute: schema.StringAttribute{ - Default: stringdefault.StaticString("test"), - }, - request: fwschema.ValidateImplementationRequest{ - Name: "test", - Path: path.Root("test"), - }, - expected: &fwschema.ValidateImplementationResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Schema Using Attribute Default For Non-Computed Attribute", - "Attribute \"test\" must be computed when using default. "+ - "This is an issue with the provider and should be reported to the provider developers.", - ), - }, - }, - }, - "default-with-computed": { - attribute: schema.StringAttribute{ - Computed: true, - Default: stringdefault.StaticString("test"), - }, - request: fwschema.ValidateImplementationRequest{ - Name: "test", - Path: path.Root("test"), - }, - expected: &fwschema.ValidateImplementationResponse{}, - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - t.Parallel() - - got := &fwschema.ValidateImplementationResponse{} - testCase.attribute.ValidateImplementation(context.Background(), testCase.request, got) - - if diff := cmp.Diff(got, testCase.expected); diff != "" { - t.Errorf("unexpected difference: %s", diff) - } - }) - } -} - func TestStringAttributeIsRequiredForImport(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-requiredForImport": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, } @@ -611,11 +447,11 @@ func TestStringAttributeIsOptionalForImport(t *testing.T) { t.Parallel() testCases := map[string]struct { - attribute schema.StringAttribute + attribute listschema.StringAttribute expected bool }{ "not-optionalForImport": { - attribute: schema.StringAttribute{}, + attribute: listschema.StringAttribute{}, expected: false, }, }