opportify-sdk 0.1.1__py3-none-any.whl → 0.3.1__py3-none-any.whl
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.
Potentially problematic release.
This version of opportify-sdk might be problematic. Click here for more details.
- openapi_client/__init__.py +186 -0
- openapi_client/api/email_insights_api.py +1491 -0
- openapi_client/api/ip_insights_api.py +1494 -0
- {lib/v1/openapi_client → openapi_client}/api_client.py +14 -7
- {lib/v1/openapi_client → openapi_client}/configuration.py +16 -5
- {lib/v1/openapi_client → openapi_client}/exceptions.py +18 -1
- openapi_client/models/__init__.py +84 -0
- {lib/v1/openapi_client → openapi_client}/models/abuse_contact.py +1 -1
- openapi_client/models/address_signals.py +99 -0
- {lib/v1/openapi_client → openapi_client}/models/admin_contact.py +1 -1
- openapi_client/models/analyze_email200_response.py +127 -0
- lib/v1/openapi_client/models/analyze_email400_response_error.py → openapi_client/models/analyze_email400_response.py +9 -9
- openapi_client/models/analyze_email403_response.py +207 -0
- openapi_client/models/analyze_email500_response.py +89 -0
- openapi_client/models/analyze_email_request.py +94 -0
- {lib/v1/openapi_client → openapi_client}/models/analyze_ip200_response.py +4 -4
- lib/v1/openapi_client/models/analyze_ip400_response_error.py → openapi_client/models/analyze_ip400_response.py +20 -20
- {lib/v1/openapi_client → openapi_client}/models/analyze_ip_request.py +1 -1
- {lib/v1/openapi_client → openapi_client}/models/asn.py +1 -1
- openapi_client/models/batch_analyze_emails202_response.py +93 -0
- openapi_client/models/batch_analyze_emails400_response.py +137 -0
- openapi_client/models/batch_analyze_emails401_response.py +89 -0
- openapi_client/models/batch_analyze_emails402_response.py +137 -0
- openapi_client/models/batch_analyze_emails403_response.py +193 -0
- openapi_client/models/batch_analyze_emails413_response.py +89 -0
- openapi_client/models/batch_analyze_emails429_response.py +89 -0
- openapi_client/models/batch_analyze_emails_request.py +93 -0
- openapi_client/models/batch_analyze_ips202_response.py +93 -0
- openapi_client/models/batch_analyze_ips400_response.py +137 -0
- openapi_client/models/batch_analyze_ips_request.py +91 -0
- {lib/v1/openapi_client → openapi_client}/models/block_listed.py +3 -4
- openapi_client/models/create_email_batch_export400_response.py +89 -0
- openapi_client/models/create_email_batch_export403_response.py +89 -0
- openapi_client/models/create_email_batch_export404_response.py +89 -0
- openapi_client/models/create_email_batch_export409_response.py +137 -0
- openapi_client/models/email_dns.py +97 -0
- openapi_client/models/email_domain.py +113 -0
- openapi_client/models/export_created_response.py +91 -0
- openapi_client/models/export_filter.py +95 -0
- openapi_client/models/export_request.py +92 -0
- openapi_client/models/export_status_response.py +119 -0
- openapi_client/models/exportnotfound.py +89 -0
- openapi_client/models/forbidden.py +89 -0
- {lib/v1/openapi_client → openapi_client}/models/geo.py +1 -1
- openapi_client/models/get_email_batch_export_status400_response.py +89 -0
- openapi_client/models/get_email_batch_export_status404_response.py +137 -0
- openapi_client/models/get_email_batch_status200_response.py +101 -0
- openapi_client/models/get_email_batch_status200_response_download_urls.py +93 -0
- openapi_client/models/get_email_batch_status404_response.py +89 -0
- openapi_client/models/get_ip_batch_status200_response.py +101 -0
- openapi_client/models/internalerror.py +89 -0
- openapi_client/models/internalerror1.py +89 -0
- openapi_client/models/invaliddata.py +89 -0
- openapi_client/models/invaliddata1.py +89 -0
- openapi_client/models/invalidemail.py +89 -0
- openapi_client/models/invalidplan.py +89 -0
- openapi_client/models/invalidplan1.py +89 -0
- openapi_client/models/invalidtoken.py +89 -0
- openapi_client/models/ipvalidationfailed.py +89 -0
- openapi_client/models/jobnotfound.py +89 -0
- openapi_client/models/jobnotready.py +89 -0
- openapi_client/models/malformedrequest.py +89 -0
- openapi_client/models/malformedrequest1.py +89 -0
- openapi_client/models/malformedrequest2.py +89 -0
- openapi_client/models/malformedrequest3.py +89 -0
- openapi_client/models/manifestnotavailable.py +89 -0
- openapi_client/models/notfound.py +89 -0
- {lib/v1/openapi_client → openapi_client}/models/organization.py +1 -1
- openapi_client/models/quotaexceeded.py +89 -0
- openapi_client/models/risk_report_email.py +92 -0
- openapi_client/models/risk_report_ip.py +89 -0
- {lib/v1/openapi_client → openapi_client}/models/tech_contact.py +1 -1
- openapi_client/models/toomanyrequests.py +89 -0
- {lib/v1/openapi_client → openapi_client}/models/trusted_provider.py +1 -1
- {lib/v1/openapi_client → openapi_client}/models/whois.py +2 -2
- {lib/v1/openapi_client → openapi_client}/rest.py +2 -1
- opportify_sdk/email_insights.py +427 -0
- opportify_sdk/ip_insights.py +410 -0
- opportify_sdk-0.3.1.dist-info/METADATA +300 -0
- opportify_sdk-0.3.1.dist-info/RECORD +86 -0
- {opportify_sdk-0.1.1.dist-info → opportify_sdk-0.3.1.dist-info}/WHEEL +1 -1
- opportify_sdk-0.3.1.dist-info/top_level.txt +2 -0
- lib/__init__.py +0 -0
- lib/v1/__init__.py +0 -0
- lib/v1/openapi_client/__init__.py +0 -63
- lib/v1/openapi_client/api/email_insights_api.py +0 -317
- lib/v1/openapi_client/api/ip_insights_api.py +0 -322
- lib/v1/openapi_client/models/__init__.py +0 -45
- lib/v1/openapi_client/models/analyze_email200_response.py +0 -113
- lib/v1/openapi_client/models/analyze_email400_response.py +0 -91
- lib/v1/openapi_client/models/analyze_email500_response.py +0 -91
- lib/v1/openapi_client/models/analyze_email500_response_error.py +0 -89
- lib/v1/openapi_client/models/analyze_email_request.py +0 -92
- lib/v1/openapi_client/models/analyze_ip400_response.py +0 -91
- lib/v1/openapi_client/models/analyze_ip404_response.py +0 -91
- lib/v1/openapi_client/models/analyze_ip500_response.py +0 -91
- lib/v1/openapi_client/models/email_dns.py +0 -87
- lib/v1/openapi_client/models/internalerror.py +0 -89
- lib/v1/openapi_client/models/invalidemail.py +0 -89
- lib/v1/openapi_client/models/ipvalidationfailed.py +0 -89
- lib/v1/openapi_client/models/malformedrequest.py +0 -89
- lib/v1/openapi_client/models/malformedrequest1.py +0 -89
- lib/v1/openapi_client/models/notfound.py +0 -89
- lib/v1/openapi_client/models/risk_report.py +0 -89
- opportify_sdk-0.1.1.dist-info/METADATA +0 -108
- opportify_sdk-0.1.1.dist-info/RECORD +0 -49
- opportify_sdk-0.1.1.dist-info/top_level.txt +0 -2
- src/email_insights.py +0 -97
- src/ip_insights.py +0 -93
- {lib/v1/openapi_client → openapi_client}/api/__init__.py +0 -0
- {lib/v1/openapi_client → openapi_client}/api_response.py +0 -0
- {lib/v1/openapi_client → openapi_client}/py.typed +0 -0
- {src → opportify_sdk}/__init__.py +0 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# src/ip_insights.py
|
|
2
|
+
import os
|
|
3
|
+
from typing import Optional, Dict, Any, List, Union
|
|
4
|
+
import openapi_client
|
|
5
|
+
from openapi_client.configuration import Configuration as ApiConfiguration
|
|
6
|
+
from openapi_client.api_client import ApiClient
|
|
7
|
+
from openapi_client.api.ip_insights_api import IPInsightsApi
|
|
8
|
+
from openapi_client.models.analyze_ip_request import AnalyzeIpRequest
|
|
9
|
+
from openapi_client.models.batch_analyze_ips_request import BatchAnalyzeIpsRequest
|
|
10
|
+
from openapi_client.models.export_request import ExportRequest
|
|
11
|
+
from openapi_client.exceptions import ApiException
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IpInsights:
|
|
15
|
+
def __init__(self, api_key: str, api_instance: Optional[IPInsightsApi] = None):
|
|
16
|
+
"""
|
|
17
|
+
Initialize the IpInsights class with the provided API key.
|
|
18
|
+
|
|
19
|
+
:param api_key: The API key for authentication.
|
|
20
|
+
:param api_instance: Optional API instance for testing purposes.
|
|
21
|
+
"""
|
|
22
|
+
self.config = ApiConfiguration()
|
|
23
|
+
self.config.api_key = {"opportifyToken": api_key}
|
|
24
|
+
self.host = "https://api.opportify.ai"
|
|
25
|
+
self.prefix = "insights"
|
|
26
|
+
self.version = "v1"
|
|
27
|
+
self.debug_mode = False
|
|
28
|
+
self.final_url = ""
|
|
29
|
+
self.config_changed = False
|
|
30
|
+
|
|
31
|
+
self._update_final_url()
|
|
32
|
+
|
|
33
|
+
if api_instance:
|
|
34
|
+
self.api_instance = api_instance
|
|
35
|
+
else:
|
|
36
|
+
self._refresh_api_instance(first_run=True)
|
|
37
|
+
|
|
38
|
+
def _refresh_api_instance(self, first_run: bool = False) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Ensures API instance is updated only if config has changed.
|
|
41
|
+
|
|
42
|
+
:param first_run: Whether this is the first initialization.
|
|
43
|
+
"""
|
|
44
|
+
if not self.config_changed and not first_run:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
self._update_final_url()
|
|
48
|
+
self.config.host = self.final_url
|
|
49
|
+
api_client = ApiClient(configuration=self.config)
|
|
50
|
+
api_client.configuration.debug = self.debug_mode
|
|
51
|
+
self.api_instance = IPInsightsApi(api_client)
|
|
52
|
+
self.config_changed = False
|
|
53
|
+
|
|
54
|
+
def _update_final_url(self) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Updates the final URL used for API requests.
|
|
57
|
+
"""
|
|
58
|
+
base = self.host.rstrip('/')
|
|
59
|
+
segments = []
|
|
60
|
+
|
|
61
|
+
prefix = self.prefix.strip('/')
|
|
62
|
+
if prefix:
|
|
63
|
+
segments.append(prefix)
|
|
64
|
+
|
|
65
|
+
version = self.version.strip('/')
|
|
66
|
+
if version:
|
|
67
|
+
segments.append(version)
|
|
68
|
+
|
|
69
|
+
self.final_url = base + ('/' + '/'.join(segments) if segments else '')
|
|
70
|
+
|
|
71
|
+
def analyze(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
72
|
+
"""
|
|
73
|
+
Analyze the IP address based on the provided parameters.
|
|
74
|
+
|
|
75
|
+
:param params: Dictionary containing parameters for IP analysis.
|
|
76
|
+
:return: The analysis result as a dictionary.
|
|
77
|
+
:raises Exception: If an API exception occurs.
|
|
78
|
+
"""
|
|
79
|
+
# Ensure latest config before API call
|
|
80
|
+
self._refresh_api_instance()
|
|
81
|
+
|
|
82
|
+
params = self._normalize_request(params)
|
|
83
|
+
analyze_ip_request = AnalyzeIpRequest(**params)
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
result = self.api_instance.analyze_ip(analyze_ip_request)
|
|
87
|
+
return result.to_dict()
|
|
88
|
+
except ApiException as e:
|
|
89
|
+
raise Exception(f"API exception: {e.reason}")
|
|
90
|
+
|
|
91
|
+
def batch_analyze(self, params: Dict[str, Any], content_type: Optional[str] = None) -> Dict[str, Any]:
|
|
92
|
+
"""
|
|
93
|
+
Submit a batch of IPs for analysis.
|
|
94
|
+
|
|
95
|
+
:param params: Dictionary containing parameters for batch IP analysis.
|
|
96
|
+
:param content_type: Optional content type (defaults to application/json).
|
|
97
|
+
Supported: 'application/json', 'multipart/form-data', 'text/plain'
|
|
98
|
+
:return: The batch job information as a dictionary (job_id, status, etc.).
|
|
99
|
+
:raises Exception: If an API exception occurs.
|
|
100
|
+
"""
|
|
101
|
+
# Ensure latest config before API call
|
|
102
|
+
self._refresh_api_instance()
|
|
103
|
+
|
|
104
|
+
# Default to application/json if not specified
|
|
105
|
+
content_type = content_type or 'application/json'
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
if content_type == 'application/json':
|
|
109
|
+
params = self._normalize_batch_request(params)
|
|
110
|
+
batch_analyze_ips_request = BatchAnalyzeIpsRequest(**params)
|
|
111
|
+
result = self.api_instance.batch_analyze_ips(
|
|
112
|
+
batch_analyze_ips_request,
|
|
113
|
+
_content_type=content_type
|
|
114
|
+
)
|
|
115
|
+
elif content_type == 'multipart/form-data':
|
|
116
|
+
if 'file' not in params or not os.path.exists(params['file']):
|
|
117
|
+
raise ValueError('File parameter is required and must be a valid file path')
|
|
118
|
+
|
|
119
|
+
with open(params['file'], 'rb') as file_handle:
|
|
120
|
+
# Create multipart data
|
|
121
|
+
files = {'file': (os.path.basename(params['file']), file_handle)}
|
|
122
|
+
data = {}
|
|
123
|
+
|
|
124
|
+
# Add optional parameters
|
|
125
|
+
enable_ai = self._resolve_boolean(params, ['enable_ai', 'enableAi'])
|
|
126
|
+
if enable_ai is not None:
|
|
127
|
+
data['enable_ai'] = 'true' if enable_ai else 'false'
|
|
128
|
+
|
|
129
|
+
# Add name parameter if provided
|
|
130
|
+
if 'name' in params:
|
|
131
|
+
data['name'] = str(params['name'])
|
|
132
|
+
|
|
133
|
+
# Prepare the request body as multipart
|
|
134
|
+
multipart_data = self._prepare_multipart_data(files, data)
|
|
135
|
+
result = self.api_instance.batch_analyze_ips(
|
|
136
|
+
multipart_data,
|
|
137
|
+
_content_type=content_type
|
|
138
|
+
)
|
|
139
|
+
elif content_type == 'text/plain':
|
|
140
|
+
if 'text' not in params:
|
|
141
|
+
raise ValueError('Text parameter is required for text/plain content type')
|
|
142
|
+
|
|
143
|
+
result = self.api_instance.batch_analyze_ips(
|
|
144
|
+
params['text'],
|
|
145
|
+
_content_type=content_type
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
raise ValueError(f'Unsupported content type: {content_type}')
|
|
149
|
+
|
|
150
|
+
return result.to_dict()
|
|
151
|
+
except ApiException as e:
|
|
152
|
+
raise Exception(f"API exception: {e.reason}")
|
|
153
|
+
|
|
154
|
+
def batch_analyze_file(self, file_path: str, options: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Submit a batch of IPs for analysis using a file.
|
|
157
|
+
|
|
158
|
+
:param file_path: Path to the file containing IPs (CSV or text).
|
|
159
|
+
:param options: Additional options like enableAi, name.
|
|
160
|
+
:return: The batch job information as a dictionary.
|
|
161
|
+
:raises Exception: If an API exception occurs.
|
|
162
|
+
"""
|
|
163
|
+
options = options or {}
|
|
164
|
+
params = {'file': file_path, **options}
|
|
165
|
+
return self.batch_analyze(params, 'multipart/form-data')
|
|
166
|
+
|
|
167
|
+
def get_batch_status(self, job_id: str) -> Dict[str, Any]:
|
|
168
|
+
"""
|
|
169
|
+
Get the status of a batch IP analysis job.
|
|
170
|
+
|
|
171
|
+
:param job_id: The unique identifier of the batch job.
|
|
172
|
+
:return: The batch job status as a dictionary.
|
|
173
|
+
:raises Exception: If an API exception occurs.
|
|
174
|
+
"""
|
|
175
|
+
# Ensure latest config before API call
|
|
176
|
+
self._refresh_api_instance()
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
result = self.api_instance.get_ip_batch_status(job_id)
|
|
180
|
+
return result.to_dict()
|
|
181
|
+
except ApiException as e:
|
|
182
|
+
raise Exception(f"API exception: {e.reason}")
|
|
183
|
+
|
|
184
|
+
def create_batch_export(self, job_id: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
Request a custom export for a completed IP batch job.
|
|
187
|
+
|
|
188
|
+
:param job_id: The unique identifier of the batch job.
|
|
189
|
+
:param payload: Optional export configuration (export_type, filters, columns).
|
|
190
|
+
:return: The export creation response as a dictionary.
|
|
191
|
+
:raises Exception: If an API exception occurs.
|
|
192
|
+
"""
|
|
193
|
+
self._refresh_api_instance()
|
|
194
|
+
|
|
195
|
+
job_id = job_id.strip()
|
|
196
|
+
if not job_id:
|
|
197
|
+
raise ValueError('Job ID cannot be empty when creating an export.')
|
|
198
|
+
|
|
199
|
+
payload = payload or {}
|
|
200
|
+
normalized_payload = self._normalize_export_request(payload)
|
|
201
|
+
export_request = ExportRequest(**normalized_payload) if normalized_payload else None
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
result = self.api_instance.create_ip_batch_export(job_id, export_request)
|
|
205
|
+
return result.to_dict()
|
|
206
|
+
except ApiException as e:
|
|
207
|
+
raise Exception(f"API exception: {e.reason}")
|
|
208
|
+
|
|
209
|
+
def get_batch_export_status(self, job_id: str, export_id: str) -> Dict[str, Any]:
|
|
210
|
+
"""
|
|
211
|
+
Retrieve the status of a previously requested IP batch export.
|
|
212
|
+
|
|
213
|
+
:param job_id: The unique identifier of the batch job.
|
|
214
|
+
:param export_id: The unique identifier of the export.
|
|
215
|
+
:return: The export status as a dictionary.
|
|
216
|
+
:raises Exception: If an API exception occurs.
|
|
217
|
+
"""
|
|
218
|
+
self._refresh_api_instance()
|
|
219
|
+
|
|
220
|
+
job_id = job_id.strip()
|
|
221
|
+
export_id = export_id.strip()
|
|
222
|
+
|
|
223
|
+
if not job_id or not export_id:
|
|
224
|
+
raise ValueError('Job ID and export ID are required to fetch export status.')
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
result = self.api_instance.get_ip_batch_export_status(job_id, export_id)
|
|
228
|
+
return result.to_dict()
|
|
229
|
+
except ApiException as e:
|
|
230
|
+
raise Exception(f"API exception: {e.reason}")
|
|
231
|
+
|
|
232
|
+
def set_host(self, host: str) -> "IpInsights":
|
|
233
|
+
"""
|
|
234
|
+
Set the host.
|
|
235
|
+
|
|
236
|
+
:param host: The host URL.
|
|
237
|
+
:return: The current instance for chaining.
|
|
238
|
+
"""
|
|
239
|
+
if self.host != host:
|
|
240
|
+
self.host = host
|
|
241
|
+
self.config_changed = True
|
|
242
|
+
return self
|
|
243
|
+
|
|
244
|
+
def set_version(self, version: str) -> "IpInsights":
|
|
245
|
+
"""
|
|
246
|
+
Set the version.
|
|
247
|
+
|
|
248
|
+
:param version: The API version.
|
|
249
|
+
:return: The current instance for chaining.
|
|
250
|
+
"""
|
|
251
|
+
if self.version != version:
|
|
252
|
+
self.version = version
|
|
253
|
+
self.config_changed = True
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
def set_prefix(self, prefix: str) -> "IpInsights":
|
|
257
|
+
"""
|
|
258
|
+
Set the prefix.
|
|
259
|
+
|
|
260
|
+
:param prefix: The URL prefix.
|
|
261
|
+
:return: The current instance for chaining.
|
|
262
|
+
"""
|
|
263
|
+
prefix = prefix.strip('/')
|
|
264
|
+
if self.prefix != prefix:
|
|
265
|
+
self.prefix = prefix
|
|
266
|
+
self.config_changed = True
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def set_debug_mode(self, debug_mode: bool) -> "IpInsights":
|
|
270
|
+
"""
|
|
271
|
+
Set the debug mode.
|
|
272
|
+
|
|
273
|
+
:param debug_mode: Enable or disable debug mode.
|
|
274
|
+
:return: The current instance for chaining.
|
|
275
|
+
"""
|
|
276
|
+
if self.debug_mode != debug_mode:
|
|
277
|
+
self.debug_mode = debug_mode
|
|
278
|
+
self.config_changed = True
|
|
279
|
+
return self
|
|
280
|
+
|
|
281
|
+
def _normalize_request(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
282
|
+
"""
|
|
283
|
+
Normalize the request parameters.
|
|
284
|
+
|
|
285
|
+
:param params: The raw parameters.
|
|
286
|
+
:return: Normalized parameters.
|
|
287
|
+
"""
|
|
288
|
+
if 'ip' not in params:
|
|
289
|
+
raise ValueError('The ip parameter is required for analysis.')
|
|
290
|
+
|
|
291
|
+
normalized = {}
|
|
292
|
+
normalized["ip"] = str(params["ip"])
|
|
293
|
+
|
|
294
|
+
# Only include optional parameters if explicitly provided by user
|
|
295
|
+
enable_ai = self._resolve_boolean(params, ['enable_ai', 'enableAi'])
|
|
296
|
+
if enable_ai is not None:
|
|
297
|
+
normalized["enable_ai"] = enable_ai
|
|
298
|
+
|
|
299
|
+
return normalized
|
|
300
|
+
|
|
301
|
+
def _normalize_batch_request(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
302
|
+
"""
|
|
303
|
+
Normalize the batch request parameters.
|
|
304
|
+
|
|
305
|
+
:param params: The raw parameters.
|
|
306
|
+
:return: Normalized parameters.
|
|
307
|
+
"""
|
|
308
|
+
if 'ips' not in params:
|
|
309
|
+
raise ValueError("'ips' parameter is required for batch analysis")
|
|
310
|
+
|
|
311
|
+
ips = params['ips']
|
|
312
|
+
if not isinstance(ips, list):
|
|
313
|
+
raise ValueError("'ips' parameter must be a list")
|
|
314
|
+
|
|
315
|
+
normalized = {}
|
|
316
|
+
normalized["ips"] = [str(ip) for ip in ips]
|
|
317
|
+
|
|
318
|
+
enable_ai = self._resolve_boolean(params, ['enable_ai', 'enableAi'])
|
|
319
|
+
if enable_ai is not None:
|
|
320
|
+
normalized['enable_ai'] = enable_ai
|
|
321
|
+
|
|
322
|
+
# Add name parameter if provided
|
|
323
|
+
if 'name' in params:
|
|
324
|
+
normalized['name'] = str(params['name'])
|
|
325
|
+
|
|
326
|
+
return normalized
|
|
327
|
+
|
|
328
|
+
def _normalize_export_request(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
329
|
+
"""
|
|
330
|
+
Normalize export payload for batch exports.
|
|
331
|
+
|
|
332
|
+
:param params: The raw parameters.
|
|
333
|
+
:return: Normalized parameters.
|
|
334
|
+
"""
|
|
335
|
+
normalized = {}
|
|
336
|
+
|
|
337
|
+
if self._has_any_key(params, ['export_type', 'exportType']):
|
|
338
|
+
value = params.get('export_type') or params.get('exportType')
|
|
339
|
+
normalized['export_type'] = str(value).lower()
|
|
340
|
+
|
|
341
|
+
if 'filters' in params and params['filters'] is not None:
|
|
342
|
+
if not isinstance(params['filters'], dict):
|
|
343
|
+
raise ValueError('Filters must be provided as a dictionary.')
|
|
344
|
+
normalized['filters'] = params['filters']
|
|
345
|
+
|
|
346
|
+
if 'columns' in params and params['columns'] is not None:
|
|
347
|
+
if not isinstance(params['columns'], list):
|
|
348
|
+
raise ValueError('Columns must be provided as a list.')
|
|
349
|
+
normalized['columns'] = [str(column) for column in params['columns']]
|
|
350
|
+
|
|
351
|
+
return normalized
|
|
352
|
+
|
|
353
|
+
def _has_any_key(self, params: Dict[str, Any], keys: List[str]) -> bool:
|
|
354
|
+
"""
|
|
355
|
+
Check if params has any of the specified keys.
|
|
356
|
+
|
|
357
|
+
:param params: The parameters dictionary.
|
|
358
|
+
:param keys: List of keys to check.
|
|
359
|
+
:return: True if any key exists.
|
|
360
|
+
"""
|
|
361
|
+
return any(key in params for key in keys)
|
|
362
|
+
|
|
363
|
+
def _resolve_boolean(self, params: Dict[str, Any], keys: List[str], default: Optional[bool] = None) -> Optional[bool]:
|
|
364
|
+
"""
|
|
365
|
+
Resolve boolean value from params using multiple possible keys.
|
|
366
|
+
|
|
367
|
+
:param params: The parameters dictionary.
|
|
368
|
+
:param keys: List of possible keys.
|
|
369
|
+
:param default: Default value if none found.
|
|
370
|
+
:return: Boolean value or default.
|
|
371
|
+
"""
|
|
372
|
+
for key in keys:
|
|
373
|
+
if key in params:
|
|
374
|
+
return self._to_boolean(params[key], key)
|
|
375
|
+
return default
|
|
376
|
+
|
|
377
|
+
def _to_boolean(self, value: Any, parameter_name: str) -> bool:
|
|
378
|
+
"""
|
|
379
|
+
Convert a value to boolean.
|
|
380
|
+
|
|
381
|
+
:param value: The value to convert.
|
|
382
|
+
:param parameter_name: Name for error messages.
|
|
383
|
+
:return: Boolean value.
|
|
384
|
+
"""
|
|
385
|
+
if isinstance(value, bool):
|
|
386
|
+
return value
|
|
387
|
+
|
|
388
|
+
if value in (1, 0, '1', '0'):
|
|
389
|
+
return bool(int(value))
|
|
390
|
+
|
|
391
|
+
if isinstance(value, str):
|
|
392
|
+
value_lower = value.lower()
|
|
393
|
+
if value_lower in ('true', 'yes', '1'):
|
|
394
|
+
return True
|
|
395
|
+
if value_lower in ('false', 'no', '0'):
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
raise ValueError(f'Invalid boolean value provided for {parameter_name}')
|
|
399
|
+
|
|
400
|
+
def _prepare_multipart_data(self, files: Dict[str, Any], data: Dict[str, Any]) -> Any:
|
|
401
|
+
"""
|
|
402
|
+
Prepare multipart form data for file upload.
|
|
403
|
+
|
|
404
|
+
:param files: Files dictionary.
|
|
405
|
+
:param data: Additional form data.
|
|
406
|
+
:return: Prepared multipart data.
|
|
407
|
+
"""
|
|
408
|
+
# For Python SDK, we'll pass the file path directly and let the OpenAPI client handle it
|
|
409
|
+
# This is a placeholder that may need adjustment based on how the openapi_client handles multipart
|
|
410
|
+
return {**files, **data}
|