pangea-sdk 3.7.0__tar.gz → 3.8.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.
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/PKG-INFO +33 -6
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/README.md +27 -2
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/__init__.py +1 -1
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/request.py +123 -32
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/__init__.py +1 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/audit.py +236 -21
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/authn.py +79 -50
- pangea_sdk-3.8.0/pangea/asyncio/services/authz.py +259 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/base.py +9 -6
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/file_scan.py +3 -4
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/intel.py +5 -6
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/redact.py +21 -3
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/vault.py +28 -12
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/config.py +10 -18
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/dump_audit.py +1 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/exceptions.py +8 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/request.py +164 -74
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/response.py +63 -17
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/__init__.py +1 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/audit/audit.py +241 -55
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/audit/exceptions.py +1 -2
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/audit/models.py +83 -21
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/audit/signing.py +1 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/audit/util.py +1 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/authn/authn.py +38 -4
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/authn/models.py +9 -9
- pangea_sdk-3.8.0/pangea/services/authz.py +377 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/base.py +34 -14
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/embargo.py +1 -2
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/file_scan.py +3 -4
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/intel.py +3 -4
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/redact.py +21 -3
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/vault/vault.py +29 -12
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/utils.py +2 -3
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pyproject.toml +13 -7
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/audit_logger.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/deep_verify.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/deprecated.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/py.typed +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/vault/models/asymmetric.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/vault/models/common.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/vault/models/secret.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/services/vault/models/symmetric.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/tools.py +0 -0
- {pangea_sdk-3.7.0 → pangea_sdk-3.8.0}/pangea/verify_audit.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pangea-sdk
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.8.0
|
4
4
|
Summary: Pangea API SDK
|
5
5
|
Home-page: https://pangea.cloud/docs/sdk/python/
|
6
6
|
License: MIT
|
@@ -18,12 +18,14 @@ Classifier: Topic :: Software Development
|
|
18
18
|
Classifier: Topic :: Software Development :: Libraries
|
19
19
|
Requires-Dist: aiohttp (>=3.8.6,<4.0.0)
|
20
20
|
Requires-Dist: asyncio (>=3.4.3,<4.0.0)
|
21
|
-
Requires-Dist: cryptography (>=42.0.
|
21
|
+
Requires-Dist: cryptography (>=42.0.7,<43.0.0)
|
22
22
|
Requires-Dist: deprecated (>=1.2.14,<2.0.0)
|
23
23
|
Requires-Dist: google-crc32c (>=1.5.0,<2.0.0)
|
24
|
-
Requires-Dist: pydantic (>=1.10.
|
25
|
-
Requires-Dist: python-dateutil (>=2.
|
24
|
+
Requires-Dist: pydantic (>=1.10.15,<2.0.0)
|
25
|
+
Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
|
26
26
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
27
|
+
Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
|
28
|
+
Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
|
27
29
|
Project-URL: Repository, https://github.com/pangeacyber/pangea-python/tree/main/packages/pangea-sdk
|
28
30
|
Description-Content-Type: text/markdown
|
29
31
|
|
@@ -43,6 +45,8 @@ above.
|
|
43
45
|
|
44
46
|
## Installation
|
45
47
|
|
48
|
+
#### GA releases
|
49
|
+
|
46
50
|
Via pip:
|
47
51
|
|
48
52
|
```bash
|
@@ -55,10 +59,32 @@ Via poetry:
|
|
55
59
|
$ poetry add pangea-sdk
|
56
60
|
```
|
57
61
|
|
62
|
+
<a name="beta-releases"></a>
|
63
|
+
|
64
|
+
#### Beta releases
|
65
|
+
|
66
|
+
Pre-release versions may be available with the `b` (beta) denotation in the
|
67
|
+
version number. These releases serve to preview beta services and APIs. Per
|
68
|
+
Semantic Versioning, they are considered unstable and do not carry the same
|
69
|
+
compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
|
70
|
+
|
71
|
+
Via pip:
|
72
|
+
|
73
|
+
```bash
|
74
|
+
$ pip3 install pangea-sdk==3.8.0b2
|
75
|
+
```
|
76
|
+
|
77
|
+
Via poetry:
|
78
|
+
|
79
|
+
```bash
|
80
|
+
$ poetry add pangea-sdk==3.8.0b2
|
81
|
+
```
|
82
|
+
|
58
83
|
## Usage
|
59
84
|
|
60
85
|
- [Documentation][]
|
61
|
-
- [Examples][]
|
86
|
+
- [GA Examples][]
|
87
|
+
- [Beta Examples][]
|
62
88
|
|
63
89
|
General usage would be to create a token for a service through the
|
64
90
|
[Pangea Console][] and then construct an API client for that respective service.
|
@@ -202,7 +228,8 @@ It accepts multiple file formats:
|
|
202
228
|
|
203
229
|
|
204
230
|
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
205
|
-
[Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
231
|
+
[GA Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
232
|
+
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
206
233
|
[Pangea Console]: https://console.pangea.cloud/
|
207
234
|
[Slack]: https://pangea.cloud/join-slack/
|
208
235
|
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -14,6 +14,8 @@ above.
|
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
17
|
+
#### GA releases
|
18
|
+
|
17
19
|
Via pip:
|
18
20
|
|
19
21
|
```bash
|
@@ -26,10 +28,32 @@ Via poetry:
|
|
26
28
|
$ poetry add pangea-sdk
|
27
29
|
```
|
28
30
|
|
31
|
+
<a name="beta-releases"></a>
|
32
|
+
|
33
|
+
#### Beta releases
|
34
|
+
|
35
|
+
Pre-release versions may be available with the `b` (beta) denotation in the
|
36
|
+
version number. These releases serve to preview beta services and APIs. Per
|
37
|
+
Semantic Versioning, they are considered unstable and do not carry the same
|
38
|
+
compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
|
39
|
+
|
40
|
+
Via pip:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
$ pip3 install pangea-sdk==3.8.0b2
|
44
|
+
```
|
45
|
+
|
46
|
+
Via poetry:
|
47
|
+
|
48
|
+
```bash
|
49
|
+
$ poetry add pangea-sdk==3.8.0b2
|
50
|
+
```
|
51
|
+
|
29
52
|
## Usage
|
30
53
|
|
31
54
|
- [Documentation][]
|
32
|
-
- [Examples][]
|
55
|
+
- [GA Examples][]
|
56
|
+
- [Beta Examples][]
|
33
57
|
|
34
58
|
General usage would be to create a token for a service through the
|
35
59
|
[Pangea Console][] and then construct an API client for that respective service.
|
@@ -173,7 +197,8 @@ It accepts multiple file formats:
|
|
173
197
|
|
174
198
|
|
175
199
|
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
176
|
-
[Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
200
|
+
[GA Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
201
|
+
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
177
202
|
[Pangea Console]: https://console.pangea.cloud/
|
178
203
|
[Slack]: https://pangea.cloud/join-slack/
|
179
204
|
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -7,14 +7,16 @@ import time
|
|
7
7
|
from typing import Dict, List, Optional, Tuple, Type, Union
|
8
8
|
|
9
9
|
import aiohttp
|
10
|
-
import pangea.exceptions as pe
|
11
10
|
from aiohttp import FormData
|
11
|
+
from typing_extensions import TypeVar
|
12
12
|
|
13
|
-
|
14
|
-
from pangea.request import PangeaRequestBase
|
15
|
-
from pangea.response import
|
13
|
+
import pangea.exceptions as pe
|
14
|
+
from pangea.request import MultipartResponse, PangeaRequestBase
|
15
|
+
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
16
16
|
from pangea.utils import default_encoder
|
17
17
|
|
18
|
+
TResult = TypeVar("TResult", bound=PangeaResponseResult, default=PangeaResponseResult)
|
19
|
+
|
18
20
|
|
19
21
|
class PangeaRequestAsync(PangeaRequestBase):
|
20
22
|
"""An object that makes direct calls to Pangea Service APIs.
|
@@ -28,12 +30,12 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
28
30
|
async def post(
|
29
31
|
self,
|
30
32
|
endpoint: str,
|
31
|
-
result_class: Type[
|
33
|
+
result_class: Type[TResult],
|
32
34
|
data: Union[str, Dict] = {},
|
33
|
-
files:
|
35
|
+
files: List[Tuple] = [],
|
34
36
|
poll_result: bool = True,
|
35
37
|
url: Optional[str] = None,
|
36
|
-
) -> PangeaResponse:
|
38
|
+
) -> PangeaResponse[TResult]:
|
37
39
|
"""Makes the POST call to a Pangea Service endpoint.
|
38
40
|
|
39
41
|
Args:
|
@@ -56,7 +58,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
56
58
|
)
|
57
59
|
transfer_method = data.get("transfer_method", None) # type: ignore[union-attr]
|
58
60
|
|
59
|
-
if files
|
61
|
+
if files and type(data) is dict and (transfer_method == TransferMethod.POST_URL.value):
|
60
62
|
requests_response = await self._full_post_presigned_url(
|
61
63
|
endpoint, result_class=result_class, data=data, files=files
|
62
64
|
)
|
@@ -66,18 +68,32 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
66
68
|
)
|
67
69
|
|
68
70
|
await self._check_http_errors(requests_response)
|
69
|
-
json_resp = await requests_response.json()
|
70
|
-
self.logger.debug(json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp}))
|
71
71
|
|
72
|
-
|
72
|
+
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
73
|
+
multipart_response = await self._process_multipart_response(requests_response)
|
74
|
+
pangea_response: PangeaResponse = PangeaResponse(
|
75
|
+
requests_response,
|
76
|
+
result_class=result_class,
|
77
|
+
json=multipart_response.pangea_json,
|
78
|
+
attached_files=multipart_response.attached_files,
|
79
|
+
)
|
80
|
+
else:
|
81
|
+
try:
|
82
|
+
json_resp = await requests_response.json()
|
83
|
+
self.logger.debug(
|
84
|
+
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
85
|
+
)
|
86
|
+
|
87
|
+
pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
88
|
+
except aiohttp.ContentTypeError as e:
|
89
|
+
raise pe.PangeaException(f"Failed to decode json response. {e}. Body: {await requests_response.text()}")
|
90
|
+
|
73
91
|
if poll_result:
|
74
92
|
pangea_response = await self._handle_queued_result(pangea_response)
|
75
93
|
|
76
94
|
return self._check_response(pangea_response)
|
77
95
|
|
78
|
-
async def get(
|
79
|
-
self, path: str, result_class: Type[PangeaResponseResult], check_response: bool = True
|
80
|
-
) -> PangeaResponse:
|
96
|
+
async def get(self, path: str, result_class: Type[TResult], check_response: bool = True) -> PangeaResponse[TResult]:
|
81
97
|
"""Makes the GET call to a Pangea Service endpoint.
|
82
98
|
|
83
99
|
Args:
|
@@ -94,7 +110,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
94
110
|
|
95
111
|
async with self.session.get(url, headers=self._headers()) as requests_response:
|
96
112
|
await self._check_http_errors(requests_response)
|
97
|
-
pangea_response = PangeaResponse(
|
113
|
+
pangea_response = PangeaResponse(
|
98
114
|
requests_response, result_class=result_class, json=await requests_response.json()
|
99
115
|
)
|
100
116
|
|
@@ -115,11 +131,11 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
115
131
|
raise pe.ServiceTemporarilyUnavailable(await resp.json())
|
116
132
|
|
117
133
|
async def poll_result_by_id(
|
118
|
-
self, request_id: str, result_class:
|
119
|
-
):
|
134
|
+
self, request_id: str, result_class: Type[TResult], check_response: bool = True
|
135
|
+
) -> PangeaResponse[TResult]:
|
120
136
|
path = self._get_poll_path(request_id)
|
121
137
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_result_once", "url": path}))
|
122
|
-
return await self.get(path, result_class, check_response=check_response)
|
138
|
+
return await self.get(path, result_class, check_response=check_response)
|
123
139
|
|
124
140
|
async def poll_result_once(self, response: PangeaResponse, check_response: bool = True):
|
125
141
|
request_id = response.request_id
|
@@ -157,12 +173,84 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
157
173
|
if resp.status < 200 or resp.status >= 300:
|
158
174
|
raise pe.PresignedUploadError(f"presigned PUT failure: {resp.status}", await resp.text())
|
159
175
|
|
176
|
+
async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
|
177
|
+
self.logger.debug(
|
178
|
+
json.dumps(
|
179
|
+
{
|
180
|
+
"service": self.service,
|
181
|
+
"action": "download_file",
|
182
|
+
"url": url,
|
183
|
+
"status": "start",
|
184
|
+
}
|
185
|
+
)
|
186
|
+
)
|
187
|
+
async with self.session.get(url, headers={}) as response:
|
188
|
+
if response.status == 200:
|
189
|
+
if filename is None:
|
190
|
+
content_disposition = response.headers.get("Content-Disposition", "")
|
191
|
+
filename = self._get_filename_from_content_disposition(content_disposition)
|
192
|
+
if filename is None:
|
193
|
+
filename = self._get_filename_from_url(url)
|
194
|
+
if filename is None:
|
195
|
+
filename = "default_filename"
|
196
|
+
|
197
|
+
content_type = response.headers.get("Content-Type", "")
|
198
|
+
self.logger.debug(
|
199
|
+
json.dumps(
|
200
|
+
{
|
201
|
+
"service": self.service,
|
202
|
+
"action": "download_file",
|
203
|
+
"url": url,
|
204
|
+
"filename": filename,
|
205
|
+
"status": "success",
|
206
|
+
}
|
207
|
+
)
|
208
|
+
)
|
209
|
+
|
210
|
+
return AttachedFile(filename=filename, file=await response.read(), content_type=content_type)
|
211
|
+
else:
|
212
|
+
raise pe.DownloadFileError(f"Failed to download file. Status: {response.status}", await response.text())
|
213
|
+
|
214
|
+
async def _get_pangea_json(self, reader: aiohttp.MultipartReader) -> Optional[Dict]:
|
215
|
+
# Iterate through parts
|
216
|
+
async for part in reader:
|
217
|
+
return await part.json()
|
218
|
+
return None
|
219
|
+
|
220
|
+
async def _get_attached_files(self, reader: aiohttp.MultipartReader) -> List[AttachedFile]:
|
221
|
+
files = []
|
222
|
+
i = 0
|
223
|
+
|
224
|
+
async for part in reader:
|
225
|
+
content_type = part.headers.get("Content-Type", "")
|
226
|
+
content_disposition = part.headers.get("Content-Disposition", "")
|
227
|
+
name = self._get_filename_from_content_disposition(content_disposition)
|
228
|
+
if name is None:
|
229
|
+
name = f"default_file_name_{i}"
|
230
|
+
i += 1
|
231
|
+
files.append(AttachedFile(name, await part.read(), content_type))
|
232
|
+
|
233
|
+
return files
|
234
|
+
|
235
|
+
async def _process_multipart_response(self, resp: aiohttp.ClientResponse) -> MultipartResponse:
|
236
|
+
# Parse the multipart response
|
237
|
+
multipart_reader = aiohttp.MultipartReader.from_response(resp)
|
238
|
+
|
239
|
+
pangea_json = await self._get_pangea_json(multipart_reader) # type: ignore[arg-type]
|
240
|
+
self.logger.debug(
|
241
|
+
json.dumps({"service": self.service, "action": "multipart response", "response": pangea_json})
|
242
|
+
)
|
243
|
+
|
244
|
+
multipart_reader = multipart_reader.__aiter__()
|
245
|
+
attached_files = await self._get_attached_files(multipart_reader) # type: ignore[arg-type]
|
246
|
+
return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
|
247
|
+
|
160
248
|
async def _http_post(
|
161
249
|
self,
|
162
250
|
url: str,
|
163
251
|
headers: Dict = {},
|
164
252
|
data: Union[str, Dict] = {},
|
165
|
-
files:
|
253
|
+
files: List[Tuple] = [],
|
166
254
|
presigned_url_post: bool = False,
|
167
255
|
) -> aiohttp.ClientResponse:
|
168
256
|
if files:
|
@@ -193,26 +281,29 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
193
281
|
self.logger.debug(
|
194
282
|
json.dumps({"service": self.service, "action": "http_put", "url": url}, default=default_encoder)
|
195
283
|
)
|
196
|
-
|
197
|
-
|
198
|
-
form.add_field(name, value[1], filename=value[0], content_type=value[2])
|
199
|
-
return await self.session.put(url, headers=headers, data=form)
|
284
|
+
_, value = files[0]
|
285
|
+
return await self.session.put(url, headers=headers, data=value[1])
|
200
286
|
|
201
287
|
async def _full_post_presigned_url(
|
202
288
|
self,
|
203
289
|
endpoint: str,
|
204
290
|
result_class: Type[PangeaResponseResult],
|
205
291
|
data: Union[str, Dict] = {},
|
206
|
-
files:
|
292
|
+
files: List[Tuple] = [],
|
207
293
|
):
|
208
|
-
if len(files) == 0:
|
294
|
+
if len(files) == 0:
|
209
295
|
raise AttributeError("files attribute should have at least 1 file")
|
210
296
|
|
211
297
|
response = await self.request_presigned_url(endpoint=endpoint, result_class=result_class, data=data)
|
212
|
-
|
213
|
-
|
298
|
+
if response.accepted_result is None:
|
299
|
+
raise pe.PangeaException("No accepted_result field when requesting presigned url")
|
300
|
+
if response.accepted_result.post_url is None:
|
301
|
+
raise pe.PresignedURLException("No presigned url", response)
|
302
|
+
|
303
|
+
data_to_presigned = response.accepted_result.post_form_data
|
304
|
+
presigned_url = response.accepted_result.post_url
|
214
305
|
|
215
|
-
await self.post_presigned_url(url=presigned_url, data=data_to_presigned, files=files)
|
306
|
+
await self.post_presigned_url(url=presigned_url, data=data_to_presigned, files=files)
|
216
307
|
return response.raw_response
|
217
308
|
|
218
309
|
async def request_presigned_url(
|
@@ -232,14 +323,14 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
232
323
|
raise e
|
233
324
|
|
234
325
|
# Receive 202
|
235
|
-
return await self._poll_presigned_url(accepted_exception.response)
|
326
|
+
return await self._poll_presigned_url(accepted_exception.response)
|
236
327
|
|
237
|
-
async def _poll_presigned_url(self, response: PangeaResponse) ->
|
328
|
+
async def _poll_presigned_url(self, response: PangeaResponse[TResult]) -> PangeaResponse[TResult]:
|
238
329
|
if response.http_status != 202:
|
239
330
|
raise AttributeError("Response should be 202")
|
240
331
|
|
241
332
|
if response.accepted_result is not None and response.accepted_result.has_upload_url:
|
242
|
-
return response
|
333
|
+
return response
|
243
334
|
|
244
335
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_presigned_url", "step": "start"}))
|
245
336
|
retry_count = 1
|
@@ -276,7 +367,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
276
367
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_presigned_url", "step": "exit"}))
|
277
368
|
|
278
369
|
if loop_resp.accepted_result is not None and not loop_resp.accepted_result.has_upload_url:
|
279
|
-
return loop_resp
|
370
|
+
return loop_resp
|
280
371
|
else:
|
281
372
|
raise loop_exc
|
282
373
|
|