Skip to content

Commit

Permalink
Merge pull request #54 from gazoakley/f-rate-limit
Browse files Browse the repository at this point in the history
Add `api_response`/`create_response` attributes
  • Loading branch information
DRuggeri authored Nov 7, 2019
2 parents 9a9a764 + 7b0e88c commit 351c5f4
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 19 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ Have a look at the [examples directory](examples) for some use cases

This provider also exports the following parameters:
- `id`: The ID of the object that is being managed.
- `api_data`: After data from the API server is read, this map will include k/v pairs usable in other terraform resources as readable objects. Currently the value is the golang fmt package's representation of the value (simple primitives are set as expected, but complex types like arrays and maps contain golang formatting).
- `api_data`: After data from the API server is read, this map will include k/v pairs usable in other Terraform resources as readable objects. Currently the value is the golang fmt package's representation of the value (simple primitives are set as expected, but complex types like arrays and maps contain golang formatting).
- `api_response`: Contains the raw JSON response read back from the API server. Can be parsed with [`jsondecode`](https://www.terraform.io/docs/configuration/functions/jsondecode.html) to allow access to deeply nested data.
- `create_response`: Contains the raw JSON response from the initial object creation - use when an API only returns required data during create. Can be parsed with [`jsondecode`](https://www.terraform.io/docs/configuration/functions/jsondecode.html) to allow access to deeply nested data.

Note that the `*_path` elements are for very specific use cases where one might initially create an object in one location, but read/update/delete it on another path. For this reason, they allow for substitution to be done by the provider internally by injecting the `id` somewhere along the path. This is similar to terraform's substitution syntax in the form of `${variable.name}`, but must be done within the provider due to structure. The only substitution available is to replace the string `{id}` with the internal (terraform) `id` of the object as learned by the `id_attribute`.

Expand Down
11 changes: 8 additions & 3 deletions restapi/api_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/davecgh/go-spew/spew"
"log"
"reflect"
"strings"

"github.com/davecgh/go-spew/spew"
)

type apiObjectOpts struct {
Expand Down Expand Up @@ -36,8 +37,9 @@ type api_object struct {
id_attribute string

/* Set internally */
data map[string]interface{} /* Data as managed by the user */
api_data map[string]interface{} /* Data as available from the API */
data map[string]interface{} /* Data as managed by the user */
api_data map[string]interface{} /* Data as available from the API */
api_response string
}

// Make an api_object to manage a RESTful object in an API
Expand Down Expand Up @@ -152,6 +154,9 @@ func (obj *api_object) update_state(state string) error {
return err
}

/* Store response body for parsing via jsondecode() */
obj.api_response = state

/* A usable ID was not passed (in constructor or here),
so we have to guess what it is from the data structure */
if obj.id == "" {
Expand Down
6 changes: 4 additions & 2 deletions restapi/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package restapi

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"log"
"os"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"
)

/* After any operation that returns API data, we'll stuff
Expand All @@ -18,6 +19,7 @@ func set_resource_state(obj *api_object, d *schema.ResourceData) {
api_data[k] = fmt.Sprintf("%v", v)
}
d.Set("api_data", api_data)
d.Set("api_response", obj.api_response)
}

/* Using GetObjectAtKey, this function verifies the resulting
Expand All @@ -33,7 +35,7 @@ func GetStringAtKey(data map[string]interface{}, path string, debug bool) (strin
if t == "string" {
return res.(string), nil
} else if t == "float64" {
return strconv.FormatFloat(res.(float64), 'f', -1, 64), nil
return strconv.FormatFloat(res.(float64), 'f', -1, 64), nil
} else {
return "", fmt.Errorf("Object at path '%s' is not a JSON string or number (float64). The go fmt package says it is '%T'", path, res)
}
Expand Down
20 changes: 11 additions & 9 deletions restapi/import_api_object_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package restapi

import (
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
"github.com/hashicorp/terraform/helper/resource"
"os"
"testing"

"github.com/Mastercard/terraform-provider-restapi/fakeserver"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccRestApiObject_importBasic(t *testing.T) {
Expand All @@ -25,7 +26,7 @@ func TestAccRestApiObject_importBasic(t *testing.T) {
copy_keys: make([]string, 0),
write_returns_object: false,
create_returns_object: false,
debug: debug,
debug: debug,
}
client, err := NewAPIClient(opt)
if err != nil {
Expand All @@ -45,12 +46,13 @@ func TestAccRestApiObject_importBasic(t *testing.T) {
),
},
{
ResourceName: "restapi_object.Foo",
ImportState: true,
ImportStateId: "1234",
ImportStateIdPrefix: "/api/objects/",
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"debug", "data"},
ResourceName: "restapi_object.Foo",
ImportState: true,
ImportStateId: "1234",
ImportStateIdPrefix: "/api/objects/",
ImportStateVerify: true,
/* create_response isn't populated during import (we don't know the API response from creation) */
ImportStateVerifyIgnore: []string{"debug", "data", "create_response"},
},
},
})
Expand Down
15 changes: 14 additions & 1 deletion restapi/resource_api_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package restapi

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"log"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"
)

func resourceRestApi() *schema.Resource {
Expand Down Expand Up @@ -76,6 +77,16 @@ func resourceRestApi() *schema.Resource {
Description: "After data from the API server is read, this map will include k/v pairs usable in other terraform resources as readable objects. Currently the value is the golang fmt package's representation of the value (simple primitives are set as expected, but complex types like arrays and maps contain golang formatting).",
Computed: true,
},
"api_response": &schema.Schema{
Type: schema.TypeString,
Description: "The raw body of the HTTP response from the last read of the object.",
Computed: true,
},
"create_response": &schema.Schema{
Type: schema.TypeString,
Description: "The raw body of the HTTP response returned when creating the object.",
Computed: true,
},
"force_new": &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Expand Down Expand Up @@ -139,6 +150,8 @@ func resourceRestApiCreate(d *schema.ResourceData, meta interface{}) error {
/* Setting terraform ID tells terraform the object was created or it exists */
d.SetId(obj.id)
set_resource_state(obj, d)
/* Only set during create for APIs that don't return sensitive data on subsequent retrieval */
d.Set("create_response", obj.api_response)
}
return err
}
Expand Down
25 changes: 22 additions & 3 deletions restapi/resource_api_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ package restapi
import (
"encoding/json"
"fmt"
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
"github.com/hashicorp/terraform/helper/resource"
"os"
"testing"

"github.com/Mastercard/terraform-provider-restapi/fakeserver"
"github.com/hashicorp/terraform/helper/resource"
)

// example.Widget represents a concrete Go type that represents an API resource
Expand All @@ -39,7 +40,7 @@ func TestAccRestApiObject_Basic(t *testing.T) {
copy_keys: make([]string, 0),
write_returns_object: false,
create_returns_object: false,
debug: debug,
debug: debug,
}
client, err := NewAPIClient(opt)
if err != nil {
Expand All @@ -61,6 +62,24 @@ func TestAccRestApiObject_Basic(t *testing.T) {
resource.TestCheckResourceAttr("restapi_object.Foo", "id", "1234"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.first", "Foo"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.last", "Bar"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
resource.TestCheckResourceAttr("restapi_object.Foo", "create_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
),
},
/* Try updating the object and check create_response is unmodified */
{
Config: generate_test_resource(
"Foo",
`{ "id": "1234", "first": "Updated", "last": "Value" }`,
make(map[string]interface{}),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckRestapiObjectExists("restapi_object.Foo", "1234", client),
resource.TestCheckResourceAttr("restapi_object.Foo", "id", "1234"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.first", "Updated"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.last", "Value"),
resource.TestCheckResourceAttr("restapi_object.Foo", "api_response", "{\"first\":\"Updated\",\"id\":\"1234\",\"last\":\"Value\"}"),
resource.TestCheckResourceAttr("restapi_object.Foo", "create_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
),
},
/* Make a complex object with id_attribute as a child of another key
Expand Down

0 comments on commit 351c5f4

Please sign in to comment.