mailgun 1.0.0__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.
- mailgun/__init__.py +10 -0
- mailgun/_version.py +1 -0
- mailgun/client.py +510 -0
- mailgun/doc_tests/files/data.csv +5 -0
- mailgun/doc_tests/files/email_previews.csv +18 -0
- mailgun/doc_tests/files/email_validation.csv +5 -0
- mailgun/doc_tests/files/mailgun_bounces_test.csv +5 -0
- mailgun/doc_tests/files/mailgun_complaints.csv +4 -0
- mailgun/doc_tests/files/mailgun_unsubscribes.csv +5 -0
- mailgun/doc_tests/files/mailgun_whitelists.csv +4 -0
- mailgun/doc_tests/files/test1.txt +1 -0
- mailgun/doc_tests/files/test2.txt +1 -0
- mailgun/doc_tests/files/test_mime.mime +7 -0
- mailgun/examples/__init__.py +0 -0
- mailgun/examples/domain_examples.py +243 -0
- mailgun/examples/email_validation_examples.py +113 -0
- mailgun/examples/events_examples.py +62 -0
- mailgun/examples/inbox_placement_examples.py +92 -0
- mailgun/examples/ip_pools_examples.py +77 -0
- mailgun/examples/ips_examples.py +59 -0
- mailgun/examples/mailing_lists_examples.py +217 -0
- mailgun/examples/messages_examples.py +135 -0
- mailgun/examples/routes_examples.py +73 -0
- mailgun/examples/suppressions_examples.py +355 -0
- mailgun/examples/tags_examples.py +94 -0
- mailgun/examples/templates_examples.py +149 -0
- mailgun/examples/webhooks_examples.py +64 -0
- mailgun/handlers/__init__.py +1 -0
- mailgun/handlers/default_handler.py +38 -0
- mailgun/handlers/domains_handler.py +98 -0
- mailgun/handlers/email_validation_handler.py +35 -0
- mailgun/handlers/error_handler.py +14 -0
- mailgun/handlers/inbox_placement_handler.py +71 -0
- mailgun/handlers/ip_pools_handler.py +43 -0
- mailgun/handlers/ips_handler.py +35 -0
- mailgun/handlers/mailinglists_handler.py +54 -0
- mailgun/handlers/messages_handler.py +33 -0
- mailgun/handlers/routes_handler.py +35 -0
- mailgun/handlers/suppressions_handler.py +110 -0
- mailgun/handlers/tags_handler.py +41 -0
- mailgun/handlers/templates_handler.py +62 -0
- mailgun-1.0.0.dist-info/METADATA +1083 -0
- mailgun-1.0.0.dist-info/RECORD +48 -0
- mailgun-1.0.0.dist-info/WHEEL +5 -0
- mailgun-1.0.0.dist-info/licenses/LICENSE +202 -0
- mailgun-1.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/tests.py +1425 -0
mailgun/__init__.py
ADDED
mailgun/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
mailgun/client.py
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""This module provides the main client and helper classes for interacting with the Mailgun API.
|
|
2
|
+
|
|
3
|
+
The `mailgun.client` module includes the core `Client` class for managing
|
|
4
|
+
API requests, configuration, and error handling, as well as utility functions
|
|
5
|
+
and classes for building request headers, URLs, and parsing responses.
|
|
6
|
+
Classes:
|
|
7
|
+
- Config: Manages configuration settings for the Mailgun API.
|
|
8
|
+
- Endpoint: Represents specific API endpoints and provides methods for
|
|
9
|
+
common HTTP operations like GET, POST, PUT, and DELETE.
|
|
10
|
+
- Client: The main API client for authenticating and making requests.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
from typing import Any
|
|
18
|
+
from typing import Callable
|
|
19
|
+
from urllib.parse import urljoin
|
|
20
|
+
|
|
21
|
+
import requests # type: ignore[import-untyped]
|
|
22
|
+
|
|
23
|
+
from mailgun.handlers.default_handler import handle_default
|
|
24
|
+
from mailgun.handlers.domains_handler import handle_domainlist
|
|
25
|
+
from mailgun.handlers.domains_handler import handle_domains
|
|
26
|
+
from mailgun.handlers.domains_handler import handle_sending_queues
|
|
27
|
+
from mailgun.handlers.email_validation_handler import handle_address_validate
|
|
28
|
+
from mailgun.handlers.error_handler import ApiError
|
|
29
|
+
from mailgun.handlers.inbox_placement_handler import handle_inbox
|
|
30
|
+
from mailgun.handlers.ip_pools_handler import handle_ippools
|
|
31
|
+
from mailgun.handlers.ips_handler import handle_ips
|
|
32
|
+
from mailgun.handlers.mailinglists_handler import handle_lists
|
|
33
|
+
from mailgun.handlers.messages_handler import handle_resend_message
|
|
34
|
+
from mailgun.handlers.routes_handler import handle_routes
|
|
35
|
+
from mailgun.handlers.suppressions_handler import handle_bounces
|
|
36
|
+
from mailgun.handlers.suppressions_handler import handle_complaints
|
|
37
|
+
from mailgun.handlers.suppressions_handler import handle_unsubscribes
|
|
38
|
+
from mailgun.handlers.suppressions_handler import handle_whitelists
|
|
39
|
+
from mailgun.handlers.tags_handler import handle_tags
|
|
40
|
+
from mailgun.handlers.templates_handler import handle_templates
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from collections.abc import Mapping
|
|
45
|
+
|
|
46
|
+
from requests.models import Response # type: ignore[import-untyped]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
HANDLERS: dict[str, Callable] = { # type: ignore[type-arg]
|
|
50
|
+
"resendmessage": handle_resend_message,
|
|
51
|
+
"domains": handle_domains,
|
|
52
|
+
"domainlist": handle_domainlist,
|
|
53
|
+
"dkim_authority": handle_domains,
|
|
54
|
+
"dkim_selector": handle_domains,
|
|
55
|
+
"web_prefix": handle_domains,
|
|
56
|
+
"sending_queues": handle_sending_queues,
|
|
57
|
+
"ips": handle_ips,
|
|
58
|
+
"ip_pools": handle_ippools,
|
|
59
|
+
"tags": handle_tags,
|
|
60
|
+
"bounces": handle_bounces,
|
|
61
|
+
"unsubscribes": handle_unsubscribes,
|
|
62
|
+
"whitelists": handle_whitelists,
|
|
63
|
+
"complaints": handle_complaints,
|
|
64
|
+
"routes": handle_routes,
|
|
65
|
+
"lists": handle_lists,
|
|
66
|
+
"templates": handle_templates,
|
|
67
|
+
"addressvalidate": handle_address_validate,
|
|
68
|
+
"inbox": handle_inbox,
|
|
69
|
+
"messages": handle_default,
|
|
70
|
+
"messages.mime": handle_default,
|
|
71
|
+
"events": handle_default,
|
|
72
|
+
"stats": handle_default,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class Config:
|
|
77
|
+
"""Config class. Configure client with basic (urls, version, headers)."""
|
|
78
|
+
|
|
79
|
+
DEFAULT_API_URL: str = "https://api.mailgun.net/"
|
|
80
|
+
API_REF: str = "https://documentation.mailgun.com/en/latest/api_reference.html"
|
|
81
|
+
user_agent: str = "mailgun-api-python/"
|
|
82
|
+
|
|
83
|
+
def __init__(self, api_url: str | None = None) -> None:
|
|
84
|
+
"""Initialize a new Config instance with specified or default API settings.
|
|
85
|
+
|
|
86
|
+
This initializer sets the API version and base URL. If no version or URL
|
|
87
|
+
is provided, it defaults to the predefined class values.
|
|
88
|
+
|
|
89
|
+
:param version: API version (default: v3)
|
|
90
|
+
:type version: str | None
|
|
91
|
+
:param api_url: API base url
|
|
92
|
+
:type api_url: str | None
|
|
93
|
+
"""
|
|
94
|
+
self.ex_handler: bool = True
|
|
95
|
+
self.api_url = api_url or self.DEFAULT_API_URL
|
|
96
|
+
|
|
97
|
+
def __getitem__(self, key: str) -> tuple[Any, dict[str, str]]:
|
|
98
|
+
"""Parse incoming split attr name, check it and prepare endpoint url.
|
|
99
|
+
|
|
100
|
+
Most urls generated here can't be generated dynamically as we are doing this
|
|
101
|
+
in build_url() method under Endpoint class.
|
|
102
|
+
:param key: incoming attr name
|
|
103
|
+
:type key: str
|
|
104
|
+
:return: url, headers
|
|
105
|
+
"""
|
|
106
|
+
key = key.lower()
|
|
107
|
+
headers = {"User-agent": self.user_agent}
|
|
108
|
+
v1_base = urljoin(self.api_url, "v1/")
|
|
109
|
+
v3_base = urljoin(self.api_url, "v3/")
|
|
110
|
+
v4_base = urljoin(self.api_url, "v4/")
|
|
111
|
+
v5_base = urljoin(self.api_url, "v5/")
|
|
112
|
+
|
|
113
|
+
special_cases = {
|
|
114
|
+
"messages": {"base": v3_base, "keys": ["messages"]},
|
|
115
|
+
"mimemessage": {"base": v3_base, "keys": ["messages.mime"]},
|
|
116
|
+
"resendmessage": {"base": v3_base, "keys": ["resendmessage"]},
|
|
117
|
+
"ippools": {"base": v3_base, "keys": ["ip_pools"]},
|
|
118
|
+
"dkimkeys": {"base": v1_base, "keys": ["dkim", "keys"]},
|
|
119
|
+
"domainlist": {"base": v4_base, "keys": ["domainlist"]},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if key in special_cases:
|
|
123
|
+
return special_cases[key], headers
|
|
124
|
+
|
|
125
|
+
# Handle DIPP endpoints
|
|
126
|
+
if "subaccount" in key:
|
|
127
|
+
if "ip_pools" in key:
|
|
128
|
+
return {
|
|
129
|
+
"base": v5_base,
|
|
130
|
+
"keys": ["accounts", "subaccounts", "ip_pools"],
|
|
131
|
+
}, headers
|
|
132
|
+
if "ip_pool" in key:
|
|
133
|
+
return {
|
|
134
|
+
"base": v5_base,
|
|
135
|
+
"keys": ["accounts", "subaccounts", "{subaccountId}", "ip_pool"],
|
|
136
|
+
}, headers
|
|
137
|
+
|
|
138
|
+
# Handle DKIM management endpoints
|
|
139
|
+
if "dkim_management" in key:
|
|
140
|
+
if "rotation" in key:
|
|
141
|
+
return {
|
|
142
|
+
"base": v1_base,
|
|
143
|
+
"keys": ["dkim_management", "domains", "{name}", "rotation"],
|
|
144
|
+
}, headers
|
|
145
|
+
if "rotate" in key:
|
|
146
|
+
return {
|
|
147
|
+
"base": v1_base,
|
|
148
|
+
"keys": ["dkim_management", "domains", "{name}", "rotate"],
|
|
149
|
+
}, headers
|
|
150
|
+
|
|
151
|
+
if "domains" in key:
|
|
152
|
+
split = key.split("_") if "_" in key else [key]
|
|
153
|
+
final_keys = split
|
|
154
|
+
|
|
155
|
+
if any(x in key for x in ("activate", "deactivate")):
|
|
156
|
+
action = "activate" if "activate" in key else "deactivate"
|
|
157
|
+
final_keys = [
|
|
158
|
+
"domains",
|
|
159
|
+
"{authority_name}",
|
|
160
|
+
"keys",
|
|
161
|
+
"{selector}",
|
|
162
|
+
action,
|
|
163
|
+
]
|
|
164
|
+
return {"base": v4_base, "keys": final_keys}, headers
|
|
165
|
+
|
|
166
|
+
if "dkimauthority" in split:
|
|
167
|
+
final_keys = ["dkim_authority"]
|
|
168
|
+
elif "dkimselector" in split:
|
|
169
|
+
final_keys = ["dkim_selector"]
|
|
170
|
+
elif "webprefix" in split:
|
|
171
|
+
final_keys = ["web_prefix"]
|
|
172
|
+
elif "sendingqueues" in split:
|
|
173
|
+
final_keys = ["sending_queues"]
|
|
174
|
+
|
|
175
|
+
v3_domain_endpoints = {
|
|
176
|
+
"credentials",
|
|
177
|
+
"connection",
|
|
178
|
+
"tracking",
|
|
179
|
+
"dkimauthority",
|
|
180
|
+
"dkimselector",
|
|
181
|
+
"webprefix",
|
|
182
|
+
"webhooks",
|
|
183
|
+
"sendingqueues",
|
|
184
|
+
}
|
|
185
|
+
base = v3_base if any(x in key for x in v3_domain_endpoints) else v4_base
|
|
186
|
+
return {"base": f"{base}domains/", "keys": final_keys}, headers
|
|
187
|
+
|
|
188
|
+
if "addressvalidate" in key:
|
|
189
|
+
return {
|
|
190
|
+
"base": f"{v4_base}address/validate",
|
|
191
|
+
"keys": key.split("_"),
|
|
192
|
+
}, headers
|
|
193
|
+
|
|
194
|
+
return {"base": v3_base, "keys": key.split("_")}, headers
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class Endpoint:
|
|
198
|
+
"""Generate request and return response."""
|
|
199
|
+
|
|
200
|
+
def __init__(
|
|
201
|
+
self,
|
|
202
|
+
url: dict[str, Any],
|
|
203
|
+
headers: dict[str, str],
|
|
204
|
+
auth: tuple[str, str] | None,
|
|
205
|
+
) -> None:
|
|
206
|
+
"""Initialize a new Endpoint instance.
|
|
207
|
+
|
|
208
|
+
:param url: URL dict with pairs {"base": "keys"}
|
|
209
|
+
:type url: dict[str, Any]
|
|
210
|
+
:param headers: Headers dict
|
|
211
|
+
:type headers: dict[str, str]
|
|
212
|
+
:param auth: requests auth tuple
|
|
213
|
+
:type auth: tuple[str, str] | None
|
|
214
|
+
"""
|
|
215
|
+
self._url = url
|
|
216
|
+
self.headers = headers
|
|
217
|
+
self._auth = auth
|
|
218
|
+
|
|
219
|
+
def api_call(
|
|
220
|
+
self,
|
|
221
|
+
auth: tuple[str, str] | None,
|
|
222
|
+
method: str,
|
|
223
|
+
url: dict[str, Any],
|
|
224
|
+
headers: dict[str, str],
|
|
225
|
+
data: Any | None = None,
|
|
226
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
227
|
+
timeout: int = 60,
|
|
228
|
+
files: dict[str, bytes] | None = None,
|
|
229
|
+
domain: str | None = None,
|
|
230
|
+
**kwargs: Any,
|
|
231
|
+
) -> Response | Any:
|
|
232
|
+
"""Build URL and make a request.
|
|
233
|
+
|
|
234
|
+
:param auth: auth data
|
|
235
|
+
:type auth: tuple[str, str] | None
|
|
236
|
+
:param method: request method
|
|
237
|
+
:type method: str
|
|
238
|
+
:param url: incoming url (base+keys)
|
|
239
|
+
:type url: dict[str, Any]
|
|
240
|
+
:param headers: incoming headers
|
|
241
|
+
:type headers: dict[str, str]
|
|
242
|
+
:param data: incoming post/put data
|
|
243
|
+
:type data: Any | None
|
|
244
|
+
:param filters: incoming params
|
|
245
|
+
:type filters: dict | None
|
|
246
|
+
:param timeout: requested timeout (60-default)
|
|
247
|
+
:type timeout: int
|
|
248
|
+
:param files: incoming files
|
|
249
|
+
:type files: dict[str, Any] | None
|
|
250
|
+
:param domain: incoming domain
|
|
251
|
+
:type domain: str | None
|
|
252
|
+
:param kwargs: kwargs
|
|
253
|
+
:type kwargs: Any
|
|
254
|
+
:return: server response from API
|
|
255
|
+
:rtype: requests.models.Response
|
|
256
|
+
:raises: TimeoutError, ApiError
|
|
257
|
+
"""
|
|
258
|
+
url = self.build_url(url, domain=domain, method=method, **kwargs)
|
|
259
|
+
req_method = getattr(requests, method)
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
return req_method(
|
|
263
|
+
url,
|
|
264
|
+
data=data,
|
|
265
|
+
params=filters,
|
|
266
|
+
headers=headers,
|
|
267
|
+
auth=auth,
|
|
268
|
+
timeout=timeout,
|
|
269
|
+
files=files,
|
|
270
|
+
verify=True,
|
|
271
|
+
stream=False,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
except requests.exceptions.Timeout:
|
|
275
|
+
raise TimeoutError
|
|
276
|
+
except requests.RequestException as e:
|
|
277
|
+
raise ApiError(e)
|
|
278
|
+
except Exception as e:
|
|
279
|
+
raise e
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def build_url(
|
|
283
|
+
url: dict[str, Any],
|
|
284
|
+
domain: str | None = None,
|
|
285
|
+
method: str | None = None,
|
|
286
|
+
**kwargs: Any,
|
|
287
|
+
) -> Any:
|
|
288
|
+
"""Build final request url using predefined handlers.
|
|
289
|
+
|
|
290
|
+
Note: Some urls are being built in Config class, as they can't be generated dynamically.
|
|
291
|
+
:param url: incoming url (base+keys)
|
|
292
|
+
:type url: dict[str, Any]
|
|
293
|
+
:param domain: incoming domain
|
|
294
|
+
:type domain: str
|
|
295
|
+
:param method: requested method
|
|
296
|
+
:type method: str
|
|
297
|
+
:param kwargs: kwargs
|
|
298
|
+
:type kwargs: Any
|
|
299
|
+
:return: built URL
|
|
300
|
+
"""
|
|
301
|
+
return HANDLERS[url["keys"][0]](url, domain, method, **kwargs)
|
|
302
|
+
|
|
303
|
+
def get(
|
|
304
|
+
self,
|
|
305
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
306
|
+
domain: str | None = None,
|
|
307
|
+
**kwargs: Any,
|
|
308
|
+
) -> Response:
|
|
309
|
+
"""GET method for API calls.
|
|
310
|
+
|
|
311
|
+
:param filters: incoming params
|
|
312
|
+
:type filters: Mapping[str, str | Any] | None
|
|
313
|
+
:param domain: incoming domain
|
|
314
|
+
:type domain: str | None
|
|
315
|
+
:param kwargs: kwargs
|
|
316
|
+
:type kwargs: Any
|
|
317
|
+
:return: api_call GET request
|
|
318
|
+
:rtype: requests.models.Response
|
|
319
|
+
"""
|
|
320
|
+
return self.api_call(
|
|
321
|
+
self._auth,
|
|
322
|
+
"get",
|
|
323
|
+
self._url,
|
|
324
|
+
domain=domain,
|
|
325
|
+
headers=self.headers,
|
|
326
|
+
filters=filters,
|
|
327
|
+
**kwargs,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def create(
|
|
331
|
+
self,
|
|
332
|
+
data: Any | None = None,
|
|
333
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
334
|
+
domain: str | None = None,
|
|
335
|
+
headers: str | None = None,
|
|
336
|
+
files: dict[str, bytes] | None = None,
|
|
337
|
+
**kwargs: Any,
|
|
338
|
+
) -> Response:
|
|
339
|
+
"""POST method for API calls.
|
|
340
|
+
|
|
341
|
+
:param data: incoming post data
|
|
342
|
+
:type data: Any | None
|
|
343
|
+
:param filters: incoming params
|
|
344
|
+
:type filters: dict
|
|
345
|
+
:param domain: incoming domain
|
|
346
|
+
:type domain: str
|
|
347
|
+
:param headers: incoming headers
|
|
348
|
+
:type headers: str | None
|
|
349
|
+
:param files: incoming files
|
|
350
|
+
:type files: dict[str, Any] | None
|
|
351
|
+
:param kwargs: kwargs
|
|
352
|
+
:type kwargs: Any
|
|
353
|
+
:return: api_call POST request
|
|
354
|
+
:rtype: requests.models.Response
|
|
355
|
+
"""
|
|
356
|
+
if "Content-type" in self.headers:
|
|
357
|
+
if self.headers["Content-type"] == "application/json":
|
|
358
|
+
data = json.dumps(data)
|
|
359
|
+
elif headers:
|
|
360
|
+
if headers == "application/json":
|
|
361
|
+
data = json.dumps(data)
|
|
362
|
+
self.headers["Content-type"] = "application/json"
|
|
363
|
+
elif headers == "multipart/form-data":
|
|
364
|
+
self.headers["Content-type"] = "multipart/form-data"
|
|
365
|
+
|
|
366
|
+
return self.api_call(
|
|
367
|
+
self._auth,
|
|
368
|
+
"post",
|
|
369
|
+
self._url,
|
|
370
|
+
files=files,
|
|
371
|
+
domain=domain,
|
|
372
|
+
headers=self.headers,
|
|
373
|
+
data=data,
|
|
374
|
+
filters=filters,
|
|
375
|
+
**kwargs,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def put(
|
|
379
|
+
self,
|
|
380
|
+
data: Any | None = None,
|
|
381
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
382
|
+
**kwargs: Any,
|
|
383
|
+
) -> Response:
|
|
384
|
+
"""PUT method for API calls.
|
|
385
|
+
|
|
386
|
+
:param data: incoming data
|
|
387
|
+
:type data: Any | None
|
|
388
|
+
:param filters: incoming params
|
|
389
|
+
:type filters: dict
|
|
390
|
+
:param kwargs: kwargs
|
|
391
|
+
:type kwargs: Any
|
|
392
|
+
:return: api_call POST request
|
|
393
|
+
:rtype: requests.models.Response
|
|
394
|
+
"""
|
|
395
|
+
return self.api_call(
|
|
396
|
+
self._auth,
|
|
397
|
+
"put",
|
|
398
|
+
self._url,
|
|
399
|
+
headers=self.headers,
|
|
400
|
+
data=data,
|
|
401
|
+
filters=filters,
|
|
402
|
+
**kwargs,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
def patch(
|
|
406
|
+
self,
|
|
407
|
+
data: Any | None = None,
|
|
408
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
409
|
+
**kwargs: Any,
|
|
410
|
+
) -> Response:
|
|
411
|
+
"""PATCH method for API calls.
|
|
412
|
+
|
|
413
|
+
:param data: incoming data
|
|
414
|
+
:type data: Any | None
|
|
415
|
+
:param filters: incoming params
|
|
416
|
+
:type filters: dict
|
|
417
|
+
:param kwargs: kwargs
|
|
418
|
+
:type kwargs: Any
|
|
419
|
+
:return: api_call PATCH request
|
|
420
|
+
:rtype: requests.models.Response
|
|
421
|
+
"""
|
|
422
|
+
return self.api_call(
|
|
423
|
+
self._auth,
|
|
424
|
+
"patch",
|
|
425
|
+
self._url,
|
|
426
|
+
headers=self.headers,
|
|
427
|
+
data=data,
|
|
428
|
+
filters=filters,
|
|
429
|
+
**kwargs,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
def update(
|
|
433
|
+
self,
|
|
434
|
+
data: Any | None,
|
|
435
|
+
filters: Mapping[str, str | Any] | None = None,
|
|
436
|
+
**kwargs: Any,
|
|
437
|
+
) -> Response:
|
|
438
|
+
"""PUT method for API calls.
|
|
439
|
+
|
|
440
|
+
:param data: incoming data
|
|
441
|
+
:type data: dict[str, Any] | None
|
|
442
|
+
:param filters: incoming params
|
|
443
|
+
:type filters: dict
|
|
444
|
+
:param kwargs: kwargs
|
|
445
|
+
:type kwargs: Any
|
|
446
|
+
:return: api_call PUT request
|
|
447
|
+
:rtype: requests.models.Response
|
|
448
|
+
"""
|
|
449
|
+
if self.headers["Content-type"] == "application/json":
|
|
450
|
+
data = json.dumps(data)
|
|
451
|
+
return self.api_call(
|
|
452
|
+
self._auth,
|
|
453
|
+
"put",
|
|
454
|
+
self._url,
|
|
455
|
+
headers=self.headers,
|
|
456
|
+
data=data,
|
|
457
|
+
filters=filters,
|
|
458
|
+
**kwargs,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
def delete(self, domain: str | None = None, **kwargs: Any) -> Response:
|
|
462
|
+
"""DELETE method for API calls.
|
|
463
|
+
|
|
464
|
+
:param domain: incoming domain
|
|
465
|
+
:type domain: str
|
|
466
|
+
:param kwargs: kwargs
|
|
467
|
+
:type kwargs: Any
|
|
468
|
+
:return: api_call DELETE request
|
|
469
|
+
:rtype: requests.models.Response
|
|
470
|
+
"""
|
|
471
|
+
return self.api_call(
|
|
472
|
+
self._auth,
|
|
473
|
+
"delete",
|
|
474
|
+
self._url,
|
|
475
|
+
headers=self.headers,
|
|
476
|
+
domain=domain,
|
|
477
|
+
**kwargs,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class Client:
|
|
482
|
+
"""Client class."""
|
|
483
|
+
|
|
484
|
+
def __init__(self, auth: tuple[str, str] | None = None, **kwargs: Any) -> None:
|
|
485
|
+
"""Initialize a new Client instance for API interaction.
|
|
486
|
+
|
|
487
|
+
This method sets up API authentication and configuration. The `auth` parameter
|
|
488
|
+
provides a tuple with the API key and secret. Additional keyword arguments can
|
|
489
|
+
specify configuration options like API version and URL.
|
|
490
|
+
|
|
491
|
+
:param auth: auth set ("username", "APIKEY")
|
|
492
|
+
:type auth: set
|
|
493
|
+
:param kwargs: kwargs
|
|
494
|
+
"""
|
|
495
|
+
self.auth = auth
|
|
496
|
+
api_url = kwargs.get("api_url")
|
|
497
|
+
self.config = Config(api_url=api_url)
|
|
498
|
+
|
|
499
|
+
def __getattr__(self, name: str) -> Any:
|
|
500
|
+
"""Get named attribute of an object, split it and execute.
|
|
501
|
+
|
|
502
|
+
:param name: attribute name (Example: client.domains_ips. names: ["domains", "ips"])
|
|
503
|
+
:type name: str
|
|
504
|
+
:return: type object (executes existing handler)
|
|
505
|
+
"""
|
|
506
|
+
split = name.split("_")
|
|
507
|
+
# identify the resource
|
|
508
|
+
fname = split[0]
|
|
509
|
+
url, headers = self.config[name]
|
|
510
|
+
return type(fname, (Endpoint,), {})(url=url, headers=headers, auth=self.auth)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
First files!!!!!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This is a test file
|
|
File without changes
|