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.
- {domaintools_api_test_upload-2.7.3/domaintools_api_test_upload.egg-info → domaintools_api_test_upload-2.7.5}/PKG-INFO +2 -2
- domaintools_api_test_upload-2.7.5/VERSION +1 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/api.py +13 -33
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/decorators.py +4 -4
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5/domaintools_api_test_upload.egg-info}/PKG-INFO +2 -2
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/pyproject.toml +1 -1
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_api.py +1 -2
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_decorators.py +32 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_filters.py +5 -12
- domaintools_api_test_upload-2.7.3/VERSION +0 -1
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/LICENSE +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/README.md +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/__init__.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/_version.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/base_results.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/__init__.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/api.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/__init__.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/accounts.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/detects.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/domains.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/feeds.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/ips.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/commands/iris.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/constants.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/main.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/utils.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/constants.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/docstring_patcher.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/exceptions.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/filters.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/request_validator.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/results.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/specs/iris-openapi.yaml +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/utils.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/SOURCES.txt +0 -0
- {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
- {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
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_api_test_upload.egg-info/requires.txt +0 -0
- {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
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools_async/__init__.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/setup.cfg +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/setup.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_async.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_cli.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_docstring_patcher.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_request_validator.py +0 -0
- {domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_utils.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
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),
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/decorators.py
RENAMED
|
@@ -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=
|
|
71
|
+
parameters=filtered_arguments,
|
|
72
72
|
)
|
|
73
73
|
except ValueError as e:
|
|
74
74
|
print(f"[Validation Error] {e}")
|
|
@@ -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"]) ==
|
|
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
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_decorators.py
RENAMED
|
@@ -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") == {}
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/tests/test_filters.py
RENAMED
|
@@ -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
|
-
|
|
25
|
+
risk_score_threshold = 69
|
|
29
26
|
|
|
30
27
|
result = self.dt_res_filter.by(
|
|
31
28
|
[
|
|
32
|
-
filter_by_riskscore(threshold=
|
|
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
|
|
File without changes
|
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/__init__.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/_version.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/base_results.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/__init__.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/constants.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/main.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/cli/utils.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/exceptions.py
RENAMED
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/filters.py
RENAMED
|
File without changes
|
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/results.py
RENAMED
|
File without changes
|
|
File without changes
|
{domaintools_api_test_upload-2.7.3 → domaintools_api_test_upload-2.7.5}/domaintools/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|