Reputation: 1063
I am working on creating a custom terraform provider by using terraform sdk. I am trying to read data from the existing API GET call. I am finding it difficult to map the JSON response from an API to terraform schema. This is my data source schema:
func dataSourceProjects() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceProjectsRead,
Schema: map[string]*schema.Schema{
"members": &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"owners": &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
}
}
This is the API JSON response:
{
"members": [
"test12",
"test8800",
"test0032",
"test1234"
],
"owners": [
"test000",
"test111",
"test12",
"test1234"
]
}
This is my Data source read function
func dataSourceProjectsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
req, err := http.NewRequest("GET", fmt.Sprintf("%s/test/team", "https://myurl/v1"), nil)
req.Header.Add("Authorization", "Bearer xxxxx")
if err != nil {
return diag.FromErr(err)
}
r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()
members := make([]string, 0)
err = json.NewDecoder(r.Body).Decode(&members)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("members", members); err != nil {
return diag.FromErr(err)
}
// always run
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
return diags
}
I keep getting this error:
Error: json: cannot unmarshal object into Go value of type []string
Upvotes: 4
Views: 1070
Reputation: 1128
Just as Matt and the error code mentioned, you're trying to unmarshal your body to an improper struct.
Basically, you don't have an array in the Body, you have an object with fields that have an array of strings.
Your way of unmarshalling would work for bodies like
["test12", "test23", "test34"]
Try to decode it as below, so use a struct with explicit mapping.
package main
import (
"encoding/json"
"fmt"
)
type Schema struct {
Members []string `json:"members"`
Owners []string `json:"owners"`
}
var jsonBody = `{
"members": [
"test12",
"test8800",
"test0032",
"test1234"
],
"owners": [
"test000",
"test111",
"test12",
"test1234"
]
}`
func main() {
var s Schema
err := json.Unmarshal([]byte(jsonBody), &s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(s)
}
}
Upvotes: 0
Reputation: 11797
server.go
package main
import (
"log"
"net/http"
)
func main() {
s := `
{
"members": [
"test12",
"test8800",
"test0032",
"test1234"
],
"owners": [
"test000",
"test111",
"test12",
"test1234"
]
}
`
http.HandleFunc("/projects", func(w http.ResponseWriter, _ *http.Request) {
log.Println("Getting Projects")
w.WriteHeader(http.StatusOK)
w.Write([]byte(s))
})
log.Println("Listening...")
log.Fatal(http.ListenAndServe(":8000", nil))
}
data_source_projects.go
package hashicups
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func dataSourceProjects() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceProjectsRead,
Schema: map[string]*schema.Schema{
"members": &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"owners": &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
}
}
func dataSourceProjectsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
req, err := http.NewRequest("GET", fmt.Sprintf("%s/projects", "http://localhost:8000"), nil)
if err != nil {
return diag.FromErr(err)
}
r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()
var projects map[string]interface{}
err = json.NewDecoder(r.Body).Decode(&projects)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("members", projects["members"]); err != nil {
return diag.FromErr(err)
}
if err := d.Set("owners", projects["owners"]); err != nil {
return diag.FromErr(err)
}
// always run
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
return diags
}
Output:
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
project = {
"id" = "1651575329"
"members" = tolist([
"test12",
"test8800",
"test0032",
"test1234",
])
"owners" = tolist([
"test000",
"test111",
"test12",
"test1234",
])
}
Upvotes: 3