Skip to content

Exactly One Multiple Attribute Validation (ExactlyOneOf / ExactlyOneAttribute) #15

@bflad

Description

@bflad

Terraform CLI and Framework Versions

Any Terraform CLI version; terraform-plugin-framework v0.8.0

Use Cases or Problem Statement

Provider developers should be able to generically validate that in a set of attributes, only exactly one of them should all contain a non-null configuration value.

Proposal

These are not necessarily mutually exclusive proposals -- both types of implementations have been requested in the past.

Proposal 1 (AttributeValidator Style)

This would be similar to the helper/schema.Schema.ExactlyOneOf field implementation.

Inside a multivalidator package, create a new unexported type that satisfies the tfsdk.AttributeValidator interface:

var _ exactlyOneOfValidator = tfsdk.AttributeValidator

type exactlyOneOfValidator struct {
  attributePaths []AttributePath
}

func (v exactlyOneOfValidator) Description(ctx context.Context) string {/*...*/}
func (v exactlyOneOfValidator) MarkdownDescription(ctx context.Context) string {/*...*/}
func (v exactlyOneOfValidator) Validate(ctx context.Context, req tfsdk.ValidateAttributeRequest, resp *tfsdk.ValidateAttributeResponse) {/*...*/}

Then, create an exported function that returns it:

func ExactlyOneOf(attributePaths AttributePath...) AttributeValidator {/*...*/}

This would allow provider developers to declare attributes such as:

tfsdk.Attribute{
  // ... other fields ...
  Type: types.String{},
  Validators: tfsdk.AttributeValidators{
    multivalidator.ExactlyOneOf(
      tftypes.NewAttributePath().WithAttributeName("other"), // until attribute paths/matchers are native
    ),
  },
},

Proposal 2 (ConfigValidator Style)

Rather than being part of the schema, this would enable provider developers to create it at the top level of a resource, etc.

Inside a configvalidator package, create a new unexported type that satisfies the tfsdk.ResourceConfigValidator interface (allowing a configuration validator across data sources, providers, and resources would require an upstream breaking change to those interfaces):

var _ exactlyOneAttributeValidator = tfsdk.ResourceConfigValidator

type exactlyOneAttributeValidator struct {
  attributePaths []AttributePath
}

func (v exactlyOneAttributeValidator) Description(ctx context.Context) string {/*...*/}
func (v exactlyOneAttributeValidator) MarkdownDescription(ctx context.Context) string {/*...*/}
func (v exactlyOneAttributeValidator) Validate(ctx context.Context, req tfsdk.ResourceConfigValidatorRequest, resp *tfsdk.ResourceConfigValidatorResponse) {/*...*/}

Then, create an exported function that returns it:

func ExactlyOneAttribute(attributePaths AttributePath...) AttributeValidator {/*...*/}

This would allow provider developers to declare resources such as:

var _ tfsdk.ResourceWithConfigValidators = exampleResource{}

func (r exampleResource) ConfigValidators(ctx context.Context) []tfsdk.ResourceConfigValidator {
  return []tfsdk.ResourceConfigValidator{
    configvalidator.ExactlyOneAttribute(
      // attribute path 1
      // attribute path 2
    ),
  },
}

Additional Information

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requesttype/multiMultiple attribute validatorstype/schemaSchema validators

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions