zscaler-sdk-python 0.9.6__tar.gz → 0.9.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/PKG-INFO +1 -1
  2. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/pyproject.toml +1 -1
  3. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/__init__.py +1 -1
  4. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/__init__.py +24 -8
  5. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/security.py +17 -5
  6. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/users.py +1 -1
  7. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/__init__.py +34 -34
  8. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/policies.py +24 -50
  9. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/LICENSE.md +0 -0
  10. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/README.md +0 -0
  11. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/cache/__init__.py +0 -0
  12. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/cache/cache.py +0 -0
  13. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/cache/no_op_cache.py +0 -0
  14. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/cache/zscaler_cache.py +0 -0
  15. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/constants.py +0 -0
  16. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/errors/__init__.py +0 -0
  17. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/errors/error.py +0 -0
  18. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/errors/http_error.py +0 -0
  19. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/errors/zscaler_api_error.py +0 -0
  20. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/exceptions/__init__.py +0 -0
  21. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/exceptions/exceptions.py +0 -0
  22. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/logger.py +0 -0
  23. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/ratelimiter/__init__.py +0 -0
  24. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/ratelimiter/ratelimiter.py +0 -0
  25. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/user_agent.py +0 -0
  26. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/utils.py +0 -0
  27. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcc/__init__.py +0 -0
  28. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcc/client.py +0 -0
  29. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcc/devices.py +0 -0
  30. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcc/secrets.py +0 -0
  31. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/__init__.py +0 -0
  32. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/activation.py +0 -0
  33. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/admin_and_role_management.py +0 -0
  34. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/client.py +0 -0
  35. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/ecgroups.py +0 -0
  36. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/locations.py +0 -0
  37. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zcon/provisioning.py +0 -0
  38. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/__init__.py +0 -0
  39. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/admin.py +0 -0
  40. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/alerts.py +0 -0
  41. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/apps.py +0 -0
  42. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/devices.py +0 -0
  43. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/filters.py +0 -0
  44. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/inventory.py +0 -0
  45. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/troubleshooting.py +0 -0
  46. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/users.py +0 -0
  47. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zdx/zdx_client.py +0 -0
  48. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/activate.py +0 -0
  49. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/admin_and_role_management.py +0 -0
  50. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/apptotal.py +0 -0
  51. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/audit_logs.py +0 -0
  52. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/authentication_settings.py +0 -0
  53. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/client.py +0 -0
  54. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/cloud_apps.py +0 -0
  55. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/cloudappcontrol.py +0 -0
  56. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/device_management.py +0 -0
  57. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/dlp.py +0 -0
  58. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/errors.py +0 -0
  59. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/firewall.py +0 -0
  60. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/forwarding_control.py +0 -0
  61. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/isolation_profile.py +0 -0
  62. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/labels.py +0 -0
  63. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/locations.py +0 -0
  64. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/sandbox.py +0 -0
  65. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/ssl_inspection.py +0 -0
  66. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/traffic.py +0 -0
  67. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/url_categories.py +0 -0
  68. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/url_filtering.py +0 -0
  69. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/web_dlp.py +0 -0
  70. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/workload_groups.py +0 -0
  71. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zia/zpa_gateway.py +0 -0
  72. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/README.md +0 -0
  73. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/app_segments.py +0 -0
  74. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/app_segments_inspection.py +0 -0
  75. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/app_segments_pra.py +0 -0
  76. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/authdomains.py +0 -0
  77. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/certificates.py +0 -0
  78. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/client.py +0 -0
  79. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/cloud_connector_groups.py +0 -0
  80. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/connectors.py +0 -0
  81. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/emergency_access.py +0 -0
  82. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/errors.py +0 -0
  83. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/idp.py +0 -0
  84. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/inspection.py +0 -0
  85. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/isolation.py +0 -0
  86. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/lss.py +0 -0
  87. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/machine_groups.py +0 -0
  88. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/microtenants.py +0 -0
  89. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/posture_profiles.py +0 -0
  90. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/privileged_remote_access.py +0 -0
  91. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/provisioning.py +0 -0
  92. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/saml_attributes.py +0 -0
  93. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/scim_attributes.py +0 -0
  94. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/scim_groups.py +0 -0
  95. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/segment_groups.py +0 -0
  96. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/server_groups.py +0 -0
  97. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/servers.py +0 -0
  98. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/service_edges.py +0 -0
  99. {zscaler_sdk_python-0.9.6 → zscaler_sdk_python-0.9.7}/zscaler/zpa/trusted_networks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zscaler-sdk-python
3
- Version: 0.9.6
3
+ Version: 0.9.7
4
4
  Summary: Official Python SDK for the Zscaler Products (Beta)
5
5
  Home-page: https://github.com/zscaler/zscaler-sdk-python
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "zscaler-sdk-python"
3
- version = "0.9.6"
3
+ version = "0.9.7"
4
4
  description = "Official Python SDK for the Zscaler Products (Beta)"
5
5
  authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
6
6
  license = "MIT"
@@ -29,7 +29,7 @@ __license__ = "MIT"
29
29
  __contributors__ = [
30
30
  "William Guilherme",
31
31
  ]
32
- __version__ = "0.9.6"
32
+ __version__ = "0.9.7"
33
33
 
34
34
  from zscaler.zdx import ZDXClientHelper # noqa
35
35
  from zscaler.zia import ZIAClientHelper # noqa
@@ -406,6 +406,9 @@ class ZIAClientHelper(ZIAClient):
406
406
  page=None,
407
407
  pagesize=None,
408
408
  search=None,
409
+ max_page_size=1000, # Default to 1000, can be adjusted based on endpoint constraints
410
+ max_items=None, # Maximum number of items to retrieve across pages
411
+ max_pages=None, # Maximum number of pages to retrieve
409
412
  ):
410
413
  """
411
414
  Fetches paginated data from the API based on specified parameters and handles pagination.
@@ -416,6 +419,8 @@ class ZIAClientHelper(ZIAClient):
416
419
  page (int): Specific page number to fetch. Defaults to 1 if not provided.
417
420
  pagesize (int): Number of items per page, default is 100, with a maximum of 1000.
418
421
  search (str): Search query to filter the results.
422
+ max_items (int): Maximum number of items to retrieve.
423
+ max_pages (int): Maximum number of pages to fetch.
419
424
 
420
425
  Returns:
421
426
  tuple: A tuple containing:
@@ -428,15 +433,18 @@ class ZIAClientHelper(ZIAClient):
428
433
  "UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
429
434
  "EMPTY_RESULTS": "No results found for page {page}.",
430
435
  }
431
-
432
- params = {}
433
- params["page"] = page if page is not None else 1 # Default to page 1
434
- params["pagesize"] = min(pagesize if pagesize is not None else 100, 1000) # Default pagesize is 100, max 1000
436
+
437
+ # Initialize pagination parameters
438
+ params = {
439
+ "page": page if page is not None else 1, # Start at page 1 if not specified
440
+ "pagesize": min(pagesize if pagesize is not None else 100, max_page_size) # Apply max_page_size limit
441
+ }
435
442
 
436
443
  if search:
437
444
  params["search"] = search
438
445
 
439
446
  ret_data = []
447
+ total_collected = 0 # Track total items collected
440
448
 
441
449
  try:
442
450
  while True:
@@ -464,18 +472,26 @@ class ZIAClientHelper(ZIAClient):
464
472
  return BoxList([]), error_msg
465
473
 
466
474
  data = convert_keys_to_snake(response_data)
467
-
475
+
468
476
  # If searching for a specific item, stop if we find a match
469
477
  if search:
470
478
  for item in data:
471
479
  if item.get("name") == search:
472
480
  ret_data.append(item)
473
481
  return BoxList(ret_data), None
474
-
475
- # If no search, collect all data from the current page
482
+
483
+ # Limit data collection based on max_items
484
+ if max_items is not None:
485
+ data = data[:max_items - total_collected] # Limit items on the current page
476
486
  ret_data.extend(data)
487
+ total_collected += len(data)
488
+
489
+ # Check if we've reached max_items or max_pages limits
490
+ if (max_items is not None and total_collected >= max_items) or \
491
+ (max_pages is not None and params["page"] >= max_pages):
492
+ break
477
493
 
478
- # Stop if we've processed all available pages
494
+ # Stop if we've processed all available pages (i.e., less than requested page size)
479
495
  if len(data) < params["pagesize"]:
480
496
  break
481
497
 
@@ -163,11 +163,23 @@ class SecurityPolicyAPI:
163
163
 
164
164
  payload = {"blacklistUrls": url_list}
165
165
 
166
- resp = self.rest.post("security/advanced/blacklistUrls?action=ADD_TO_LIST", json=payload).status_code
167
-
168
- # Return the object if it was updated successfully
169
- if resp == 204:
170
- return self.get_blacklist()
166
+ try:
167
+ # Send the POST request to add URLs to the blacklist
168
+ response = self.rest.post("security/advanced/blacklistUrls?action=ADD_TO_LIST", json=payload)
169
+
170
+ # Check if the response includes an empty 'blacklistUrls', signaling no update
171
+ if "blacklistUrls" in response and not response["blacklistUrls"]:
172
+ raise Exception("Failed to add URLs to blacklist: The API response returned an empty 'blacklistUrls' list.")
173
+
174
+ # Verify the URLs were added by checking the current blacklist
175
+ updated_blacklist = self.get_blacklist()
176
+ if all(url in updated_blacklist for url in url_list):
177
+ return updated_blacklist
178
+ else:
179
+ raise Exception("Failed to add URLs to blacklist: URLs were not present in the updated blacklist.")
180
+
181
+ except Exception as exc:
182
+ raise Exception(f"Failed to add URLs to blacklist: {exc}")
171
183
 
172
184
  def replace_blacklist(self, url_list: list) -> BoxList:
173
185
  """
@@ -71,7 +71,7 @@ class UserManagementAPI:
71
71
  """
72
72
  list, _ = self.rest.get_paginated_data(path="/departments", **kwargs)
73
73
  return list
74
-
74
+
75
75
  def get_department(self, department_id: str) -> Box:
76
76
  """
77
77
  Returns the department details for a given department.
@@ -369,27 +369,27 @@ class ZPAClientHelper(ZPAClient):
369
369
  return self.send("DELETE", path, json, params, api_version=api_version)
370
370
 
371
371
  def get_paginated_data(
372
- self,
373
- path=None,
374
- params=None,
375
- expected_status_code=200,
376
- api_version: str = None,
377
- search=None,
378
- search_field="name",
379
- max_pages=None,
380
- max_items=None,
381
- all_entries=False, # Return all SCIM groups including the deleted ones if set to true
382
- sort_order=None,
383
- sort_by=None,
384
- sort_dir=None,
385
- start_time=None,
386
- end_time=None,
387
- idp_group_id=None,
388
- scim_user_id=None,
389
- scim_username=None,
390
- page=None,
391
- pagesize=None,
392
- microtenant_id=None,
372
+ self,
373
+ path=None,
374
+ params=None,
375
+ expected_status_code=200,
376
+ api_version: str = None,
377
+ search=None,
378
+ search_field="name",
379
+ max_pages=None,
380
+ max_items=None,
381
+ all_entries=False,
382
+ sort_order=None,
383
+ sort_by=None,
384
+ sort_dir=None,
385
+ start_time=None,
386
+ end_time=None,
387
+ idp_group_id=None,
388
+ scim_user_id=None,
389
+ scim_username=None,
390
+ page=None,
391
+ pagesize=None,
392
+ microtenant_id=None,
393
393
  ):
394
394
  """
395
395
  Fetches paginated data from the ZPA API based on specified parameters and handles various types of API pagination.
@@ -432,15 +432,10 @@ class ZPAClientHelper(ZPAClient):
432
432
  if params is None:
433
433
  params = {}
434
434
 
435
- if (page is not None or pagesize is not None) and (max_pages is not None or max_items is not None):
436
- raise ValueError(
437
- "Do not mix 'page' or 'pagesize' with 'max_pages' or 'max_items'. Choose either set of parameters."
438
- )
439
-
440
- params["page"] = page if page is not None else 1 # Default to page 1 if not specified
441
- params["pagesize"] = min(pagesize if pagesize is not None else 20, 500) # Apply maximum constraint and handle default
435
+ # Set initial pagination params
436
+ params["page"] = page or 1
437
+ params["pagesize"] = min(pagesize, 500) if pagesize else 500
442
438
 
443
- # Check for microtenantId in function arguments first, then environment variable
444
439
  if microtenant_id:
445
440
  params["microtenantId"] = microtenant_id
446
441
  elif self.microtenant_id and "microtenantId" not in params:
@@ -472,7 +467,8 @@ class ZPAClientHelper(ZPAClient):
472
467
 
473
468
  try:
474
469
  while True:
475
- if max_pages is not None and (page is not None and page > max_pages):
470
+ # Stop if max_pages reached
471
+ if max_pages is not None and params["page"] > max_pages:
476
472
  break
477
473
 
478
474
  should_wait, delay = self.rate_limiter.wait("GET")
@@ -490,23 +486,27 @@ class ZPAClientHelper(ZPAClient):
490
486
 
491
487
  response_data = response.json()
492
488
  data = response_data.get("list", [])
493
- if not data and (params["page"] == 1):
489
+ if not data and params["page"] == 1:
494
490
  error_msg = ERROR_MESSAGES["EMPTY_RESULTS"]
495
491
  logger.warn(error_msg)
496
492
  return BoxList([]), error_msg
497
493
 
494
+ # Convert and extend the collected data
498
495
  data = convert_keys_to_snake(data)
499
496
  ret_data.extend(data[: max_items - total_collected] if max_items is not None else data)
500
497
  total_collected += len(data)
501
498
 
499
+ # Check if we’ve collected the max_items
502
500
  if max_items is not None and total_collected >= max_items:
503
501
  break
504
502
 
505
- next_page = response_data.get("nextPage")
506
- if not next_page or (max_pages is not None and params["page"] >= max_pages):
503
+ # Determine if there is a next page based on totalPages, converting totalPages to an integer if present
504
+ total_pages = int(response_data.get("totalPages", 0)) # Default to 0 if not provided
505
+ if not total_pages or params["page"] >= total_pages:
507
506
  break
508
507
 
509
- params["page"] = next_page if params["page"] is None else params["page"] + 1
508
+ # Move to the next page
509
+ params["page"] += 1
510
510
 
511
511
  finally:
512
512
  time.sleep(2) # Ensure a delay between requests regardless of outcome
@@ -56,7 +56,7 @@ class PolicySetsAPI:
56
56
  conditions (list): List of condition dicts or tuples.
57
57
 
58
58
  Returns:
59
- :obj:`dict`: The conditions template.
59
+ :obj:`list`: The conditions template.
60
60
 
61
61
  """
62
62
  template = []
@@ -78,75 +78,47 @@ class PolicySetsAPI:
78
78
  "COUNTRY_CODE": [],
79
79
  }
80
80
 
81
- current_operator = "OR" # Default operator
82
-
81
+ operators_for_types = {} # Dictionary to store specific operators for each object type
82
+
83
83
  for condition in conditions:
84
- # Check if the first item is an operator, like "AND" or "OR"
84
+ # Check if the first item in a tuple is an operator, like "AND" or "OR"
85
85
  if isinstance(condition, tuple) and isinstance(condition[0], str) and condition[0].upper() in ["AND", "OR"]:
86
- current_operator = condition[0].upper() # Set the current operator
86
+ operator = condition[0].upper()
87
87
  condition = condition[1] # The second element is the actual condition
88
+ else:
89
+ operator = "OR" # Default operator if none specified
88
90
 
91
+ # Process each condition and categorize by object type and operator
89
92
  if isinstance(condition, tuple) and len(condition) == 3:
90
- # Handle each object type according to its pattern
91
93
  object_type = condition[0].upper()
92
94
  lhs = condition[1]
93
95
  rhs = condition[2]
96
+ operand = {"objectType": object_type, "lhs": lhs, "rhs": rhs}
97
+
98
+ # Track the operator for the current object type
99
+ operators_for_types[object_type] = operator
94
100
 
95
101
  if object_type in ["APP", "APP_GROUP"]:
96
- app_and_app_group_operands.append({"objectType": object_type, "lhs": "id", "rhs": rhs})
102
+ app_and_app_group_operands.append(operand)
97
103
  elif object_type in object_types_to_operands:
98
- if object_type == "CLIENT_TYPE":
99
- if rhs in {
100
- "zpn_client_type_exporter",
101
- "zpn_client_type_machine_tunnel",
102
- "zpn_client_type_ip_anchoring",
103
- "zpn_client_type_edge_connector",
104
- "zpn_client_type_zapp",
105
- "zpn_client_type_slogger",
106
- }:
107
- object_types_to_operands[object_type].append({"objectType": object_type, "lhs": "id", "rhs": rhs})
108
- elif object_type in [
109
- "PLATFORM",
110
- "POSTURE",
111
- "TRUSTED_NETWORK",
112
- "SAML",
113
- "SCIM",
114
- "SCIM_GROUP",
115
- "COUNTRY_CODE",
116
- ]:
117
- object_types_to_operands[object_type].append({"objectType": object_type, "lhs": lhs, "rhs": rhs})
118
- else:
119
- object_types_to_operands[object_type].append({"objectType": object_type, "lhs": "id", "rhs": rhs})
120
-
104
+ object_types_to_operands[object_type].append(operand)
105
+
121
106
  elif isinstance(condition, dict):
122
- # This part allows passing operator explicitly through conditions
123
107
  if "operator" in condition:
124
- current_operator = condition["operator"]
125
- continue # Move to the next condition after setting the operator
108
+ operators_for_types["default"] = condition["operator"]
109
+ continue # Skip to the next condition after setting the operator
126
110
 
127
- # Handle the dictionary logic based on the Go code schema
128
111
  condition_template = {}
129
- # Extracting keys from the condition dictionary
130
112
  for key in ["id", "negated", "operator"]:
131
113
  if key in condition:
132
114
  condition_template[key] = condition[key]
133
115
 
134
- # Handling the operands
135
116
  operands = condition.get("operands", [])
136
117
  condition_template["operands"] = []
137
118
 
138
119
  for operand in operands:
139
120
  operand_template = {}
140
-
141
- # Extracting keys from the operand dictionary
142
- for operand_key in [
143
- "id",
144
- "idp_id",
145
- "name",
146
- "lhs",
147
- "rhs",
148
- "objectType",
149
- ]:
121
+ for operand_key in ["id", "idp_id", "name", "lhs", "rhs", "objectType"]:
150
122
  if operand_key in operand:
151
123
  operand_template[operand_key] = operand[operand_key]
152
124
 
@@ -154,14 +126,16 @@ class PolicySetsAPI:
154
126
 
155
127
  template.append(condition_template)
156
128
 
157
- # Combine APP and APP_GROUP operands into one block
129
+ # Combine APP and APP_GROUP operands with their specific operator
158
130
  if app_and_app_group_operands:
159
- template.append({"operator": current_operator, "operands": app_and_app_group_operands})
131
+ app_group_operator = operators_for_types.get("APP", "OR")
132
+ template.append({"operator": app_group_operator, "operands": app_and_app_group_operands})
160
133
 
161
- # Combine other object types into their own blocks
134
+ # Combine other object types into their blocks with their respective operator
162
135
  for object_type, operands in object_types_to_operands.items():
163
136
  if operands:
164
- template.append({"operator": current_operator, "operands": operands})
137
+ operator = operators_for_types.get(object_type, "OR")
138
+ template.append({"operator": operator, "operands": operands})
165
139
 
166
140
  return template
167
141