Skip to content

Commit 7616c02

Browse files
authored
Merge pull request #182 from jgrumboe/destroy-data
Add support for destroy_data and update_data to be sent
2 parents 3c7c9b5 + cb4ab82 commit 7616c02

File tree

6 files changed

+150
-17
lines changed

6 files changed

+150
-17
lines changed

.github/workflows/release.yaml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,34 @@ on:
1414
push:
1515
tags:
1616
- 'v*'
17+
permissions:
18+
contents: write
1719
jobs:
1820
goreleaser:
1921
runs-on: ubuntu-latest
2022
steps:
2123
-
2224
name: Checkout
23-
uses: actions/checkout@v2.4.0
25+
uses: actions/checkout@v3
2426
-
2527
name: Unshallow
2628
run: git fetch --prune --unshallow
2729
-
2830
name: Set up Go
29-
uses: actions/setup-go@v2
31+
uses: actions/setup-go@v3
3032
with:
31-
go-version: 1.17
33+
go-version-file: 'go.mod'
34+
cache: true
3235
-
3336
name: Import GPG key
37+
uses: crazy-max/ghaction-import-gpg@v5
3438
id: import_gpg
35-
uses: hashicorp/[email protected]
36-
env:
37-
# These secrets will need to be configured for the repository:
38-
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
39-
PASSPHRASE: ${{ secrets.PASSPHRASE }}
39+
with:
40+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
41+
passphrase: ${{ secrets.PASSPHRASE }}
4042
-
4143
name: Run GoReleaser
42-
uses: goreleaser/goreleaser-action@v2.8.1
44+
uses: goreleaser/goreleaser-action@v3.0.0
4345
with:
4446
version: latest
4547
args: release --parallelism 2 --rm-dist --timeout 1h --release-notes .release_info.md

docs/resources/object.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ description: |-
2424
- **create_path** (String, Optional) Defaults to `path`. The API path that represents where to CREATE (POST) objects of this type on the API server. The string `{id}` will be replaced with the terraform ID of the object if the data contains the `id_attribute`.
2525
- **debug** (Boolean, Optional) Whether to emit verbose debug output while working with the API object on the server.
2626
- **destroy_method** (String, Optional) Defaults to `destroy_method` set on the provider. Allows per-resource override of `destroy_method` (see `destroy_method` provider config documentation)
27+
- **destroy_data** (String, Optional) Valid JSON data that this provider will send to the API server on DESTROY (DELETE) operations. If not set, defaults to the value of `data`.
2728
- **destroy_path** (String, Optional) Defaults to `path/{id}`. The API path that represents where to DESTROY (DELETE) objects of this type on the API server. The string `{id}` will be replaced with the terraform ID of the object.
2829
- **force_new** (List of String, Optional) Any changes to these values will result in recreating the resource instead of updating.
2930
- **id** (String, Optional) The ID of this resource.
@@ -34,6 +35,7 @@ description: |-
3435
- **read_path** (String, Optional) Defaults to `path/{id}`. The API path that represents where to READ (GET) objects of this type on the API server. The string `{id}` will be replaced with the terraform ID of the object.
3536
- **read_search** (Map of String, Optional) Custom search for `read_path`. This map will take `search_key`, `search_value`, `results_key` and `query_string` (see datasource config documentation)
3637
- **update_method** (String, Optional) Defaults to `update_method` set on the provider. Allows per-resource override of `update_method` (see `update_method` provider config documentation)
38+
- **update_data** (String, Optional) Valid JSON data that this provider will send to the API server on UPDATE (PUT) operations. If not set, defaults to the value of `data`.
3739
- **update_path** (String, Optional) Defaults to `path/{id}`. The API path that represents where to UPDATE (PUT) objects of this type on the API server. The string `{id}` will be replaced with the terraform ID of the object.
3840

3941
### Read-only

restapi/api_client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ type apiClientOpt struct {
3030
createMethod string
3131
readMethod string
3232
updateMethod string
33+
updateData string
3334
destroyMethod string
35+
destroyData string
3436
copyKeys []string
3537
writeReturnsObject bool
3638
createReturnsObject bool
@@ -61,7 +63,9 @@ type APIClient struct {
6163
createMethod string
6264
readMethod string
6365
updateMethod string
66+
updateData string
6467
destroyMethod string
68+
destroyData string
6569
copyKeys []string
6670
writeReturnsObject bool
6771
createReturnsObject bool
@@ -158,7 +162,9 @@ func NewAPIClient(opt *apiClientOpt) (*APIClient, error) {
158162
createMethod: opt.createMethod,
159163
readMethod: opt.readMethod,
160164
updateMethod: opt.updateMethod,
165+
updateData: opt.updateData,
161166
destroyMethod: opt.destroyMethod,
167+
destroyData: opt.destroyData,
162168
copyKeys: opt.copyKeys,
163169
writeReturnsObject: opt.writeReturnsObject,
164170
createReturnsObject: opt.createReturnsObject,

restapi/api_object.go

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ type apiObjectOpts struct {
1919
createMethod string
2020
readMethod string
2121
updateMethod string
22+
updateData string
2223
destroyMethod string
24+
destroyData string
2325
deletePath string
2426
searchPath string
2527
queryString string
@@ -50,11 +52,13 @@ type APIObject struct {
5052

5153
/* Set internally */
5254
data map[string]interface{} /* Data as managed by the user */
55+
updateData map[string]interface{} /* Update data as managed by the user */
56+
destroyData map[string]interface{} /* Destroy data as managed by the user */
5357
apiData map[string]interface{} /* Data as available from the API */
5458
apiResponse string
5559
}
5660

57-
//NewAPIObject makes an APIobject to manage a RESTful object in an API
61+
// NewAPIObject makes an APIobject to manage a RESTful object in an API
5862
func NewAPIObject(iClient *APIClient, opts *apiObjectOpts) (*APIObject, error) {
5963
if opts.debug {
6064
log.Printf("api_object.go: Constructing debug api_object\n")
@@ -78,10 +82,15 @@ func NewAPIObject(iClient *APIClient, opts *apiObjectOpts) (*APIObject, error) {
7882
if opts.updateMethod == "" {
7983
opts.updateMethod = iClient.updateMethod
8084
}
85+
if opts.updateData == "" {
86+
opts.updateData = iClient.updateData
87+
}
8188
if opts.destroyMethod == "" {
8289
opts.destroyMethod = iClient.destroyMethod
8390
}
84-
91+
if opts.destroyData == "" {
92+
opts.destroyData = iClient.destroyData
93+
}
8594
if opts.postPath == "" {
8695
opts.postPath = opts.path
8796
}
@@ -115,6 +124,8 @@ func NewAPIObject(iClient *APIClient, opts *apiObjectOpts) (*APIObject, error) {
115124
id: opts.id,
116125
idAttribute: opts.idAttribute,
117126
data: make(map[string]interface{}),
127+
updateData: make(map[string]interface{}),
128+
destroyData: make(map[string]interface{}),
118129
apiData: make(map[string]interface{}),
119130
}
120131

@@ -146,6 +157,28 @@ func NewAPIObject(iClient *APIClient, opts *apiObjectOpts) (*APIObject, error) {
146157
}
147158
}
148159

160+
if opts.updateData != "" {
161+
if opts.debug {
162+
log.Printf("api_object.go: Parsing update data: '%s'", opts.updateData)
163+
}
164+
165+
err := json.Unmarshal([]byte(opts.updateData), &obj.updateData)
166+
if err != nil {
167+
return &obj, fmt.Errorf("api_object.go: error parsing update data provided: %v", err.Error())
168+
}
169+
}
170+
171+
if opts.destroyData != "" {
172+
if opts.debug {
173+
log.Printf("api_object.go: Parsing destroy data: '%s'", opts.destroyData)
174+
}
175+
176+
err := json.Unmarshal([]byte(opts.destroyData), &obj.destroyData)
177+
if err != nil {
178+
return &obj, fmt.Errorf("api_object.go: error parsing destroy data provided: %v", err.Error())
179+
}
180+
}
181+
149182
if opts.debug {
150183
log.Printf("api_object.go: Constructed object: %s", obj.toString())
151184
}
@@ -169,13 +202,18 @@ func (obj *APIObject) toString() string {
169202
buffer.WriteString(fmt.Sprintf("debug: %t\n", obj.debug))
170203
buffer.WriteString(fmt.Sprintf("read_search: %s\n", spew.Sdump(obj.readSearch)))
171204
buffer.WriteString(fmt.Sprintf("data: %s\n", spew.Sdump(obj.data)))
205+
buffer.WriteString(fmt.Sprintf("update_data: %s\n", spew.Sdump(obj.updateData)))
206+
buffer.WriteString(fmt.Sprintf("destroy_data: %s\n", spew.Sdump(obj.destroyData)))
172207
buffer.WriteString(fmt.Sprintf("api_data: %s\n", spew.Sdump(obj.apiData)))
173208
return buffer.String()
174209
}
175210

176-
/* Centralized function to ensure that our data as managed by
177-
the api_object is updated with data that has come back from
178-
the API */
211+
/*
212+
Centralized function to ensure that our data as managed by
213+
214+
the api_object is updated with data that has come back from
215+
the API
216+
*/
179217
func (obj *APIObject) updateState(state string) error {
180218
if obj.debug {
181219
log.Printf("api_object.go: Updating API object state to '%s'\n", state)
@@ -327,6 +365,14 @@ func (obj *APIObject) updateObject() error {
327365

328366
b, _ := json.Marshal(obj.data)
329367

368+
updateData, _ := json.Marshal(obj.updateData)
369+
if string(updateData) != "{}" {
370+
if obj.debug {
371+
log.Printf("api_object.go: Using update data '%s'", string(updateData))
372+
}
373+
b = updateData
374+
}
375+
330376
putPath := obj.putPath
331377
if obj.queryString != "" {
332378
if obj.debug {
@@ -368,7 +414,16 @@ func (obj *APIObject) deleteObject() error {
368414
deletePath = fmt.Sprintf("%s?%s", obj.deletePath, obj.queryString)
369415
}
370416

371-
_, err := obj.apiClient.sendRequest(obj.destroyMethod, strings.Replace(deletePath, "{id}", obj.id, -1), "")
417+
b := []byte{}
418+
destroyData, _ := json.Marshal(obj.destroyData)
419+
if string(destroyData) != "{}" {
420+
if obj.debug {
421+
log.Printf("api_object.go: Using destroy data '%s'", string(destroyData))
422+
}
423+
b = destroyData
424+
}
425+
426+
_, err := obj.apiClient.sendRequest(obj.destroyMethod, strings.Replace(deletePath, "{id}", obj.id, -1), string(b))
372427
if err != nil {
373428
return err
374429
}

restapi/api_object_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,21 @@ func TestAPIObject(t *testing.T) {
212212
}
213213
})
214214

215+
/* Update once more with update_data */
216+
t.Run("update_object_with_update_data", func(t *testing.T) {
217+
if testDebug {
218+
log.Printf("api_object_test.go: Testing update_object() with update_data")
219+
}
220+
testingObjects["minimal"].updateData["Thing"] = "knife"
221+
testingObjects["minimal"].updateObject()
222+
if err != nil {
223+
t.Fatalf("api_object_test.go: Failed in update_object() test: %s", err)
224+
} else if testingObjects["minimal"].apiData["Thing"] != "knife" {
225+
t.Fatalf("api_object_test.go: Failed to update 'Thing' field of 'minimal' object. Expected it to be '%s' but it is '%s'\nFull obj: %+v\n",
226+
"knife", testingObjects["minimal"].apiData["Thing"], testingObjects["minimal"])
227+
}
228+
})
229+
215230
/* Delete one and make sure a 404 follows */
216231
t.Run("delete_object", func(t *testing.T) {
217232
if testDebug {
@@ -233,9 +248,9 @@ func TestAPIObject(t *testing.T) {
233248
err = testingObjects["pet"].createObject()
234249
if err != nil {
235250
t.Fatalf("api_object_test.go: Failed in create_object() test: %s", err)
236-
} else if testingObjects["minimal"].apiData["Thing"] != "spoon" {
251+
} else if testingObjects["minimal"].apiData["Thing"] != "knife" {
237252
t.Fatalf("api_object_test.go: Failed to update 'Thing' field of 'minimal' object. Expected it to be '%s' but it is '%s'\nFull obj: %+v\n",
238-
"spoon", testingObjects["minimal"].apiData["Thing"], testingObjects["minimal"])
253+
"knife", testingObjects["minimal"].apiData["Thing"], testingObjects["minimal"])
239254
}
240255

241256
/* verify it's there */
@@ -276,6 +291,19 @@ func TestAPIObject(t *testing.T) {
276291
}
277292
})
278293

294+
/* Delete it again with destroy_data and make sure a 404 follows */
295+
t.Run("delete_object_with_destroy_data", func(t *testing.T) {
296+
if testDebug {
297+
log.Printf("api_object_test.go: Testing delete_object() with destroy_data")
298+
}
299+
testingObjects["pet"].destroyData["destroy"] = "true"
300+
testingObjects["pet"].deleteObject()
301+
err = testingObjects["pet"].readObject()
302+
if err != nil {
303+
t.Fatalf("api_object_test.go: 'pet' object deleted, but an error was returned when reading the object (expected the provider to cope with this!\n")
304+
}
305+
})
306+
279307
if testDebug {
280308
log.Println("api_object_test.go: Stopping HTTP server")
281309
}

restapi/resource_api_object.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,40 @@ func resourceRestAPI() *schema.Resource {
142142
ForceNew: true,
143143
Description: "Any changes to these values will result in recreating the resource instead of updating.",
144144
},
145+
"update_data": {
146+
Type: schema.TypeString,
147+
Optional: true,
148+
Description: "Valid JSON data to pass during to update requests.",
149+
Sensitive: isDataSensitive,
150+
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
151+
v := val.(string)
152+
if v != "" {
153+
data := make(map[string]interface{})
154+
err := json.Unmarshal([]byte(v), &data)
155+
if err != nil {
156+
errs = append(errs, fmt.Errorf("update_data attribute is invalid JSON: %v", err))
157+
}
158+
}
159+
return warns, errs
160+
},
161+
},
162+
"destroy_data": {
163+
Type: schema.TypeString,
164+
Optional: true,
165+
Description: "Valid JSON data to pass during to destroy requests.",
166+
Sensitive: isDataSensitive,
167+
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
168+
v := val.(string)
169+
if v != "" {
170+
data := make(map[string]interface{})
171+
err := json.Unmarshal([]byte(v), &data)
172+
if err != nil {
173+
errs = append(errs, fmt.Errorf("destroy_data attribute is invalid JSON: %v", err))
174+
}
175+
}
176+
return warns, errs
177+
},
178+
},
145179
}, /* End schema */
146180

147181
}
@@ -365,9 +399,15 @@ func buildAPIObjectOpts(d *schema.ResourceData) (*apiObjectOpts, error) {
365399
if v, ok := d.GetOk("update_method"); ok {
366400
opts.updateMethod = v.(string)
367401
}
402+
if v, ok := d.GetOk("update_data"); ok {
403+
opts.updateData = v.(string)
404+
}
368405
if v, ok := d.GetOk("destroy_method"); ok {
369406
opts.destroyMethod = v.(string)
370407
}
408+
if v, ok := d.GetOk("destroy_data"); ok {
409+
opts.destroyData = v.(string)
410+
}
371411
if v, ok := d.GetOk("destroy_path"); ok {
372412
opts.deletePath = v.(string)
373413
}

0 commit comments

Comments
 (0)