eodag 2.12.1__py3-none-any.whl → 3.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.
- eodag/__init__.py +6 -8
- eodag/api/core.py +654 -538
- eodag/api/product/__init__.py +12 -2
- eodag/api/product/_assets.py +59 -16
- eodag/api/product/_product.py +100 -93
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +192 -96
- eodag/api/search_result.py +69 -10
- eodag/cli.py +55 -25
- eodag/config.py +391 -116
- eodag/plugins/apis/base.py +11 -168
- eodag/plugins/apis/ecmwf.py +36 -25
- eodag/plugins/apis/usgs.py +80 -35
- eodag/plugins/authentication/aws_auth.py +13 -4
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +2 -2
- eodag/plugins/authentication/header.py +31 -6
- eodag/plugins/authentication/keycloak.py +17 -84
- eodag/plugins/authentication/oauth.py +3 -3
- eodag/plugins/authentication/openid_connect.py +268 -49
- eodag/plugins/authentication/qsauth.py +4 -1
- eodag/plugins/authentication/sas_auth.py +9 -2
- eodag/plugins/authentication/token.py +98 -47
- eodag/plugins/authentication/token_exchange.py +122 -0
- eodag/plugins/crunch/base.py +3 -1
- eodag/plugins/crunch/filter_date.py +3 -9
- eodag/plugins/crunch/filter_latest_intersect.py +0 -3
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
- eodag/plugins/crunch/filter_overlap.py +4 -8
- eodag/plugins/crunch/filter_property.py +5 -11
- eodag/plugins/download/aws.py +149 -185
- eodag/plugins/download/base.py +88 -97
- eodag/plugins/download/creodias_s3.py +1 -1
- eodag/plugins/download/http.py +638 -310
- eodag/plugins/download/s3rest.py +47 -45
- eodag/plugins/manager.py +228 -88
- eodag/plugins/search/__init__.py +36 -0
- eodag/plugins/search/base.py +239 -30
- eodag/plugins/search/build_search_result.py +382 -37
- eodag/plugins/search/cop_marine.py +441 -0
- eodag/plugins/search/creodias_s3.py +25 -20
- eodag/plugins/search/csw.py +5 -7
- eodag/plugins/search/data_request_search.py +61 -30
- eodag/plugins/search/qssearch.py +713 -255
- eodag/plugins/search/static_stac_search.py +106 -40
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1921 -34
- eodag/resources/providers.yml +4091 -3655
- eodag/resources/stac.yml +50 -216
- eodag/resources/stac_api.yml +71 -25
- eodag/resources/stac_provider.yml +5 -0
- eodag/resources/user_conf_template.yml +89 -32
- eodag/rest/__init__.py +6 -0
- eodag/rest/cache.py +70 -0
- eodag/rest/config.py +68 -0
- eodag/rest/constants.py +26 -0
- eodag/rest/core.py +735 -0
- eodag/rest/errors.py +178 -0
- eodag/rest/server.py +264 -431
- eodag/rest/stac.py +442 -836
- eodag/rest/types/collections_search.py +44 -0
- eodag/rest/types/eodag_search.py +238 -47
- eodag/rest/types/queryables.py +164 -0
- eodag/rest/types/stac_search.py +273 -0
- eodag/rest/utils/__init__.py +216 -0
- eodag/rest/utils/cql_evaluate.py +119 -0
- eodag/rest/utils/rfc3339.py +64 -0
- eodag/types/__init__.py +106 -10
- eodag/types/bbox.py +15 -14
- eodag/types/download_args.py +40 -0
- eodag/types/search_args.py +57 -7
- eodag/types/whoosh.py +79 -0
- eodag/utils/__init__.py +110 -91
- eodag/utils/constraints.py +37 -45
- eodag/utils/exceptions.py +39 -22
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/repr.py +113 -0
- eodag/utils/requests.py +128 -0
- eodag/utils/rest.py +100 -0
- eodag/utils/stac_reader.py +93 -21
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
- eodag-3.0.0.dist-info/RECORD +109 -0
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
- eodag/plugins/apis/cds.py +0 -540
- eodag/rest/types/stac_queryables.py +0 -134
- eodag/rest/utils.py +0 -1133
- eodag-2.12.1.dist-info/RECORD +0 -94
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -19,8 +19,8 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import time
|
|
22
|
-
from datetime import datetime, timedelta
|
|
23
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
22
|
+
from datetime import datetime, timedelta, timezone
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, cast
|
|
24
24
|
|
|
25
25
|
import requests
|
|
26
26
|
|
|
@@ -30,10 +30,11 @@ from eodag.api.product.metadata_mapping import (
|
|
|
30
30
|
mtd_cfg_as_conversion_and_querypath,
|
|
31
31
|
properties_from_json,
|
|
32
32
|
)
|
|
33
|
+
from eodag.plugins.search import PreparedSearch
|
|
33
34
|
from eodag.plugins.search.base import Search
|
|
34
|
-
from eodag.rest.stac import DEFAULT_MISSION_START_DATE
|
|
35
35
|
from eodag.utils import (
|
|
36
36
|
DEFAULT_ITEMS_PER_PAGE,
|
|
37
|
+
DEFAULT_MISSION_START_DATE,
|
|
37
38
|
DEFAULT_PAGE,
|
|
38
39
|
GENERIC_PRODUCT_TYPE,
|
|
39
40
|
HTTP_REQ_TIMEOUT,
|
|
@@ -41,7 +42,12 @@ from eodag.utils import (
|
|
|
41
42
|
deepcopy,
|
|
42
43
|
string_to_jsonpath,
|
|
43
44
|
)
|
|
44
|
-
from eodag.utils.exceptions import
|
|
45
|
+
from eodag.utils.exceptions import (
|
|
46
|
+
NotAvailableError,
|
|
47
|
+
RequestError,
|
|
48
|
+
TimeOutError,
|
|
49
|
+
ValidationError,
|
|
50
|
+
)
|
|
45
51
|
|
|
46
52
|
if TYPE_CHECKING:
|
|
47
53
|
from eodag.config import PluginConfig
|
|
@@ -57,6 +63,8 @@ class DataRequestSearch(Search):
|
|
|
57
63
|
- if finished - fetch the result of the job
|
|
58
64
|
"""
|
|
59
65
|
|
|
66
|
+
data_request_id: Optional[str]
|
|
67
|
+
|
|
60
68
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
61
69
|
super(DataRequestSearch, self).__init__(provider, config)
|
|
62
70
|
self.config.__dict__.setdefault("result_type", "json")
|
|
@@ -101,14 +109,13 @@ class DataRequestSearch(Search):
|
|
|
101
109
|
self.config.pagination["next_page_url_key_path"] = string_to_jsonpath(
|
|
102
110
|
self.config.pagination.get("next_page_url_key_path", None)
|
|
103
111
|
)
|
|
104
|
-
self.download_info = {}
|
|
112
|
+
self.download_info: Dict[str, Any] = {}
|
|
105
113
|
self.data_request_id = None
|
|
106
114
|
|
|
107
|
-
def discover_product_types(self) -> Optional[Dict[str, Any]]:
|
|
115
|
+
def discover_product_types(self, **kwargs: Any) -> Optional[Dict[str, Any]]:
|
|
108
116
|
"""Fetch product types is disabled for `DataRequestSearch`
|
|
109
117
|
|
|
110
118
|
:returns: empty dict
|
|
111
|
-
:rtype: (optional) dict
|
|
112
119
|
"""
|
|
113
120
|
return None
|
|
114
121
|
|
|
@@ -119,26 +126,30 @@ class DataRequestSearch(Search):
|
|
|
119
126
|
|
|
120
127
|
def query(
|
|
121
128
|
self,
|
|
122
|
-
|
|
123
|
-
items_per_page: int = DEFAULT_ITEMS_PER_PAGE,
|
|
124
|
-
page: int = DEFAULT_PAGE,
|
|
125
|
-
count: bool = True,
|
|
129
|
+
prep: PreparedSearch = PreparedSearch(),
|
|
126
130
|
**kwargs: Any,
|
|
127
131
|
) -> Tuple[List[EOProduct], Optional[int]]:
|
|
128
132
|
"""
|
|
129
133
|
performs the search for a provider where several steps are required to fetch the data
|
|
130
134
|
"""
|
|
135
|
+
if kwargs.get("sort_by"):
|
|
136
|
+
raise ValidationError(f"{self.provider} does not support sorting feature")
|
|
137
|
+
|
|
131
138
|
product_type = kwargs.get("productType", None)
|
|
139
|
+
|
|
140
|
+
if product_type is None:
|
|
141
|
+
raise ValidationError("Required productType is missing")
|
|
142
|
+
|
|
132
143
|
# replace "product_type" to "providerProductType" in search args if exists
|
|
133
144
|
# for compatibility with DataRequestSearch method
|
|
134
145
|
if kwargs.get("product_type"):
|
|
135
146
|
kwargs["providerProductType"] = kwargs.pop("product_type", None)
|
|
136
|
-
provider_product_type = self._map_product_type(product_type or "")
|
|
147
|
+
provider_product_type = cast(str, self._map_product_type(product_type or ""))
|
|
137
148
|
keywords = {k: v for k, v in kwargs.items() if k != "auth" and v is not None}
|
|
138
149
|
|
|
139
150
|
if provider_product_type and provider_product_type != GENERIC_PRODUCT_TYPE:
|
|
140
151
|
keywords["productType"] = provider_product_type
|
|
141
|
-
|
|
152
|
+
else:
|
|
142
153
|
keywords["productType"] = product_type
|
|
143
154
|
|
|
144
155
|
# provider product type specific conf
|
|
@@ -185,7 +196,7 @@ class DataRequestSearch(Search):
|
|
|
185
196
|
if not keywords.get("completionTimeFromAscendingNode", None):
|
|
186
197
|
keywords["completionTimeFromAscendingNode"] = getattr(
|
|
187
198
|
self.config, "product_type_config", {}
|
|
188
|
-
).get("missionEndDate", datetime.
|
|
199
|
+
).get("missionEndDate", datetime.now(timezone.utc).isoformat())
|
|
189
200
|
|
|
190
201
|
# ask for data_request_id if not set (it must exist when iterating over pages)
|
|
191
202
|
if not self.data_request_id:
|
|
@@ -246,24 +257,27 @@ class DataRequestSearch(Search):
|
|
|
246
257
|
self, product_type: str, eodag_product_type: str, **kwargs: Any
|
|
247
258
|
) -> str:
|
|
248
259
|
headers = getattr(self.auth, "headers", USER_AGENT)
|
|
260
|
+
ssl_verify = getattr(self.config.ssl_verify, "ssl_verify", True)
|
|
249
261
|
try:
|
|
250
262
|
url = self.config.data_request_url
|
|
251
|
-
request_body = format_query_params(
|
|
252
|
-
eodag_product_type, self.config, **kwargs
|
|
253
|
-
)
|
|
263
|
+
request_body = format_query_params(eodag_product_type, self.config, kwargs)
|
|
254
264
|
logger.debug(
|
|
255
265
|
f"Sending search job request to {url} with {str(request_body)}"
|
|
256
266
|
)
|
|
257
267
|
request_job = requests.post(
|
|
258
|
-
url,
|
|
268
|
+
url,
|
|
269
|
+
json=request_body,
|
|
270
|
+
headers=headers,
|
|
271
|
+
timeout=HTTP_REQ_TIMEOUT,
|
|
272
|
+
verify=ssl_verify,
|
|
259
273
|
)
|
|
260
274
|
request_job.raise_for_status()
|
|
261
275
|
except requests.exceptions.Timeout as exc:
|
|
262
276
|
raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
|
|
263
277
|
except requests.RequestException as e:
|
|
264
|
-
raise RequestError(
|
|
265
|
-
f"search job for product_type {product_type} could not be created
|
|
266
|
-
)
|
|
278
|
+
raise RequestError.from_error(
|
|
279
|
+
e, f"search job for product_type {product_type} could not be created"
|
|
280
|
+
) from e
|
|
267
281
|
else:
|
|
268
282
|
logger.info("search job for product_type %s created", product_type)
|
|
269
283
|
return request_job.json()["jobId"]
|
|
@@ -271,28 +285,35 @@ class DataRequestSearch(Search):
|
|
|
271
285
|
def _cancel_request(self, data_request_id: str) -> None:
|
|
272
286
|
logger.info("deleting request job %s", data_request_id)
|
|
273
287
|
delete_url = f"{self.config.data_request_url}/{data_request_id}"
|
|
288
|
+
headers = getattr(self.auth, "headers", USER_AGENT)
|
|
274
289
|
try:
|
|
275
290
|
delete_resp = requests.delete(
|
|
276
|
-
delete_url, headers=
|
|
291
|
+
delete_url, headers=headers, timeout=HTTP_REQ_TIMEOUT
|
|
277
292
|
)
|
|
278
293
|
delete_resp.raise_for_status()
|
|
279
294
|
except requests.exceptions.Timeout as exc:
|
|
280
295
|
raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
|
|
281
296
|
except requests.RequestException as e:
|
|
282
|
-
raise RequestError(
|
|
297
|
+
raise RequestError.from_error(e, "_cancel_request failed") from e
|
|
283
298
|
|
|
284
299
|
def _check_request_status(self, data_request_id: str) -> bool:
|
|
285
300
|
logger.debug("checking status of request job %s", data_request_id)
|
|
286
301
|
status_url = self.config.status_url + data_request_id
|
|
302
|
+
headers = getattr(self.auth, "headers", USER_AGENT)
|
|
303
|
+
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
304
|
+
|
|
287
305
|
try:
|
|
288
306
|
status_resp = requests.get(
|
|
289
|
-
status_url,
|
|
307
|
+
status_url,
|
|
308
|
+
headers=headers,
|
|
309
|
+
timeout=HTTP_REQ_TIMEOUT,
|
|
310
|
+
verify=ssl_verify,
|
|
290
311
|
)
|
|
291
312
|
status_resp.raise_for_status()
|
|
292
313
|
except requests.exceptions.Timeout as exc:
|
|
293
314
|
raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
|
|
294
315
|
except requests.RequestException as e:
|
|
295
|
-
raise RequestError(
|
|
316
|
+
raise RequestError.from_error(e, "_check_request_status failed") from e
|
|
296
317
|
else:
|
|
297
318
|
status_data = status_resp.json()
|
|
298
319
|
if "status_code" in status_data and status_data["status_code"] in [
|
|
@@ -300,7 +321,9 @@ class DataRequestSearch(Search):
|
|
|
300
321
|
404,
|
|
301
322
|
]:
|
|
302
323
|
logger.error(f"_check_request_status failed: {status_data}")
|
|
303
|
-
|
|
324
|
+
error = RequestError("authentication token expired during request")
|
|
325
|
+
error.status_code = status_data["status_code"]
|
|
326
|
+
raise error
|
|
304
327
|
if status_data["status"] == "failed":
|
|
305
328
|
logger.error(f"_check_request_status failed: {status_data}")
|
|
306
329
|
raise RequestError(
|
|
@@ -315,9 +338,11 @@ class DataRequestSearch(Search):
|
|
|
315
338
|
url = self.config.result_url.format(
|
|
316
339
|
jobId=data_request_id, items_per_page=items_per_page, page=page
|
|
317
340
|
)
|
|
341
|
+
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
342
|
+
headers = getattr(self.auth, "headers", USER_AGENT)
|
|
318
343
|
try:
|
|
319
344
|
return requests.get(
|
|
320
|
-
url, headers=
|
|
345
|
+
url, headers=headers, timeout=HTTP_REQ_TIMEOUT, verify=ssl_verify
|
|
321
346
|
).json()
|
|
322
347
|
except requests.exceptions.Timeout as exc:
|
|
323
348
|
raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
|
|
@@ -362,8 +387,11 @@ class DataRequestSearch(Search):
|
|
|
362
387
|
total_items_nb_key_path = string_to_jsonpath(
|
|
363
388
|
self.config.pagination["total_items_nb_key_path"]
|
|
364
389
|
)
|
|
365
|
-
|
|
366
|
-
|
|
390
|
+
found_total_items_nb_paths = total_items_nb_key_path.find(results)
|
|
391
|
+
if found_total_items_nb_paths and not isinstance(
|
|
392
|
+
found_total_items_nb_paths, int
|
|
393
|
+
):
|
|
394
|
+
total_items_nb = found_total_items_nb_paths[0].value
|
|
367
395
|
else:
|
|
368
396
|
total_items_nb = 0
|
|
369
397
|
for p in products:
|
|
@@ -399,7 +427,10 @@ class DataRequestSearch(Search):
|
|
|
399
427
|
path = string_to_jsonpath(custom_filters["filter_attribute"])
|
|
400
428
|
indexes = custom_filters["indexes"].split("-")
|
|
401
429
|
for record in results:
|
|
402
|
-
|
|
430
|
+
found_paths = path.find(record)
|
|
431
|
+
if not found_paths or isinstance(found_paths, int):
|
|
432
|
+
continue
|
|
433
|
+
filter_param = found_paths[0].value
|
|
403
434
|
filter_value = filter_param[int(indexes[0]) : int(indexes[1])]
|
|
404
435
|
filter_clause = "'" + filter_value + "' " + custom_filters["filter_clause"]
|
|
405
436
|
if eval(filter_clause):
|