Skip to content

Commit 351c5f4

Browse files
authored
Merge pull request #54 from gazoakley/f-rate-limit
Add `api_response`/`create_response` attributes
2 parents 9a9a764 + 7b0e88c commit 351c5f4

File tree

6 files changed

+62
-19
lines changed

6 files changed

+62
-19
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ Have a look at the [examples directory](examples) for some use cases
5555

5656
This provider also exports the following parameters:
5757
- `id`: The ID of the object that is being managed.
58-
- `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).
58+
- `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).
59+
- `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.
60+
- `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.
5961

6062
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`.
6163

restapi/api_object.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8-
"github.com/davecgh/go-spew/spew"
98
"log"
109
"reflect"
1110
"strings"
11+
12+
"github.com/davecgh/go-spew/spew"
1213
)
1314

1415
type apiObjectOpts struct {
@@ -36,8 +37,9 @@ type api_object struct {
3637
id_attribute string
3738

3839
/* Set internally */
39-
data map[string]interface{} /* Data as managed by the user */
40-
api_data map[string]interface{} /* Data as available from the API */
40+
data map[string]interface{} /* Data as managed by the user */
41+
api_data map[string]interface{} /* Data as available from the API */
42+
api_response string
4143
}
4244

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

157+
/* Store response body for parsing via jsondecode() */
158+
obj.api_response = state
159+
155160
/* A usable ID was not passed (in constructor or here),
156161
so we have to guess what it is from the data structure */
157162
if obj.id == "" {

restapi/common.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package restapi
22

33
import (
44
"fmt"
5-
"github.com/hashicorp/terraform/helper/schema"
65
"log"
76
"os"
87
"strconv"
98
"strings"
9+
10+
"github.com/hashicorp/terraform/helper/schema"
1011
)
1112

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

2325
/* Using GetObjectAtKey, this function verifies the resulting
@@ -33,7 +35,7 @@ func GetStringAtKey(data map[string]interface{}, path string, debug bool) (strin
3335
if t == "string" {
3436
return res.(string), nil
3537
} else if t == "float64" {
36-
return strconv.FormatFloat(res.(float64), 'f', -1, 64), nil
38+
return strconv.FormatFloat(res.(float64), 'f', -1, 64), nil
3739
} else {
3840
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)
3941
}

restapi/import_api_object_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package restapi
22

33
import (
4-
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
5-
"github.com/hashicorp/terraform/helper/resource"
64
"os"
75
"testing"
6+
7+
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
8+
"github.com/hashicorp/terraform/helper/resource"
89
)
910

1011
func TestAccRestApiObject_importBasic(t *testing.T) {
@@ -25,7 +26,7 @@ func TestAccRestApiObject_importBasic(t *testing.T) {
2526
copy_keys: make([]string, 0),
2627
write_returns_object: false,
2728
create_returns_object: false,
28-
debug: debug,
29+
debug: debug,
2930
}
3031
client, err := NewAPIClient(opt)
3132
if err != nil {
@@ -45,12 +46,13 @@ func TestAccRestApiObject_importBasic(t *testing.T) {
4546
),
4647
},
4748
{
48-
ResourceName: "restapi_object.Foo",
49-
ImportState: true,
50-
ImportStateId: "1234",
51-
ImportStateIdPrefix: "/api/objects/",
52-
ImportStateVerify: true,
53-
ImportStateVerifyIgnore: []string{"debug", "data"},
49+
ResourceName: "restapi_object.Foo",
50+
ImportState: true,
51+
ImportStateId: "1234",
52+
ImportStateIdPrefix: "/api/objects/",
53+
ImportStateVerify: true,
54+
/* create_response isn't populated during import (we don't know the API response from creation) */
55+
ImportStateVerifyIgnore: []string{"debug", "data", "create_response"},
5456
},
5557
},
5658
})

restapi/resource_api_object.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package restapi
22

33
import (
44
"fmt"
5-
"github.com/hashicorp/terraform/helper/schema"
65
"log"
76
"strconv"
87
"strings"
8+
9+
"github.com/hashicorp/terraform/helper/schema"
910
)
1011

1112
func resourceRestApi() *schema.Resource {
@@ -76,6 +77,16 @@ func resourceRestApi() *schema.Resource {
7677
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).",
7778
Computed: true,
7879
},
80+
"api_response": &schema.Schema{
81+
Type: schema.TypeString,
82+
Description: "The raw body of the HTTP response from the last read of the object.",
83+
Computed: true,
84+
},
85+
"create_response": &schema.Schema{
86+
Type: schema.TypeString,
87+
Description: "The raw body of the HTTP response returned when creating the object.",
88+
Computed: true,
89+
},
7990
"force_new": &schema.Schema{
8091
Type: schema.TypeList,
8192
Elem: &schema.Schema{Type: schema.TypeString},
@@ -139,6 +150,8 @@ func resourceRestApiCreate(d *schema.ResourceData, meta interface{}) error {
139150
/* Setting terraform ID tells terraform the object was created or it exists */
140151
d.SetId(obj.id)
141152
set_resource_state(obj, d)
153+
/* Only set during create for APIs that don't return sensitive data on subsequent retrieval */
154+
d.Set("create_response", obj.api_response)
142155
}
143156
return err
144157
}

restapi/resource_api_object_test.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ package restapi
1414
import (
1515
"encoding/json"
1616
"fmt"
17-
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
18-
"github.com/hashicorp/terraform/helper/resource"
1917
"os"
2018
"testing"
19+
20+
"github.com/Mastercard/terraform-provider-restapi/fakeserver"
21+
"github.com/hashicorp/terraform/helper/resource"
2122
)
2223

2324
// example.Widget represents a concrete Go type that represents an API resource
@@ -39,7 +40,7 @@ func TestAccRestApiObject_Basic(t *testing.T) {
3940
copy_keys: make([]string, 0),
4041
write_returns_object: false,
4142
create_returns_object: false,
42-
debug: debug,
43+
debug: debug,
4344
}
4445
client, err := NewAPIClient(opt)
4546
if err != nil {
@@ -61,6 +62,24 @@ func TestAccRestApiObject_Basic(t *testing.T) {
6162
resource.TestCheckResourceAttr("restapi_object.Foo", "id", "1234"),
6263
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.first", "Foo"),
6364
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.last", "Bar"),
65+
resource.TestCheckResourceAttr("restapi_object.Foo", "api_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
66+
resource.TestCheckResourceAttr("restapi_object.Foo", "create_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
67+
),
68+
},
69+
/* Try updating the object and check create_response is unmodified */
70+
{
71+
Config: generate_test_resource(
72+
"Foo",
73+
`{ "id": "1234", "first": "Updated", "last": "Value" }`,
74+
make(map[string]interface{}),
75+
),
76+
Check: resource.ComposeTestCheckFunc(
77+
testAccCheckRestapiObjectExists("restapi_object.Foo", "1234", client),
78+
resource.TestCheckResourceAttr("restapi_object.Foo", "id", "1234"),
79+
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.first", "Updated"),
80+
resource.TestCheckResourceAttr("restapi_object.Foo", "api_data.last", "Value"),
81+
resource.TestCheckResourceAttr("restapi_object.Foo", "api_response", "{\"first\":\"Updated\",\"id\":\"1234\",\"last\":\"Value\"}"),
82+
resource.TestCheckResourceAttr("restapi_object.Foo", "create_response", "{\"first\":\"Foo\",\"id\":\"1234\",\"last\":\"Bar\"}"),
6483
),
6584
},
6685
/* Make a complex object with id_attribute as a child of another key

0 commit comments

Comments
 (0)