Map attribute type to use with types.MapValueFrom()

Given example datasource structures and schema (see end of the post) what would be a correct way to get attr.Type for the types.MapValueFrom() call in Create() Read() etc?

For ListNestedAttribute I can do the following to get the type:

schemaType := model.getTypeFor("addresses")
list := types.ListValueFrom(ctx, schemaType, entries)

getTypeFor("addresses") checks if “attributes” element from schema map is ListNestedAttribute and if so, returns type of the NestedObject. This works fine.

For MapNestedAttribute attributes this doesn’t work, whatever type of NestedObject really is, it’s not what MapValueFrom expects, and I get the following error:

│ cannot use type provider.AddressObjectsResourceAddressesObject as schema type basetypes.MapType; basetypes.MapType must be an attr.TypeWithAttributeTypes to hold
│ provider.AddressObjectsResourceAddressesObject

The example code below:

import 	dsschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"

type AddressObjectsDataSourceModel struct {
	Addresses types.Map              `tfsdk:"addresses"`
}

func AddressObjectsDataSourceSchema() dsschema.Schema {
	return dsschema.Schema{
		Attributes: map[string]dsschema.Attribute{
			"addresses": dsschema.MapNestedAttribute{
				Required:     true,
				Optional:     false,
				Computed:     false,
				Sensitive:    false,
				NestedObject: AddressObjectsDataSourceAddressesSchema(),
			},
		},
	}
}

func (o *AddressObjectsDataSourceModel) getTypeFor(name string) attr.Type {
	schema := AddressObjectsDataSourceSchema()
	if attr, ok := schema.Attributes[name]; !ok {
		panic(fmt.Sprintf("could not resolve schema for attribute %s", name))
	} else {
		switch attr := attr.(type) {
		case dsschema.ListNestedAttribute:
			return attr.NestedObject.Type()
		case dsschema.MapNestedAttribute:
			return attr.NestedObject.Type()
		default:
			return attr.GetType()
		}
	}

	panic("unreachable")
}


func AddressObjectsDataSourceAddressesSchema() dsschema.NestedAttributeObject {
	return dsschema.NestedAttributeObject{
		Attributes: map[string]dsschema.Attribute{
			"ip_netmask": dsschema.StringAttribute{
				Description: "The IP netmask value.",
				Computed:    false,
				Required:    false,
				Optional:    true,
				Sensitive:   false,
			},
		},
	}
}

Hi @kklimonda-cl,

Sorry that you’re running into trouble here. Is the getTypeFor() function only for getting the attr.Type for use with functions like types.MapValueFrom()? If so, the framework provides an ElementType(ctx) method to get the element types that you can use instead.

func (d *dataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
	var model AddressObjectsDataSourceModel

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

	var convertDiags diag.Diagnostics
	mapValue, convertDiags = types.MapValueFrom(ctx, model.Addresses.ElementType(ctx), addresses)
	if convertDiags.HasError() {
		resp.Diagnostics.Append(convertDiags...)
		return
	}

}

Thanks, that pointed me in the right direction.