zscaler-sdk-python 0.3.1__tar.gz → 0.5.0__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 (77) hide show
  1. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/PKG-INFO +5 -5
  2. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/pyproject.toml +7 -6
  3. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/__init__.py +1 -1
  4. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/constants.py +1 -1
  5. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/utils.py +35 -3
  6. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/firewall.py +0 -3
  7. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/forwarding_control.py +11 -14
  8. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/zpa_gateway.py +21 -26
  9. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/__init__.py +63 -24
  10. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments.py +157 -76
  11. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments_inspection.py +45 -54
  12. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/app_segments_pra.py +49 -59
  13. zscaler_sdk_python-0.5.0/zscaler/zpa/authdomains.py +41 -0
  14. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/certificates.py +90 -59
  15. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/client.py +3 -0
  16. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/connectors.py +139 -88
  17. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/emergency_access.py +16 -1
  18. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/idp.py +18 -3
  19. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/inspection.py +215 -227
  20. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/machine_groups.py +24 -10
  21. zscaler_sdk_python-0.5.0/zscaler/zpa/microtenants.py +217 -0
  22. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/policies.py +242 -405
  23. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/privileged_remote_access.py +48 -5
  24. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/provisioning.py +35 -16
  25. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/segment_groups.py +51 -41
  26. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/server_groups.py +34 -12
  27. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/servers.py +42 -22
  28. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/service_edges.py +241 -61
  29. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/trusted_networks.py +20 -5
  30. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/LICENSE.md +0 -0
  31. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/README.md +0 -0
  32. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/__init__.py +0 -0
  33. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/cache.py +0 -0
  34. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/no_op_cache.py +0 -0
  35. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/cache/zscaler_cache.py +0 -0
  36. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/__init__.py +0 -0
  37. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/error.py +0 -0
  38. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/http_error.py +0 -0
  39. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/errors/zscaler_api_error.py +0 -0
  40. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/exceptions/__init__.py +0 -0
  41. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/exceptions/exceptions.py +0 -0
  42. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/logger.py +0 -0
  43. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/ratelimiter/__init__.py +0 -0
  44. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/ratelimiter/ratelimiter.py +0 -0
  45. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/user_agent.py +0 -0
  46. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/__init__.py +0 -0
  47. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/activate.py +0 -0
  48. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/admin_and_role_management.py +0 -0
  49. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/apptotal.py +0 -0
  50. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/audit_logs.py +0 -0
  51. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/authentication_settings.py +0 -0
  52. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/client.py +0 -0
  53. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/cloud_apps.py +0 -0
  54. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/device_management.py +0 -0
  55. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/dlp.py +0 -0
  56. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/errors.py +0 -0
  57. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/isolation_profile.py +0 -0
  58. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/labels.py +0 -0
  59. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/locations.py +0 -0
  60. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/sandbox.py +0 -0
  61. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/security.py +0 -0
  62. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/ssl_inspection.py +0 -0
  63. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/traffic.py +0 -0
  64. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/url_categories.py +0 -0
  65. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/url_filtering.py +0 -0
  66. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/users.py +0 -0
  67. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/web_dlp.py +0 -0
  68. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zia/workload_groups.py +0 -0
  69. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/README.md +0 -0
  70. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/cloud_connector_groups.py +0 -0
  71. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/errors.py +0 -0
  72. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/isolation.py +0 -0
  73. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/lss.py +0 -0
  74. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/posture_profiles.py +0 -0
  75. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/saml_attributes.py +0 -0
  76. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/scim_attributes.py +0 -0
  77. {zscaler_sdk_python-0.3.1 → zscaler_sdk_python-0.5.0}/zscaler/zpa/scim_groups.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zscaler-sdk-python
3
- Version: 0.3.1
3
+ Version: 0.5.0
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
@@ -24,21 +24,21 @@ Provides-Extra: dev
24
24
  Requires-Dist: aenum ; extra == "dev"
25
25
  Requires-Dist: arrow
26
26
  Requires-Dist: black (>=24.3.0) ; extra == "dev"
27
- Requires-Dist: certifi
27
+ Requires-Dist: certifi (>=2024.2.2)
28
28
  Requires-Dist: charset-normalizer
29
- Requires-Dist: cryptography (>=3.4,<43.0)
29
+ Requires-Dist: cryptography (>=42.0.7)
30
30
  Requires-Dist: flake8
31
31
  Requires-Dist: flatdict
32
32
  Requires-Dist: idna
33
33
  Requires-Dist: okta (>=2.9.7)
34
34
  Requires-Dist: pycryptodomex
35
35
  Requires-Dist: pydash ; extra == "dev"
36
- Requires-Dist: python-box (>=7.1.1,<8.0.0)
36
+ Requires-Dist: python-box (>=7.2.0,<8.0.0)
37
37
  Requires-Dist: python-dateutil
38
38
  Requires-Dist: pytz
39
39
  Requires-Dist: pyyaml
40
40
  Requires-Dist: requests (>=2.32.0)
41
- Requires-Dist: responses
41
+ Requires-Dist: responses (>=0.25.3)
42
42
  Requires-Dist: restfly
43
43
  Requires-Dist: six
44
44
  Requires-Dist: xmltodict
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "zscaler-sdk-python"
3
- version = "0.3.1"
3
+ version = "0.5.0"
4
4
  description = "Official Python SDK for the Zscaler Products (Beta)"
5
5
  authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
6
6
  license = "MIT"
@@ -30,13 +30,13 @@ packages = [
30
30
  [tool.poetry.dependencies]
31
31
  python = ">=3.8,<4.0"
32
32
  arrow = "*"
33
- certifi = "*"
33
+ certifi = ">=2024.2.2"
34
34
  charset-normalizer = "*"
35
35
  idna = "*"
36
- python-box = "^7.1.1"
36
+ python-box = "^7.2.0"
37
37
  python-dateutil = "*"
38
38
  requests = ">=2.32.0"
39
- responses = "*"
39
+ responses = ">=0.25.3"
40
40
  restfly = "*"
41
41
  six = "*"
42
42
  flatdict = "*"
@@ -49,12 +49,13 @@ pydash = "*"
49
49
  flake8 = "*"
50
50
  pytz = "*"
51
51
  black = ">=24.3.0"
52
- cryptography = ">=3.4,<43.0"
52
+ # cryptography = ">=3.4,<43.0"
53
+ cryptography = ">=42.0.7"
53
54
  okta = ">=2.9.7"
54
55
 
55
56
  [tool.poetry.dev-dependencies]
56
57
  black = ">=24.3.0"
57
- pytest = "^8.2.1"
58
+ pytest = ">=8.2.1"
58
59
  pytest-asyncio = ">=0.23.7"
59
60
  pytest-mock = "*"
60
61
  pytest-recording = "*"
@@ -29,7 +29,7 @@ __license__ = "MIT"
29
29
  __contributors__ = [
30
30
  "William Guilherme",
31
31
  ]
32
- __version__ = "0.3.1"
32
+ __version__ = "0.5.0"
33
33
 
34
34
  from zscaler.zia import ZIAClientHelper # noqa
35
35
  from zscaler.zpa import ZPAClientHelper # noqa
@@ -14,7 +14,7 @@ ZPA_BASE_URLS = {
14
14
 
15
15
  DEV_AUTH_URL = "https://authn1.dev.zpath.net/authn/v1/oauth/token"
16
16
 
17
- RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
17
+ RETRYABLE_STATUS_CODES = {409, 429, 500, 502, 503, 504}
18
18
  MAX_RETRIES = 5
19
19
  BACKOFF_FACTOR = 1
20
20
  BACKOFF_BASE_DURATION = 2
@@ -147,11 +147,42 @@ def add_id_groups(id_groups: list, kwargs: dict, payload: dict):
147
147
  return
148
148
 
149
149
 
150
+ # def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
151
+ # for entry in id_groups:
152
+ # if kwargs.get(entry[0]):
153
+ # # Ensure each ID is treated as an integer before adding it to the payload
154
+ # payload[entry[1]] = [{"id": int(param_id)} for param_id in kwargs.pop(entry[0])]
155
+ # return
156
+
157
+ # ####### Function used in the ZIA Forwarding Control Rule #######
158
+ # def transform_fwd_id_fields(id_groups: list, kwargs: dict, payload: dict):
159
+ # for entry in id_groups:
160
+ # key, payload_key = entry
161
+ # if key in kwargs:
162
+ # value = kwargs.pop(key)
163
+ # if isinstance(value, dict):
164
+ # payload[payload_key] = {k: v for k, v in value.items() if k in ['id', 'name']}
165
+ # elif isinstance(value, list):
166
+ # payload[payload_key] = [{"id": int(item)} if isinstance(item, str) and item.isdigit() else item for item in value]
167
+ # return
168
+
150
169
  def transform_common_id_fields(id_groups: list, kwargs: dict, payload: dict):
151
170
  for entry in id_groups:
152
- if kwargs.get(entry[0]):
153
- # Ensure each ID is treated as an integer before adding it to the payload
154
- payload[entry[1]] = [{"id": int(param_id)} for param_id in kwargs.pop(entry[0])]
171
+ key, payload_key = entry
172
+ if key in kwargs:
173
+ value = kwargs.pop(key)
174
+ if key in ["zpa_gateway", "proxy_gateway", "zpa_server_group"]:
175
+ # Handle zpa_gateway, proxy_gateway, and zpa_server_group
176
+ if isinstance(value, dict):
177
+ payload[payload_key] = {snake_to_camel(k): v for k, v in value.items() if k in ['id', 'name', 'external_id']}
178
+ elif key in ["zpa_app_segments", "zpa_application_segments", "zpa_application_segment_groups"]:
179
+ # Handle zpa_app_segments, zpa_application_segments, and zpa_application_segment_groups
180
+ if isinstance(value, list):
181
+ payload[payload_key] = [{"externalId": item["external_id"], "name": item["name"]} for item in value]
182
+ else:
183
+ # General case for ID transformations
184
+ if isinstance(value, list):
185
+ payload[payload_key] = [{"id": int(item)} if isinstance(item, (str, int)) else item for item in value]
155
186
  return
156
187
 
157
188
 
@@ -544,3 +575,4 @@ def dump_response(
544
575
  log_lines.append(f"\n{response_body}")
545
576
  log_lines.append("-" * 68)
546
577
  logger.info("\n".join(log_lines))
578
+
@@ -233,10 +233,7 @@ class FirewallPolicyAPI:
233
233
 
234
234
  response = self.rest.put(f"firewallFilteringRules/{rule_id}", json=payload)
235
235
  if isinstance(response, Response) and not response.ok:
236
- # Handle error response
237
236
  raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
238
-
239
- # Return the updated object
240
237
  return self.get_rule(rule_id)
241
238
 
242
239
  def delete_rule(self, rule_id: str) -> int:
@@ -68,10 +68,7 @@ class ForwardingControlAPI:
68
68
  ... pprint(rule)
69
69
 
70
70
  """
71
- response = self.rest.get("/forwardingRules")
72
- if isinstance(response, Response):
73
- return None
74
- return response
71
+ return self.rest.get("forwardingRules")
75
72
 
76
73
  def get_rule(self, rule_id: str) -> Box:
77
74
  """
@@ -158,18 +155,19 @@ class ForwardingControlAPI:
158
155
 
159
156
  # Transform ID fields in kwargs
160
157
  transform_common_id_fields(self.reformat_params, kwargs, payload)
158
+
161
159
  for key, value in kwargs.items():
162
160
  if value is not None:
163
- payload[snake_to_camel(key)] = value
161
+ if key == "state" and isinstance(value, bool):
162
+ payload[key] = "ENABLED" if value else "DISABLED"
163
+ else:
164
+ payload[key] = value
164
165
 
165
166
  # Convert the entire payload's keys to camelCase before sending
166
167
  camel_payload = recursive_snake_to_camel(payload)
167
- for key, value in kwargs.items():
168
- if value is not None:
169
- camel_payload[snake_to_camel(key)] = value
170
168
 
171
169
  # Send POST request to create the rule
172
- response = self.rest.post("forwardingRules", json=payload)
170
+ response = self.rest.post("forwardingRules", json=camel_payload)
173
171
  if isinstance(response, Response):
174
172
  # Handle error response
175
173
  status_code = response.status_code
@@ -227,7 +225,6 @@ class ForwardingControlAPI:
227
225
  ... description="TT#1965232866")
228
226
 
229
227
  """
230
-
231
228
  # Set payload to value of existing record and convert nested dict keys.
232
229
  payload = convert_keys(self.get_rule(rule_id))
233
230
 
@@ -240,14 +237,14 @@ class ForwardingControlAPI:
240
237
 
241
238
  # Add remaining optional parameters to payload
242
239
  for key, value in kwargs.items():
243
- payload[snake_to_camel(key)] = value
240
+ if key == "state" and isinstance(value, bool):
241
+ payload[snake_to_camel(key)] = "ENABLED" if value else "DISABLED"
242
+ else:
243
+ payload[snake_to_camel(key)] = value
244
244
 
245
245
  response = self.rest.put(f"forwardingRules/{rule_id}", json=payload)
246
246
  if isinstance(response, Response) and not response.ok:
247
- # Handle error response
248
247
  raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
249
-
250
- # Return the updated object
251
248
  return self.get_rule(rule_id)
252
249
 
253
250
  def delete_rule(self, rule_id: str) -> int:
@@ -17,7 +17,7 @@
17
17
  from box import Box, BoxList
18
18
  from requests import Response
19
19
 
20
- from zscaler.utils import convert_keys, snake_to_camel
20
+ from zscaler.utils import convert_keys, snake_to_camel, transform_common_id_fields
21
21
  from zscaler.zia import ZIAClient
22
22
 
23
23
 
@@ -68,7 +68,6 @@ class ZPAGatewayAPI:
68
68
  self,
69
69
  name: str,
70
70
  zpa_server_group: dict = None,
71
- zpa_app_segments: list = None,
72
71
  **kwargs,
73
72
  ) -> Box:
74
73
  """
@@ -95,24 +94,21 @@ class ZPAGatewayAPI:
95
94
  """
96
95
  payload = {"name": name, "type": "ZPA"}
97
96
 
97
+ # Add zpa_server_group to kwargs
98
98
  if zpa_server_group:
99
- payload["zpaServerGroup"] = {
100
- "externalId": zpa_server_group.get("external_id"),
101
- "name": zpa_server_group.get("name"),
102
- }
99
+ kwargs["zpa_server_group"] = zpa_server_group
100
+
101
+ # Define the id groups specific to this function
102
+ zpa_gateway_id_groups = [
103
+ ("zpa_server_group", "zpaServerGroup"),
104
+ ]
105
+ transform_common_id_fields(zpa_gateway_id_groups, kwargs, payload)
103
106
 
104
- if zpa_app_segments:
105
- payload["zpaAppSegments"] = [
106
- {"externalId": segment.get("external_id"), "name": segment.get("name")} for segment in zpa_app_segments
107
- ]
108
-
109
- # Add other optional parameters to payload
110
107
  for key, value in kwargs.items():
111
108
  payload[snake_to_camel(key)] = value
112
109
 
113
110
  response = self.rest.post("zpaGateways", json=payload)
114
111
  if isinstance(response, Response):
115
- # Handle error response
116
112
  status_code = response.status_code
117
113
  raise Exception(f"API call failed with status {status_code}: {response.json()}")
118
114
  return response
@@ -143,25 +139,24 @@ class ZPAGatewayAPI:
143
139
  """
144
140
  payload = convert_keys(self.get_gateway(gateway_id))
145
141
 
146
- # Update payload with provided arguments
147
- for key, value in kwargs.items():
148
- if key == "zpa_server_group" and isinstance(value, dict):
149
- # Convert nested keys in zpa_server_group to camelCase
150
- value = {snake_to_camel(k): v for k, v in value.items()}
151
- elif key == "zpa_app_segments" and isinstance(value, list):
152
- # Convert nested keys in zpa_app_segments to camelCase
153
- value = [{snake_to_camel(k): v for k, v in item.items()} for item in value]
142
+ if "zpa_server_group" in kwargs:
143
+ kwargs["zpa_server_group"] = kwargs["zpa_server_group"]
144
+
145
+ # Define the id groups specific to this function
146
+ zpa_gateway_id_groups = [
147
+ ("zpa_server_group", "zpaServerGroup"),
148
+ ]
154
149
 
150
+ transform_common_id_fields(zpa_gateway_id_groups, kwargs, payload)
151
+
152
+ for key, value in kwargs.items():
155
153
  payload[snake_to_camel(key)] = value
156
154
 
157
155
  response = self.rest.put(f"zpaGateways/{gateway_id}", json=payload)
158
156
  if isinstance(response, Response) and not response.ok:
159
- # Handle error response
160
- raise Exception(f"API call failed with status {response.status_code}: " f"{response.json()}")
161
-
162
- # Return the updated object
157
+ raise Exception(f"API call failed with status {response.status_code}: {response.json()}")
163
158
  return self.get_gateway(gateway_id)
164
-
159
+
165
160
  def delete_gateway(self, gateway_id):
166
161
  """
167
162
  Deletes the specified ZPA Gateway.
@@ -26,6 +26,7 @@ from zscaler.utils import (
26
26
  retry_with_backoff,
27
27
  snake_to_camel,
28
28
  )
29
+ from zscaler.zpa.authdomains import AuthDomainsAPI
29
30
  from zscaler.zpa.app_segments import ApplicationSegmentAPI
30
31
  from zscaler.zpa.app_segments_inspection import AppSegmentsInspectionAPI
31
32
  from zscaler.zpa.app_segments_pra import AppSegmentsPRAAPI
@@ -39,6 +40,7 @@ from zscaler.zpa.inspection import InspectionControllerAPI
39
40
  from zscaler.zpa.isolation import IsolationAPI
40
41
  from zscaler.zpa.lss import LSSConfigControllerAPI
41
42
  from zscaler.zpa.machine_groups import MachineGroupsAPI
43
+ from zscaler.zpa.microtenants import MicrotenantsAPI
42
44
  from zscaler.zpa.policies import PolicySetsAPI
43
45
  from zscaler.zpa.posture_profiles import PostureProfilesAPI
44
46
  from zscaler.zpa.privileged_remote_access import PrivilegedRemoteAccessAPI
@@ -81,12 +83,12 @@ class ZPAClientHelper(ZPAClient):
81
83
  client_secret,
82
84
  customer_id,
83
85
  cloud,
86
+ microtenant_id=None,
84
87
  timeout=240,
85
88
  cache=None,
86
89
  fail_safe=False,
87
90
  ):
88
91
  # Initialize rate limiter
89
- # You may want to adjust these parameters as per your rate limit configuration
90
92
  self.rate_limiter = RateLimiter(
91
93
  get_limit=20, # Adjusted to allow 20 GET requests per 10 seconds
92
94
  post_put_delete_limit=10, # Adjusted to allow 10 POST/PUT/DELETE requests per 10 seconds
@@ -94,7 +96,6 @@ class ZPAClientHelper(ZPAClient):
94
96
  post_put_delete_freq=10, # Adjust frequency to 10 seconds
95
97
  )
96
98
 
97
- # Validate cloud value
98
99
  if cloud not in ZPA_BASE_URLS:
99
100
  valid_clouds = ", ".join(ZPA_BASE_URLS.keys())
100
101
  raise ValueError(
@@ -102,15 +103,13 @@ class ZPAClientHelper(ZPAClient):
102
103
  f"Please use one of the following supported values: {valid_clouds}"
103
104
  )
104
105
 
105
- # Continue with existing initialization...
106
- # Select the appropriate URL
107
106
  self.baseurl = ZPA_BASE_URLS.get(cloud, ZPA_BASE_URLS["PRODUCTION"])
108
-
109
107
  self.timeout = timeout
110
108
  self.client_id = client_id
111
109
  self.client_secret = client_secret
112
110
  self.customer_id = customer_id
113
111
  self.cloud = cloud
112
+ self.microtenant_id = microtenant_id or os.getenv("ZPA_MICROTENANT_ID")
114
113
  self.url = f"{self.baseurl}/mgmtconfig/v1/admin/customers/{customer_id}"
115
114
  self.user_config_url = f"{self.baseurl}/userconfig/v1/customers/{customer_id}"
116
115
  self.v2_url = f"{self.baseurl}/mgmtconfig/v2/admin/customers/{customer_id}"
@@ -118,7 +117,6 @@ class ZPAClientHelper(ZPAClient):
118
117
  self.cbi_url = f"{self.baseurl}/cbiconfig/cbi/api/customers/{customer_id}"
119
118
  self.fail_safe = fail_safe
120
119
 
121
- # Cache setup
122
120
  cache_enabled = os.environ.get("ZSCALER_CLIENT_CACHE_ENABLED", "true").lower() == "true"
123
121
  if cache is None:
124
122
  if cache_enabled:
@@ -130,7 +128,6 @@ class ZPAClientHelper(ZPAClient):
130
128
  else:
131
129
  self.cache = cache
132
130
 
133
- # Initialize user-agent
134
131
  ua = UserAgent()
135
132
  self.user_agent = ua.get_user_agent_string()
136
133
  self.access_token = None
@@ -184,20 +181,34 @@ class ZPAClientHelper(ZPAClient):
184
181
  elif api_version == "cbiconfig_v1":
185
182
  api = self.cbi_url
186
183
 
184
+ if params is None:
185
+ params = {}
186
+
187
+ if json and "microtenant_id" in json:
188
+ microtenant_id = json.pop("microtenant_id")
189
+ else:
190
+ microtenant_id = self.microtenant_id
191
+
192
+ if microtenant_id:
193
+ params["microtenantId"] = microtenant_id
194
+
187
195
  url = f"{api}/{path.lstrip('/')}"
196
+ if params:
197
+ url = f"{url}?{urllib.parse.urlencode(params)}"
198
+
188
199
  start_time = time.time()
189
200
  headers_with_user_agent = self.headers.copy()
190
201
  headers_with_user_agent["User-Agent"] = self.user_agent
191
202
  request_uuid = uuid.uuid4()
192
- dump_request(logger, url, method, json, params, headers_with_user_agent, request_uuid)
193
- cache_key = self.cache.create_key(url, params)
203
+ dump_request(logger, url, method, json, None, headers_with_user_agent, request_uuid)
204
+ cache_key = self.cache.create_key(url, None)
194
205
  if method == "GET" and self.cache.contains(cache_key):
195
206
  resp = self.cache.get(cache_key)
196
207
  dump_response(
197
208
  logger=logger,
198
209
  url=url,
199
210
  method=method,
200
- params=params,
211
+ params=None,
201
212
  resp=resp,
202
213
  request_uuid=request_uuid,
203
214
  start_time=start_time,
@@ -217,13 +228,14 @@ class ZPAClientHelper(ZPAClient):
217
228
  method,
218
229
  url,
219
230
  json=json,
231
+ params=None,
220
232
  headers=headers_with_user_agent,
221
233
  timeout=self.timeout,
222
234
  )
223
235
  dump_response(
224
236
  logger=logger,
225
237
  url=url,
226
- params=params,
238
+ params=None,
227
239
  method=method,
228
240
  resp=resp,
229
241
  request_uuid=request_uuid,
@@ -366,6 +378,7 @@ class ZPAClientHelper(ZPAClient):
366
378
  search_field="name",
367
379
  max_pages=None,
368
380
  max_items=None,
381
+ all_entries=False, # Return all SCIM groups including the deleted ones if set to true
369
382
  sort_order=None,
370
383
  sort_by=None,
371
384
  sort_dir=None,
@@ -373,8 +386,10 @@ class ZPAClientHelper(ZPAClient):
373
386
  end_time=None,
374
387
  idp_group_id=None,
375
388
  scim_user_id=None,
389
+ scim_username=None,
376
390
  page=None,
377
- pagesize=20,
391
+ pagesize=None,
392
+ microtenant_id=None,
378
393
  ):
379
394
  """
380
395
  Fetches paginated data from the ZPA API based on specified parameters and handles various types of API pagination.
@@ -417,15 +432,19 @@ class ZPAClientHelper(ZPAClient):
417
432
  if params is None:
418
433
  params = {}
419
434
 
420
- if (page is not None or pagesize != 20) and (max_pages is not None or max_items is not None):
435
+ if (page is not None or pagesize is not None) and (max_pages is not None or max_items is not None):
421
436
  raise ValueError(
422
437
  "Do not mix 'page' or 'pagesize' with 'max_pages' or 'max_items'. Choose either set of parameters."
423
438
  )
424
439
 
425
- params["pagesize"] = min(pagesize, 500) # Apply maximum constraint and handle default
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
426
442
 
427
- if page:
428
- params["page"] = page
443
+ # Check for microtenantId in function arguments first, then environment variable
444
+ if microtenant_id:
445
+ params["microtenantId"] = microtenant_id
446
+ elif self.microtenant_id and "microtenantId" not in params:
447
+ params["microtenantId"] = self.microtenant_id
429
448
 
430
449
  if search:
431
450
  api_search_field = snake_to_camel(search_field)
@@ -443,6 +462,10 @@ class ZPAClientHelper(ZPAClient):
443
462
  params["idpGroupId"] = idp_group_id
444
463
  if scim_user_id:
445
464
  params["scimUserId"] = scim_user_id
465
+ if scim_username:
466
+ params["scimUserName"] = scim_username
467
+ if all_entries:
468
+ params["allEntries"] = all_entries
446
469
 
447
470
  total_collected = 0
448
471
  ret_data = []
@@ -456,17 +479,18 @@ class ZPAClientHelper(ZPAClient):
456
479
  if should_wait:
457
480
  time.sleep(delay)
458
481
 
459
- url = f"{path}?{urllib.parse.urlencode(params)}"
460
- response = self.send("GET", url, api_version=api_version)
482
+ response = self.send("GET", path=path, params=params, api_version=api_version)
461
483
 
462
484
  if response.status_code != expected_status_code:
463
- error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(status_code=response.status_code, page=page)
485
+ error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
486
+ status_code=response.status_code, page=params["page"]
487
+ )
464
488
  logger.error(error_msg)
465
489
  return BoxList([]), error_msg
466
490
 
467
491
  response_data = response.json()
468
492
  data = response_data.get("list", [])
469
- if not data and (page is None or page == 1):
493
+ if not data and (params["page"] == 1):
470
494
  error_msg = ERROR_MESSAGES["EMPTY_RESULTS"]
471
495
  logger.warn(error_msg)
472
496
  return BoxList([]), error_msg
@@ -478,12 +502,11 @@ class ZPAClientHelper(ZPAClient):
478
502
  if max_items is not None and total_collected >= max_items:
479
503
  break
480
504
 
481
- nextPage = response_data.get("nextPage")
482
- if not nextPage or (max_pages is not None and page >= max_pages):
505
+ next_page = response_data.get("nextPage")
506
+ if not next_page or (max_pages is not None and params["page"] >= max_pages):
483
507
  break
484
508
 
485
- page = nextPage if page is None else page + 1
486
- params["page"] = page
509
+ params["page"] = next_page if params["page"] is None else params["page"] + 1
487
510
 
488
511
  finally:
489
512
  time.sleep(2) # Ensure a delay between requests regardless of outcome
@@ -495,6 +518,14 @@ class ZPAClientHelper(ZPAClient):
495
518
 
496
519
  return BoxList(ret_data), None
497
520
 
521
+ @property
522
+ def authdomains(self):
523
+ """
524
+ The interface object for the :ref:`ZPA Auth Domains interface <zpa-authdomains>`.
525
+
526
+ """
527
+ return AuthDomainsAPI(self)
528
+
498
529
  @property
499
530
  def app_segments(self):
500
531
  """
@@ -591,6 +622,14 @@ class ZPAClientHelper(ZPAClient):
591
622
  """
592
623
  return MachineGroupsAPI(self)
593
624
 
625
+ @property
626
+ def microtenants(self):
627
+ """
628
+ The interface object for the :ref:`ZPA Microtenants interface <zpa-microtenants>`.
629
+
630
+ """
631
+ return MicrotenantsAPI(self)
632
+
594
633
  @property
595
634
  def policies(self):
596
635
  """