Skip to content

Commit 701c9e6

Browse files
authored
fix: wekeo_main order/download (#25)
1 parent 2078767 commit 701c9e6

6 files changed

Lines changed: 101 additions & 5 deletions

File tree

stac_fastapi/eodag/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ class Settings(ApiSettings):
4343
description=("Hide from clients items assets' origin URLs starting with URLs from the list"),
4444
)
4545

46+
auto_order_whitelist: Annotated[Union[str, list[str]], BeforeValidator(str2liststr)] = Field(
47+
default=[
48+
"wekeo_main",
49+
],
50+
description=("Do the order at the same time as the download"),
51+
)
52+
4653
fetch_providers: bool = Field(default=False, description="Fetch additional collections from all providers.")
4754

4855
download_base_url: str = Field(

stac_fastapi/eodag/extensions/data_download.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from stac_fastapi.types.extension import ApiExtension
3636
from stac_fastapi.types.search import APIRequest
3737

38+
from stac_fastapi.eodag.config import get_settings
3839
from stac_fastapi.eodag.errors import (
3940
DownloadError,
4041
MisconfiguredError,
@@ -119,6 +120,26 @@ def get_data(
119120
f"Could not find {item_id} item in {product_type} collection",
120121
f" for backend {federation_backend}.",
121122
)
123+
124+
settings = get_settings()
125+
auto_order_whitelist = settings.auto_order_whitelist
126+
if federation_backend in auto_order_whitelist:
127+
logger.info(f"Provider {federation_backend} is whitelisted, ordering product before download")
128+
129+
auth = product.downloader_auth.authenticate() if product.downloader_auth else None
130+
logger.debug(f"Polling product {product}")
131+
try:
132+
product.downloader.order(product=product, auth=auth) # type: ignore
133+
# when a NotAvailableError is catched, it means the product is not ready and still needs to be polled
134+
except NotAvailableError:
135+
product.properties["storageStatus"] = STAGING_STATUS
136+
except Exception as e:
137+
if (
138+
isinstance(e, DownloadError) or isinstance(e, ValidationError)
139+
) and "order status could not be checked" in e.args[0]:
140+
raise NotFoundError(f"Item {item_id} does not exist. Please order it first") from e
141+
raise NotFoundError(e) from e
142+
122143
auth = product.downloader_auth.authenticate() if product.downloader_auth else None
123144

124145
if product.downloader is None:

stac_fastapi/eodag/models/stac_metadata.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,12 @@ def create_stac_item(
289289
else None
290290
)
291291
# create assets only if product is not offline
292-
if product.properties.get("storageStatus", ONLINE_STATUS) != OFFLINE_STATUS:
292+
settings = get_settings()
293+
auto_order_whitelist = settings.auto_order_whitelist
294+
if (
295+
product.properties.get("storageStatus", ONLINE_STATUS) != OFFLINE_STATUS
296+
or product.provider in auto_order_whitelist
297+
):
293298
for k, v in product.assets.items():
294299
# TODO: download extension with origin link (make it optional ?)
295300
asset_model = model.model_validate(v)
@@ -339,7 +344,7 @@ def create_stac_item(
339344

340345
feature["stac_extensions"] = list(stac_extensions)
341346

342-
if extension_names:
347+
if extension_names and product.provider not in auto_order_whitelist:
343348
if "CollectionOrderExtension" in extension_names and (
344349
not product.properties.get("orderLink", False)
345350
or feature["properties"].get("order:status", "") != "orderable"

stac_fastapi/eodag/telemetry.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
)
3434
from opentelemetry.sdk.metrics._internal.export import PeriodicExportingMetricReader
3535
from opentelemetry.sdk.metrics._internal.view import View
36-
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
36+
37+
# See https://github.com/open-telemetry/opentelemetry-python/issues/4615 for the type ignore
38+
from opentelemetry.sdk.resources import Resource # type: ignore[attr-defined]
3739
from opentelemetry.sdk.trace import TracerProvider
3840
from opentelemetry.sdk.trace.export import BatchSpanProcessor
41+
from opentelemetry.semconv.resource import ResourceAttributes
3942

4043
if TYPE_CHECKING:
4144
from fastapi import FastAPI
@@ -128,7 +131,7 @@ def instrument_fastapi(
128131
) -> None:
129132
"""Instrument FastAPI app."""
130133
logger.info("Instrument FastAPI app")
131-
resource = Resource(attributes={SERVICE_NAME: "stac-fastapi-eodag"})
134+
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "stac-fastapi-eodag"})
132135
tracer_provider = create_tracer_provider(resource)
133136
meter_provider = create_meter_provider(resource)
134137
FastAPIInstrumentor.instrument_app(
@@ -141,7 +144,7 @@ def instrument_fastapi(
141144
def instrument_eodag(eodag_api: EODataAccessGateway):
142145
"""Instrument EODAG app"""
143146
logger.info("Instrument EODAG app")
144-
resource = Resource(attributes={SERVICE_NAME: "stac-fastapi-eodag"})
147+
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "stac-fastapi-eodag"})
145148
tracer_provider = create_tracer_provider(resource)
146149
meter_provider = create_meter_provider(resource)
147150
EODAGInstrumentor(eodag_api).instrument(

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,14 @@ def mock_base_stream_download_dict(mocker):
326326
return mocker.patch.object(Download, "_stream_download_dict")
327327

328328

329+
@pytest.fixture(scope="function")
330+
def mock_http_base_stream_download_dict(mocker):
331+
"""
332+
Mocks the `_stream_download_dict` method of the `Download` plugin.
333+
"""
334+
return mocker.patch.object(HTTPDownload, "_stream_download_dict")
335+
336+
329337
@pytest.fixture(scope="function")
330338
def mock_order(mocker):
331339
"""

tests/test_download.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919

2020
import os
2121

22+
from eodag import SearchResult
23+
from eodag.api.product import EOProduct
24+
from eodag.config import PluginConfig
25+
from eodag.plugins.download.http import HTTPDownload
26+
27+
from stac_fastapi.eodag.config import get_settings
28+
2229

2330
async def test_download_item_from_collection_stream(
2431
request_valid_raw, defaults, mock_base_stream_download_dict, mock_base_authenticate, stream_response
@@ -46,3 +53,48 @@ async def test_download_item_from_collection_no_stream(
4653
mock_download.assert_called_once()
4754
# downloaded file should have been immediatly deleted from the server
4855
assert not os.path.exists(expected_file), f"File {expected_file} should have been deleted"
56+
57+
58+
async def test_download_auto_order_whitelist(
59+
request_valid_raw,
60+
mock_base_authenticate,
61+
mock_order,
62+
mock_search,
63+
defaults,
64+
mock_http_base_stream_download_dict,
65+
stream_response,
66+
):
67+
"""Test that the order method is called when downloading a product
68+
from a federation backend included in the auto_order_whitelist.
69+
70+
This test simulates downloading a product from a federated backend ('peps')
71+
and checks that the order function is triggered when the backend is present
72+
in the auto_order_whitelist configuration.
73+
"""
74+
get_settings().auto_order_whitelist = ["peps"]
75+
federation_backend = "peps"
76+
collection_id = defaults.product_type
77+
78+
url = f"data/peps/{defaults.product_type}/dummy_id/downloadLink"
79+
80+
product = EOProduct(
81+
federation_backend,
82+
dict(
83+
geometry="POINT (0 0)",
84+
title="dummy_product",
85+
id="dummy_id",
86+
),
87+
)
88+
product.product_type = collection_id
89+
config = PluginConfig()
90+
config.priority = 0
91+
downloader = HTTPDownload("peps", config)
92+
93+
product.register_downloader(downloader=downloader, authenticator=None)
94+
95+
mock_search.return_value = SearchResult([product])
96+
mock_http_base_stream_download_dict.return_value = stream_response
97+
98+
await request_valid_raw(url, search_result=SearchResult([product]))
99+
100+
assert mock_order.called

0 commit comments

Comments
 (0)