Skip to content

Commit bfb23d9

Browse files
authored
Merge pull request #163 from magodo/update_body_patches_remove
`restful_resource` - Supports `update_body_patches.*.removed` to remove values from the update body
2 parents 7d0c96e + 7739ed2 commit bfb23d9

File tree

3 files changed

+94
-27
lines changed

3 files changed

+94
-27
lines changed

docs/resources/resource.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,11 @@ Optional:
300300
Required:
301301

302302
- `path` (String) The path (in [gjson syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)) to the attribute to [patch](https://github.com/tidwall/sjson?tab=readme-ov-file#set-a-value).
303-
- `raw_json` (String) The raw json used as the patch value. It can contain `$(body.x.y.z)` parameter that reference property from the `state.output`.
303+
304+
Optional:
305+
306+
- `raw_json` (String) The raw json used as the patch value. It can contain `$(body.x.y.z)` parameter that reference property from the `state.output`. Exactly one of `raw_json` and `removed` shall be specified.
307+
- `removed` (Boolean) Remove the value specified by `path` from the update body. Exactly one of `raw_json` and `removed` shall be specified
304308

305309
## Import
306310

internal/provider/resource.go

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
jsonpatch "github.com/evanphx/json-patch"
12+
"github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator"
1213
"github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator"
1314
"github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator"
1415
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
@@ -105,6 +106,7 @@ type resourceData struct {
105106
type bodyPatchData struct {
106107
Path types.String `tfsdk:"path"`
107108
RawJSON types.String `tfsdk:"raw_json"`
109+
Removed types.Bool `tfsdk:"removed"`
108110
}
109111

110112
type pollData struct {
@@ -422,9 +424,25 @@ func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp
422424
Required: true,
423425
},
424426
"raw_json": schema.StringAttribute{
425-
Description: "The raw json used as the patch value. It can contain `$(body.x.y.z)` parameter that reference property from the `state.output`.",
426-
MarkdownDescription: "The raw json used as the patch value. It can contain `$(body.x.y.z)` parameter that reference property from the `state.output`.",
427-
Required: true,
427+
MarkdownDescription: "The raw json used as the patch value. It can contain `$(body.x.y.z)` parameter that reference property from the `state.output`. Exactly one of `raw_json` and `removed` shall be specified.",
428+
Optional: true,
429+
Validators: []validator.String{
430+
stringvalidator.ExactlyOneOf(
431+
path.MatchRelative().AtParent().AtName("raw_json"),
432+
path.MatchRelative().AtParent().AtName("removed"),
433+
),
434+
},
435+
},
436+
"removed": schema.BoolAttribute{
437+
MarkdownDescription: "Remove the value specified by `path` from the update body. Exactly one of `raw_json` and `removed` shall be specified",
438+
Optional: true,
439+
Validators: []validator.Bool{
440+
boolvalidator.Equals(true),
441+
boolvalidator.ExactlyOneOf(
442+
path.MatchRelative().AtParent().AtName("raw_json"),
443+
path.MatchRelative().AtParent().AtName("removed"),
444+
),
445+
},
428446
},
429447
},
430448
},
@@ -1356,22 +1374,34 @@ func (r Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *
13561374
if len(patches) != 0 {
13571375
planBodyStr := string(planBody)
13581376
for i, patch := range patches {
1359-
pv, err := exparam.ExpandBody(patch.RawJSON.ValueString(), stateOutput)
1360-
if err != nil {
1361-
resp.Diagnostics.AddError(
1362-
fmt.Sprintf("Failed to expand the %d-th patch for expression params", i),
1363-
err.Error(),
1364-
)
1365-
return
1366-
}
1377+
switch {
1378+
case !patch.Removed.IsNull():
1379+
planBodyStr, err = sjson.Delete(planBodyStr, patch.Path.ValueString())
1380+
if err != nil {
1381+
resp.Diagnostics.AddError(
1382+
fmt.Sprintf("Failed to delete json for the %d-th patch at path %q", i, patch.Path.ValueString()),
1383+
err.Error(),
1384+
)
1385+
return
1386+
}
1387+
case !patch.RawJSON.IsNull():
1388+
pv, err := exparam.ExpandBody(patch.RawJSON.ValueString(), stateOutput)
1389+
if err != nil {
1390+
resp.Diagnostics.AddError(
1391+
fmt.Sprintf("Failed to expand the %d-th patch for expression params", i),
1392+
err.Error(),
1393+
)
1394+
return
1395+
}
13671396

1368-
planBodyStr, err = sjson.SetRaw(planBodyStr, patch.Path.ValueString(), pv)
1369-
if err != nil {
1370-
resp.Diagnostics.AddError(
1371-
fmt.Sprintf("Failed to set json for the %d-th patch for expression params", i),
1372-
err.Error(),
1373-
)
1374-
return
1397+
planBodyStr, err = sjson.SetRaw(planBodyStr, patch.Path.ValueString(), pv)
1398+
if err != nil {
1399+
resp.Diagnostics.AddError(
1400+
fmt.Sprintf("Failed to set json for the %d-th patch with %q", i, pv),
1401+
err.Error(),
1402+
)
1403+
return
1404+
}
13751405
}
13761406
}
13771407
planBody = []byte(planBodyStr)

internal/provider/resource_jsonserver_test.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,18 +282,29 @@ func TestResource_JSONServer_UpdateBodyPatch(t *testing.T) {
282282
ProtoV6ProviderFactories: acceptance.ProviderFactory(),
283283
Steps: []resource.TestStep{
284284
{
285-
Config: d.updateBodyPatch("foo"),
285+
// The initial creation won't honor the "update_body_patches"
286+
Config: d.updateBodyPatchBase(),
286287
ConfigStateChecks: []statecheck.StateCheck{
287288
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output").AtMapKey("id"), knownvalue.NotNull()),
288289
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output").AtMapKey("foo"), knownvalue.NotNull()),
289290
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output"), knownvalue.MapSizeExact(2)),
290291
},
291292
},
292293
{
293-
Config: d.updateBodyPatch("bar"),
294+
Config: d.updateBodyPatchUpdate(),
294295
ConfigStateChecks: []statecheck.StateCheck{
295-
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output").AtMapKey("addon").AtMapKey("a"), knownvalue.StringExact("hmm")),
296-
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output").AtMapKey("addon").AtMapKey("b"), knownvalue.NotNull()),
296+
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output"), knownvalue.MapExact(
297+
map[string]knownvalue.Check{
298+
"addon": knownvalue.MapExact(
299+
map[string]knownvalue.Check{
300+
"a": knownvalue.StringExact("hmm"),
301+
"b": knownvalue.NotNull(),
302+
},
303+
),
304+
"id": knownvalue.NotNull(),
305+
},
306+
)),
307+
statecheck.ExpectKnownValue(addr, tfjsonpath.New("output"), knownvalue.MapSizeExact(2)),
297308
},
298309
},
299310
},
@@ -644,7 +655,7 @@ resource "restful_resource" "test" {
644655
`, d.url, v)
645656
}
646657

647-
func (d jsonServerData) updateBodyPatch(v string) string {
658+
func (d jsonServerData) updateBodyPatchBase() string {
648659
return fmt.Sprintf(`
649660
provider "restful" {
650661
base_url = %q
@@ -653,8 +664,24 @@ provider "restful" {
653664
resource "restful_resource" "test" {
654665
path = "posts"
655666
body = {
656-
foo = %q
667+
foo = "bar"
657668
}
669+
read_path = "$(path)/$(body.id)"
670+
}
671+
`, d.url)
672+
}
673+
func (d jsonServerData) updateBodyPatchUpdate() string {
674+
return fmt.Sprintf(`
675+
provider "restful" {
676+
base_url = %q
677+
}
678+
679+
resource "restful_resource" "test" {
680+
path = "posts"
681+
body = {
682+
foo = "bar"
683+
}
684+
read_path = "$(path)/$(body.id)"
658685
update_body_patches = [
659686
{
660687
path = "addon.a"
@@ -664,10 +691,16 @@ resource "restful_resource" "test" {
664691
path = "addon.b"
665692
raw_json = "$(body.id)"
666693
},
694+
{
695+
path = "foo"
696+
removed = true
697+
},
667698
]
668-
read_path = "$(path)/$(body.id)"
699+
lifecycle {
700+
ignore_changes = [body.foo]
701+
}
669702
}
670-
`, d.url, v)
703+
`, d.url)
671704
}
672705

673706
func (d jsonServerData) ephemeralBodyOverlap() string {

0 commit comments

Comments
 (0)