@@ -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