Using `map[string]interface{}` and `[]interface{}` in terraform provider framework

136 views Asked by At

I am trying to construct a terraform provider that can deal with arbitrary map and list inputs. (I am using the provider "framework")

Let's assume my upstream API expects a JSON payload like this (in psuedo-code):

name: string
input1: map(any)
input2: list(any)

So we could send this:

{
  "name": "foobar"
  "input1":  {
      "apple": "tree"
    },
  "input2": ["hello", 123]
}

But also this:

{
  "name": "foobaz"
  "input1":  {
      "this": {
         "is": "nested"
      },
     "this-though": "isnt"
    },
  "input2": [{"here": "things}]
}

etc.

To achieve the above I am using the following as my payload in the API client:

type MyModel struct {
    Name   string                 `json:"name,omitempty"`
    Input1 map[string]interface{} `json:"input1,omitempty"`
    Input2 []interface{}          `json:"input2,omitempty"`
}

For the resource schema I am using:

  resp.Schema = schema.Schema{
    Description: "Manages a smart contract.",
    Attributes: map[string]schema.Attribute{
        "ID": schema.StringAttribute{
            Computed:    true,
        },
        "Name": schema.StringAttribute{
            Required:    true,
        },
        "input1": schema.MapAttribute{ <---- What should this be?
            Required:    true,
        },
        "input2": schema.ListAttribute{ <---- What should this be?
            Required:    true,
        },
    },

Below is an example of how I am attempting to implement Create

// Create a new resource.
func (r *myModelResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
    // Retrieve values from plan
    var plan myModelModel
    diags := req.Plan.Get(ctx, &plan)
    resp.Diagnostics.Append(diags...)
    if resp.Diagnostics.HasError() {
        return
    }

    newMyModel := client.MyModel{
        Name:   plan.Name.ValueString(),
        Input1: plan.Input1.ValueString(),  // <---- THIS IS WRONG. What should I do here?
        Input2: plan.Input2.ValueString(),  // <---- THIS IS WRONG. What should I do here?
    }

    // Update existing myModel
    myModel, err := r.client.CreateMyModel(newMyModel)
    if err != nil {
        resp.Diagnostics.AddError(
            "Error Updating MyModel",
            "Could not update MyModel, unexpected error: "+err.Error(),
        )
        return
    }

    plan.Name = types.StringValue(myModel.Name)
    plan.Input1 = types.StringValue(myModel.Input1) // <---- THIS IS WRONG. What should I do here?
    plan.Input2 = types.StringValue(myModel.Input2) // <---- THIS IS WRONG. What should I do here?

    // Set state to fully populated data
    diags = resp.State.Set(ctx, plan)
    resp.Diagnostics.Append(diags...)
    if resp.Diagnostics.HasError() {
        return
    }
}

What should my schema attribute types be, and how should I do the conversion to and from MyModel?

0

There are 0 answers