types.List Usage

I am building a new Terraform provider and I’m stuck at handling lists during data conversion.

This is the error when I do a terraform plan:

│ Expected framework type from provider logic: types.ListType[basetypes.StringType] / underlying type: tftypes.List[tftypes.String]
│ Received framework type from provider logic: types.ListType[!!! MISSING TYPE !!!] / underlying type: tftypes.List[tftypes.DynamicPseudoType]
│ Path: allowed_scopes_impersonation

Here is the schema.Shema that I am using for the resource (data source is, of course, quite similar):

func UserResourceSchema(ctx context.Context) schema.Schema {
	return schema.Schema{
		Attributes: map[string]schema.Attribute{
			"allowed_scopes_impersonation": schema.ListAttribute{
				ElementType: types.StringType,
                                Optional: true
				Computed:    true,
			},
			"data_scope_id": schema.StringAttribute{
				Computed:            true,
			},
                        .........
               }
        }
}

This is the Terraform model that I’m using:

type UserResourceTFModel struct {
	AllowedScopesImpersonation types.List     `tfsdk:"allowed_scopes_impersonation"`
	DataScopeId                types.String   `tfsdk:"data_scope_id"`
        .........

And the corresponding JSON model:

type UserAttrs struct {
	AllowedScopesImpersonation []string `json:"allowed_scopes_impersonation"`
	DataScopeId                string   `json:"data_scope_id"`

What I am not grokking is how to build the JSON from the Terraform plan data for the list type (to send to the API) and how to then convert the JSON back to Terraform to update the state:

func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var plan UserResourceTFModel

	resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
	if resp.Diagnostics.HasError() { return }

	userAttrs := axonius.UserAttrs{
		AllowedScopesImpersonation: [Here is the first problem],
		DataScopeId:                plan.DataScopeId.ValueString(),
                .........

        .........
	response, err := r.client.CreateUser(clientCtx, userAttrs)

	var createdUser UserResourceTFModel

	createdUser.AllowedScopesImpersonation = [Here is the next problem]
	createdUser.DataScopeId = types.StringValue(response.DataScopeId)
        .........

	resp.Diagnostics.Append(resp.State.Set(ctx, &createUser)...)
	if resp.Diagnostics.HasError() { return }

Any and all help is greatly appreciated.

Hi @gene.sexton :wave:

I believe you should be able to make use of the methods and functions exposed for List values described in List - Accessing Values, and List - Setting Values.

Specifically, you should be able to generate AllowedScopesImpersonation []string using the ElementsAs() method. To convert a []string to a ListValue you can use the ListValueFrom() function.

The following is a contrived example:

func (e *exampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var data exampleResourceData

	diags := req.Plan.Get(ctx, &data)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	var allowedScopesImpersonation []string

	diags = data.ListAttribute.ElementsAs(ctx, &allowedScopesImpersonation, false)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	// use allowedScopesImpersonation in API call

	listValue, diags := types.ListValueFrom(ctx, types.StringType, allowedScopesImpersonation)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	data.ListAttribute = listValue

	diags = resp.State.Set(ctx, &data)
	resp.Diagnostics.Append(diags...)
}
1 Like

@bendbennett Thank you for this.

I was beating my head against the wall and not noticing ListAttribute as a parameter of the List type object (i.e. I was trying to use various permutations of data.AllowedScopesImpersonation.ElementAs(...)).