eodag 3.0.0b3__py3-none-any.whl → 3.1.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/api/core.py +347 -247
- eodag/api/product/_assets.py +44 -15
- eodag/api/product/_product.py +58 -47
- eodag/api/product/drivers/__init__.py +81 -4
- eodag/api/product/drivers/base.py +65 -4
- eodag/api/product/drivers/generic.py +65 -0
- eodag/api/product/drivers/sentinel1.py +97 -0
- eodag/api/product/drivers/sentinel2.py +95 -0
- eodag/api/product/metadata_mapping.py +129 -93
- eodag/api/search_result.py +28 -12
- eodag/cli.py +61 -24
- eodag/config.py +457 -167
- eodag/plugins/apis/base.py +10 -4
- eodag/plugins/apis/ecmwf.py +53 -23
- eodag/plugins/apis/usgs.py +41 -17
- eodag/plugins/authentication/aws_auth.py +30 -18
- eodag/plugins/authentication/base.py +14 -3
- eodag/plugins/authentication/generic.py +14 -3
- eodag/plugins/authentication/header.py +14 -6
- eodag/plugins/authentication/keycloak.py +44 -25
- eodag/plugins/authentication/oauth.py +18 -4
- eodag/plugins/authentication/openid_connect.py +192 -171
- eodag/plugins/authentication/qsauth.py +12 -4
- eodag/plugins/authentication/sas_auth.py +22 -5
- eodag/plugins/authentication/token.py +95 -17
- eodag/plugins/authentication/token_exchange.py +19 -19
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +8 -5
- eodag/plugins/crunch/filter_date.py +9 -6
- eodag/plugins/crunch/filter_latest_intersect.py +9 -8
- eodag/plugins/crunch/filter_latest_tpl_name.py +8 -8
- eodag/plugins/crunch/filter_overlap.py +9 -11
- eodag/plugins/crunch/filter_property.py +10 -10
- eodag/plugins/download/aws.py +181 -105
- eodag/plugins/download/base.py +49 -67
- eodag/plugins/download/creodias_s3.py +40 -2
- eodag/plugins/download/http.py +247 -223
- eodag/plugins/download/s3rest.py +29 -28
- eodag/plugins/manager.py +176 -41
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +123 -60
- eodag/plugins/search/build_search_result.py +1046 -355
- eodag/plugins/search/cop_marine.py +132 -39
- eodag/plugins/search/creodias_s3.py +19 -68
- eodag/plugins/search/csw.py +48 -8
- eodag/plugins/search/data_request_search.py +124 -23
- eodag/plugins/search/qssearch.py +531 -310
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +23 -24
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1295 -355
- eodag/resources/providers.yml +1819 -3010
- eodag/resources/stac.yml +3 -163
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +115 -99
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -4
- eodag/rest/constants.py +0 -1
- eodag/rest/core.py +157 -117
- eodag/rest/errors.py +181 -0
- eodag/rest/server.py +57 -339
- eodag/rest/stac.py +133 -581
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +41 -30
- eodag/rest/types/queryables.py +42 -32
- eodag/rest/types/stac_search.py +15 -16
- eodag/rest/utils/__init__.py +14 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +153 -32
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +4 -4
- eodag/types/queryables.py +183 -73
- eodag/types/search_args.py +6 -6
- eodag/types/whoosh.py +127 -3
- eodag/utils/__init__.py +228 -106
- eodag/utils/exceptions.py +47 -26
- eodag/utils/import_system.py +2 -2
- eodag/utils/logging.py +37 -77
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +13 -15
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +231 -0
- eodag/utils/stac_reader.py +11 -11
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/METADATA +81 -81
- eodag-3.1.0.dist-info/RECORD +113 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/WHEEL +1 -1
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/entry_points.txt +5 -2
- eodag/resources/constraints/climate-dt.json +0 -13
- eodag/resources/constraints/extremes-dt.json +0 -8
- eodag/utils/constraints.py +0 -244
- eodag-3.0.0b3.dist-info/RECORD +0 -110
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/LICENSE +0 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/top_level.txt +0 -0
eodag/plugins/download/s3rest.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
import os.path
|
|
23
|
-
from typing import TYPE_CHECKING,
|
|
23
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
24
24
|
from xml.dom import minidom
|
|
25
25
|
from xml.parsers.expat import ExpatError
|
|
26
26
|
|
|
@@ -54,6 +54,7 @@ from eodag.utils.exceptions import (
|
|
|
54
54
|
if TYPE_CHECKING:
|
|
55
55
|
from eodag.api.product import EOProduct
|
|
56
56
|
from eodag.config import PluginConfig
|
|
57
|
+
from eodag.types import S3SessionKwargs
|
|
57
58
|
from eodag.types.download_args import DownloadConf
|
|
58
59
|
from eodag.utils import Unpack
|
|
59
60
|
|
|
@@ -62,23 +63,28 @@ logger = logging.getLogger("eodag.download.s3rest")
|
|
|
62
63
|
|
|
63
64
|
class S3RestDownload(Download):
|
|
64
65
|
"""Http download on S3-like object storage location
|
|
65
|
-
|
|
66
|
+
|
|
67
|
+
For example using Mundi REST API (free account)
|
|
66
68
|
https://mundiwebservices.com/keystoneapi/uploads/documents/CWS-DATA-MUT-087-EN-Mundi_Download_v1.1.pdf#page=13
|
|
67
69
|
|
|
68
|
-
Re-use AwsDownload bucket some handling methods
|
|
70
|
+
Re-use AwsDownload bucket and some handling methods
|
|
69
71
|
|
|
70
72
|
:param provider: provider name
|
|
71
73
|
:param config: Download plugin configuration:
|
|
72
74
|
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
75
|
+
* :attr:`~eodag.config.PluginConfig.base_uri` (``str``) (**mandatory**): default endpoint url
|
|
76
|
+
* :attr:`~eodag.config.PluginConfig.extract` (``bool``): extract downloaded archive or not
|
|
77
|
+
* :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): authentication error code
|
|
78
|
+
* :attr:`~eodag.config.PluginConfig.bucket_path_level` (``int``): bucket location index in ``path.split('/')``
|
|
79
|
+
* :attr:`~eodag.config.PluginConfig.order_enabled` (``bool``): whether order is enabled
|
|
80
|
+
or not if product is `OFFLINE`
|
|
81
|
+
* :attr:`~eodag.config.PluginConfig.order_method` (``str``) HTTP request method, ``GET`` (default) or ``POST``
|
|
82
|
+
* :attr:`~eodag.config.PluginConfig.order_headers` (``[dict[str, str]]``): order request headers
|
|
83
|
+
* :attr:`~eodag.config.PluginConfig.order_on_response` (:class:`~eodag.config.PluginConfig.OrderOnResponse`):
|
|
84
|
+
a typed dictionary containing the key :attr:`~eodag.config.PluginConfig.OrderOnResponse.metadata_mapping`
|
|
85
|
+
which can be used to add new product properties based on the data in response to the order request
|
|
86
|
+
* :attr:`~eodag.config.PluginConfig.order_status` (:class:`~eodag.config.PluginConfig.OrderStatus`):
|
|
87
|
+
Order status handling
|
|
82
88
|
"""
|
|
83
89
|
|
|
84
90
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
@@ -88,10 +94,10 @@ class S3RestDownload(Download):
|
|
|
88
94
|
def download(
|
|
89
95
|
self,
|
|
90
96
|
product: EOProduct,
|
|
91
|
-
auth: Optional[Union[AuthBase,
|
|
97
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
92
98
|
progress_callback: Optional[ProgressCallback] = None,
|
|
93
|
-
wait:
|
|
94
|
-
timeout:
|
|
99
|
+
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
100
|
+
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
95
101
|
**kwargs: Unpack[DownloadConf],
|
|
96
102
|
) -> Optional[str]:
|
|
97
103
|
"""Download method for S3 REST API.
|
|
@@ -125,9 +131,9 @@ class S3RestDownload(Download):
|
|
|
125
131
|
and "storageStatus" in product.properties
|
|
126
132
|
and product.properties["storageStatus"] != ONLINE_STATUS
|
|
127
133
|
):
|
|
128
|
-
self.http_download_plugin.
|
|
134
|
+
self.http_download_plugin._order(product=product, auth=auth)
|
|
129
135
|
|
|
130
|
-
@self.
|
|
136
|
+
@self._order_download_retry(product, wait, timeout)
|
|
131
137
|
def download_request(
|
|
132
138
|
product: EOProduct,
|
|
133
139
|
auth: AuthBase,
|
|
@@ -137,9 +143,7 @@ class S3RestDownload(Download):
|
|
|
137
143
|
):
|
|
138
144
|
# check order status
|
|
139
145
|
if product.properties.get("orderStatusLink", None):
|
|
140
|
-
self.http_download_plugin.
|
|
141
|
-
product=product, auth=auth
|
|
142
|
-
)
|
|
146
|
+
self.http_download_plugin._order_status(product=product, auth=auth)
|
|
143
147
|
|
|
144
148
|
# get bucket urls
|
|
145
149
|
bucket_name, prefix = get_bucket_name_and_prefix(
|
|
@@ -189,12 +193,9 @@ class S3RestDownload(Download):
|
|
|
189
193
|
auth_errors = [auth_errors]
|
|
190
194
|
if err.response and err.response.status_code in auth_errors:
|
|
191
195
|
raise AuthenticationError(
|
|
192
|
-
"
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
err.response.text.strip(),
|
|
196
|
-
self.provider,
|
|
197
|
-
)
|
|
196
|
+
f"Please check your credentials for {self.provider}.",
|
|
197
|
+
f"HTTP Error {err.response.status_code} returned.",
|
|
198
|
+
err.response.text.strip(),
|
|
198
199
|
)
|
|
199
200
|
# product not available
|
|
200
201
|
elif (
|
|
@@ -225,7 +226,7 @@ class S3RestDownload(Download):
|
|
|
225
226
|
self.__class__.__name__,
|
|
226
227
|
bucket_contents.text,
|
|
227
228
|
)
|
|
228
|
-
raise RequestError(
|
|
229
|
+
raise RequestError.from_error(err) from err
|
|
229
230
|
try:
|
|
230
231
|
xmldoc = minidom.parseString(bucket_contents.text)
|
|
231
232
|
except ExpatError as err:
|
|
@@ -270,7 +271,7 @@ class S3RestDownload(Download):
|
|
|
270
271
|
os.remove(record_filename)
|
|
271
272
|
|
|
272
273
|
# total size for progress_callback
|
|
273
|
-
size_list:
|
|
274
|
+
size_list: list[int] = [
|
|
274
275
|
int(node.firstChild.nodeValue) # type: ignore[attr-defined]
|
|
275
276
|
for node in xmldoc.getElementsByTagName("Size")
|
|
276
277
|
if node.firstChild is not None
|
eodag/plugins/manager.py
CHANGED
|
@@ -18,37 +18,39 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
+
import re
|
|
21
22
|
from operator import attrgetter
|
|
22
23
|
from pathlib import Path
|
|
23
|
-
from typing import
|
|
24
|
-
TYPE_CHECKING,
|
|
25
|
-
Any,
|
|
26
|
-
Dict,
|
|
27
|
-
Iterator,
|
|
28
|
-
List,
|
|
29
|
-
Optional,
|
|
30
|
-
Tuple,
|
|
31
|
-
Type,
|
|
32
|
-
Union,
|
|
33
|
-
cast,
|
|
34
|
-
)
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast
|
|
35
25
|
|
|
36
26
|
import pkg_resources
|
|
37
27
|
|
|
38
|
-
from eodag.config import
|
|
28
|
+
from eodag.config import (
|
|
29
|
+
AUTH_TOPIC_KEYS,
|
|
30
|
+
PLUGINS_TOPICS_KEYS,
|
|
31
|
+
load_config,
|
|
32
|
+
merge_configs,
|
|
33
|
+
)
|
|
39
34
|
from eodag.plugins.apis.base import Api
|
|
40
35
|
from eodag.plugins.authentication.base import Authentication
|
|
41
36
|
from eodag.plugins.base import EODAGPluginMount
|
|
42
37
|
from eodag.plugins.crunch.base import Crunch
|
|
43
38
|
from eodag.plugins.download.base import Download
|
|
44
39
|
from eodag.plugins.search.base import Search
|
|
45
|
-
from eodag.utils import GENERIC_PRODUCT_TYPE
|
|
46
|
-
from eodag.utils.exceptions import
|
|
40
|
+
from eodag.utils import GENERIC_PRODUCT_TYPE, deepcopy, dict_md5sum
|
|
41
|
+
from eodag.utils.exceptions import (
|
|
42
|
+
AuthenticationError,
|
|
43
|
+
MisconfiguredError,
|
|
44
|
+
UnsupportedProvider,
|
|
45
|
+
)
|
|
47
46
|
|
|
48
47
|
if TYPE_CHECKING:
|
|
48
|
+
from requests.auth import AuthBase
|
|
49
|
+
|
|
49
50
|
from eodag.api.product import EOProduct
|
|
50
51
|
from eodag.config import PluginConfig, ProviderConfig
|
|
51
52
|
from eodag.plugins.base import PluginTopic
|
|
53
|
+
from eodag.types import S3SessionKwargs
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
logger = logging.getLogger("eodag.plugins.manager")
|
|
@@ -70,13 +72,13 @@ class PluginManager:
|
|
|
70
72
|
supported by ``eodag``
|
|
71
73
|
"""
|
|
72
74
|
|
|
73
|
-
supported_topics =
|
|
75
|
+
supported_topics = set(PLUGINS_TOPICS_KEYS)
|
|
74
76
|
|
|
75
|
-
product_type_to_provider_config_map:
|
|
77
|
+
product_type_to_provider_config_map: dict[str, list[ProviderConfig]]
|
|
76
78
|
|
|
77
|
-
skipped_plugins:
|
|
79
|
+
skipped_plugins: list[str]
|
|
78
80
|
|
|
79
|
-
def __init__(self, providers_config:
|
|
81
|
+
def __init__(self, providers_config: dict[str, ProviderConfig]) -> None:
|
|
80
82
|
self.skipped_plugins = []
|
|
81
83
|
self.providers_config = providers_config
|
|
82
84
|
# Load all the plugins. This will make all plugin classes of a particular
|
|
@@ -132,14 +134,14 @@ class PluginManager:
|
|
|
132
134
|
self.rebuild()
|
|
133
135
|
|
|
134
136
|
def rebuild(
|
|
135
|
-
self, providers_config: Optional[
|
|
137
|
+
self, providers_config: Optional[dict[str, ProviderConfig]] = None
|
|
136
138
|
) -> None:
|
|
137
139
|
"""(Re)Build plugin manager mapping and cache"""
|
|
138
140
|
if providers_config is not None:
|
|
139
141
|
self.providers_config = providers_config
|
|
140
142
|
|
|
141
143
|
self.build_product_type_to_provider_config_map()
|
|
142
|
-
self._built_plugins_cache:
|
|
144
|
+
self._built_plugins_cache: dict[tuple[str, str, str], Any] = {}
|
|
143
145
|
|
|
144
146
|
def build_product_type_to_provider_config_map(self) -> None:
|
|
145
147
|
"""Build mapping conf between product types and providers"""
|
|
@@ -199,7 +201,7 @@ class PluginManager:
|
|
|
199
201
|
)
|
|
200
202
|
return plugin
|
|
201
203
|
|
|
202
|
-
configs: Optional[
|
|
204
|
+
configs: Optional[list[ProviderConfig]]
|
|
203
205
|
if product_type:
|
|
204
206
|
configs = self.product_type_to_provider_config_map.get(product_type)
|
|
205
207
|
if not configs:
|
|
@@ -249,25 +251,146 @@ class PluginManager:
|
|
|
249
251
|
)
|
|
250
252
|
return plugin
|
|
251
253
|
|
|
252
|
-
def get_auth_plugin(
|
|
254
|
+
def get_auth_plugin(
|
|
255
|
+
self, associated_plugin: PluginTopic, product: Optional[EOProduct] = None
|
|
256
|
+
) -> Optional[Authentication]:
|
|
257
|
+
"""Build and return the authentication plugin associated to the given
|
|
258
|
+
search/download plugin
|
|
259
|
+
|
|
260
|
+
.. versionchanged:: v3.0.0
|
|
261
|
+
``get_auth_plugin()`` now needs ``associated_plugin`` instead of ``provider``
|
|
262
|
+
as argument.
|
|
263
|
+
|
|
264
|
+
:param associated_plugin: The search/download plugin to which the authentication
|
|
265
|
+
plugin is linked
|
|
266
|
+
:param product: The product to download. ``None`` for search authentication
|
|
267
|
+
:returns: The Authentication plugin
|
|
268
|
+
"""
|
|
269
|
+
# matching url from product to download
|
|
270
|
+
if product is not None and len(product.assets) > 0:
|
|
271
|
+
matching_url = next(iter(product.assets.values()))["href"]
|
|
272
|
+
elif product is not None:
|
|
273
|
+
matching_url = product.properties.get(
|
|
274
|
+
"downloadLink"
|
|
275
|
+
) or product.properties.get("orderLink")
|
|
276
|
+
else:
|
|
277
|
+
# search auth
|
|
278
|
+
matching_url = getattr(associated_plugin.config, "api_endpoint", None)
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
auth_plugin = next(
|
|
282
|
+
self.get_auth_plugins(
|
|
283
|
+
associated_plugin.provider,
|
|
284
|
+
matching_url=matching_url,
|
|
285
|
+
matching_conf=associated_plugin.config,
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
except StopIteration:
|
|
289
|
+
auth_plugin = None
|
|
290
|
+
return auth_plugin
|
|
291
|
+
|
|
292
|
+
def get_auth_plugins(
|
|
293
|
+
self,
|
|
294
|
+
provider: str,
|
|
295
|
+
matching_url: Optional[str] = None,
|
|
296
|
+
matching_conf: Optional[PluginConfig] = None,
|
|
297
|
+
) -> Iterator[Authentication]:
|
|
253
298
|
"""Build and return the authentication plugin for the given product_type and
|
|
254
299
|
provider
|
|
255
300
|
|
|
256
301
|
:param provider: The provider for which to get the authentication plugin
|
|
257
|
-
:
|
|
302
|
+
:param matching_url: url to compare with plugin matching_url pattern
|
|
303
|
+
:param matching_conf: configuration to compare with plugin matching_conf
|
|
304
|
+
:returns: All the Authentication plugins for the given criteria
|
|
258
305
|
"""
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
306
|
+
auth_conf: Optional[PluginConfig] = None
|
|
307
|
+
|
|
308
|
+
def _is_auth_plugin_matching(
|
|
309
|
+
auth_conf: PluginConfig,
|
|
310
|
+
matching_url: Optional[str],
|
|
311
|
+
matching_conf: Optional[PluginConfig],
|
|
312
|
+
) -> bool:
|
|
313
|
+
plugin_matching_conf = getattr(auth_conf, "matching_conf", {})
|
|
314
|
+
if matching_conf:
|
|
315
|
+
if (
|
|
316
|
+
plugin_matching_conf
|
|
317
|
+
and matching_conf.__dict__.items() >= plugin_matching_conf.items()
|
|
318
|
+
):
|
|
319
|
+
# conf matches
|
|
320
|
+
return True
|
|
321
|
+
plugin_matching_url = getattr(auth_conf, "matching_url", None)
|
|
322
|
+
if matching_url:
|
|
323
|
+
if plugin_matching_url and re.match(
|
|
324
|
+
rf"{plugin_matching_url}", matching_url
|
|
325
|
+
):
|
|
326
|
+
# url matches
|
|
327
|
+
return True
|
|
328
|
+
# no match
|
|
329
|
+
return False
|
|
330
|
+
|
|
331
|
+
# providers configs with given provider at first
|
|
332
|
+
sorted_providers_config = deepcopy(self.providers_config)
|
|
333
|
+
sorted_providers_config = {
|
|
334
|
+
provider: sorted_providers_config.pop(provider),
|
|
335
|
+
**sorted_providers_config,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
for plugin_provider, provider_conf in sorted_providers_config.items():
|
|
339
|
+
for key in AUTH_TOPIC_KEYS:
|
|
340
|
+
auth_conf = getattr(provider_conf, key, None)
|
|
341
|
+
if auth_conf is None:
|
|
342
|
+
continue
|
|
343
|
+
# plugin without configured match criteria: only works for given provider
|
|
344
|
+
unconfigured_match = (
|
|
345
|
+
True
|
|
346
|
+
if (
|
|
347
|
+
not getattr(auth_conf, "matching_conf", {})
|
|
348
|
+
and not getattr(auth_conf, "matching_url", None)
|
|
349
|
+
and provider == plugin_provider
|
|
350
|
+
)
|
|
351
|
+
else False
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
if unconfigured_match or _is_auth_plugin_matching(
|
|
355
|
+
auth_conf, matching_url, matching_conf
|
|
356
|
+
):
|
|
357
|
+
auth_conf.priority = provider_conf.priority
|
|
358
|
+
plugin = cast(
|
|
359
|
+
Authentication,
|
|
360
|
+
self._build_plugin(plugin_provider, auth_conf, Authentication),
|
|
361
|
+
)
|
|
362
|
+
yield plugin
|
|
363
|
+
else:
|
|
364
|
+
continue
|
|
365
|
+
|
|
366
|
+
def get_auth(
|
|
367
|
+
self,
|
|
368
|
+
provider: str,
|
|
369
|
+
matching_url: Optional[str] = None,
|
|
370
|
+
matching_conf: Optional[PluginConfig] = None,
|
|
371
|
+
) -> Optional[Union[AuthBase, S3SessionKwargs]]:
|
|
372
|
+
"""Authenticate and return the authenticated object for the first matching
|
|
373
|
+
authentication plugin
|
|
374
|
+
|
|
375
|
+
:param provider: The provider for which to get the authentication plugin
|
|
376
|
+
:param matching_url: url to compare with plugin matching_url pattern
|
|
377
|
+
:param matching_conf: configuration to compare with plugin matching_conf
|
|
378
|
+
:returns: All the Authentication plugins for the given criteria
|
|
379
|
+
"""
|
|
380
|
+
for auth_plugin in self.get_auth_plugins(provider, matching_url, matching_conf):
|
|
381
|
+
if auth_plugin and callable(getattr(auth_plugin, "authenticate", None)):
|
|
382
|
+
try:
|
|
383
|
+
auth = auth_plugin.authenticate()
|
|
384
|
+
return auth
|
|
385
|
+
except (AuthenticationError, MisconfiguredError) as e:
|
|
386
|
+
logger.debug(f"Could not authenticate on {provider}: {str(e)}")
|
|
387
|
+
continue
|
|
388
|
+
else:
|
|
389
|
+
logger.debug(
|
|
390
|
+
f"Could not authenticate on {provider} using {auth_plugin} plugin"
|
|
391
|
+
)
|
|
392
|
+
continue
|
|
393
|
+
return None
|
|
271
394
|
|
|
272
395
|
@staticmethod
|
|
273
396
|
def get_crunch_plugin(name: str, **options: Any) -> Crunch:
|
|
@@ -304,15 +427,17 @@ class PluginManager:
|
|
|
304
427
|
# Sort the provider configs, taking into account the new priority order
|
|
305
428
|
provider_configs.sort(key=attrgetter("priority"), reverse=True)
|
|
306
429
|
# Update the priority of already built plugins of the given provider
|
|
307
|
-
for provider_name, topic_class in self._built_plugins_cache:
|
|
430
|
+
for provider_name, topic_class, auth_match_md5 in self._built_plugins_cache:
|
|
308
431
|
if provider_name == provider:
|
|
309
|
-
self._built_plugins_cache[
|
|
432
|
+
self._built_plugins_cache[
|
|
433
|
+
(provider, topic_class, auth_match_md5)
|
|
434
|
+
].priority = priority
|
|
310
435
|
|
|
311
436
|
def _build_plugin(
|
|
312
437
|
self,
|
|
313
438
|
provider: str,
|
|
314
439
|
plugin_conf: PluginConfig,
|
|
315
|
-
topic_class:
|
|
440
|
+
topic_class: type[PluginTopic],
|
|
316
441
|
) -> Union[Api, Search, Download, Authentication, Crunch]:
|
|
317
442
|
"""Build the plugin of the given topic with the given plugin configuration and
|
|
318
443
|
registered as the given provider
|
|
@@ -325,8 +450,16 @@ class PluginManager:
|
|
|
325
450
|
:class:`~eodag.plugin.authentication.Authentication` or
|
|
326
451
|
:class:`~eodag.plugin.crunch.Crunch`
|
|
327
452
|
"""
|
|
453
|
+
# md5 hash to helps identifying an auth plugin within several for a given provider
|
|
454
|
+
# (each has distinct matching settings)
|
|
455
|
+
auth_match_md5 = dict_md5sum(
|
|
456
|
+
{
|
|
457
|
+
"matching_url": getattr(plugin_conf, "matching_url", None),
|
|
458
|
+
"matching_conf": getattr(plugin_conf, "matching_conf", None),
|
|
459
|
+
}
|
|
460
|
+
)
|
|
328
461
|
cached_instance = self._built_plugins_cache.setdefault(
|
|
329
|
-
(provider, topic_class.__name__), None
|
|
462
|
+
(provider, topic_class.__name__, auth_match_md5), None
|
|
330
463
|
)
|
|
331
464
|
if cached_instance is not None:
|
|
332
465
|
return cached_instance
|
|
@@ -336,5 +469,7 @@ class PluginManager:
|
|
|
336
469
|
plugin: Union[Api, Search, Download, Authentication, Crunch] = plugin_class(
|
|
337
470
|
provider, plugin_conf
|
|
338
471
|
)
|
|
339
|
-
self._built_plugins_cache[
|
|
472
|
+
self._built_plugins_cache[
|
|
473
|
+
(provider, topic_class.__name__, auth_match_md5)
|
|
474
|
+
] = plugin
|
|
340
475
|
return plugin
|
eodag/plugins/search/__init__.py
CHANGED
|
@@ -24,11 +24,12 @@ from typing import TYPE_CHECKING
|
|
|
24
24
|
from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from typing import Any,
|
|
27
|
+
from typing import Any, Optional, Union
|
|
28
28
|
|
|
29
29
|
from requests.auth import AuthBase
|
|
30
30
|
|
|
31
31
|
from eodag.plugins.authentication.base import Authentication
|
|
32
|
+
from eodag.types import S3SessionKwargs
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
@dataclass
|
|
@@ -38,7 +39,7 @@ class PreparedSearch:
|
|
|
38
39
|
product_type: Optional[str] = None
|
|
39
40
|
page: Optional[int] = DEFAULT_PAGE
|
|
40
41
|
items_per_page: Optional[int] = DEFAULT_ITEMS_PER_PAGE
|
|
41
|
-
auth: Optional[Union[AuthBase,
|
|
42
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None
|
|
42
43
|
auth_plugin: Optional[Authentication] = None
|
|
43
44
|
count: bool = True
|
|
44
45
|
url: Optional[str] = None
|
|
@@ -46,9 +47,9 @@ class PreparedSearch:
|
|
|
46
47
|
exception_message: Optional[str] = None
|
|
47
48
|
|
|
48
49
|
need_count: bool = field(init=False, repr=False)
|
|
49
|
-
query_params:
|
|
50
|
+
query_params: dict[str, Any] = field(init=False, repr=False)
|
|
50
51
|
query_string: str = field(init=False, repr=False)
|
|
51
|
-
search_urls:
|
|
52
|
-
product_type_def_params:
|
|
52
|
+
search_urls: list[str] = field(init=False, repr=False)
|
|
53
|
+
product_type_def_params: dict[str, Any] = field(init=False, repr=False)
|
|
53
54
|
total_items_nb: int = field(init=False, repr=False)
|
|
54
55
|
sort_by_qs: str = field(init=False, repr=False)
|