Skip to content

Allow Optional SingleNestedBlocks and Required Attributes within #740

@kenchan0130

Description

@kenchan0130

Module version

v1.2.0

Use-cases

There are times when we want to define multiple distinct SingleNestedBlocks, and would like to make those blocks optional.

In conjunction, there are also cases where we would like to make some attributes of the SingleNestedBlock required.

Here's a specific example of definitions:

schema.Schema{
	Blocks: map[string]schema.Block{
		"block1": schema.SingleNestedBlock{
			Validators: []validator.Object{
				objectvalidator.ExactlyOneOf(path.Expressions{
					path.MatchRoot("block1"),
					path.MatchRoot("block2"),
				}...),
			},
			Attributes: map[string]schema.Attribute{
				"attrA": schema.StringAttribute{
					Required: true,
				},
			},
		},
		"block2": schema.SingleNestedBlock{
			Validators: []validator.Object{
				objectvalidator.ExactlyOneOf(path.Expressions{
					path.MatchRoot("block1"),
					path.MatchRoot("block2"),
				}...),
			},
                         Attributes: map[string]schema.Attribute{
				"attrB": schema.StringAttribute{
					Optional: true,
				},
			},
		},
	}
}
resource "test_resource" "example" {
   block2 {
      attrB = "exmaple"
   }
}

However, due to the current specification of SingleNestedBlock, if one or more attributes of the block we want to make optional are required, the parent block also functions as if it were required.

Error: Missing Configuration for Required Attribute
 
   with test_resource.example,
   on terraform_plugin_test.tf line 2, in resource "test_resource" "example":
    2: resource "test_resource" "example" {
 
 Must set a configuration value for the block1.attrA attribute as the
 provider has marked it as required.

Attempted Solutions

schema.Schema{
	Blocks: map[string]schema.Block{
		"block1": schema.ListNestedBlock{
			Validators: []validator.List{
				listvalidator.ExactlyOneOf(path.Expressions{
					path.MatchRoot("block1"),
					path.MatchRoot("block2"),
				}...),
				listvalidator.SizeAtMost(1),
			},
			NestedObject: schema.NestedBlockObject{
				Attributes: map[string]schema.Attribute{
					"attrA": schema.StringAttribute{
						Required: true,
					},
				},
			},
                },
		"block2": schema.SingleNestedBlock{
			Validators: []validator.Object{
				objectvalidator.ExactlyOneOf(path.Expressions{
					path.MatchRoot("block1"),
					path.MatchRoot("block2"),
				}...),
			},
		},
	}
}

This use case can be achieved by using ListNestedBlock. However, for users who will end up using this provider, the internal implementation as a list, such as test_resource.example.block1[0], leaks into the resource interface. This is not a favorable situation for users.

Proposal

type SingleNestedBlock struct {
   // ... existing methods ...
  
  Required: bool,
  Optional: bool,
}

For instance, it could be considered to prepare an attribute that determines whether the SingleNestedBlock is optional or not. If it's optional, then the requirement of the child attributes could be ignored.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions