python-anvil 5.0.3b1__tar.gz → 5.1.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.
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/PKG-INFO +1 -1
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/pyproject.toml +1 -1
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api.py +5 -1
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/requests.py +20 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/constants.py +8 -1
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/test_api.py +59 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/LICENSE.md +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/README.md +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/__init__.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/__init__.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/base.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/__init__.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/base.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/create_etch_packet.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/forge_submit.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/generate_etch_signing_url.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/helpers.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/payload.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/cli.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/exceptions.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/http.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/models.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/py.typed +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/__init__.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/conftest.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/payloads.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/test_cli.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/test_http.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/test_models.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/tests/test_utils.py +0 -0
- {python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/utils.py +0 -0
|
@@ -15,7 +15,7 @@ from .api_resources.payload import (
|
|
|
15
15
|
ForgeSubmitPayload,
|
|
16
16
|
GeneratePDFPayload,
|
|
17
17
|
)
|
|
18
|
-
from .api_resources.requests import PlainRequest, RestRequest
|
|
18
|
+
from .api_resources.requests import FullyQualifiedRequest, PlainRequest, RestRequest
|
|
19
19
|
from .http import GQLClient, HTTPClient
|
|
20
20
|
|
|
21
21
|
|
|
@@ -117,6 +117,10 @@ class Anvil:
|
|
|
117
117
|
api = RestRequest(self.client, options=options)
|
|
118
118
|
return api
|
|
119
119
|
|
|
120
|
+
def request_fully_qualified(self, options: Optional[dict] = None):
|
|
121
|
+
api = FullyQualifiedRequest(self.client, options=options)
|
|
122
|
+
return api
|
|
123
|
+
|
|
120
124
|
def fill_pdf(
|
|
121
125
|
self, template_id: str, payload: Union[dict, AnyStr, FillPDFPayload], **kwargs
|
|
122
126
|
):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Any, Dict
|
|
2
2
|
|
|
3
|
+
from python_anvil.constants import VALID_HOSTS
|
|
3
4
|
from python_anvil.http import HTTPClient
|
|
4
5
|
|
|
5
6
|
|
|
@@ -161,3 +162,22 @@ class PlainRequest(BaseAnvilHttpRequest):
|
|
|
161
162
|
|
|
162
163
|
def get_url(self):
|
|
163
164
|
return f"{self.API_HOST}/{self.API_BASE}"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class FullyQualifiedRequest(BaseAnvilHttpRequest):
|
|
168
|
+
"""A request class that validates URLs point to Anvil domains."""
|
|
169
|
+
|
|
170
|
+
def get_url(self):
|
|
171
|
+
return "" # Not used since we expect full URLs
|
|
172
|
+
|
|
173
|
+
def _validate_url(self, url):
|
|
174
|
+
if not any(url.startswith(host) for host in VALID_HOSTS):
|
|
175
|
+
raise ValueError(f"URL must start with one of: {', '.join(VALID_HOSTS)}")
|
|
176
|
+
|
|
177
|
+
def get(self, url, params=None, **kwargs):
|
|
178
|
+
self._validate_url(url)
|
|
179
|
+
return super().get(url, params, **kwargs)
|
|
180
|
+
|
|
181
|
+
def post(self, url, data=None, **kwargs):
|
|
182
|
+
self._validate_url(url)
|
|
183
|
+
return super().post(url, data, **kwargs)
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
"""Basic constants used in the library."""
|
|
2
2
|
|
|
3
3
|
GRAPHQL_ENDPOINT: str = "https://graphql.useanvil.com"
|
|
4
|
-
REST_ENDPOINT = "https://app.useanvil.com/api/v1
|
|
4
|
+
REST_ENDPOINT = "https://app.useanvil.com/api/v1"
|
|
5
|
+
ANVIL_HOST = "https://app.useanvil.com"
|
|
6
|
+
|
|
7
|
+
VALID_HOSTS = [
|
|
8
|
+
ANVIL_HOST,
|
|
9
|
+
REST_ENDPOINT,
|
|
10
|
+
GRAPHQL_ENDPOINT,
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
RETRIES_LIMIT = 5
|
|
7
14
|
REQUESTS_LIMIT = {
|
|
@@ -9,6 +9,7 @@ from python_anvil.api_resources.payload import (
|
|
|
9
9
|
CreateEtchPacketPayload,
|
|
10
10
|
ForgeSubmitPayload,
|
|
11
11
|
)
|
|
12
|
+
from python_anvil.constants import VALID_HOSTS
|
|
12
13
|
|
|
13
14
|
from ..api_resources.payload import FillPDFPayload
|
|
14
15
|
from . import payloads
|
|
@@ -396,3 +397,61 @@ def describe_api():
|
|
|
396
397
|
anvil.forge_submit(payload=payload)
|
|
397
398
|
assert m_request_post.call_count == 1
|
|
398
399
|
assert _expected_data in m_request_post.call_args
|
|
400
|
+
|
|
401
|
+
def describe_rest_request_absolute_url_behavior():
|
|
402
|
+
@pytest.mark.parametrize(
|
|
403
|
+
"url, should_raise",
|
|
404
|
+
[
|
|
405
|
+
("some/relative/path", True),
|
|
406
|
+
("https://external.example.com/full/path/file.pdf", True),
|
|
407
|
+
*[(host + "/some-endpoint", False) for host in VALID_HOSTS],
|
|
408
|
+
],
|
|
409
|
+
)
|
|
410
|
+
@mock.patch("python_anvil.api_resources.requests.AnvilRequest._request")
|
|
411
|
+
def test_get_behavior(mock_request, anvil, url, should_raise):
|
|
412
|
+
mock_request.return_value = (b"fake_content", 200, {})
|
|
413
|
+
rest_client = anvil.request_fully_qualified()
|
|
414
|
+
|
|
415
|
+
if should_raise:
|
|
416
|
+
with pytest.raises(
|
|
417
|
+
ValueError,
|
|
418
|
+
match="URL must start with one of: https://app.useanvil.com",
|
|
419
|
+
):
|
|
420
|
+
rest_client.get(url)
|
|
421
|
+
else:
|
|
422
|
+
rest_client.get(url)
|
|
423
|
+
mock_request.assert_called_once_with(
|
|
424
|
+
"GET",
|
|
425
|
+
url,
|
|
426
|
+
params=None,
|
|
427
|
+
retry=True,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
@pytest.mark.parametrize(
|
|
431
|
+
"url, should_raise",
|
|
432
|
+
[
|
|
433
|
+
("some/relative/path", True),
|
|
434
|
+
("https://external.example.com/full/path/file.pdf", True),
|
|
435
|
+
*[(host + "/some-endpoint", False) for host in VALID_HOSTS],
|
|
436
|
+
],
|
|
437
|
+
)
|
|
438
|
+
@mock.patch("python_anvil.api_resources.requests.AnvilRequest._request")
|
|
439
|
+
def test_post_behavior(mock_request, anvil, url, should_raise):
|
|
440
|
+
mock_request.return_value = (b"fake_content", 200, {})
|
|
441
|
+
rest_client = anvil.request_fully_qualified()
|
|
442
|
+
|
|
443
|
+
if should_raise:
|
|
444
|
+
with pytest.raises(
|
|
445
|
+
ValueError,
|
|
446
|
+
match="URL must start with one of: https://app.useanvil.com",
|
|
447
|
+
):
|
|
448
|
+
rest_client.post(url, data={})
|
|
449
|
+
else:
|
|
450
|
+
rest_client.post(url, data={})
|
|
451
|
+
mock_request.assert_called_once_with(
|
|
452
|
+
"POST",
|
|
453
|
+
url,
|
|
454
|
+
json={},
|
|
455
|
+
retry=True,
|
|
456
|
+
params=None,
|
|
457
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_anvil-5.0.3b1 → python_anvil-5.1.0}/python_anvil/api_resources/mutations/forge_submit.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|