Skip to content
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

Consider raising error when a List (or Set) attribute defines both ElementType and CustomType #947

Open
ewbankkit opened this issue Mar 7, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@ewbankkit
Copy link
Contributor

Module version

% go list -m github.com/hashicorp/terraform-plugin-framework/...
github.com/hashicorp/terraform-plugin-framework v1.6.1

Use-cases

If the schema definition of a List (or Set) attribute contains both ElementType and CustomType then ElementType is not effective.
Consider reporting such a case during schema verification.

References

Relates hashicorp/terraform-plugin-docs#342.
Relates hashicorp/terraform-plugin-docs#343.

@ewbankkit ewbankkit added the enhancement New feature or request label Mar 7, 2024
@bendbennett
Copy link
Contributor

bendbennett commented Mar 8, 2024

Schema definitions which use a custom type for attributes which embed a collection type (e.g., basetypes.ListType), and do not specify the ElementType of the embedded type will generate unexpected output.

The following two examples are taken from hashicorp/terraform-plugin-docs#342, and hashicorp/terraform-plugin-docs#343, respectively:

"health_check_arns": schema.ListAttribute{
	ElementType: types.StringType,
	CustomType:  cctypes.MultisetType, // `MultisetType embeds `ListType` 
        /* ... */
}, 
"framework_controls": schema.SetNestedAttribute{
	NestedObject: schema.NestedAttributeObject{ 
		Attributes: map[string]schema.Attribute{ 
			"control_scope": schema.SingleNestedAttribute{
				Attributes: map[string]schema.Attribute{ 
					"compliance_resource_ids": schema.ListAttribute{
						ElementType: types.StringType,
						CustomType:  cctypes.MultisetType, // MultisetType embeds `ListType`
						/* ... */
					},
					/* ... */

In both instances, the CustomType specified (i.e., MultisetType), has an embedded ListType which has an ElementType which is nil as the ElementType defined directly on the attribute is not used by the CustomType by default.

As a consequence, this results in the GetProviderSchema RPC returning an element type of DynamicPseudoType, for instance:

    block: {
      attributes: {
        name: "framework_controls"
        nested_type: {
          attributes: {
            name: "control_scope"
            nested_type: {
              attributes: {
                name: "compliance_resource_ids"
                type: "[\"list\",\"dynamic\"]"
                description: "The ID of the only AWS resource that you want your control scope to contain."
                optional: true
                computed: true
              }

The usage of DynamicePseudoType arises because during the execution of the GetProviderSchema RPC, there is a call to SchemaAttribute(), which calls a.GetType().TerraformType(ctx), and subsequently ElementType():

func (l ListType) ElementType() attr.Type {
	if l.ElemType == nil {
		return missingType{}
	}

	return l.ElemType
}

The TerraformType() call on missingType{} returns the following:

func (t missingType) TerraformType(_ context.Context) tftypes.Type {
	return tftypes.DynamicPseudoType
}

Depending upon the where in the schema this occurs, this can give rise to unexpected results (e.g., Adding CustomType to a ListAttribute changes generated description to List of Dynamic), or errors (e.g., Unexpected error NestingSet blocks may not contain attributes of cty.DynamicPseudoType). In the latter case, the error is raised in Terraform core due to a validation failure.

@bflad
Copy link
Contributor

bflad commented Mar 8, 2024

Drive-by note: I see this issue two ways --

One, we could do what is proposed here since it reflects the current framework implementation. I would classify this more as a bug fix in the sense of not notifying provider developers more upfront of the errant implementation and needing to unfortunately discover/triage the issue other ways.

Two, this could potentially be turned around as a framework feature request that attributes could automatically do what you tried to do there with both fields. There is the attr.TypeWithElementType interface already available that could potentially help in this situation if the CustomType implemented it and the framework attribute GetType() logic checked for it. While it would likely solve this exact situation, I'm not sure off the top of my head whether just adjusting that GetType() logic would fully enable correct type handling everywhere else in the framework though. We would need to verify. 😄

Maybe its worth treating this issue as-is as a bug fix and creating a separate feature request to see if about the feasibility of using the additional type system interface if its detected on the custom type. When it comes to more gnarly schema definitions like collection-based nested attributes, needing to effectively repeat all the type information of a NestedObject, which already knows its underlying type information, is certainly a hassle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants