eodag 3.9.1__py3-none-any.whl → 4.0.0a1__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.
Files changed (71) hide show
  1. eodag/api/core.py +378 -419
  2. eodag/api/product/__init__.py +3 -3
  3. eodag/api/product/_product.py +68 -40
  4. eodag/api/product/drivers/__init__.py +3 -5
  5. eodag/api/product/drivers/base.py +1 -18
  6. eodag/api/product/metadata_mapping.py +151 -215
  7. eodag/api/search_result.py +13 -7
  8. eodag/cli.py +72 -395
  9. eodag/config.py +46 -50
  10. eodag/plugins/apis/base.py +2 -2
  11. eodag/plugins/apis/ecmwf.py +20 -21
  12. eodag/plugins/apis/usgs.py +37 -33
  13. eodag/plugins/authentication/aws_auth.py +36 -1
  14. eodag/plugins/authentication/base.py +18 -3
  15. eodag/plugins/authentication/sas_auth.py +15 -0
  16. eodag/plugins/crunch/filter_date.py +3 -3
  17. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  18. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  19. eodag/plugins/download/aws.py +45 -41
  20. eodag/plugins/download/base.py +13 -14
  21. eodag/plugins/download/http.py +65 -65
  22. eodag/plugins/manager.py +28 -29
  23. eodag/plugins/search/__init__.py +3 -4
  24. eodag/plugins/search/base.py +128 -77
  25. eodag/plugins/search/build_search_result.py +105 -107
  26. eodag/plugins/search/cop_marine.py +44 -47
  27. eodag/plugins/search/csw.py +33 -33
  28. eodag/plugins/search/qssearch.py +335 -354
  29. eodag/plugins/search/stac_list_assets.py +1 -1
  30. eodag/plugins/search/static_stac_search.py +31 -31
  31. eodag/resources/{product_types.yml → collections.yml} +2353 -2429
  32. eodag/resources/ext_collections.json +1 -0
  33. eodag/resources/ext_product_types.json +1 -1
  34. eodag/resources/providers.yml +2432 -2714
  35. eodag/resources/stac_provider.yml +46 -90
  36. eodag/types/queryables.py +55 -91
  37. eodag/types/search_args.py +1 -1
  38. eodag/utils/__init__.py +94 -21
  39. eodag/utils/exceptions.py +6 -6
  40. eodag/utils/free_text_search.py +3 -3
  41. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/METADATA +11 -88
  42. eodag-4.0.0a1.dist-info/RECORD +92 -0
  43. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
  44. eodag/plugins/authentication/oauth.py +0 -60
  45. eodag/plugins/download/creodias_s3.py +0 -64
  46. eodag/plugins/download/s3rest.py +0 -351
  47. eodag/plugins/search/data_request_search.py +0 -565
  48. eodag/resources/stac.yml +0 -294
  49. eodag/resources/stac_api.yml +0 -2105
  50. eodag/rest/__init__.py +0 -24
  51. eodag/rest/cache.py +0 -70
  52. eodag/rest/config.py +0 -67
  53. eodag/rest/constants.py +0 -26
  54. eodag/rest/core.py +0 -764
  55. eodag/rest/errors.py +0 -210
  56. eodag/rest/server.py +0 -604
  57. eodag/rest/server.wsgi +0 -6
  58. eodag/rest/stac.py +0 -1032
  59. eodag/rest/templates/README +0 -1
  60. eodag/rest/types/__init__.py +0 -18
  61. eodag/rest/types/collections_search.py +0 -44
  62. eodag/rest/types/eodag_search.py +0 -386
  63. eodag/rest/types/queryables.py +0 -174
  64. eodag/rest/types/stac_search.py +0 -272
  65. eodag/rest/utils/__init__.py +0 -207
  66. eodag/rest/utils/cql_evaluate.py +0 -119
  67. eodag/rest/utils/rfc3339.py +0 -64
  68. eodag-3.9.1.dist-info/RECORD +0 -115
  69. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
  70. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
  71. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/top_level.txt +0 -0
@@ -57,6 +57,7 @@ from eodag.utils import (
57
57
  DEFAULT_SEARCH_TIMEOUT,
58
58
  deepcopy,
59
59
  dict_items_recursive_sort,
60
+ format_string,
60
61
  get_geometry_from_various,
61
62
  )
62
63
  from eodag.utils.cache import instance_cached_method
@@ -144,7 +145,6 @@ ECMWF_KEYWORDS = {
144
145
  COP_DS_KEYWORDS = {
145
146
  "aerosol_type",
146
147
  "altitude",
147
- "product_type",
148
148
  "band",
149
149
  "cdr_type",
150
150
  "data_format",
@@ -176,6 +176,7 @@ COP_DS_KEYWORDS = {
176
176
  "pressure_level",
177
177
  "processing_level",
178
178
  "processing_type",
179
+ "product_type",
179
180
  "product_version",
180
181
  "quantity",
181
182
  "rcm",
@@ -200,9 +201,9 @@ COP_DS_KEYWORDS = {
200
201
 
201
202
  ALLOWED_KEYWORDS = ECMWF_KEYWORDS | COP_DS_KEYWORDS
202
203
 
203
- END = "completionTimeFromAscendingNode"
204
+ END = "end_datetime"
204
205
 
205
- START = "startTimeFromAscendingNode"
206
+ START = "start_datetime"
206
207
 
207
208
 
208
209
  def ecmwf_mtd() -> dict[str, Any]:
@@ -305,11 +306,11 @@ def append_time(input_date: date, time: Optional[str]) -> datetime:
305
306
  """
306
307
  Parses a time string in format HHMM and appends it to a date.
307
308
 
308
- if the time string is in format HH:MM we convert it to HHMM
309
+ if the time string is in format HH:MM or HH_MM we convert it to HHMM
309
310
  """
310
311
  if not time:
311
312
  time = "0000"
312
- time = time.replace(":", "")
313
+ time = re.sub(":|_", "", time)
313
314
  if time == "2400":
314
315
  time = "0000"
315
316
  dt = datetime.combine(input_date, datetime.strptime(time, "%H%M").time())
@@ -435,14 +436,15 @@ class ECMWFSearch(PostJsonSearch):
435
436
  used to parse metadata but that must not be included to the query
436
437
  * :attr:`~eodag.config.PluginConfig.end_date_excluded` (``bool``): Set to `False` if
437
438
  provider does not include end date to search
439
+ * :attr:`~eodag.config.PluginConfig.dates_required` (``bool``): if date parameters are mandatory in the request
438
440
  * :attr:`~eodag.config.PluginConfig.discover_queryables`
439
441
  (:class:`~eodag.config.PluginConfig.DiscoverQueryables`): configuration to fetch the queryables from a
440
442
  provider queryables endpoint; It has the following keys:
441
443
 
442
444
  * :attr:`~eodag.config.PluginConfig.DiscoverQueryables.fetch_url` (``str``): url to fetch the queryables valid
443
- for all product types
444
- * :attr:`~eodag.config.PluginConfig.DiscoverQueryables.product_type_fetch_url` (``str``): url to fetch the
445
- queryables for a specific product type
445
+ for all collections
446
+ * :attr:`~eodag.config.PluginConfig.DiscoverQueryables.collection_fetch_url` (``str``): url to fetch the
447
+ queryables for a specific collection
446
448
  * :attr:`~eodag.config.PluginConfig.DiscoverQueryables.constraints_url` (``str``): url of the constraint file
447
449
  used to build queryables
448
450
  """
@@ -453,10 +455,10 @@ class ECMWFSearch(PostJsonSearch):
453
455
  **{
454
456
  "id": "$.id",
455
457
  "title": "$.id",
456
- "storageStatus": OFFLINE_STATUS,
457
- "downloadLink": "$.null",
458
+ "order:status": OFFLINE_STATUS,
459
+ "eodag:download_link": "$.null",
458
460
  "geometry": ["feature", "$.geometry"],
459
- "defaultGeometry": "POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90))",
461
+ "eodag:default_geometry": "POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90))",
460
462
  },
461
463
  **config.metadata_mapping,
462
464
  }
@@ -499,10 +501,10 @@ class ECMWFSearch(PostJsonSearch):
499
501
  :param kwargs: keyword arguments to be used in the search
500
502
  :returns: list of products and number of products (optional)
501
503
  """
502
- product_type = prep.product_type
503
- if not product_type:
504
- product_type = kwargs.get("productType")
505
- kwargs = self._preprocess_search_params(kwargs, product_type)
504
+ collection = prep.collection
505
+ if not collection:
506
+ collection = kwargs.get("collection")
507
+ kwargs = self._preprocess_search_params(kwargs, collection)
506
508
  result, num_items = super().query(prep, **kwargs)
507
509
  if prep.count and not num_items:
508
510
  num_items = 1
@@ -514,11 +516,11 @@ class ECMWFSearch(PostJsonSearch):
514
516
  super().clear()
515
517
 
516
518
  def build_query_string(
517
- self, product_type: str, query_dict: dict[str, Any]
519
+ self, collection: str, query_dict: dict[str, Any]
518
520
  ) -> tuple[dict[str, Any], str]:
519
521
  """Build The query string using the search parameters
520
522
 
521
- :param product_type: product type id
523
+ :param collection: collection id
522
524
  :param query_dict: keyword arguments to be used in the query string
523
525
  :return: formatted query params and encode query string
524
526
  """
@@ -533,21 +535,21 @@ class ECMWFSearch(PostJsonSearch):
533
535
  ordered_kwargs.update(query_dict)
534
536
 
535
537
  return super().build_query_string(
536
- product_type=product_type, query_dict=ordered_kwargs
538
+ collection=collection, query_dict=ordered_kwargs
537
539
  )
538
540
 
539
541
  def _preprocess_search_params(
540
- self, params: dict[str, Any], product_type: Optional[str]
542
+ self, params: dict[str, Any], collection: Optional[str]
541
543
  ) -> dict[str, Any]:
542
544
  """Preprocess search parameters before making a request to the CDS API.
543
545
 
544
546
  This method is responsible for checking and updating the provided search parameters
545
- to ensure that required parameters like 'productType', 'startTimeFromAscendingNode',
546
- 'completionTimeFromAscendingNode', and 'geometry' are properly set. If not specified
547
+ to ensure that required parameters like 'collection', 'start_datetime',
548
+ 'end_datetime', and 'geometry' are properly set. If not specified
547
549
  in the input parameters, default values or values from the configuration are used.
548
550
 
549
551
  :param params: Search parameters to be preprocessed.
550
- :param product_type: (optional) product type id
552
+ :param collection: (optional) collection id
551
553
  """
552
554
 
553
555
  _dc_qs = params.get("_dc_qs")
@@ -574,7 +576,7 @@ class ECMWFSearch(PostJsonSearch):
574
576
  # dates
575
577
  # check if default dates have to be added
576
578
  if getattr(self.config, "dates_required", False):
577
- self._check_date_params(params, product_type)
579
+ self._check_date_params(params, collection)
578
580
 
579
581
  # adapt end date if it is midnight
580
582
  if END in params:
@@ -611,65 +613,61 @@ class ECMWFSearch(PostJsonSearch):
611
613
  return params
612
614
 
613
615
  def _check_date_params(
614
- self, keywords: dict[str, Any], product_type: Optional[str]
616
+ self, keywords: dict[str, Any], collection: Optional[str]
615
617
  ) -> None:
616
618
  """checks if start and end date are present in the keywords and adds them if not"""
617
619
 
618
620
  if START and END in keywords:
619
621
  return
620
622
 
621
- product_type_conf = getattr(self.config, "metadata_mapping", {})
623
+ collection_conf = getattr(self.config, "metadata_mapping", {})
622
624
  if (
623
- product_type
624
- and product_type in self.config.products
625
- and "metadata_mapping" in self.config.products[product_type]
625
+ collection
626
+ and collection in self.config.products
627
+ and "metadata_mapping" in self.config.products[collection]
626
628
  ):
627
- product_type_conf = self.config.products[product_type]["metadata_mapping"]
629
+ collection_conf = self.config.products[collection]["metadata_mapping"]
628
630
 
629
631
  # start time given, end time missing
630
632
  if START in keywords:
631
633
  keywords[END] = (
632
634
  keywords[START]
633
- if END in product_type_conf
634
- else self.get_product_type_cfg_value(
635
- "missionEndDate", today().isoformat()
636
- )
635
+ if END in collection_conf
636
+ # else self.get_collection_cfg_value(
637
+ else self.get_collection_cfg_dates(None, today().isoformat())[1]
637
638
  )
638
639
  return
639
640
 
640
- if END in product_type_conf:
641
- mapping = product_type_conf[START]
641
+ if END in collection_conf:
642
+ mapping = collection_conf[START]
642
643
  if not isinstance(mapping, list):
643
- mapping = product_type_conf[END]
644
+ mapping = collection_conf[END]
644
645
  if isinstance(mapping, list):
645
646
  # if startTime is not given but other time params (e.g. year/month/(day)) are given,
646
647
  # no default date is required
647
648
  start, end = ecmwf_temporal_to_eodag(keywords)
648
649
  if start is None:
649
- keywords[START] = self.get_product_type_cfg_value(
650
- "missionStartDate", DEFAULT_MISSION_START_DATE
650
+ col_start, col_end = self.get_collection_cfg_dates(
651
+ DEFAULT_MISSION_START_DATE, today().isoformat()
651
652
  )
653
+ keywords[START] = col_start
652
654
  keywords[END] = (
653
- keywords[START]
654
- if END in product_type_conf
655
- else self.get_product_type_cfg_value(
656
- "missionEndDate", today().isoformat()
657
- )
655
+ keywords[START] if END in collection_conf else col_end
658
656
  )
659
657
  else:
660
658
  keywords[START] = start
661
659
  keywords[END] = end
662
660
 
663
- def _get_product_type_queryables(
664
- self, product_type: Optional[str], alias: Optional[str], filters: dict[str, Any]
661
+ def _get_collection_queryables(
662
+ self, collection: Optional[str], alias: Optional[str], filters: dict[str, Any]
665
663
  ) -> QueryablesDict:
666
664
  """Override to set additional_properties to false."""
667
665
  default_values: dict[str, Any] = deepcopy(
668
- getattr(self.config, "products", {}).get(product_type, {})
666
+ getattr(self.config, "products", {}).get(collection, {})
669
667
  )
670
668
  default_values.pop("metadata_mapping", None)
671
669
 
672
- filters["productType"] = product_type
670
+ filters["collection"] = collection
673
671
  queryables = self.discover_queryables(**{**default_values, **filters}) or {}
674
672
 
675
673
  return QueryablesDict(additional_properties=False, **queryables)
@@ -679,13 +677,13 @@ class ECMWFSearch(PostJsonSearch):
679
677
  ) -> Optional[dict[str, Annotated[Any, FieldInfo]]]:
680
678
  """Fetch queryables list from provider using its constraints file
681
679
 
682
- :param kwargs: additional filters for queryables (`productType` and other search
680
+ :param kwargs: additional filters for queryables (`collection` and other search
683
681
  arguments)
684
682
  :returns: fetched queryable parameters dict
685
683
  """
686
- product_type = kwargs.pop("productType")
684
+ collection = kwargs.pop("collection")
687
685
 
688
- pt_config = self.get_product_type_def_params(product_type)
686
+ pt_config = self.get_collection_def_params(collection)
689
687
 
690
688
  default_values = deepcopy(pt_config)
691
689
  default_values.pop("metadata_mapping", None)
@@ -698,7 +696,7 @@ class ECMWFSearch(PostJsonSearch):
698
696
 
699
697
  # extract default datetime
700
698
  processed_filters = self._preprocess_search_params(
701
- deepcopy(filters), product_type
699
+ deepcopy(filters), collection
702
700
  )
703
701
 
704
702
  constraints_url = format_metadata(
@@ -714,7 +712,7 @@ class ECMWFSearch(PostJsonSearch):
714
712
  form: list[dict[str, Any]] = self._fetch_data(form_url)
715
713
 
716
714
  formated_filters = self.format_as_provider_keyword(
717
- product_type, processed_filters
715
+ collection, processed_filters
718
716
  )
719
717
  # we re-apply kwargs input to consider override of year, month, day and time.
720
718
  for k, v in {**default_values, **kwargs}.items():
@@ -762,15 +760,15 @@ class ECMWFSearch(PostJsonSearch):
762
760
  else:
763
761
  values_url = getattr(self.config, "available_values_url", "")
764
762
  if not values_url:
765
- return self.queryables_from_metadata_mapping(product_type)
763
+ return self.queryables_from_metadata_mapping(collection)
766
764
  if "{" in values_url:
767
- values_url = values_url.format(**filters)
765
+ values_url = format_string(None, values_url, **filters)
768
766
  data = self._fetch_data(values_url)
769
767
  available_values = data["constraints"]
770
768
  required_keywords = data.get("required", [])
771
769
 
772
770
  # To check if all keywords are queryable parameters, we check if they are in the
773
- # available values or the product type config (available values calculated from the
771
+ # available values or the collection config (available values calculated from the
774
772
  # constraints might not include all queryables)
775
773
  for keyword in filters:
776
774
  if (
@@ -781,6 +779,7 @@ class ECMWFSearch(PostJsonSearch):
781
779
  START,
782
780
  END,
783
781
  "geom",
782
+ "geometry",
784
783
  }
785
784
  and keyword not in [f["name"] for f in form]
786
785
  and keyword.removeprefix(ECMWF_PREFIX)
@@ -1021,7 +1020,9 @@ class ECMWFSearch(PostJsonSearch):
1021
1020
  if default and prop.get("type") == "string" and isinstance(default, list):
1022
1021
  default = ",".join(default)
1023
1022
 
1024
- is_required = bool(element.get("required"))
1023
+ is_required = bool(element.get("required")) and bool(
1024
+ available_values.get(name)
1025
+ )
1025
1026
  if is_required:
1026
1027
  required_list.append(name)
1027
1028
 
@@ -1075,32 +1076,32 @@ class ECMWFSearch(PostJsonSearch):
1075
1076
  return queryables
1076
1077
 
1077
1078
  def format_as_provider_keyword(
1078
- self, product_type: str, properties: dict[str, Any]
1079
+ self, collection: str, properties: dict[str, Any]
1079
1080
  ) -> dict[str, Any]:
1080
1081
  """Return provider equivalent keyword names from EODAG keywords.
1081
1082
 
1082
- :param product_type: product type id
1083
+ :param collection: collection id
1083
1084
  :param properties: dict of properties to be formatted
1084
1085
  :return: dict of formatted properties
1085
1086
  """
1086
- properties["productType"] = product_type
1087
+ properties["collection"] = collection
1087
1088
 
1088
- # provider product type specific conf
1089
- product_type_def_params = self.get_product_type_def_params(
1090
- product_type, format_variables=properties
1089
+ # provider collection specific conf
1090
+ collection_def_params = self.get_collection_def_params(
1091
+ collection, format_variables=properties
1091
1092
  )
1092
1093
 
1093
- # Add to the query, the queryable parameters set in the provider product type definition
1094
+ # Add to the query, the queryable parameters set in the provider collection definition
1094
1095
  properties.update(
1095
1096
  {
1096
1097
  k: v
1097
- for k, v in product_type_def_params.items()
1098
+ for k, v in collection_def_params.items()
1098
1099
  if k not in properties.keys()
1099
1100
  and k in self.config.metadata_mapping.keys()
1100
1101
  and isinstance(self.config.metadata_mapping[k], list)
1101
1102
  }
1102
1103
  )
1103
- qp, _ = self.build_query_string(product_type, properties)
1104
+ qp, _ = self.build_query_string(collection, properties)
1104
1105
 
1105
1106
  return qp
1106
1107
 
@@ -1133,7 +1134,7 @@ class ECMWFSearch(PostJsonSearch):
1133
1134
  :returns: list of single :class:`~eodag.api.product._product.EOProduct`
1134
1135
  """
1135
1136
 
1136
- product_type = kwargs.get("productType")
1137
+ collection = kwargs.get("collection")
1137
1138
 
1138
1139
  result = results[0]
1139
1140
 
@@ -1151,27 +1152,27 @@ class ECMWFSearch(PostJsonSearch):
1151
1152
 
1152
1153
  if result:
1153
1154
  properties = result
1154
- properties.update(result.pop("request_params", None) or {})
1155
+ properties.update(result.pop("eodag:request_params", None) or {})
1155
1156
 
1156
1157
  properties = {k: v for k, v in properties.items() if not k.startswith("__")}
1157
1158
 
1158
1159
  properties["geometry"] = properties.get("area") or DEFAULT_GEOMETRY
1159
1160
 
1160
1161
  start, end = ecmwf_temporal_to_eodag(properties)
1161
- properties["startTimeFromAscendingNode"] = start
1162
- properties["completionTimeFromAscendingNode"] = end
1162
+ properties["start_datetime"] = start
1163
+ properties["end_datetime"] = end
1163
1164
 
1164
1165
  else:
1165
1166
  # use all available query_params to parse properties
1166
1167
  result_data: dict[str, Any] = {
1167
- **results.product_type_def_params,
1168
+ **results.collection_def_params,
1168
1169
  **sorted_unpaginated_qp,
1169
1170
  **{"qs": sorted_unpaginated_qp},
1170
1171
  }
1171
1172
 
1172
- # update result with product_type_def_params and search args if not None (and not auth)
1173
+ # update result with collection_def_params and search args if not None (and not auth)
1173
1174
  kwargs.pop("auth", None)
1174
- result_data.update(results.product_type_def_params)
1175
+ result_data.update(results.collection_def_params)
1175
1176
  result_data = {
1176
1177
  **result_data,
1177
1178
  **{k: v for k, v in kwargs.items() if v is not None},
@@ -1186,17 +1187,18 @@ class ECMWFSearch(PostJsonSearch):
1186
1187
  query_hash = hashlib.sha1(str(result_data).encode("UTF-8")).hexdigest()
1187
1188
 
1188
1189
  properties["title"] = properties["id"] = (
1189
- (product_type or kwargs.get("dataset", self.provider)).upper()
1190
+ (collection or kwargs.get("dataset", self.provider)).upper()
1190
1191
  + "_ORDERABLE_"
1191
1192
  + query_hash
1192
1193
  )
1193
- # use product_type_config as default properties
1194
- product_type_config = getattr(self.config, "product_type_config", {})
1195
- properties = dict(product_type_config, **properties)
1194
+
1195
+ # collection alias (required by opentelemetry-instrumentation-eodag)
1196
+ if alias := getattr(self.config, "collection_config", {}).get("alias"):
1197
+ properties["eodag:alias"] = alias
1196
1198
 
1197
1199
  qs = geojson.dumps(sorted_unpaginated_qp)
1198
1200
 
1199
- # used by server mode to generate downloadlink href
1201
+ # used by server mode to generate eodag:download_link href
1200
1202
  # TODO: to remove once the legacy server is removed
1201
1203
  properties["_dc_qs"] = quote_plus(qs)
1202
1204
 
@@ -1245,12 +1247,12 @@ def _check_id(product: EOProduct) -> EOProduct:
1245
1247
  if not on_response_mm:
1246
1248
  return product
1247
1249
 
1248
- logger.debug(f"Update product properties using given orderId {product_id}")
1250
+ logger.debug(f"Update product properties using given eodag:order_id {product_id}")
1249
1251
  on_response_mm_jsonpath = mtd_cfg_as_conversion_and_querypath(
1250
1252
  on_response_mm,
1251
1253
  )
1252
1254
  properties_update = properties_from_json(
1253
- {}, {**on_response_mm_jsonpath, **{"orderId": (None, product_id)}}
1255
+ {}, {**on_response_mm_jsonpath, **{"eodag:order_id": (None, product_id)}}
1254
1256
  )
1255
1257
  product.properties.update(
1256
1258
  {k: v for k, v in properties_update.items() if v != NOT_AVAILABLE}
@@ -1263,7 +1265,7 @@ def _check_id(product: EOProduct) -> EOProduct:
1263
1265
  product.downloader._order_status(product=product, auth=auth) # type: ignore
1264
1266
  # when a NotAvailableError is catched, it means the product is not ready and still needs to be polled
1265
1267
  except NotAvailableError:
1266
- product.properties["storageStatus"] = STAGING_STATUS
1268
+ product.properties["order:status"] = STAGING_STATUS
1267
1269
  except Exception as e:
1268
1270
  if (
1269
1271
  isinstance(e, DownloadError) or isinstance(e, ValidationError)
@@ -1275,16 +1277,16 @@ def _check_id(product: EOProduct) -> EOProduct:
1275
1277
 
1276
1278
  # update product id
1277
1279
  product.properties["id"] = product_id
1278
- # update product type if needed
1279
- if product.product_type is None:
1280
- product.product_type = product.properties.get("ecmwf:dataset")
1280
+ # update collection if needed
1281
+ if product.collection is None:
1282
+ product.collection = product.properties.get("ecmwf:dataset")
1281
1283
  # update product title
1282
1284
  product.properties["title"] = (
1283
- (product.product_type or product.provider).upper() + "_" + product_id
1285
+ (product.collection or product.provider).upper() + "_" + product_id
1284
1286
  )
1285
- # use NOT_AVAILABLE as fallback product_type to avoid using guess_product_type
1286
- if product.product_type is None:
1287
- product.product_type = NOT_AVAILABLE
1287
+ # use NOT_AVAILABLE as fallback collection to avoid using guess_collection
1288
+ if product.collection is None:
1289
+ product.collection = NOT_AVAILABLE
1288
1290
 
1289
1291
  return product
1290
1292
 
@@ -1363,15 +1365,15 @@ class MeteoblueSearch(ECMWFSearch):
1363
1365
  return [response.json()]
1364
1366
 
1365
1367
  def build_query_string(
1366
- self, product_type: str, query_dict: dict[str, Any]
1368
+ self, collection: str, query_dict: dict[str, Any]
1367
1369
  ) -> tuple[dict[str, Any], str]:
1368
1370
  """Build The query string using the search parameters
1369
1371
 
1370
- :param product_type: product type id
1372
+ :param collection: collection id
1371
1373
  :param query_dict: keyword arguments to be used in the query string
1372
1374
  :return: formatted query params and encode query string
1373
1375
  """
1374
- return QueryStringSearch.build_query_string(self, product_type, query_dict)
1376
+ return QueryStringSearch.build_query_string(self, collection, query_dict)
1375
1377
 
1376
1378
  def normalize_results(self, results, **kwargs):
1377
1379
  """Build :class:`~eodag.api.product._product.EOProduct` from provider result
@@ -1381,7 +1383,7 @@ class MeteoblueSearch(ECMWFSearch):
1381
1383
  :returns: list of single :class:`~eodag.api.product._product.EOProduct`
1382
1384
  """
1383
1385
 
1384
- product_type = kwargs.get("productType")
1386
+ collection = kwargs.get("collection")
1385
1387
 
1386
1388
  result = results[0]
1387
1389
 
@@ -1415,9 +1417,9 @@ class MeteoblueSearch(ECMWFSearch):
1415
1417
 
1416
1418
  query_hash = hashlib.sha1(str(qs).encode("UTF-8")).hexdigest()
1417
1419
 
1418
- # update result with product_type_def_params and search args if not None (and not auth)
1420
+ # update result with collection_def_params and search args if not None (and not auth)
1419
1421
  kwargs.pop("auth", None)
1420
- result.update(results.product_type_def_params)
1422
+ result.update(results.collection_def_params)
1421
1423
  result = dict(result, **{k: v for k, v in kwargs.items() if v is not None})
1422
1424
 
1423
1425
  # parse properties
@@ -1427,17 +1429,16 @@ class MeteoblueSearch(ECMWFSearch):
1427
1429
  discovery_config=getattr(self.config, "discover_metadata", {}),
1428
1430
  )
1429
1431
 
1430
- properties = {
1431
- # use product_type_config as default properties
1432
- **getattr(self.config, "product_type_config", {}),
1433
- **{ecmwf_format(k): v for k, v in parsed_properties.items()},
1434
- }
1432
+ properties = {ecmwf_format(k): v for k, v in parsed_properties.items()}
1433
+ # collection alias (required by opentelemetry-instrumentation-eodag)
1434
+ if alias := getattr(self.config, "collection_config", {}).get("alias"):
1435
+ properties["eodag:alias"] = alias
1435
1436
 
1436
1437
  def slugify(date_str: str) -> str:
1437
1438
  return date_str.split("T")[0].replace("-", "")
1438
1439
 
1439
1440
  # build product id
1440
- product_id = (product_type or self.provider).upper()
1441
+ product_id = (collection or self.provider).upper()
1441
1442
 
1442
1443
  start = properties.get(START, NOT_AVAILABLE)
1443
1444
  end = properties.get(END, NOT_AVAILABLE)
@@ -1451,17 +1452,14 @@ class MeteoblueSearch(ECMWFSearch):
1451
1452
 
1452
1453
  properties["id"] = properties["title"] = product_id
1453
1454
 
1454
- # used by server mode to generate downloadlink href
1455
+ # used by server mode to generate eodag:download_link href
1455
1456
  properties["_dc_qs"] = quote_plus(qs)
1456
1457
 
1457
1458
  product = EOProduct(
1458
1459
  provider=self.provider,
1459
- productType=product_type,
1460
+ collection=collection,
1460
1461
  properties=properties,
1461
1462
  )
1462
- # use product_type_config as default properties
1463
- product_type_config = getattr(self.config, "product_type_config", {})
1464
- product.properties = dict(product_type_config, **product.properties)
1465
1463
 
1466
1464
  return [
1467
1465
  product,
@@ -1507,9 +1505,9 @@ class WekeoECMWFSearch(ECMWFSearch):
1507
1505
  # id is order id (only letters and numbers) -> use parent normalize results
1508
1506
  return super().normalize_results(results, **kwargs)
1509
1507
 
1510
- # formating of orderLink requires access to the productType value.
1508
+ # formating of eodag:order_link requires access to the collection value.
1511
1509
  results.data = [
1512
- {**result, **results.product_type_def_params} for result in results
1510
+ {**result, **results.collection_def_params} for result in results
1513
1511
  ]
1514
1512
 
1515
1513
  normalized = QueryStringSearch.normalize_results(self, results, **kwargs)
@@ -1534,7 +1532,7 @@ class WekeoECMWFSearch(ECMWFSearch):
1534
1532
  dataset = "_".join(splitted_id[:-1])
1535
1533
  query_hash = splitted_id[-1]
1536
1534
  product.properties["title"] = product.properties["id"] = (
1537
- (product.product_type or dataset or self.provider).upper()
1535
+ (product.collection or dataset or self.provider).upper()
1538
1536
  + "_ORDERABLE_"
1539
1537
  + query_hash
1540
1538
  )