eodag 3.5.1__py3-none-any.whl → 3.7.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 CHANGED
@@ -69,6 +69,7 @@ from eodag.utils import (
69
69
  DEFAULT_MAX_ITEMS_PER_PAGE,
70
70
  DEFAULT_PAGE,
71
71
  GENERIC_PRODUCT_TYPE,
72
+ GENERIC_STAC_PROVIDER,
72
73
  HTTP_REQ_TIMEOUT,
73
74
  MockResponse,
74
75
  _deprecated,
@@ -2006,20 +2007,6 @@ class EODataAccessGateway:
2006
2007
  nb_res,
2007
2008
  search_plugin.provider,
2008
2009
  )
2009
- # Hitting for instance
2010
- # https://theia.cnes.fr/atdistrib/resto2/api/collections/SENTINEL2/
2011
- # search.json?startDate=2019-03-01&completionDate=2019-06-15
2012
- # &processingLevel=LEVEL2A&maxRecords=1&page=1
2013
- # returns a number (properties.totalResults) that is the number of
2014
- # products in the collection (here SENTINEL2) instead of the estimated
2015
- # total number of products matching the search criteria (start/end date).
2016
- # Remove this warning when this is fixed upstream by THEIA.
2017
- if search_plugin.provider == "theia":
2018
- logger.warning(
2019
- "Results found on provider 'theia' is the total number of products "
2020
- "available in the searched collection (e.g. SENTINEL2) instead of "
2021
- "the total number of products matching the search criteria"
2022
- )
2023
2010
  except Exception as e:
2024
2011
  if raise_errors:
2025
2012
  # Raise the error, letting the application wrapping eodag know that
@@ -2476,3 +2463,31 @@ class EODataAccessGateway:
2476
2463
  )
2477
2464
  # Remove the ID since this is equal to productType.
2478
2465
  plugin.config.product_type_config.pop("ID", None)
2466
+
2467
+ def import_stac_items(self, items_urls: list[str]) -> SearchResult:
2468
+ """Import STAC items from a list of URLs and convert them to SearchResult.
2469
+
2470
+ - Origin provider and download links will be set if item comes from an EODAG
2471
+ server.
2472
+ - If item comes from a known EODAG provider, result will be registered to it,
2473
+ ready to download and its metadata normalized.
2474
+ - If item comes from an unknown provider, a generic STAC provider will be used.
2475
+
2476
+ :param items_urls: A list of STAC items URLs to import
2477
+ :returns: A SearchResult containing the imported STAC items
2478
+ """
2479
+ json_items = []
2480
+ for item_url in items_urls:
2481
+ json_items.extend(fetch_stac_items(item_url))
2482
+
2483
+ # add a generic STAC provider that might be needed to handle the items
2484
+ self.add_provider(GENERIC_STAC_PROVIDER)
2485
+
2486
+ results = SearchResult([])
2487
+ for json_item in json_items:
2488
+ if search_result := SearchResult._from_stac_item(
2489
+ json_item, self._plugins_manager
2490
+ ):
2491
+ results.extend(search_result)
2492
+
2493
+ return results
@@ -17,6 +17,12 @@
17
17
  # limitations under the License.
18
18
  #
19
19
  """EODAG product package"""
20
+
21
+ from typing import TYPE_CHECKING, Any, Optional
22
+
23
+ if TYPE_CHECKING:
24
+ from eodag.plugins.manager import PluginManager
25
+
20
26
  try:
21
27
  # import from eodag-cube if installed
22
28
  from eodag_cube.api.product import ( # pyright: ignore[reportMissingImports]
@@ -30,3 +36,27 @@ except ImportError:
30
36
 
31
37
  # exportable content
32
38
  __all__ = ["Asset", "AssetsDict", "EOProduct"]
39
+
40
+
41
+ def unregistered_product_from_item(
42
+ feature: dict[str, Any], provider: str, plugins_manager: "PluginManager"
43
+ ) -> Optional[EOProduct]:
44
+ """Create an EOProduct from a STAC item, map its metadata, but without registering its plugins.
45
+
46
+ :param feature: The STAC item to convert into an EOProduct.
47
+ :param provider: The associated provider from which configuration should be used for mapping.
48
+ :param plugins_manager: The plugins manager instance to use for retrieving search plugins.
49
+ :returns: An EOProduct instance if the item can be normalized, otherwise None.
50
+ """
51
+ for search_plugin in plugins_manager.get_search_plugins(provider=provider):
52
+ if hasattr(search_plugin, "normalize_results"):
53
+ products = search_plugin.normalize_results([feature])
54
+ if len(products) > 0:
55
+ # properties cleanup
56
+ for prop in ("start_datetime", "end_datetime"):
57
+ products[0].properties.pop(prop, None)
58
+ # set product type if not already set
59
+ if products[0].product_type is None:
60
+ products[0].product_type = products[0].properties.get("productType")
61
+ return products[0]
62
+ return None
@@ -219,7 +219,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
219
219
  elif value is not None:
220
220
  converted = self.custom_converter(value)
221
221
  else:
222
- converted = ""
222
+ converted = None
223
223
  # Clear this state variable in case the same converter is used to
224
224
  # resolve other named arguments
225
225
  self.custom_converter = None
@@ -374,6 +374,18 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
374
374
  def convert_to_geojson(value: Any) -> str:
375
375
  return geojson.dumps(value)
376
376
 
377
+ @staticmethod
378
+ def convert_to_geojson_polytope(
379
+ value: BaseGeometry,
380
+ ) -> Union[dict[Any, Any], str]:
381
+ # ECMWF Polytope uses non-geojson structure for features
382
+ if isinstance(value, Polygon):
383
+ return {
384
+ "type": "polygon",
385
+ "shape": [[y, x] for x, y in value.exterior.coords],
386
+ }
387
+ raise ValidationError("to_geojson_polytope only accepts shapely Polygon")
388
+
377
389
  @staticmethod
378
390
  def convert_from_ewkt(ewkt_string: str) -> Union[BaseGeometry, str]:
379
391
  """Convert EWKT (Extended Well-Known text) to shapely geometry"""
@@ -488,10 +500,14 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
488
500
 
489
501
  @staticmethod
490
502
  def convert_get_group_name(string: str, pattern: str) -> str:
503
+ sanitized_pattern = pattern.replace(" ", "_SPACE_")
491
504
  try:
492
- match = re.search(pattern, str(string))
505
+ match = re.search(sanitized_pattern, str(string))
493
506
  if match:
494
- return match.lastgroup or NOT_AVAILABLE
507
+ if result := match.lastgroup:
508
+ return result.replace("_SPACE_", " ")
509
+ else:
510
+ return NOT_AVAILABLE
495
511
  except AttributeError:
496
512
  pass
497
513
  logger.warning(
@@ -1342,6 +1358,7 @@ def format_query_params(
1342
1358
  formatted_query_param = remove_str_array_quotes(
1343
1359
  formatted_query_param
1344
1360
  )
1361
+
1345
1362
  # json query string (for POST request)
1346
1363
  update_nested_dict(
1347
1364
  query_params,
@@ -17,23 +17,30 @@
17
17
  # limitations under the License.
18
18
  from __future__ import annotations
19
19
 
20
+ import logging
20
21
  from collections import UserList
21
22
  from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
22
23
 
23
24
  from shapely.geometry import GeometryCollection, shape
24
25
  from typing_extensions import Doc
25
26
 
26
- from eodag.api.product import EOProduct
27
+ from eodag.api.product import EOProduct, unregistered_product_from_item
27
28
  from eodag.plugins.crunch.filter_date import FilterDate
28
29
  from eodag.plugins.crunch.filter_latest_intersect import FilterLatestIntersect
29
30
  from eodag.plugins.crunch.filter_latest_tpl_name import FilterLatestByName
30
31
  from eodag.plugins.crunch.filter_overlap import FilterOverlap
31
32
  from eodag.plugins.crunch.filter_property import FilterProperty
33
+ from eodag.utils import GENERIC_STAC_PROVIDER, STAC_SEARCH_PLUGINS
34
+ from eodag.utils.exceptions import MisconfiguredError
32
35
 
33
36
  if TYPE_CHECKING:
34
37
  from shapely.geometry.base import BaseGeometry
35
38
 
36
39
  from eodag.plugins.crunch.base import Crunch
40
+ from eodag.plugins.manager import PluginManager
41
+
42
+
43
+ logger = logging.getLogger("eodag.search_result")
37
44
 
38
45
 
39
46
  class SearchResult(UserList[EOProduct]):
@@ -211,6 +218,153 @@ class SearchResult(UserList[EOProduct]):
211
218
 
212
219
  return super().extend(other)
213
220
 
221
+ @classmethod
222
+ def _from_stac_item(
223
+ cls, feature: dict[str, Any], plugins_manager: PluginManager
224
+ ) -> SearchResult:
225
+ """Create a SearchResult from a STAC item.
226
+
227
+ :param feature: A STAC item as a dictionary
228
+ :param plugins_manager: The EODAG plugin manager instance
229
+ :returns: A SearchResult containing the EOProduct(s) created from the STAC item
230
+ """
231
+ # Try importing from EODAG Server
232
+ if results := _import_stac_item_from_eodag_server(feature, plugins_manager):
233
+ return results
234
+
235
+ # try importing from a known STAC provider
236
+ if results := _import_stac_item_from_known_provider(feature, plugins_manager):
237
+ return results
238
+
239
+ # try importing from an unknown STAC provider
240
+ return _import_stac_item_from_unknown_provider(feature, plugins_manager)
241
+
242
+
243
+ def _import_stac_item_from_eodag_server(
244
+ feature: dict[str, Any], plugins_manager: PluginManager
245
+ ) -> Optional[SearchResult]:
246
+ """Import a STAC item from EODAG Server.
247
+
248
+ :param feature: A STAC item as a dictionary
249
+ :param plugins_manager: The EODAG plugin manager instance
250
+ :returns: A SearchResult containing the EOProduct(s) created from the STAC item
251
+ """
252
+ provider = None
253
+ if backends := feature["properties"].get("federation:backends"):
254
+ provider = backends[0]
255
+ elif providers := feature["properties"].get("providers"):
256
+ provider = providers[0].get("name")
257
+ if provider is not None:
258
+ logger.debug("Trying to import STAC item from EODAG Server")
259
+ # assets coming from a STAC provider
260
+ assets = {
261
+ k: v["alternate"]["origin"]
262
+ for k, v in feature.get("assets", {}).items()
263
+ if k not in ("thumbnail", "downloadLink")
264
+ and "origin" in v.get("alternate", {})
265
+ }
266
+ if assets:
267
+ updated_item = {**feature, **{"assets": assets}}
268
+ else:
269
+ # item coming from a non-STAC provider
270
+ updated_item = {**feature}
271
+ download_link = (
272
+ feature.get("assets", {})
273
+ .get("downloadLink", {})
274
+ .get("alternate", {})
275
+ .get("origin", {})
276
+ .get("href")
277
+ )
278
+ if download_link:
279
+ updated_item["assets"] = {}
280
+ updated_item["links"] = [{"rel": "self", "href": download_link}]
281
+ else:
282
+ updated_item = {}
283
+ try:
284
+ eo_product = unregistered_product_from_item(
285
+ updated_item, GENERIC_STAC_PROVIDER, plugins_manager
286
+ )
287
+ except MisconfiguredError:
288
+ eo_product = None
289
+ if eo_product is not None:
290
+ eo_product.provider = provider
291
+ eo_product._register_downloader_from_manager(plugins_manager)
292
+ return SearchResult([eo_product])
293
+ return None
294
+
295
+
296
+ def _import_stac_item_from_known_provider(
297
+ feature: dict[str, Any], plugins_manager: PluginManager
298
+ ) -> Optional[SearchResult]:
299
+ """Import a STAC item from an already-configured STAC provider.
300
+
301
+ :param feature: A STAC item as a dictionary
302
+ :param plugins_manager: The EODAG plugin manager instance
303
+ :returns: A SearchResult containing the EOProduct(s) created from the STAC item
304
+ """
305
+ item_hrefs = [f for f in feature.get("links", []) if f.get("rel") == "self"]
306
+ item_href = item_hrefs[0]["href"] if len(item_hrefs) > 0 else None
307
+ imported_products = SearchResult([])
308
+ for search_plugin in plugins_manager.get_search_plugins():
309
+ # only try STAC search plugins
310
+ if (
311
+ search_plugin.config.type in STAC_SEARCH_PLUGINS
312
+ and search_plugin.provider != GENERIC_STAC_PROVIDER
313
+ and hasattr(search_plugin, "normalize_results")
314
+ ):
315
+ provider_base_url = search_plugin.config.api_endpoint.removesuffix(
316
+ "/search"
317
+ )
318
+ # compare the item href with the provider base URL
319
+ if item_href and item_href.startswith(provider_base_url):
320
+ products = search_plugin.normalize_results([feature])
321
+ if len(products) == 0 or len(products[0].assets) == 0:
322
+ continue
323
+ logger.debug(
324
+ "Trying to import STAC item from %s", search_plugin.provider
325
+ )
326
+ eo_product = products[0]
327
+
328
+ configured_pts = [
329
+ k
330
+ for k, v in search_plugin.config.products.items()
331
+ if v.get("productType") == feature.get("collection")
332
+ ]
333
+ if len(configured_pts) > 0:
334
+ eo_product.product_type = configured_pts[0]
335
+ else:
336
+ eo_product.product_type = feature.get("collection")
337
+
338
+ eo_product._register_downloader_from_manager(plugins_manager)
339
+ imported_products.append(eo_product)
340
+ if len(imported_products) > 0:
341
+ return imported_products
342
+ return None
343
+
344
+
345
+ def _import_stac_item_from_unknown_provider(
346
+ feature: dict[str, Any], plugins_manager: PluginManager
347
+ ) -> SearchResult:
348
+ """Import a STAC item from an unknown STAC provider.
349
+
350
+ :param feature: A STAC item as a dictionary
351
+ :param plugins_manager: The EODAG plugin manager instance
352
+ :returns: A SearchResult containing the EOProduct(s) created from the STAC item
353
+ """
354
+ try:
355
+ logger.debug("Trying to import STAC item from unknown provider")
356
+ eo_product = unregistered_product_from_item(
357
+ feature, GENERIC_STAC_PROVIDER, plugins_manager
358
+ )
359
+ except MisconfiguredError:
360
+ pass
361
+ if eo_product is not None:
362
+ eo_product.product_type = feature.get("collection")
363
+ eo_product._register_downloader_from_manager(plugins_manager)
364
+ return SearchResult([eo_product])
365
+ else:
366
+ return SearchResult([])
367
+
214
368
 
215
369
  class RawSearchResult(UserList[dict[str, Any]]):
216
370
  """An object representing a collection of raw/unparsed search results obtained from a provider.
eodag/cli.py CHANGED
@@ -49,11 +49,12 @@ import sys
49
49
  import textwrap
50
50
  from importlib.metadata import metadata
51
51
  from typing import TYPE_CHECKING, Any, Mapping
52
+ from urllib.parse import parse_qs
52
53
 
53
54
  import click
54
55
 
55
- from eodag.api.core import EODataAccessGateway
56
- from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE, parse_qs
56
+ from eodag.api.core import EODataAccessGateway, SearchResult
57
+ from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE
57
58
  from eodag.utils.exceptions import NoMatchingProductType, UnsupportedProvider
58
59
  from eodag.utils.logging import setup_logging
59
60
 
@@ -109,14 +110,15 @@ class MutuallyExclusiveOption(click.Option):
109
110
  """Raise error or use parent handle_parse_result()"""
110
111
  if self.mutually_exclusive.intersection(opts) and self.name in opts:
111
112
  raise click.UsageError(
112
- "Illegal usage: `{}` is mutually exclusive with "
113
- "arguments `{}`.".format(self.name, ", ".join(self.mutually_exclusive))
113
+ "Illegal usage: `{}` is mutually exclusive with arguments `{}`.".format(
114
+ self.name, ", ".join(self.mutually_exclusive)
115
+ )
114
116
  )
115
117
 
116
118
  return super(MutuallyExclusiveOption, self).handle_parse_result(ctx, opts, args)
117
119
 
118
120
 
119
- @click.group()
121
+ @click.group(chain=True)
120
122
  @click.option(
121
123
  "-v",
122
124
  "--verbose",
@@ -212,6 +214,18 @@ def version() -> None:
212
214
  "-S", "--sensorType", help="Search for products matching this type of sensor"
213
215
  )
214
216
  @click.option("--id", help="Search for the product identified by this id")
217
+ @click.option(
218
+ "--locations",
219
+ type=str,
220
+ help="Custom query-string argument(s) to select locations. "
221
+ "Format :'key1=value1&key2=value2'. Example: --locations country=FRA&continent=Africa",
222
+ )
223
+ @click.option(
224
+ "-q",
225
+ "--query",
226
+ type=str,
227
+ help="Custom query-string argument(s). Format :'key1=value1&key2=value2'",
228
+ )
215
229
  @click.option(
216
230
  "--cruncher",
217
231
  type=click.Choice(CRUNCHERS),
@@ -262,19 +276,9 @@ def version() -> None:
262
276
  @click.option(
263
277
  "--count",
264
278
  is_flag=True,
265
- help="Whether to run a query with a count request or not.",
266
- )
267
- @click.option(
268
- "--locations",
269
- type=str,
270
- help="Custom query-string argument(s) to select locations. "
271
- "Format :'key1=value1&key2=value2'. Example: --locations country=FRA&continent=Africa",
272
- )
273
- @click.option(
274
- "-q",
275
- "--query",
276
- type=str,
277
- help="Custom query-string argument(s). Format :'key1=value1&key2=value2'",
279
+ help="Make a count request together with search (Enabling count will significantly "
280
+ "slow down search requests for some providers, and might be unavailable for some"
281
+ "others).",
278
282
  )
279
283
  @click.pass_context
280
284
  def search_crunch(ctx: Context, **kwargs: Any) -> None:
@@ -407,6 +411,7 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
407
411
  storage_filepath += ".geojson"
408
412
  result_storage = gateway.serialize(results, filename=storage_filepath)
409
413
  click.echo("Results stored at '{}'".format(result_storage))
414
+ ctx.obj["search_results"] = results
410
415
 
411
416
 
412
417
  @eodag.command(name="list", help="List supported product types")
@@ -528,12 +533,26 @@ def discover_pt(ctx: Context, **kwargs: Any) -> None:
528
533
  click.echo("Results stored at '{}'".format(storage_filepath))
529
534
 
530
535
 
531
- @eodag.command(help="Download a list of products from a serialized search result")
536
+ @eodag.command(
537
+ help="""Download a list of products from a serialized search result or STAC items URLs/paths
538
+
539
+ Examples:
540
+
541
+ eodag download --search-results /path/to/search_results.geojson
542
+
543
+ eodag download --stac-item https://example.com/stac/item1.json --stac-item /path/to/item2.json
544
+ """,
545
+ )
532
546
  @click.option(
533
547
  "--search-results",
534
548
  type=click.Path(exists=True, dir_okay=False),
535
549
  help="Path to a serialized search result",
536
550
  )
551
+ @click.option(
552
+ "--stac-item",
553
+ multiple=True,
554
+ help="URL/path of a STAC item to download (multiple values accepted)",
555
+ )
537
556
  @click.option(
538
557
  "-f",
539
558
  "--conf",
@@ -546,13 +565,20 @@ def discover_pt(ctx: Context, **kwargs: Any) -> None:
546
565
  show_default=False,
547
566
  help="Download only quicklooks of products instead full set of files",
548
567
  )
568
+ @click.option(
569
+ "--output-dir",
570
+ type=click.Path(dir_okay=True, file_okay=False),
571
+ help="Products or quicklooks download directory (Default: local temporary directory)",
572
+ )
549
573
  @click.pass_context
550
574
  def download(ctx: Context, **kwargs: Any) -> None:
551
575
  """Download a bunch of products from a serialized search result"""
552
576
  search_result_path = kwargs.pop("search_results")
553
- if not search_result_path:
577
+ stac_items = kwargs.pop("stac_item")
578
+ search_results = ctx.obj.get("search_results")
579
+ if not search_result_path and not stac_items and search_results is None:
554
580
  with click.Context(download) as ctx:
555
- click.echo("Nothing to do (no search results file provided)")
581
+ click.echo("Nothing to do (no search results file or stac item provided)")
556
582
  click.echo(download.get_help(ctx))
557
583
  sys.exit(1)
558
584
  setup_logging(verbose=ctx.obj["verbosity"])
@@ -561,25 +587,24 @@ def download(ctx: Context, **kwargs: Any) -> None:
561
587
  conf_file = click.format_filename(conf_file)
562
588
 
563
589
  satim_api = EODataAccessGateway(user_conf_file_path=conf_file)
564
- search_results = satim_api.deserialize(search_result_path)
565
590
 
591
+ search_results = search_results or SearchResult([])
592
+ if search_result_path:
593
+ search_results.extend(satim_api.deserialize_and_register(search_result_path))
594
+ if stac_items:
595
+ search_results.extend(satim_api.import_stac_items(list(stac_items)))
596
+
597
+ output_dir = kwargs.pop("output_dir")
566
598
  get_quicklooks = kwargs.pop("quicklooks")
599
+
567
600
  if get_quicklooks:
601
+ # Download only quicklooks
568
602
  click.echo(
569
603
  "Flag 'quicklooks' specified, downloading only quicklooks of products"
570
604
  )
571
605
 
572
606
  for idx, product in enumerate(search_results):
573
- if product.downloader is None:
574
- downloader = satim_api._plugins_manager.get_download_plugin(product)
575
- auth = product.downloader_auth
576
- if auth is None:
577
- auth = satim_api._plugins_manager.get_auth_plugin(
578
- downloader, product
579
- )
580
- search_results[idx].register_downloader(downloader, auth)
581
-
582
- downloaded_file = product.get_quicklook()
607
+ downloaded_file = product.get_quicklook(output_dir=output_dir)
583
608
  if not downloaded_file:
584
609
  click.echo(
585
610
  "A quicklook may have been downloaded but we cannot locate it. "
@@ -589,18 +614,8 @@ def download(ctx: Context, **kwargs: Any) -> None:
589
614
  click.echo("Downloaded {}".format(downloaded_file))
590
615
 
591
616
  else:
592
- # register downloader
593
- for idx, product in enumerate(search_results):
594
- if product.downloader is None:
595
- downloader = satim_api._plugins_manager.get_download_plugin(product)
596
- auth = product.downloader_auth
597
- if auth is None:
598
- auth = satim_api._plugins_manager.get_auth_plugin(
599
- downloader, product
600
- )
601
- search_results[idx].register_downloader(downloader, auth)
602
-
603
- downloaded_files = satim_api.download_all(search_results)
617
+ # Download products
618
+ downloaded_files = satim_api.download_all(search_results, output_dir=output_dir)
604
619
  if downloaded_files and len(downloaded_files) > 0:
605
620
  for downloaded_file in downloaded_files:
606
621
  if downloaded_file is None:
@@ -674,6 +689,7 @@ def serve_rest(
674
689
  setup_logging(verbose=ctx.obj["verbosity"])
675
690
  try:
676
691
  import uvicorn
692
+ import uvicorn.config
677
693
  except ImportError:
678
694
  raise ImportError(
679
695
  "Feature not available, please install eodag[server] or eodag[all]"
eodag/config.py CHANGED
@@ -46,6 +46,7 @@ from jsonpath_ng import JSONPath
46
46
  from eodag.api.product.metadata_mapping import mtd_cfg_as_conversion_and_querypath
47
47
  from eodag.utils import (
48
48
  HTTP_REQ_TIMEOUT,
49
+ STAC_SEARCH_PLUGINS,
49
50
  USER_AGENT,
50
51
  cached_yaml_load,
51
52
  cached_yaml_load_all,
@@ -451,6 +452,11 @@ class PluginConfig(yaml.YAMLObject):
451
452
  discover_queryables: PluginConfig.DiscoverQueryables
452
453
  #: :class:`~eodag.plugins.search.base.Search` The mapping between eodag metadata and the plugin specific metadata
453
454
  metadata_mapping: dict[str, Union[str, list[str]]]
455
+ #: :class:`~eodag.plugins.search.base.Search` :attr:`~eodag.config.PluginConfig.metadata_mapping` got from the given
456
+ #: product type
457
+ metadata_mapping_from_product: str
458
+ #: :class:`~eodag.plugins.search.base.Search` A mapping for the metadata of individual assets
459
+ assets_mapping: dict[str, dict[str, Any]]
454
460
  #: :class:`~eodag.plugins.search.base.Search` Parameters to remove from queryables
455
461
  remove_from_queryables: list[str]
456
462
  #: :class:`~eodag.plugins.search.base.Search` Parameters to be passed as is in the search url query string
@@ -819,12 +825,7 @@ def provider_config_init(
819
825
  if (
820
826
  stac_search_default_conf is not None
821
827
  and provider_config.search
822
- and provider_config.search.type
823
- in [
824
- "StacSearch",
825
- "StacListAssets",
826
- "StaticStacSearch",
827
- ]
828
+ and provider_config.search.type in STAC_SEARCH_PLUGINS
828
829
  ):
829
830
  # search config set to stac defaults overriden with provider config
830
831
  per_provider_stac_provider_config = deepcopy(stac_search_default_conf)
@@ -23,6 +23,7 @@ import string
23
23
  from datetime import datetime, timedelta, timezone
24
24
  from random import SystemRandom
25
25
  from typing import TYPE_CHECKING, Any, Optional
26
+ from urllib.parse import parse_qs, urlparse
26
27
 
27
28
  import jwt
28
29
  import requests
@@ -34,9 +35,7 @@ from eodag.utils import (
34
35
  DEFAULT_TOKEN_EXPIRATION_MARGIN,
35
36
  HTTP_REQ_TIMEOUT,
36
37
  USER_AGENT,
37
- parse_qs,
38
38
  repeatfunc,
39
- urlparse,
40
39
  )
41
40
  from eodag.utils.exceptions import (
42
41
  AuthenticationError,