Skip to content

Commit

Permalink
something works...
Browse files Browse the repository at this point in the history
  • Loading branch information
rambleraptor committed Jan 14, 2025
1 parent 71a70e4 commit 408b3db
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 67 deletions.
72 changes: 34 additions & 38 deletions internal/provider/client.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,65 @@
package provider

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"

"github.com/aep-dev/aep-lib-go/pkg/api"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

func Create(r *api.Resource, c *http.Client, serverUrl string, body map[string]interface{}) error {
func Create(ctx context.Context, r *api.Resource, c *http.Client, serverUrl string, body map[string]interface{}) (map[string]interface{}, error) {
suffix := ""
if r.CreateMethod.SupportsUserSettableCreate {
id, ok := body["path"]
id, ok := body["id"]
if !ok {
return fmt.Errorf("path field not found in %v", body)
return nil, fmt.Errorf("id field not found in %v", body)
}
idString, ok := id.(string)
if !ok {
return fmt.Errorf("path field is not string %v", id)
return nil, fmt.Errorf("id field is not string %v", id)
}

suffix = fmt.Sprintf("?id=%s", idString)
}
url, err := createBase(r, body, serverUrl, suffix)
url, err := createBase(ctx, r, body, serverUrl, suffix)
if err != nil {
return err
return nil, err
}

jsonBody, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("error marshalling JSON: %v", err)
return nil, fmt.Errorf("error marshalling JSON: %v", err)
}

req, err := http.NewRequest("POST", url, strings.NewReader(string(jsonBody)))
if err != nil {
return fmt.Errorf("error creating post request: %v", err)
return nil, fmt.Errorf("error creating post request: %v", err)
}

_, err = c.Do(req)
return err
resp, err := c.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %v", err)
}
tflog.Info(ctx, fmt.Sprintf("Response body: %q", string(respBody)))
var data map[string]interface{}
err = json.Unmarshal(respBody, &data)
if err != nil {
return nil, err
}
return data, nil
}

func Read(r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) (map[string]interface{}, error) {
func Read(ctx context.Context, r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) (map[string]interface{}, error) {
id, ok := parameters["path"]
if !ok {
return nil, fmt.Errorf("path field not found in %v", parameters)
Expand All @@ -53,7 +69,7 @@ func Read(r *api.Resource, c *http.Client, serverUrl string, parameters map[stri
return nil, fmt.Errorf("path field is not string %v", id)
}

url, err := createBase(r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
url, err := createBase(ctx, r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -83,7 +99,7 @@ func Read(r *api.Resource, c *http.Client, serverUrl string, parameters map[stri
return data, nil
}

func Delete(r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) error {
func Delete(ctx context.Context, r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) error {
id, ok := parameters["path"]
if !ok {
return fmt.Errorf("path field not found in %v", parameters)
Expand All @@ -93,7 +109,7 @@ func Delete(r *api.Resource, c *http.Client, serverUrl string, parameters map[st
return fmt.Errorf("path field is not string %v", id)
}

url, err := createBase(r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
url, err := createBase(ctx, r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
if err != nil {
return err
}
Expand All @@ -107,7 +123,7 @@ func Delete(r *api.Resource, c *http.Client, serverUrl string, parameters map[st
return err
}

func Update(r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) error {
func Update(ctx context.Context, r *api.Resource, c *http.Client, serverUrl string, parameters map[string]interface{}) error {
id, ok := parameters["path"]
if !ok {
return fmt.Errorf("path field not found in %v", parameters)
Expand All @@ -117,7 +133,7 @@ func Update(r *api.Resource, c *http.Client, serverUrl string, parameters map[st
return fmt.Errorf("path field is not string %v", id)
}

url, err := createBase(r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
url, err := createBase(ctx, r, parameters, serverUrl, fmt.Sprintf("/%s", idString))
if err != nil {
return err
}
Expand All @@ -136,26 +152,6 @@ func Update(r *api.Resource, c *http.Client, serverUrl string, parameters map[st
return err
}

func createBase(r *api.Resource, body map[string]interface{}, serverUrl string, suffix string) (string, error) {
pElems := []string{}
for i, p := range r.PatternElems {
// last element, we assume this was handled by the caller.
if i == len(r.PatternElems)-1 {
continue
}
if i%2 == 0 {
pElems = append(pElems, p)
} else {
v, ok := body[p]
if !ok {
return "", fmt.Errorf("%s not found in resource", p)
}
s, ok := v.(string)
if !ok {
return "", fmt.Errorf("%s value %v cannot be converted to string", p, v)
}
pElems = append(pElems, s)
}
}
return serverUrl + "/" + strings.Join(pElems, "/") + suffix, nil
func createBase(ctx context.Context, r *api.Resource, body map[string]interface{}, serverUrl string, suffix string) (string, error) {

Check failure on line 155 in internal/provider/client.go

View workflow job for this annotation

GitHub Actions / Build

`createBase` - `ctx` is unused (unparam)

Check failure on line 155 in internal/provider/client.go

View workflow job for this annotation

GitHub Actions / Build

`createBase` - `ctx` is unused (unparam)
return serverUrl + "/" + r.Plural + suffix, nil
}
14 changes: 13 additions & 1 deletion internal/provider/data/resource.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// Inspiration: https://github.com/hashicorp/terraform-plugin-framework/issues/1035#issuecomment-2396927170

package data

import (
Expand Down Expand Up @@ -41,7 +43,7 @@ type Resource struct {
//
// It assumes the ID value exists and is a string type.
func (r Resource) GetId() string {
return *r.Values["id"].String
return *r.Values["path"].String
}

// WithType adds type information into a Resource as this is not stored as part
Expand Down Expand Up @@ -110,5 +112,15 @@ func (r *Resource) ToJSON() (map[string]interface{}, error) {
}

return jsonDataMap, nil
}

// TODO: This is super brittle!
func ToResource(m map[string]interface{}, r *Resource) error {
for k, v := range m {
vString, ok := v.(string)
if ok {
r.Values[k] = Value{String: &vString}
}
}
return nil
}
78 changes: 56 additions & 22 deletions internal/provider/example_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-provider-scaffolding/internal/provider/data"
)

Expand Down Expand Up @@ -65,29 +66,41 @@ func (r *ExampleResource) schemaAttributes() map[string]schema.Attribute {
case "number":
a = schema.NumberAttribute{
MarkdownDescription: "",
Computed: prop.ReadOnly,
Required: false,
Optional: true,
}
m[name] = a
case "string":
a = schema.StringAttribute{
MarkdownDescription: "",
Computed: prop.ReadOnly,
Required: false,
Optional: true,
}
m[name] = a
case "boolean":
a = schema.BoolAttribute{
MarkdownDescription: "",
Computed: prop.ReadOnly,
Required: false,
Optional: true,
}
m[name] = a
case "integer":
a = schema.Int64Attribute{
MarkdownDescription: "",
Computed: prop.ReadOnly,
Required: false,
Optional: true,
}
m[name] = a
}
}
m["id"] = schema.StringAttribute{
MarkdownDescription: "The id of the resource",
Required: true,
}
return m
}

Expand All @@ -112,114 +125,135 @@ func (r *ExampleResource) Configure(ctx context.Context, req resource.ConfigureR
}

func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
data := &data.Resource{}
dataResource := &data.Resource{}

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
resp.Diagnostics.Append(req.Plan.Get(ctx, &dataResource)...)

if resp.Diagnostics.HasError() {
return
}

jsonDataMap, err := data.ToJSON()
jsonDataMap, err := dataResource.ToJSON()
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal JSON, got error: %s", err))
return
}

err = Create(r.resource, r.client, r.api.ServerURL, jsonDataMap)
a, err := Create(ctx, r.resource, r.client, r.api.ServerURL, jsonDataMap)

if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create example, got error: %s", err))
return
}

a, err := Read(r.resource, r.client, r.api.ServerURL, jsonDataMap)
tflog.Info(ctx, fmt.Sprintf("resource state: %v", a))

err = data.ToResource(a, dataResource)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal example, got error: %s", err))
return
}

tflog.Info(ctx, fmt.Sprintf("about to save: %v"))

Check failure on line 158 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

Check failure on line 158 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &a)...)
resp.Diagnostics.Append(resp.State.Set(ctx, dataResource)...)
}

func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
data := &data.Resource{}
dataResource := &data.Resource{}

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
resp.Diagnostics.Append(req.State.Get(ctx, &dataResource)...)

if resp.Diagnostics.HasError() {
return
}

jsonDataMap, err := data.ToJSON()
jsonDataMap, err := dataResource.ToJSON()
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal JSON, got error: %s", err))
return
}

a, err := Read(r.resource, r.client, r.api.ServerURL, jsonDataMap)
a, err := Read(ctx, r.resource, r.client, r.api.ServerURL, jsonDataMap)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
return
}

err = data.ToResource(a, dataResource)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal example, got error: %s", err))
return
}

tflog.Info(ctx, fmt.Sprintf("about to save: %v"))

Check failure on line 192 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

Check failure on line 192 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &a)...)
resp.Diagnostics.Append(resp.State.Set(ctx, dataResource)...)
}

func (r *ExampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
data := &data.Resource{}
dataResource := &data.Resource{}

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
resp.Diagnostics.Append(req.Plan.Get(ctx, &dataResource)...)

if resp.Diagnostics.HasError() {
return
}

jsonDataMap, err := data.ToJSON()
jsonDataMap, err := dataResource.ToJSON()
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal JSON, got error: %s", err))
return
}

err = Update(r.resource, r.client, r.api.ServerURL, jsonDataMap)
err = Update(ctx, r.resource, r.client, r.api.ServerURL, jsonDataMap)

if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update example, got error: %s", err))
return
}

a, err := Read(r.resource, r.client, r.api.ServerURL, jsonDataMap)
a, err := Read(ctx, r.resource, r.client, r.api.ServerURL, jsonDataMap)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
return
}
tflog.Info(ctx, fmt.Sprintf("Create response: %v", a))

err = data.ToResource(a, dataResource)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal example, got error: %s", err))
return
}

tflog.Info(ctx, fmt.Sprintf("about to save: %v"))

Check failure on line 234 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

Check failure on line 234 in internal/provider/example_resource.go

View workflow job for this annotation

GitHub Actions / Build

printf: fmt.Sprintf format %v reads arg #1, but call has 0 args (govet)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &a)...)
resp.Diagnostics.Append(resp.State.Set(ctx, dataResource)...)
}

func (r *ExampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
data := &data.Resource{}
dataResource := &data.Resource{}

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
resp.Diagnostics.Append(req.State.Get(ctx, &dataResource)...)

if resp.Diagnostics.HasError() {
return
}

jsonDataMap, err := data.ToJSON()
jsonDataMap, err := dataResource.ToJSON()
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal JSON, got error: %s", err))
return
}

err = Delete(r.resource, r.client, r.api.ServerURL, jsonDataMap)
err = Delete(ctx, r.resource, r.client, r.api.ServerURL, jsonDataMap)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete example, got error: %s", err))
return
Expand Down
Loading

0 comments on commit 408b3db

Please sign in to comment.