zscaler-sdk-python 0.9.4__tar.gz → 0.9.6__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.4 → zscaler_sdk_python-0.9.6}/PKG-INFO +3 -3
  2. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/pyproject.toml +4 -5
  3. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/__init__.py +1 -1
  4. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/__init__.py +82 -50
  5. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/admin_and_role_management.py +2 -1
  6. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/labels.py +2 -1
  7. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/locations.py +19 -45
  8. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/traffic.py +2 -1
  9. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/users.py +6 -3
  10. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/connectors.py +11 -2
  11. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/policies.py +18 -4
  12. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/service_edges.py +1 -1
  13. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/LICENSE.md +0 -0
  14. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/README.md +0 -0
  15. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/cache/__init__.py +0 -0
  16. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/cache/cache.py +0 -0
  17. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/cache/no_op_cache.py +0 -0
  18. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/cache/zscaler_cache.py +0 -0
  19. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/constants.py +0 -0
  20. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/errors/__init__.py +0 -0
  21. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/errors/error.py +0 -0
  22. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/errors/http_error.py +0 -0
  23. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/errors/zscaler_api_error.py +0 -0
  24. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/exceptions/__init__.py +0 -0
  25. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/exceptions/exceptions.py +0 -0
  26. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/logger.py +0 -0
  27. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/ratelimiter/__init__.py +0 -0
  28. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/ratelimiter/ratelimiter.py +0 -0
  29. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/user_agent.py +0 -0
  30. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/utils.py +0 -0
  31. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcc/__init__.py +0 -0
  32. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcc/client.py +0 -0
  33. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcc/devices.py +0 -0
  34. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcc/secrets.py +0 -0
  35. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/__init__.py +0 -0
  36. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/activation.py +0 -0
  37. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/admin_and_role_management.py +0 -0
  38. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/client.py +0 -0
  39. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/ecgroups.py +0 -0
  40. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/locations.py +0 -0
  41. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zcon/provisioning.py +0 -0
  42. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/__init__.py +0 -0
  43. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/admin.py +0 -0
  44. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/alerts.py +0 -0
  45. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/apps.py +0 -0
  46. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/devices.py +0 -0
  47. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/filters.py +0 -0
  48. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/inventory.py +0 -0
  49. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/troubleshooting.py +0 -0
  50. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/users.py +0 -0
  51. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zdx/zdx_client.py +0 -0
  52. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/activate.py +0 -0
  53. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/apptotal.py +0 -0
  54. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/audit_logs.py +0 -0
  55. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/authentication_settings.py +0 -0
  56. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/client.py +0 -0
  57. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/cloud_apps.py +0 -0
  58. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/cloudappcontrol.py +0 -0
  59. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/device_management.py +0 -0
  60. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/dlp.py +0 -0
  61. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/errors.py +0 -0
  62. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/firewall.py +0 -0
  63. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/forwarding_control.py +0 -0
  64. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/isolation_profile.py +0 -0
  65. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/sandbox.py +0 -0
  66. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/security.py +0 -0
  67. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/ssl_inspection.py +0 -0
  68. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/url_categories.py +0 -0
  69. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/url_filtering.py +0 -0
  70. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/web_dlp.py +0 -0
  71. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/workload_groups.py +0 -0
  72. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zia/zpa_gateway.py +0 -0
  73. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/README.md +0 -0
  74. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/__init__.py +0 -0
  75. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/app_segments.py +0 -0
  76. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/app_segments_inspection.py +0 -0
  77. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/app_segments_pra.py +0 -0
  78. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/authdomains.py +0 -0
  79. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/certificates.py +0 -0
  80. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/client.py +0 -0
  81. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/cloud_connector_groups.py +0 -0
  82. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/emergency_access.py +0 -0
  83. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/errors.py +0 -0
  84. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/idp.py +0 -0
  85. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/inspection.py +0 -0
  86. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/isolation.py +0 -0
  87. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/lss.py +0 -0
  88. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/machine_groups.py +0 -0
  89. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/microtenants.py +0 -0
  90. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/posture_profiles.py +0 -0
  91. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/privileged_remote_access.py +0 -0
  92. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/provisioning.py +0 -0
  93. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/saml_attributes.py +0 -0
  94. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/scim_attributes.py +0 -0
  95. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/scim_groups.py +0 -0
  96. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/segment_groups.py +0 -0
  97. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/server_groups.py +0 -0
  98. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/zscaler/zpa/servers.py +0 -0
  99. {zscaler_sdk_python-0.9.4 → zscaler_sdk_python-0.9.6}/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.4
3
+ Version: 0.9.6
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
@@ -42,8 +42,8 @@ Requires-Dist: requests (>=2.32.3)
42
42
  Requires-Dist: responses (>=0.25.3)
43
43
  Requires-Dist: restfly (>=1.5.0)
44
44
  Requires-Dist: six
45
- Requires-Dist: xmltodict
46
- Requires-Dist: yarl
45
+ Requires-Dist: xmltodict (>=0.14.2)
46
+ Requires-Dist: yarl (>=1.14.0)
47
47
  Project-URL: Bug Tracker, https://github.com/zscaler/zscaler-sdk-python/issues
48
48
  Project-URL: Documentation, https://zscaler-sdk-python.readthedocs.io
49
49
  Project-URL: Repository, https://github.com/zscaler/zscaler-sdk-python
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "zscaler-sdk-python"
3
- version = "0.9.4"
3
+ version = "0.9.6"
4
4
  description = "Official Python SDK for the Zscaler Products (Beta)"
5
5
  authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
6
6
  license = "MIT"
@@ -41,15 +41,14 @@ restfly = ">=1.5.0"
41
41
  six = "*"
42
42
  flatdict = "*"
43
43
  pyyaml = "*"
44
- xmltodict = "*"
45
- yarl = "*"
44
+ xmltodict = ">=0.14.2"
45
+ yarl = ">=1.14.0"
46
46
  pycryptodomex = ">=3.20.0"
47
47
  aenum = "*"
48
48
  pydash = ">=8.0.3"
49
49
  flake8 = "*"
50
50
  pytz = ">=2024.2"
51
51
  black = ">=24.3.0"
52
- # cryptography = ">=3.4,<43.0"
53
52
  cryptography = ">=43.0.0"
54
53
  okta = ">=2.9.7"
55
54
  aiohttp = ">=3.10.2"
@@ -61,7 +60,7 @@ pytest-asyncio = "^0.23.8"
61
60
  pytest-mock = "*"
62
61
  pytest-recording = "^0.13.2"
63
62
  pytest-cov = "*"
64
- pyfakefs = "^5.6.0"
63
+ pyfakefs = ">=5.6.0"
65
64
  isort = "*"
66
65
  wheel = "*"
67
66
  sphinx = "^7.4.7"
@@ -29,7 +29,7 @@ __license__ = "MIT"
29
29
  __contributors__ = [
30
30
  "William Guilherme",
31
31
  ]
32
- __version__ = "0.9.4"
32
+ __version__ = "0.9.6"
33
33
 
34
34
  from zscaler.zdx import ZDXClientHelper # noqa
35
35
  from zscaler.zia import ZIAClientHelper # noqa
@@ -399,67 +399,99 @@ class ZIAClientHelper(ZIAClient):
399
399
  time.sleep(delay)
400
400
  return self.send("DELETE", path, json, params)
401
401
 
402
- ERROR_MESSAGES = {
403
- "UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
404
- "MISSING_DATA_KEY": "The key '{data_key_name}' was not found in the response for page {page}.",
405
- "EMPTY_RESULTS": "No results found for page {page}.",
406
- }
407
-
408
- def get_paginated_data(self, path=None, data_key_name=None, data_per_page=5, expected_status_code=200):
409
- """
410
- Fetch paginated data from the ZIA API.
411
- ...
402
+ def get_paginated_data(
403
+ self,
404
+ path=None,
405
+ expected_status_code=200,
406
+ page=None,
407
+ pagesize=None,
408
+ search=None,
409
+ ):
410
+ """
411
+ Fetches paginated data from the API based on specified parameters and handles pagination.
412
+
413
+ Args:
414
+ path (str): The API endpoint path to send requests to.
415
+ expected_status_code (int): The expected HTTP status code for a successful request. Defaults to 200.
416
+ page (int): Specific page number to fetch. Defaults to 1 if not provided.
417
+ pagesize (int): Number of items per page, default is 100, with a maximum of 1000.
418
+ search (str): Search query to filter the results.
412
419
 
413
420
  Returns:
414
- - list: List of fetched items.
415
- - str: Error message, if any occurred.
421
+ tuple: A tuple containing:
422
+ - BoxList: A list of fetched items wrapped in a BoxList for easy access.
423
+ - str: An error message if any occurred during the data fetching process.
416
424
  """
425
+ logger = logging.getLogger(__name__)
417
426
 
418
- page = 1
419
- ret_data = []
420
- error_message = None
421
-
422
- while True:
423
- required_url = f"{path}"
424
- should_wait, delay = self.rate_limiter.wait("GET")
425
- if should_wait:
426
- time.sleep(delay)
427
-
428
- # Now proceed with sending the request
429
- response = self.send(
430
- method="GET",
431
- path=required_url,
432
- params={"page": page, "pageSize": data_per_page},
433
- )
427
+ ERROR_MESSAGES = {
428
+ "UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
429
+ "EMPTY_RESULTS": "No results found for page {page}.",
430
+ }
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
434
435
 
435
- if response.status_code != expected_status_code:
436
- error_message = self.ERROR_MESSAGES["UNEXPECTED_STATUS"].format(status_code=response.status_code, page=page)
437
- logger.error(error_message)
438
- break
439
- data_json = response.json()
440
- if isinstance(data_json, list):
441
- data = data_json
442
- else:
443
- data = data_json.get(data_key_name)
436
+ if search:
437
+ params["search"] = search
438
+
439
+ ret_data = []
444
440
 
445
- if data is None:
446
- error_message = self.ERROR_MESSAGES["MISSING_DATA_KEY"].format(data_key_name=data_key_name, page=page)
447
- logger.error(error_message)
448
- break
441
+ try:
442
+ while True:
443
+ # Apply rate-limiting if necessary
444
+ should_wait, delay = self.rate_limiter.wait("GET")
445
+ if should_wait:
446
+ time.sleep(delay)
447
+
448
+ # Send the request to the API
449
+ response = self.send("GET", path=path, params=params)
450
+
451
+ # Check for unexpected status code
452
+ if response.status_code != expected_status_code:
453
+ error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
454
+ status_code=response.status_code, page=params["page"]
455
+ )
456
+ logger.error(error_msg)
457
+ return BoxList([]), error_msg
458
+
459
+ # Parse the response as a flat list of items
460
+ response_data = response.json()
461
+ if not isinstance(response_data, list):
462
+ error_msg = ERROR_MESSAGES["EMPTY_RESULTS"].format(page=params["page"])
463
+ logger.warn(error_msg)
464
+ return BoxList([]), error_msg
465
+
466
+ data = convert_keys_to_snake(response_data)
467
+
468
+ # If searching for a specific item, stop if we find a match
469
+ if search:
470
+ for item in data:
471
+ if item.get("name") == search:
472
+ ret_data.append(item)
473
+ return BoxList(ret_data), None
474
+
475
+ # If no search, collect all data from the current page
476
+ ret_data.extend(data)
477
+
478
+ # Stop if we've processed all available pages
479
+ if len(data) < params["pagesize"]:
480
+ break
449
481
 
450
- if not data: # Checks for empty data
451
- logger.info(self.ERROR_MESSAGES["EMPTY_RESULTS"].format(page=page))
452
- break
482
+ # Move to the next page
483
+ params["page"] += 1
453
484
 
454
- ret_data.extend(convert_keys_to_snake(data))
485
+ finally:
486
+ time.sleep(2) # Ensure a delay between requests regardless of outcome
455
487
 
456
- # Check for more pages
457
- if len(data) == 0 or isinstance(data_json, dict) and int(response.json().get("totalPages")) <= page + 1:
458
- break
488
+ if not ret_data:
489
+ error_msg = ERROR_MESSAGES["EMPTY_RESULTS"].format(page=params["page"])
490
+ logger.warn(error_msg)
491
+ return BoxList([]), error_msg
459
492
 
460
- page += 1
493
+ return BoxList(ret_data), None
461
494
 
462
- return BoxList(ret_data), error_message
463
495
 
464
496
  @property
465
497
  def admin_and_role_management(self):
@@ -48,7 +48,8 @@ class AdminAndRoleManagementAPI:
48
48
  >>> users = zia.admin_and_role_management.list_users('admin@example.com')
49
49
 
50
50
  """
51
- return BoxList(Iterator(self.rest, "adminUsers", **kwargs))
51
+ list, _ = self.rest.get_paginated_data(path="/adminUsers", **kwargs)
52
+ return list
52
53
 
53
54
  def get_user(self, user_id: str) -> Box:
54
55
  """
@@ -58,7 +58,8 @@ class RuleLabelsAPI:
58
58
  ... print(label)
59
59
 
60
60
  """
61
- return BoxList(Iterator(self.rest, "ruleLabels", **kwargs))
61
+ list, _ = self.rest.get_paginated_data(path="/ruleLabels", **kwargs)
62
+ return list
62
63
 
63
64
  def get_label(self, label_id: str) -> Box:
64
65
  """
@@ -64,7 +64,8 @@ class LocationsAPI:
64
64
  ... print(location)
65
65
 
66
66
  """
67
- return BoxList(Iterator(self.rest, "locations", **kwargs))
67
+ list, _ = self.rest.get_paginated_data(path="/locations", **kwargs)
68
+ return list
68
69
 
69
70
  def get_location(self, location_id: str = None, location_name: str = None) -> Box:
70
71
  """
@@ -482,7 +483,7 @@ class LocationsAPI:
482
483
  raise Exception(f"API call failed with status {status_code}: {response.json()}")
483
484
  return response
484
485
 
485
- def list_location_groups(self) -> BoxList:
486
+ def list_location_groups(self, **kwargs) -> BoxList:
486
487
  """
487
488
  Return a list of location groups in ZIA.
488
489
 
@@ -490,7 +491,11 @@ class LocationsAPI:
490
491
  **kwargs: Optional keyword args.
491
492
 
492
493
  Keyword Args:
493
- groupType (str): The location group's type (i.e., Static or Dynamic).
494
+ name (str): The location group's name.
495
+ comments (str): Additional comments or information about the location group.
496
+ groupType (str): The location group's type (i.e., STATIC_GROUP or DYNAMIC_GROUP).
497
+ location_id (int): The unique identifier for a location within a location group.
498
+ last_mod_user (str): The admin who modified the location group last.
494
499
 
495
500
  Returns:
496
501
  :obj:`BoxList`: A list of location group resource records.
@@ -499,7 +504,14 @@ class LocationsAPI:
499
504
  Get a list of all configured location groups:
500
505
  >>> location = zia.locations.list_location_groups()
501
506
  """
502
- return self.rest.get("locations/groups")
507
+ return BoxList(
508
+ Iterator(
509
+ self.rest,
510
+ f"locations/groups",
511
+ max_pages=1,
512
+ **kwargs,
513
+ )
514
+ )
503
515
 
504
516
  def get_location_group_by_id(self, group_id: int) -> Box:
505
517
  """
@@ -517,25 +529,6 @@ class LocationsAPI:
517
529
  """
518
530
  return self.rest.get(f"locations/groups/{group_id}")
519
531
 
520
- def get_location_group_by_name(self, group_name: str, page: int = 1, page_size: int = 100) -> Box:
521
- """
522
- Return a specific location group by its name in ZIA.
523
-
524
- Args:
525
- group_name (str): The name of the location group.
526
- page (int, optional): Page number to retrieve. Defaults to 1.
527
- page_size (int, optional): Number of records per page. Defaults to 100.
528
-
529
- Returns:
530
- :obj:`Box`: A location group resource record.
531
-
532
- Examples:
533
- Get a specific location group by its name:
534
- >>> location = zia.locations.get_location_group_by_name("Unassigned Locations")
535
- """
536
- params = {"page": page, "pageSize": page_size, "search": group_name}
537
- return self.rest.get("locations/groups", params=params)
538
-
539
532
  def list_location_groups_lite(self, page: int = 1, page_size: int = 100) -> BoxList:
540
533
  """
541
534
  Returns a list of location groups (lite version) by their ID where only name and ID is returned in ZIA.
@@ -544,7 +537,7 @@ class LocationsAPI:
544
537
  **kwargs: Optional keyword args.
545
538
 
546
539
  Keyword Args:
547
- groupType (str): The location group's type (i.e., Static or Dynamic).
540
+ groupType (str): The location group's type (i.e., STATIC_GROUP or DYNAMIC_GROUP).
548
541
 
549
542
  Returns:
550
543
  :obj:`BoxList`: A list of location group resource records.
@@ -572,25 +565,6 @@ class LocationsAPI:
572
565
  """
573
566
  return self.rest.get(f"locations/groups/lite/{group_id}")
574
567
 
575
- def get_location_group_lite_by_name(self, group_name: str, page: int = 1, page_size: int = 100) -> BoxList:
576
- """
577
- Return specific location groups (lite version) by their name where only name and ID is returned in ZIA.
578
-
579
- Args:
580
- group_name (str): The name of the location group.
581
- page (int, optional): Page number to retrieve. Defaults to 1.
582
- page_size (int, optional): Number of records per page. Defaults to 100.
583
-
584
- Returns:
585
- :obj:`BoxList`: A list of location group resource records with only ID and name.
586
-
587
- Examples:
588
- Get specific location groups (lite version) by name:
589
- >>> locations = zia.locations.get_location_group_lite_by_name("Unassigned Locations")
590
- """
591
- params = {"page": page, "pageSize": page_size, "search": group_name}
592
- return self.rest.get("locations/groups/lite", params=params)
593
-
594
568
  def list_location_groups_count(self, **kwargs) -> BoxList:
595
569
  """
596
570
  Returns a list of location groups for your organization.
@@ -599,7 +573,7 @@ class LocationsAPI:
599
573
  **kwargs: Optional keyword args.
600
574
 
601
575
  Keyword Args:
602
- group_type (str): The location group's type (i.e., Static or Dynamic).
576
+ group_type (str): The location group's type (i.e., STATIC_GROUP or DYNAMIC_GROUP).
603
577
  last_mod_user (str): The admin who modified the location group last.
604
578
  version (int): The version parameter is for Zscaler internal use only. Used by the service for backup operations.
605
579
  name (str): The location group's name.
@@ -611,7 +585,7 @@ class LocationsAPI:
611
585
 
612
586
  Examples:
613
587
  Gets the list of location groups for your organization:
614
- >>> location = zia.locations.list_location_groups_count(group_type='Static', name='Corporate')
588
+ >>> location = zia.locations.list_location_groups_count(group_type='STATIC_GROUP', name='Corporate')
615
589
  """
616
590
  params = {}
617
591
  optional_params = ["group_type", "last_mod_user", "version", "name", "comments", "location_id"]
@@ -642,7 +642,8 @@ class TrafficForwardingAPI:
642
642
  >>> for credential in zia.traffic.list_vpn_credentials(page_size=200, max_pages=2):
643
643
  ... print(credential)
644
644
  """
645
- return BoxList(Iterator(self.rest, "vpnCredentials", **kwargs))
645
+ list, _ = self.rest.get_paginated_data(path="/vpnCredentials", **kwargs)
646
+ return list
646
647
 
647
648
  def add_vpn_credential(self, authentication_type: str, pre_shared_key: str = None, **kwargs) -> Box:
648
649
  """
@@ -69,7 +69,8 @@ class UserManagementAPI:
69
69
  >>> for department in zia.users.list_departments(page_size=200, max_pages=2):
70
70
  ... print(department)
71
71
  """
72
- return BoxList(Iterator(self.rest, "departments", **kwargs))
72
+ list, _ = self.rest.get_paginated_data(path="/departments", **kwargs)
73
+ return list
73
74
 
74
75
  def get_department(self, department_id: str) -> Box:
75
76
  """
@@ -127,7 +128,8 @@ class UserManagementAPI:
127
128
  ... print(group)
128
129
 
129
130
  """
130
- return BoxList(Iterator(self.rest, "groups", **kwargs))
131
+ list, _ = self.rest.get_paginated_data(path="/groups", **kwargs)
132
+ return list
131
133
 
132
134
  def get_group(self, group_id: str) -> Box:
133
135
  """
@@ -194,7 +196,8 @@ class UserManagementAPI:
194
196
  ... print(user)
195
197
 
196
198
  """
197
- return BoxList(Iterator(self.rest, "users", **kwargs))
199
+ list, _ = self.rest.get_paginated_data(path="/users", **kwargs)
200
+ return list
198
201
 
199
202
  def add_user(self, name: str, email: str, groups: list, department: dict, **kwargs) -> Box:
200
203
  """
@@ -159,7 +159,7 @@ class AppConnectorControllerAPI:
159
159
  params["microtenantId"] = kwargs.pop("microtenant_id")
160
160
  return self.rest.delete(f"connector/{connector_id}", params=params).status_code
161
161
 
162
- def bulk_delete_connectors(self, connector_ids: list, **kwargs) -> int:
162
+ def bulk_delete_connectors(self, connector_ids: list, **kwargs) -> Box:
163
163
  """
164
164
  Deletes all specified App Connectors from ZPA.
165
165
 
@@ -177,7 +177,16 @@ class AppConnectorControllerAPI:
177
177
  params = {}
178
178
  if "microtenant_id" in kwargs:
179
179
  params["microtenantId"] = kwargs.pop("microtenant_id")
180
- return self.rest.post("connector/bulkDelete", json=payload)
180
+
181
+ response = self.rest.post("connector/bulkDelete", json=payload, params=params)
182
+
183
+ if isinstance(response, Response):
184
+ # Check for HTTP status and handle errors
185
+ if response.status_code != 200:
186
+ raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
187
+
188
+ # If successful, return the response
189
+ return response
181
190
 
182
191
  def list_connector_groups(self, **kwargs) -> BoxList:
183
192
  """
@@ -78,7 +78,14 @@ class PolicySetsAPI:
78
78
  "COUNTRY_CODE": [],
79
79
  }
80
80
 
81
+ current_operator = "OR" # Default operator
82
+
81
83
  for condition in conditions:
84
+ # Check if the first item is an operator, like "AND" or "OR"
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
87
+ condition = condition[1] # The second element is the actual condition
88
+
82
89
  if isinstance(condition, tuple) and len(condition) == 3:
83
90
  # Handle each object type according to its pattern
84
91
  object_type = condition[0].upper()
@@ -110,10 +117,15 @@ class PolicySetsAPI:
110
117
  object_types_to_operands[object_type].append({"objectType": object_type, "lhs": lhs, "rhs": rhs})
111
118
  else:
112
119
  object_types_to_operands[object_type].append({"objectType": object_type, "lhs": "id", "rhs": rhs})
120
+
113
121
  elif isinstance(condition, dict):
122
+ # This part allows passing operator explicitly through conditions
123
+ if "operator" in condition:
124
+ current_operator = condition["operator"]
125
+ continue # Move to the next condition after setting the operator
126
+
114
127
  # Handle the dictionary logic based on the Go code schema
115
128
  condition_template = {}
116
-
117
129
  # Extracting keys from the condition dictionary
118
130
  for key in ["id", "negated", "operator"]:
119
131
  if key in condition:
@@ -144,12 +156,12 @@ class PolicySetsAPI:
144
156
 
145
157
  # Combine APP and APP_GROUP operands into one block
146
158
  if app_and_app_group_operands:
147
- template.append({"operator": "OR", "operands": app_and_app_group_operands})
159
+ template.append({"operator": current_operator, "operands": app_and_app_group_operands})
148
160
 
149
161
  # Combine other object types into their own blocks
150
162
  for object_type, operands in object_types_to_operands.items():
151
163
  if operands:
152
- template.append({"operator": "OR", "operands": operands})
164
+ template.append({"operator": current_operator, "operands": operands})
153
165
 
154
166
  return template
155
167
 
@@ -390,7 +402,9 @@ class PolicySetsAPI:
390
402
  ('app', 'id', '88888'),
391
403
  ('app_group', 'id', '77777),
392
404
  ('client_type', 'zpn_client_type_exporter', 'zpn_client_type_zapp'),
393
- ('trusted_network', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx', True)]
405
+ ("OR", 'trusted_network', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx', True))
406
+ ("OR", ("posture", "d019df8b-ec97-4087-a892-749b5abca54c", "false")),
407
+ ]
394
408
  custom_msg (str):
395
409
  A custom message.
396
410
  description (str):
@@ -155,7 +155,7 @@ class ServiceEdgesAPI:
155
155
  params["microtenantId"] = kwargs.pop("microtenant_id")
156
156
  return self.rest.delete(f"serviceEdge/{service_edge_id}", params=params).status_code
157
157
 
158
- def bulk_delete_service_edges(self, service_edge_ids: list, **kwargs) -> int:
158
+ def bulk_delete_service_edges(self, service_edge_ids: list, **kwargs) -> Box:
159
159
  """
160
160
  Bulk deletes the specified Service Edges from ZPA.
161
161