Skip to content

Commit fa21938

Browse files
authored
fix: Fixed Update function in zia_url_categories (#505)
1 parent f0da464 commit fa21938

File tree

6 files changed

+120
-24
lines changed

6 files changed

+120
-24
lines changed

GNUmakefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,14 @@ test\:integration\:zscalertwo:
196196
build13: GOOS=$(shell go env GOOS)
197197
build13: GOARCH=$(shell go env GOARCH)
198198
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
199-
build13: DESTINATION=$(APPDATA)/terraform.d/plugins/$(ZIA_PROVIDER_NAMESPACE)/4.6.4/$(GOOS)_$(GOARCH)
199+
build13: DESTINATION=$(APPDATA)/terraform.d/plugins/$(ZIA_PROVIDER_NAMESPACE)/4.6.5/$(GOOS)_$(GOARCH)
200200
else
201-
build13: DESTINATION=$(HOME)/.terraform.d/plugins/$(ZIA_PROVIDER_NAMESPACE)/4.6.4/$(GOOS)_$(GOARCH)
201+
build13: DESTINATION=$(HOME)/.terraform.d/plugins/$(ZIA_PROVIDER_NAMESPACE)/4.6.5/$(GOOS)_$(GOARCH)
202202
endif
203203
build13: fmtcheck
204204
@echo "==> Installing plugin to $(DESTINATION)"
205205
@mkdir -p $(DESTINATION)
206-
go build -o $(DESTINATION)/terraform-provider-zia_v4.6.4
206+
go build -o $(DESTINATION)/terraform-provider-zia_v4.6.5
207207

208208
coverage: test
209209
@echo "✓ Opening coverage for unit tests ..."

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
[![codecov](https://codecov.io/gh/zscaler/terraform-provider-zia/graph/badge.svg?token=A9J4AJS7F5)](https://codecov.io/gh/zscaler/terraform-provider-zia)
55
[![License](https://img.shields.io/github/license/zscaler/terraform-provider-zia?color=blue)](https://github.com/zscaler/terraform-provider-zia/v3/blob/master/LICENSE)
66
[![Zscaler Community](https://img.shields.io/badge/zscaler-community-blue)](https://community.zscaler.com/)
7-
[![Slack](https://img.shields.io/badge/Join%20Our%20Community-Slack-blue)](https://forms.gle/3iMJvVmJDvmUy36q9)
87

98
<a href="https://terraform.io">
109
<img src="https://raw.githubusercontent.com/hashicorp/terraform-website/master/public/img/logo-text.svg" alt="Terraform logo" title="Terraform" height="50" />

docs/guides/release-notes.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,21 @@ description: |-
1212
Track all ZIA Terraform provider's releases. New resources, features, and bug fixes will be tracked here.
1313

1414
---
15-
``Last updated: v4.6.4``
15+
``Last updated: v4.6.5``
1616

1717
---
1818

19+
## 4.6.5 (December, 9 2025)
20+
21+
### Notes
22+
23+
- Release date: **(December, 9 2025)**
24+
- Supported Terraform version: **v1.x**
25+
26+
### Bug Fixes
27+
28+
- [PR #505](https://github.com/zscaler/terraform-provider-zia/pull/505) - Fixed `zia_url_categories` resource to include optional parameters `action` to support partial updates via `ADD_TO_LIST` and `REMOVE_FROM_LIST`. The resource auto detects additional and removal of urls during updates.
29+
1930
## 4.6.4 (November, 24 2025)
2031

2132
### Notes

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/hashicorp/go-hclog v1.6.3
1111
github.com/hashicorp/terraform-plugin-sdk v1.17.2
1212
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1
13-
github.com/zscaler/zscaler-sdk-go/v3 v3.8.7
13+
github.com/zscaler/zscaler-sdk-go/v3 v3.8.9
1414
)
1515

1616
require (
@@ -59,12 +59,12 @@ require (
5959
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
6060
github.com/zclconf/go-cty v1.17.0 // indirect
6161
golang.org/x/crypto v0.45.0 // indirect
62-
golang.org/x/mod v0.29.0 // indirect
62+
golang.org/x/mod v0.30.0 // indirect
6363
golang.org/x/net v0.47.0 // indirect
64-
golang.org/x/sync v0.18.0 // indirect
64+
golang.org/x/sync v0.19.0 // indirect
6565
golang.org/x/sys v0.38.0 // indirect
66-
golang.org/x/text v0.31.0 // indirect
67-
golang.org/x/tools v0.38.0 // indirect
66+
golang.org/x/text v0.32.0 // indirect
67+
golang.org/x/tools v0.39.0 // indirect
6868
google.golang.org/appengine v1.6.8 // indirect
6969
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
7070
google.golang.org/grpc v1.75.1 // indirect

go.sum

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRK
399399
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
400400
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
401401
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
402-
github.com/zscaler/zscaler-sdk-go/v3 v3.8.7 h1:czTF8MfzSwEk+x3H6qzTmw56zWaFPz4tL4Ra1s/8mRI=
403-
github.com/zscaler/zscaler-sdk-go/v3 v3.8.7/go.mod h1:q+lmvQsHVUfYItLzFjfVy3C2V6tBBDyvd8mfJmWNkPk=
402+
github.com/zscaler/zscaler-sdk-go/v3 v3.8.9 h1:mkGBX8VyZL0ReVjNd56rzI8mX1Rh0xeB68AFhXmgTmg=
403+
github.com/zscaler/zscaler-sdk-go/v3 v3.8.9/go.mod h1:35JChbqhSLc9SDxQ3d2n+/WUurAYP/ISKe4lC8Qm0R8=
404404
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
405405
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
406406
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -463,8 +463,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
463463
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
464464
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
465465
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
466-
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
467-
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
466+
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
467+
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
468468
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
469469
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
470470
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -520,8 +520,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
520520
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
521521
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
522522
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
523-
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
524-
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
523+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
524+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
525525
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
526526
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
527527
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -587,8 +587,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
587587
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
588588
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
589589
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
590-
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
591-
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
590+
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
591+
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
592592
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
593593
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
594594
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -636,8 +636,8 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
636636
golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
637637
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
638638
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
639-
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
640-
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
639+
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
640+
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
641641
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
642642
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
643643
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

zia/resource_zia_url_categories.go

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,17 +367,94 @@ func resourceURLCategoriesUpdate(ctx context.Context, d *schema.ResourceData, me
367367
}
368368

369369
log.Printf("[INFO] Updating custom url category ID: %v\n", id)
370-
req := expandURLCategory(d)
371370

372-
if _, err := urlcategories.Get(ctx, service, id); err != nil {
371+
// Get current state from API
372+
currentCategory, err := urlcategories.Get(ctx, service, id)
373+
if err != nil {
373374
if respErr, ok := err.(*errorx.ErrorResponse); ok && respErr.IsObjectNotFound() {
374375
d.SetId("")
375376
return nil
376377
}
378+
return diag.FromErr(fmt.Errorf("failed to get current url category: %w", err))
377379
}
378380

379-
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &req); err != nil {
380-
return diag.FromErr(err)
381+
// Get desired state from config
382+
desiredCategory := expandURLCategory(d)
383+
384+
// Calculate URL differences
385+
currentUrls := stringSliceToMap(currentCategory.Urls)
386+
desiredUrls := stringSliceToMap(desiredCategory.Urls)
387+
388+
var urlsToAdd []string
389+
var urlsToRemove []string
390+
391+
for url := range desiredUrls {
392+
if !currentUrls[url] {
393+
urlsToAdd = append(urlsToAdd, url)
394+
}
395+
}
396+
397+
for url := range currentUrls {
398+
if !desiredUrls[url] {
399+
urlsToRemove = append(urlsToRemove, url)
400+
}
401+
}
402+
403+
// Determine update strategy based on changes
404+
hasAdds := len(urlsToAdd) > 0
405+
hasRemoves := len(urlsToRemove) > 0
406+
hasOnlyAdds := hasAdds && !hasRemoves
407+
hasOnlyRemoves := hasRemoves && !hasAdds
408+
hasBoth := hasAdds && hasRemoves
409+
410+
log.Printf("[DEBUG] URL changes detected - Adds: %d, Removes: %d", len(urlsToAdd), len(urlsToRemove))
411+
412+
// Use incremental updates when we have URL changes
413+
// The action parameter only affects URLs; other attributes are still updated normally
414+
if hasOnlyAdds {
415+
log.Printf("[INFO] Using incremental update with ADD_TO_LIST for %d URLs", len(urlsToAdd))
416+
// Send full desired category structure, but only URLs in payload are added to existing list
417+
// Other attributes in desiredCategory will be updated normally
418+
addCategory := desiredCategory
419+
addCategory.Urls = urlsToAdd
420+
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &addCategory, "ADD_TO_LIST"); err != nil {
421+
return diag.FromErr(fmt.Errorf("failed to add URLs: %w", err))
422+
}
423+
} else if hasOnlyRemoves {
424+
log.Printf("[INFO] Using incremental update with REMOVE_FROM_LIST for %d URLs", len(urlsToRemove))
425+
// Send full desired category structure, but only URLs in payload are removed from existing list
426+
// Other attributes in desiredCategory will be updated normally
427+
removeCategory := desiredCategory
428+
removeCategory.Urls = urlsToRemove
429+
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &removeCategory, "REMOVE_FROM_LIST"); err != nil {
430+
return diag.FromErr(fmt.Errorf("failed to remove URLs: %w", err))
431+
}
432+
} else if hasBoth {
433+
// When both adds and removes are present, we need to do both operations
434+
// First remove URLs, then add new ones
435+
log.Printf("[INFO] Using incremental updates - removing %d URLs, then adding %d URLs", len(urlsToRemove), len(urlsToAdd))
436+
437+
// First, remove URLs (send full desired category structure for other attribute updates)
438+
removeCategory := desiredCategory
439+
removeCategory.Urls = urlsToRemove
440+
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &removeCategory, "REMOVE_FROM_LIST"); err != nil {
441+
return diag.FromErr(fmt.Errorf("failed to remove URLs: %w", err))
442+
}
443+
444+
// Then, add URLs (send full desired category structure for other attribute updates)
445+
if len(urlsToAdd) > 0 {
446+
addCategory := desiredCategory
447+
addCategory.Urls = urlsToAdd
448+
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &addCategory, "ADD_TO_LIST"); err != nil {
449+
return diag.FromErr(fmt.Errorf("failed to add URLs: %w", err))
450+
}
451+
}
452+
} else {
453+
// No URL changes, but other attributes might have changed - use full update
454+
log.Printf("[INFO] No URL changes detected, using full update for other attribute changes")
455+
if _, _, err := urlcategories.UpdateURLCategories(ctx, service, id, &desiredCategory, ""); err != nil {
456+
return diag.FromErr(err)
457+
}
381458
}
382459

383460
// Check if ZIA_ACTIVATION is set to a truthy value before triggering activation
@@ -486,3 +563,12 @@ func expandURLCategoryScopes(d *schema.ResourceData) []urlcategories.Scopes {
486563
}
487564
return scopes
488565
}
566+
567+
// stringSliceToMap converts a string slice to a map for efficient lookups
568+
func stringSliceToMap(slice []string) map[string]bool {
569+
result := make(map[string]bool, len(slice))
570+
for _, s := range slice {
571+
result[s] = true
572+
}
573+
return result
574+
}

0 commit comments

Comments
 (0)