domaintools-api-test-upload 2.7.3__tar.gz → 2.7.5__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 (48) hide show
  1. {domaintools_api_test_upload-2.7.3/domaintools_api_test_upload.egg-info → domaintools_api_test_upload-2.7.5}/PKG-INFO +2 -2
  2. domaintools_api_test_upload-2.7.5/VERSION +1 -0
  3. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/api.py +13 -33
  4. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/decorators.py +4 -4
  5. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5/domaintools_api_test_upload.egg-info}/PKG-INFO +2 -2
  6. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/pyproject.toml +1 -1
  7. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_api.py +1 -2
  8. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_decorators.py +32 -0
  9. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_filters.py +5 -12
  10. domaintools_api_test_upload-2.7.3/VERSION +0 -1
  11. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/LICENSE +0 -0
  12. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/README.md +0 -0
  13. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/__init__.py +0 -0
  14. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/_version.py +0 -0
  15. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/base_results.py +0 -0
  16. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/__init__.py +0 -0
  17. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/api.py +0 -0
  18. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/__init__.py +0 -0
  19. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/accounts.py +0 -0
  20. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/detects.py +0 -0
  21. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/domains.py +0 -0
  22. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/feeds.py +0 -0
  23. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/ips.py +0 -0
  24. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/iris.py +0 -0
  25. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/constants.py +0 -0
  26. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/main.py +0 -0
  27. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/utils.py +0 -0
  28. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/constants.py +0 -0
  29. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/docstring_patcher.py +0 -0
  30. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/exceptions.py +0 -0
  31. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/filters.py +0 -0
  32. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/request_validator.py +0 -0
  33. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/results.py +0 -0
  34. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/specs/iris-openapi.yaml +0 -0
  35. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/utils.py +0 -0
  36. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/SOURCES.txt +0 -0
  37. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/dependency_links.txt +0 -0
  38. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/entry_points.txt +0 -0
  39. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/requires.txt +0 -0
  40. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/top_level.txt +0 -0
  41. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_async/__init__.py +0 -0
  42. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/setup.cfg +0 -0
  43. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/setup.py +0 -0
  44. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_async.py +0 -0
  45. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_cli.py +0 -0
  46. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_docstring_patcher.py +0 -0
  47. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_request_validator.py +0 -0
  48. {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
- Name: domaintools_api_test_upload
3
- Version: 2.7.3
2
+ Name: domaintools-api-test-upload
3
+ Version: 2.7.5
4
4
  Summary: DomainTools Official Python API
5
5
  Author-email: DomainTools <integrations@domaintools.com>
6
6
  License: The MIT License (MIT)
@@ -0,0 +1 @@
1
+ 2.7.5
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, timedelta, timezone
2
2
  from hashlib import sha1, sha256
3
3
  from hmac import new as hmac
4
- from importlib import resources
4
+ from pathlib import Path
5
5
  from typing import Union
6
6
 
7
7
  import re
@@ -113,9 +113,10 @@ class API(object):
113
113
  raise Exception("Proxy URL must be a string. For example: '127.0.0.1:8888'")
114
114
 
115
115
  def _initialize_specs(self):
116
+ package_root = Path(__file__).parent
116
117
  for spec_name, file_path in SPECS_MAPPING.items():
118
+ specs_file_path = f"{package_root}/specs/{file_path}"
117
119
  try:
118
- specs_file_path = resources.files("domaintools.specs").joinpath(file_path)
119
120
  with open(specs_file_path, "r", encoding="utf-8") as f:
120
121
  spec_content = yaml.safe_load(f)
121
122
  if not spec_content:
@@ -127,11 +128,7 @@ class API(object):
127
128
  print(f"Error loading {specs_file_path}: {e}")
128
129
 
129
130
  def _get_ssl_default_context(self, verify_ssl: Union[str, bool]):
130
- return (
131
- ssl.create_default_context(cafile=verify_ssl)
132
- if isinstance(verify_ssl, str)
133
- else verify_ssl
134
- )
131
+ return ssl.create_default_context(cafile=verify_ssl) if isinstance(verify_ssl, str) else verify_ssl
135
132
 
136
133
  def _build_api_url(self, api_url=None, api_port=None):
137
134
  """Build the API url based on the given url and port. Defaults to `https://api.domaintools.com`"""
@@ -159,18 +156,11 @@ class API(object):
159
156
  hours = limit_hours and 3600 / float(limit_hours)
160
157
  minutes = limit_minutes and 60 / float(limit_minutes)
161
158
 
162
- self.limits[product["id"]] = {
163
- "interval": timedelta(seconds=minutes or hours or default)
164
- }
159
+ self.limits[product["id"]] = {"interval": timedelta(seconds=minutes or hours or default)}
165
160
 
166
161
  def _results(self, product, path, cls=Results, **kwargs):
167
162
  """Returns _results for the specified API path with the specified **kwargs parameters"""
168
- if (
169
- product != "account-information"
170
- and self.rate_limit
171
- and not self.limits_set
172
- and not self.limits
173
- ):
163
+ if product != "account-information" and self.rate_limit and not self.limits_set and not self.limits:
174
164
  always_sign_api_key_previous_value = self.always_sign_api_key
175
165
  header_authentication_previous_value = self.header_authentication
176
166
  self._rate_limit(product)
@@ -214,9 +204,7 @@ class API(object):
214
204
  else:
215
205
  raise ValueError(
216
206
  "Invalid value '{0}' for 'key_sign_hash'. "
217
- "Values available are {1}".format(
218
- self.key_sign_hash, ",".join(AVAILABLE_KEY_SIGN_HASHES)
219
- )
207
+ "Values available are {1}".format(self.key_sign_hash, ",".join(AVAILABLE_KEY_SIGN_HASHES))
220
208
  )
221
209
 
222
210
  parameters["timestamp"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -228,9 +216,7 @@ class API(object):
228
216
 
229
217
  def account_information(self, **kwargs):
230
218
  """Provides a snapshot of your accounts current API usage"""
231
- return self._results(
232
- "account-information", "/v1/account", items_path=("products",), **kwargs
233
- )
219
+ return self._results("account-information", "/v1/account", items_path=("products",), **kwargs)
234
220
 
235
221
  def available_api_calls(self):
236
222
  """Provides a list of api calls that you can use based on your account information."""
@@ -433,9 +419,7 @@ class API(object):
433
419
 
434
420
  def reverse_ip(self, domain=None, limit=None, **kwargs):
435
421
  """Pass in a domain name."""
436
- return self._results(
437
- "reverse-ip", "/v1/{0}/reverse-ip".format(domain), limit=limit, **kwargs
438
- )
422
+ return self._results("reverse-ip", "/v1/{0}/reverse-ip".format(domain), limit=limit, **kwargs)
439
423
 
440
424
  def host_domains(self, ip=None, limit=None, **kwargs):
441
425
  """Pass in an IP address."""
@@ -609,12 +593,8 @@ class API(object):
609
593
  younger_than_date = kwargs.pop("younger_than_date", {}) or None
610
594
  older_than_date = kwargs.pop("older_than_date", {}) or None
611
595
  updated_after = kwargs.pop("updated_after", {}) or None
612
- include_domains_with_missing_field = (
613
- kwargs.pop("include_domains_with_missing_field", {}) or None
614
- )
615
- exclude_domains_with_missing_field = (
616
- kwargs.pop("exclude_domains_with_missing_field", {}) or None
617
- )
596
+ include_domains_with_missing_field = kwargs.pop("include_domains_with_missing_field", {}) or None
597
+ exclude_domains_with_missing_field = kwargs.pop("exclude_domains_with_missing_field", {}) or None
618
598
 
619
599
  filtered_results = DTResultFilter(result_set=results).by(
620
600
  [
@@ -676,7 +656,7 @@ class API(object):
676
656
  create_date=None,
677
657
  active=None,
678
658
  search_hash=None,
679
- risk_score=None,
659
+ risk_score_threshold=None,
680
660
  younger_than_date=None,
681
661
  older_than_date=None,
682
662
  updated_after=None,
@@ -731,7 +711,7 @@ class API(object):
731
711
 
732
712
  filtered_results = DTResultFilter(result_set=results).by(
733
713
  [
734
- filter_by_riskscore(threshold=risk_score),
714
+ filter_by_riskscore(threshold=risk_score_threshold),
735
715
  filter_by_expire_date(date=younger_than_date, lookup_type="before"),
736
716
  filter_by_expire_date(date=older_than_date, lookup_type="after"),
737
717
  filter_by_date_updated_after(date=updated_after),
@@ -51,9 +51,7 @@ def api_endpoint(spec_name: str, path: str, methods: Union[str, List[str]]):
51
51
 
52
52
  if "domains" in arguments.keys():
53
53
  domains = arguments.pop("domains")
54
- arguments["domain"] = (
55
- ",".join(domains) if isinstance(domains, (list, tuple)) else domains
56
- )
54
+ arguments["domain"] = ",".join(domains) if isinstance(domains, (list, tuple)) else domains
57
55
 
58
56
  if spec:
59
57
  # Determine which HTTP method is currently being executed.
@@ -61,6 +59,8 @@ def api_endpoint(spec_name: str, path: str, methods: Union[str, List[str]]):
61
59
  # Otherwise, default to the first method defined in the decorator.
62
60
  current_method = kwargs.get("method", normalized_methods[0])
63
61
 
62
+ filtered_arguments = {k: v for k, v in arguments.items() if v is not None}
63
+
64
64
  # Run Validation
65
65
  # This will raise a ValueError and stop execution if validation fails.
66
66
  try:
@@ -68,7 +68,7 @@ def api_endpoint(spec_name: str, path: str, methods: Union[str, List[str]]):
68
68
  spec=spec,
69
69
  path=path,
70
70
  method=current_method,
71
- parameters=arguments,
71
+ parameters=filtered_arguments,
72
72
  )
73
73
  except ValueError as e:
74
74
  print(f"[Validation Error] {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
- Name: domaintools_api_test_upload
3
- Version: 2.7.3
2
+ Name: domaintools-api-test-upload
3
+ Version: 2.7.5
4
4
  Summary: DomainTools Official Python API
5
5
  Author-email: DomainTools <integrations@domaintools.com>
6
6
  License: The MIT License (MIT)
@@ -3,7 +3,7 @@ requires = ["setuptools"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
- name = "domaintools_api_test_upload"
6
+ name = "domaintools-api-test-upload"
7
7
  dependencies = [
8
8
  "httpx==0.28.1",
9
9
  "pyyaml",
@@ -143,7 +143,6 @@ def test_name_server_monitor():
143
143
  def test_parsed_whois():
144
144
  api_call = api.parsed_whois("google.com")
145
145
  with api_call as result:
146
- assert "registrant" in result
147
146
  assert "registration" in result
148
147
  assert "name_servers" in result
149
148
  assert "whois" in result
@@ -476,7 +475,7 @@ def test_iris_detect_watched_domains():
476
475
  detect_results = api.iris_detect_watched_domains(
477
476
  monitor_id="nAwmQg2pqg", sort=["risk_score"], order="desc"
478
477
  )
479
- assert len(detect_results["watchlist_domains"]) == 3
478
+ assert len(detect_results["watchlist_domains"]) == 5
480
479
 
481
480
  detect_results = api.iris_detect_watched_domains(escalation_types="blocked")
482
481
  assert detect_results["count"] == 1
@@ -217,3 +217,35 @@ class TestApiEndpointDecorator:
217
217
  call_kwargs = mock_validate.call_args[1]
218
218
 
219
219
  assert call_kwargs.get("parameters") == {"name": "test-name"}
220
+
221
+ def test_none_arguments_are_filtered_before_validation(self, mock_client):
222
+ """
223
+ Test that parameters with None values are excluded from the arguments
224
+ passed to the validator.
225
+ """
226
+
227
+ @api_endpoint(spec_name="v1", path="/users", methods="POST")
228
+ def create_user(name=None, status=None):
229
+ return "Success"
230
+
231
+ with patch("domaintools.request_validator.RequestValidator.validate") as mock_validate:
232
+ create_user(mock_client, name="Alice", status=None)
233
+
234
+ call_kwargs = mock_validate.call_args[1]
235
+ assert call_kwargs.get("parameters") == {"name": "Alice"}
236
+ assert "status" not in call_kwargs.get("parameters", {})
237
+
238
+ def test_all_none_arguments_passes_empty_parameters(self, mock_client):
239
+ """
240
+ Test that when all arguments are None, an empty dict is passed to the validator.
241
+ """
242
+
243
+ @api_endpoint(spec_name="v1", path="/users", methods="POST")
244
+ def create_user(name=None, body=None):
245
+ return "Success"
246
+
247
+ with patch("domaintools.request_validator.RequestValidator.validate") as mock_validate:
248
+ create_user(mock_client, name=None, body=None)
249
+
250
+ call_kwargs = mock_validate.call_args[1]
251
+ assert call_kwargs.get("parameters") == {}
@@ -16,20 +16,17 @@ class DTFiltersTest(unittest.TestCase):
16
16
  domaintools_iris_result = iris_investigate_data.domaintools()
17
17
  int_chase_iris_result = iris_investigate_data.int_chase()
18
18
 
19
- test_results = {
20
- "results": domaintools_iris_result["results"]
21
- + int_chase_iris_result["results"]
22
- }
19
+ test_results = {"results": domaintools_iris_result["results"] + int_chase_iris_result["results"]}
23
20
 
24
21
  self.dt_res_filter = DTResultFilter(result_set=test_results)
25
22
 
26
23
  def test_filter_by_riskscore(self):
27
24
  """Test domaintools filter by riskcore"""
28
- risk_score = 69
25
+ risk_score_threshold = 69
29
26
 
30
27
  result = self.dt_res_filter.by(
31
28
  [
32
- filter_by_riskscore(threshold=risk_score),
29
+ filter_by_riskscore(threshold=risk_score_threshold),
33
30
  ]
34
31
  )
35
32
 
@@ -77,9 +74,7 @@ class DTFiltersTest(unittest.TestCase):
77
74
 
78
75
  results = self.dt_res_filter.by(
79
76
  [
80
- filter_by_field(
81
- field=include_domains_with_missing_field, filter_type="include"
82
- ),
77
+ filter_by_field(field=include_domains_with_missing_field, filter_type="include"),
83
78
  ]
84
79
  )
85
80
 
@@ -90,9 +85,7 @@ class DTFiltersTest(unittest.TestCase):
90
85
 
91
86
  results = self.dt_res_filter.by(
92
87
  [
93
- filter_by_field(
94
- field=exclude_domains_with_missing_field, filter_type="exclude"
95
- ),
88
+ filter_by_field(field=exclude_domains_with_missing_field, filter_type="exclude"),
96
89
  ]
97
90
  )
98
91
 
@@ -1 +0,0 @@
1
- 2.7.3