Provider Framework - Creating a set from []string?

I’m currently trying to implement read and hitting my head against a wall.

I have an attribute allowed_values which is of type types.SetType(). I just want to add it to the state, but it seems I do it the wrong way, as it’s throwing a nil pointer exception.

I’m currently doing:

// Schema & structs
"allowed_values": {
	Optional: true,
	Type: types.SetType{
		ElemType: types.StringType,
	},
}

type DeploymentTypeParametersModel struct {
	Type          types.String `tfsdk:"type"`
	Name          types.String `tfsdk:"name"`
	ReadOnly      types.Bool   `tfsdk:"read_only"`
	Required      types.Bool   `tfsdk:"required"`
	AllowedValues types.Set    `tfsdk:"allowed_values"`
	IsIdentifier  types.Bool   `tfsdk:"is_identifier"`
	Default       types.String `tfsdk:"default"`
}
if v.AllowedValues != nil {
  if len(v.AllowedValues) > 0 {
    elems := make([]attr.Value, 0)
    for _, elem := range v.AllowedValues {
      tfval := tftypes.NewValue(tftypes.String, elem)
      set := types.SetType{
        ElemType: types.StringType,
      }
      val, err := set.ValueFromTerraform(ctx, tfval)
      if err != nil {
        resp.Diagnostics.AddError(
          "GetDeploymentType() failed",
          fmt.Sprintf("Underlying error: %v", err),
        )
        return
      }

      elems = append(elems, val)
    }
    p.AllowedValues = types.Set{
      ElemType: types.StringType,
      Elems:    elems,
    }
  }
}

What is the right way to create a new set from a []string?

Got it. I’ve used tfsdk.ValueFrom

var model DeploymentTypeParametersModel

resp.Diagnostics.Append(tfsdk.ValueFrom(
  ctx,
  v.AllowedValues,
  types.SetType{ElemType: types.StringType},
  &model.AllowedValues,
)...)

Hi @tiwood :wave: Glad you figured out a working solution for your case.

Just to drop an additional aside about value handling in the framework, if you wanted to, you could also opt out of using a single data model that uses a types.Set type for handling that particular data. While the types types are strongly encouraged for handling data coming from Terraform which may contain unknown values (such as configuration and plan data), state values are guaranteed to not have unknown values. This means that for functionality such as Read methods, you could in theory (depending on your actual schema) setup something with more standard Go types such as:

// This example shows pointer types but could elide
// the pointers for Required attributes.
type DeploymentTypeParametersStateModel struct{
	Type          *string `tfsdk:"type"`
	Name          *string `tfsdk:"name"`
	ReadOnly      *bool   `tfsdk:"read_only"`
	Required      *bool   `tfsdk:"required"`
	AllowedValues []string    `tfsdk:"allowed_values"`
	IsIdentifier  *bool   `tfsdk:"is_identifier"`
	Default       *string `tfsdk:"default"`
}

An another option, the framework can also support setting attributes via reflection rules with more standard Go types, e.g.

allowedValues := []string{"one", "two"}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("allowed_values"), allowedValues)...)

Which may be easier in certain situations.

Hope this helps!