eodag 3.0.0b2__py3-none-any.whl → 3.0.1__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 +295 -287
- eodag/api/product/__init__.py +10 -4
- eodag/api/product/_assets.py +2 -14
- eodag/api/product/_product.py +16 -30
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +12 -31
- eodag/api/search_result.py +33 -12
- eodag/cli.py +35 -19
- eodag/config.py +455 -155
- eodag/plugins/apis/base.py +13 -7
- eodag/plugins/apis/ecmwf.py +16 -7
- eodag/plugins/apis/usgs.py +68 -16
- eodag/plugins/authentication/aws_auth.py +25 -7
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +14 -3
- eodag/plugins/authentication/header.py +12 -4
- eodag/plugins/authentication/keycloak.py +41 -22
- eodag/plugins/authentication/oauth.py +11 -1
- eodag/plugins/authentication/openid_connect.py +183 -167
- eodag/plugins/authentication/qsauth.py +12 -4
- eodag/plugins/authentication/sas_auth.py +19 -2
- eodag/plugins/authentication/token.py +59 -11
- eodag/plugins/authentication/token_exchange.py +19 -19
- eodag/plugins/crunch/base.py +7 -2
- eodag/plugins/crunch/filter_date.py +8 -11
- eodag/plugins/crunch/filter_latest_intersect.py +5 -7
- eodag/plugins/crunch/filter_latest_tpl_name.py +2 -5
- eodag/plugins/crunch/filter_overlap.py +9 -15
- eodag/plugins/crunch/filter_property.py +9 -14
- eodag/plugins/download/aws.py +84 -99
- eodag/plugins/download/base.py +36 -77
- eodag/plugins/download/creodias_s3.py +11 -2
- eodag/plugins/download/http.py +134 -109
- eodag/plugins/download/s3rest.py +37 -43
- eodag/plugins/manager.py +173 -41
- eodag/plugins/search/__init__.py +9 -9
- eodag/plugins/search/base.py +35 -35
- eodag/plugins/search/build_search_result.py +55 -64
- eodag/plugins/search/cop_marine.py +113 -32
- eodag/plugins/search/creodias_s3.py +20 -8
- eodag/plugins/search/csw.py +41 -1
- eodag/plugins/search/data_request_search.py +119 -14
- eodag/plugins/search/qssearch.py +619 -197
- eodag/plugins/search/static_stac_search.py +25 -23
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +211 -56
- eodag/resources/providers.yml +1762 -1809
- eodag/resources/stac.yml +3 -163
- eodag/resources/user_conf_template.yml +134 -119
- eodag/rest/config.py +1 -2
- eodag/rest/constants.py +0 -1
- eodag/rest/core.py +70 -92
- eodag/rest/errors.py +181 -0
- eodag/rest/server.py +24 -330
- eodag/rest/stac.py +105 -630
- eodag/rest/types/eodag_search.py +17 -15
- eodag/rest/types/queryables.py +5 -14
- eodag/rest/types/stac_search.py +18 -13
- eodag/rest/utils/rfc3339.py +0 -1
- eodag/types/__init__.py +24 -6
- eodag/types/download_args.py +14 -5
- eodag/types/queryables.py +1 -2
- eodag/types/search_args.py +10 -11
- eodag/types/whoosh.py +0 -2
- eodag/utils/__init__.py +97 -136
- eodag/utils/constraints.py +0 -8
- eodag/utils/exceptions.py +23 -9
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/requests.py +13 -23
- eodag/utils/rest.py +0 -4
- eodag/utils/stac_reader.py +3 -15
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/METADATA +41 -24
- eodag-3.0.1.dist-info/RECORD +109 -0
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/WHEEL +1 -1
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/entry_points.txt +1 -0
- eodag/resources/constraints/climate-dt.json +0 -13
- eodag/resources/constraints/extremes-dt.json +0 -8
- eodag-3.0.0b2.dist-info/RECORD +0 -110
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/LICENSE +0 -0
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/top_level.txt +0 -0
eodag/rest/server.py
CHANGED
|
@@ -20,7 +20,6 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
import re
|
|
23
|
-
import traceback
|
|
24
23
|
from contextlib import asynccontextmanager
|
|
25
24
|
from importlib.metadata import version
|
|
26
25
|
from json import JSONDecodeError
|
|
@@ -36,13 +35,13 @@ from typing import (
|
|
|
36
35
|
|
|
37
36
|
from fastapi import APIRouter as FastAPIRouter
|
|
38
37
|
from fastapi import FastAPI, HTTPException, Request
|
|
38
|
+
from fastapi.concurrency import run_in_threadpool
|
|
39
39
|
from fastapi.middleware.cors import CORSMiddleware
|
|
40
40
|
from fastapi.openapi.utils import get_openapi
|
|
41
41
|
from fastapi.responses import ORJSONResponse
|
|
42
42
|
from pydantic import ValidationError as pydanticValidationError
|
|
43
43
|
from pygeofilter.backends.cql2_json import to_cql2
|
|
44
44
|
from pygeofilter.parsers.cql2_text import parse as parse_cql2_text
|
|
45
|
-
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
46
45
|
|
|
47
46
|
from eodag.config import load_stac_api_config
|
|
48
47
|
from eodag.rest.cache import init_cache
|
|
@@ -59,23 +58,11 @@ from eodag.rest.core import (
|
|
|
59
58
|
get_stac_extension_oseo,
|
|
60
59
|
search_stac_items,
|
|
61
60
|
)
|
|
62
|
-
from eodag.rest.
|
|
61
|
+
from eodag.rest.errors import add_exception_handlers
|
|
63
62
|
from eodag.rest.types.queryables import QueryablesGetParams
|
|
64
63
|
from eodag.rest.types.stac_search import SearchPostRequest, sortby2list
|
|
65
64
|
from eodag.rest.utils import format_pydantic_error, str2json, str2list
|
|
66
65
|
from eodag.utils import parse_header, update_nested_dict
|
|
67
|
-
from eodag.utils.exceptions import (
|
|
68
|
-
AuthenticationError,
|
|
69
|
-
DownloadError,
|
|
70
|
-
MisconfiguredError,
|
|
71
|
-
NoMatchingProductType,
|
|
72
|
-
NotAvailableError,
|
|
73
|
-
RequestError,
|
|
74
|
-
TimeOutError,
|
|
75
|
-
UnsupportedProductType,
|
|
76
|
-
UnsupportedProvider,
|
|
77
|
-
ValidationError,
|
|
78
|
-
)
|
|
79
66
|
|
|
80
67
|
if TYPE_CHECKING:
|
|
81
68
|
from fastapi.types import DecoratedCallable
|
|
@@ -84,12 +71,6 @@ if TYPE_CHECKING:
|
|
|
84
71
|
from starlette.responses import Response as StarletteResponse
|
|
85
72
|
|
|
86
73
|
logger = logging.getLogger("eodag.rest.server")
|
|
87
|
-
ERRORS_WITH_500_STATUS_CODE = {
|
|
88
|
-
"MisconfiguredError",
|
|
89
|
-
"AuthenticationError",
|
|
90
|
-
"DownloadError",
|
|
91
|
-
"RequestError",
|
|
92
|
-
}
|
|
93
74
|
|
|
94
75
|
|
|
95
76
|
class APIRouter(FastAPIRouter):
|
|
@@ -197,6 +178,8 @@ app.add_middleware(
|
|
|
197
178
|
allow_headers=["*"],
|
|
198
179
|
)
|
|
199
180
|
|
|
181
|
+
add_exception_handlers(app)
|
|
182
|
+
|
|
200
183
|
|
|
201
184
|
@app.middleware("http")
|
|
202
185
|
async def forward_middleware(
|
|
@@ -221,141 +204,10 @@ async def forward_middleware(
|
|
|
221
204
|
return response
|
|
222
205
|
|
|
223
206
|
|
|
224
|
-
@app.exception_handler(StarletteHTTPException)
|
|
225
|
-
async def default_exception_handler(
|
|
226
|
-
request: Request, error: Exception
|
|
227
|
-
) -> ORJSONResponse:
|
|
228
|
-
"""Default errors handle"""
|
|
229
|
-
description = (
|
|
230
|
-
getattr(error, "description", None)
|
|
231
|
-
or getattr(error, "detail", None)
|
|
232
|
-
or str(error)
|
|
233
|
-
)
|
|
234
|
-
return ORJSONResponse(
|
|
235
|
-
status_code=error.status_code,
|
|
236
|
-
content={"description": description},
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
@app.exception_handler(ValidationError)
|
|
241
|
-
async def handle_invalid_usage_with_validation_error(
|
|
242
|
-
request: Request, error: ValidationError
|
|
243
|
-
) -> ORJSONResponse:
|
|
244
|
-
"""Invalid usage [400] ValidationError handle"""
|
|
245
|
-
if error.parameters:
|
|
246
|
-
for error_param in error.parameters:
|
|
247
|
-
stac_param = EODAGSearch.to_stac(error_param)
|
|
248
|
-
error.message = error.message.replace(error_param, stac_param)
|
|
249
|
-
logger.debug(traceback.format_exc())
|
|
250
|
-
return await default_exception_handler(
|
|
251
|
-
request,
|
|
252
|
-
HTTPException(
|
|
253
|
-
status_code=400,
|
|
254
|
-
detail=f"{type(error).__name__}: {str(error.message)}",
|
|
255
|
-
),
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
@app.exception_handler(NoMatchingProductType)
|
|
260
|
-
@app.exception_handler(UnsupportedProductType)
|
|
261
|
-
@app.exception_handler(UnsupportedProvider)
|
|
262
|
-
async def handle_invalid_usage(request: Request, error: Exception) -> ORJSONResponse:
|
|
263
|
-
"""Invalid usage [400] errors handle"""
|
|
264
|
-
return await default_exception_handler(
|
|
265
|
-
request,
|
|
266
|
-
HTTPException(
|
|
267
|
-
status_code=400,
|
|
268
|
-
detail=f"{type(error).__name__}: {str(error)}",
|
|
269
|
-
),
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
@app.exception_handler(NotAvailableError)
|
|
274
|
-
async def handle_resource_not_found(
|
|
275
|
-
request: Request, error: Exception
|
|
276
|
-
) -> ORJSONResponse:
|
|
277
|
-
"""Not found [404] errors handle"""
|
|
278
|
-
return await default_exception_handler(
|
|
279
|
-
request,
|
|
280
|
-
HTTPException(
|
|
281
|
-
status_code=404,
|
|
282
|
-
detail=f"{type(error).__name__}: {str(error)}",
|
|
283
|
-
),
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
@app.exception_handler(MisconfiguredError)
|
|
288
|
-
@app.exception_handler(AuthenticationError)
|
|
289
|
-
async def handle_auth_error(request: Request, error: Exception) -> ORJSONResponse:
|
|
290
|
-
"""These errors should be sent as internal server error to the client"""
|
|
291
|
-
logger.error("%s: %s", type(error).__name__, str(error))
|
|
292
|
-
return await default_exception_handler(
|
|
293
|
-
request,
|
|
294
|
-
HTTPException(
|
|
295
|
-
status_code=500,
|
|
296
|
-
detail="Internal server error: please contact the administrator",
|
|
297
|
-
),
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
@app.exception_handler(DownloadError)
|
|
302
|
-
async def handle_download_error(request: Request, error: Exception) -> ORJSONResponse:
|
|
303
|
-
"""DownloadError should be sent as internal server error with details to the client"""
|
|
304
|
-
logger.error(f"{type(error).__name__}: {str(error)}")
|
|
305
|
-
return await default_exception_handler(
|
|
306
|
-
request,
|
|
307
|
-
HTTPException(
|
|
308
|
-
status_code=500,
|
|
309
|
-
detail=f"{type(error).__name__}: {str(error)}",
|
|
310
|
-
),
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
@app.exception_handler(RequestError)
|
|
315
|
-
async def handle_request_error(request: Request, error: RequestError) -> ORJSONResponse:
|
|
316
|
-
"""RequestError should be sent as internal server error with details to the client"""
|
|
317
|
-
if getattr(error, "history", None):
|
|
318
|
-
error_history_tmp = list(error.history)
|
|
319
|
-
for i, search_error in enumerate(error_history_tmp):
|
|
320
|
-
if search_error[1].__class__.__name__ in ERRORS_WITH_500_STATUS_CODE:
|
|
321
|
-
search_error[1].args = ("an internal error occured",)
|
|
322
|
-
error_history_tmp[i] = search_error
|
|
323
|
-
continue
|
|
324
|
-
if getattr(error, "parameters", None):
|
|
325
|
-
for error_param in error.parameters:
|
|
326
|
-
stac_param = EODAGSearch.to_stac(error_param)
|
|
327
|
-
search_error[1].args = (
|
|
328
|
-
search_error[1].args[0].replace(error_param, stac_param),
|
|
329
|
-
)
|
|
330
|
-
error_history_tmp[i] = search_error
|
|
331
|
-
error.history = set(error_history_tmp)
|
|
332
|
-
logger.error(f"{type(error).__name__}: {str(error)}")
|
|
333
|
-
return await default_exception_handler(
|
|
334
|
-
request,
|
|
335
|
-
HTTPException(
|
|
336
|
-
status_code=500,
|
|
337
|
-
detail=f"{type(error).__name__}: {str(error)}",
|
|
338
|
-
),
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
@app.exception_handler(TimeOutError)
|
|
343
|
-
async def handle_timeout(request: Request, error: Exception) -> ORJSONResponse:
|
|
344
|
-
"""Timeout [504] errors handle"""
|
|
345
|
-
logger.error(f"{type(error).__name__}: {str(error)}")
|
|
346
|
-
return await default_exception_handler(
|
|
347
|
-
request,
|
|
348
|
-
HTTPException(
|
|
349
|
-
status_code=504,
|
|
350
|
-
detail=f"{type(error).__name__}: {str(error)}",
|
|
351
|
-
),
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
207
|
@router.api_route(methods=["GET", "HEAD"], path="/", tags=["Capabilities"])
|
|
356
208
|
async def catalogs_root(request: Request) -> ORJSONResponse:
|
|
357
209
|
"""STAC catalogs root"""
|
|
358
|
-
logger.
|
|
210
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
359
211
|
|
|
360
212
|
response = await get_stac_catalogs(
|
|
361
213
|
request=request,
|
|
@@ -367,9 +219,9 @@ async def catalogs_root(request: Request) -> ORJSONResponse:
|
|
|
367
219
|
|
|
368
220
|
|
|
369
221
|
@router.api_route(methods=["GET", "HEAD"], path="/conformance", tags=["Capabilities"])
|
|
370
|
-
def conformance() -> ORJSONResponse:
|
|
222
|
+
def conformance(request: Request) -> ORJSONResponse:
|
|
371
223
|
"""STAC conformance"""
|
|
372
|
-
logger.
|
|
224
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
373
225
|
response = get_stac_conformance()
|
|
374
226
|
|
|
375
227
|
return ORJSONResponse(response)
|
|
@@ -382,7 +234,7 @@ def conformance() -> ORJSONResponse:
|
|
|
382
234
|
)
|
|
383
235
|
def stac_extension_oseo(request: Request) -> ORJSONResponse:
|
|
384
236
|
"""STAC OGC / OpenSearch extension for EO"""
|
|
385
|
-
logger.
|
|
237
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
386
238
|
response = get_stac_extension_oseo(url=request.state.url)
|
|
387
239
|
|
|
388
240
|
return ORJSONResponse(response)
|
|
@@ -398,14 +250,14 @@ def stac_collections_item_download(
|
|
|
398
250
|
collection_id: str, item_id: str, request: Request
|
|
399
251
|
) -> StarletteResponse:
|
|
400
252
|
"""STAC collection item download"""
|
|
401
|
-
logger.
|
|
253
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
402
254
|
|
|
403
255
|
arguments = dict(request.query_params)
|
|
404
256
|
provider = arguments.pop("provider", None)
|
|
405
257
|
|
|
406
258
|
return download_stac_item(
|
|
407
259
|
request=request,
|
|
408
|
-
|
|
260
|
+
collection_id=collection_id,
|
|
409
261
|
item_id=item_id,
|
|
410
262
|
provider=provider,
|
|
411
263
|
**arguments,
|
|
@@ -422,14 +274,14 @@ def stac_collections_item_download_asset(
|
|
|
422
274
|
collection_id: str, item_id: str, asset: str, request: Request
|
|
423
275
|
):
|
|
424
276
|
"""STAC collection item asset download"""
|
|
425
|
-
logger.
|
|
277
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
426
278
|
|
|
427
279
|
arguments = dict(request.query_params)
|
|
428
280
|
provider = arguments.pop("provider", None)
|
|
429
281
|
|
|
430
282
|
return download_stac_item(
|
|
431
283
|
request=request,
|
|
432
|
-
|
|
284
|
+
collection_id=collection_id,
|
|
433
285
|
item_id=item_id,
|
|
434
286
|
provider=provider,
|
|
435
287
|
asset=asset,
|
|
@@ -446,7 +298,7 @@ def stac_collections_item(
|
|
|
446
298
|
collection_id: str, item_id: str, request: Request, provider: Optional[str] = None
|
|
447
299
|
) -> ORJSONResponse:
|
|
448
300
|
"""STAC collection item by id"""
|
|
449
|
-
logger.
|
|
301
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
450
302
|
|
|
451
303
|
search_request = SearchPostRequest(
|
|
452
304
|
provider=provider, ids=[item_id], collections=[collection_id], limit=1
|
|
@@ -519,13 +371,10 @@ async def list_collection_queryables(
|
|
|
519
371
|
that can be filtered using comparison operators.
|
|
520
372
|
|
|
521
373
|
:param request: The incoming request object.
|
|
522
|
-
:type request: fastapi.Request
|
|
523
374
|
:param collection_id: The identifier of the collection for which to retrieve queryable properties.
|
|
524
|
-
:type collection_id: str
|
|
525
375
|
:returns: A json object containing the list of available queryable properties for the specified collection.
|
|
526
|
-
:rtype: Any
|
|
527
376
|
"""
|
|
528
|
-
logger.
|
|
377
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
529
378
|
additional_params = dict(request.query_params)
|
|
530
379
|
provider = additional_params.pop("provider", None)
|
|
531
380
|
|
|
@@ -548,7 +397,7 @@ async def collection_by_id(
|
|
|
548
397
|
collection_id: str, request: Request, provider: Optional[str] = None
|
|
549
398
|
) -> ORJSONResponse:
|
|
550
399
|
"""STAC collection by id"""
|
|
551
|
-
logger.
|
|
400
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
552
401
|
|
|
553
402
|
response = await get_collection(
|
|
554
403
|
request=request,
|
|
@@ -579,7 +428,7 @@ async def collections(
|
|
|
579
428
|
Can be filtered using parameters: instrument, platform, platformSerialIdentifier, sensorType,
|
|
580
429
|
processingLevel
|
|
581
430
|
"""
|
|
582
|
-
logger.
|
|
431
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
583
432
|
|
|
584
433
|
collections = await all_collections(
|
|
585
434
|
request, provider, q, platform, instrument, constellation, datetime
|
|
@@ -587,161 +436,6 @@ async def collections(
|
|
|
587
436
|
return ORJSONResponse(collections)
|
|
588
437
|
|
|
589
438
|
|
|
590
|
-
@router.api_route(
|
|
591
|
-
methods=["GET", "HEAD"],
|
|
592
|
-
path="/catalogs/{catalogs:path}/items/{item_id}/download",
|
|
593
|
-
tags=["Data"],
|
|
594
|
-
include_in_schema=False,
|
|
595
|
-
)
|
|
596
|
-
def stac_catalogs_item_download(
|
|
597
|
-
catalogs: str, item_id: str, request: Request
|
|
598
|
-
) -> StarletteResponse:
|
|
599
|
-
"""STAC Catalog item download"""
|
|
600
|
-
logger.debug("URL: %s", request.url)
|
|
601
|
-
|
|
602
|
-
arguments = dict(request.query_params)
|
|
603
|
-
provider = arguments.pop("provider", None)
|
|
604
|
-
|
|
605
|
-
list_catalog = catalogs.strip("/").split("/")
|
|
606
|
-
|
|
607
|
-
return download_stac_item(
|
|
608
|
-
request=request,
|
|
609
|
-
catalogs=list_catalog,
|
|
610
|
-
item_id=item_id,
|
|
611
|
-
provider=provider,
|
|
612
|
-
**arguments,
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
@router.api_route(
|
|
617
|
-
methods=["GET", "HEAD"],
|
|
618
|
-
path="/catalogs/{catalogs:path}/items/{item_id}/download/{asset_filter}",
|
|
619
|
-
tags=["Data"],
|
|
620
|
-
include_in_schema=False,
|
|
621
|
-
)
|
|
622
|
-
def stac_catalogs_item_download_asset(
|
|
623
|
-
catalogs: str, item_id: str, asset_filter: str, request: Request
|
|
624
|
-
):
|
|
625
|
-
"""STAC Catalog item asset download"""
|
|
626
|
-
logger.debug("URL: %s", request.url)
|
|
627
|
-
|
|
628
|
-
arguments = dict(request.query_params)
|
|
629
|
-
provider = arguments.pop("provider", None)
|
|
630
|
-
|
|
631
|
-
list_catalog = catalogs.strip("/").split("/")
|
|
632
|
-
|
|
633
|
-
return download_stac_item(
|
|
634
|
-
request,
|
|
635
|
-
catalogs=list_catalog,
|
|
636
|
-
item_id=item_id,
|
|
637
|
-
provider=provider,
|
|
638
|
-
asset=asset_filter,
|
|
639
|
-
**arguments,
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
@router.api_route(
|
|
644
|
-
methods=["GET", "HEAD"],
|
|
645
|
-
path="/catalogs/{catalogs:path}/items/{item_id}",
|
|
646
|
-
tags=["Data"],
|
|
647
|
-
include_in_schema=False,
|
|
648
|
-
)
|
|
649
|
-
def stac_catalogs_item(
|
|
650
|
-
catalogs: str, item_id: str, request: Request, provider: Optional[str] = None
|
|
651
|
-
):
|
|
652
|
-
"""Fetch catalog's single features."""
|
|
653
|
-
logger.debug("URL: %s", request.url)
|
|
654
|
-
|
|
655
|
-
list_catalog = catalogs.strip("/").split("/")
|
|
656
|
-
|
|
657
|
-
search_request = SearchPostRequest(provider=provider, ids=[item_id], limit=1)
|
|
658
|
-
|
|
659
|
-
item_collection = search_stac_items(request, search_request, catalogs=list_catalog)
|
|
660
|
-
|
|
661
|
-
if not item_collection["features"]:
|
|
662
|
-
raise HTTPException(
|
|
663
|
-
status_code=404,
|
|
664
|
-
detail=f"Item {item_id} in Catalog {catalogs} does not exist.",
|
|
665
|
-
)
|
|
666
|
-
|
|
667
|
-
return ORJSONResponse(item_collection["features"][0])
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
@router.api_route(
|
|
671
|
-
methods=["GET", "HEAD"],
|
|
672
|
-
path="/catalogs/{catalogs:path}/items",
|
|
673
|
-
tags=["Data"],
|
|
674
|
-
include_in_schema=False,
|
|
675
|
-
)
|
|
676
|
-
def stac_catalogs_items(
|
|
677
|
-
catalogs: str,
|
|
678
|
-
request: Request,
|
|
679
|
-
provider: Optional[str] = None,
|
|
680
|
-
bbox: Optional[str] = None,
|
|
681
|
-
datetime: Optional[str] = None,
|
|
682
|
-
limit: Optional[int] = None,
|
|
683
|
-
page: Optional[int] = None,
|
|
684
|
-
sortby: Optional[str] = None,
|
|
685
|
-
crunch: Optional[str] = None,
|
|
686
|
-
) -> ORJSONResponse:
|
|
687
|
-
"""Fetch catalog's features"""
|
|
688
|
-
logger.debug("URL: %s", request.state.url)
|
|
689
|
-
|
|
690
|
-
base_args = {
|
|
691
|
-
"provider": provider,
|
|
692
|
-
"datetime": datetime,
|
|
693
|
-
"bbox": str2list(bbox),
|
|
694
|
-
"limit": limit,
|
|
695
|
-
"page": page,
|
|
696
|
-
"sortby": sortby2list(sortby),
|
|
697
|
-
"crunch": crunch,
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
clean = {k: v for k, v in base_args.items() if v is not None and v != []}
|
|
701
|
-
|
|
702
|
-
list_catalog = catalogs.strip("/").split("/")
|
|
703
|
-
|
|
704
|
-
try:
|
|
705
|
-
search_request = SearchPostRequest.model_validate(clean)
|
|
706
|
-
except pydanticValidationError as e:
|
|
707
|
-
raise HTTPException(status_code=400, detail=format_pydantic_error(e)) from e
|
|
708
|
-
|
|
709
|
-
response = search_stac_items(
|
|
710
|
-
request=request,
|
|
711
|
-
search_request=search_request,
|
|
712
|
-
catalogs=list_catalog,
|
|
713
|
-
)
|
|
714
|
-
return ORJSONResponse(response)
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
@router.api_route(
|
|
718
|
-
methods=["GET", "HEAD"],
|
|
719
|
-
path="/catalogs/{catalogs:path}",
|
|
720
|
-
tags=["Capabilities"],
|
|
721
|
-
include_in_schema=False,
|
|
722
|
-
)
|
|
723
|
-
async def stac_catalogs(
|
|
724
|
-
catalogs: str, request: Request, provider: Optional[str] = None
|
|
725
|
-
) -> ORJSONResponse:
|
|
726
|
-
"""Describe the given catalog and list available sub-catalogs"""
|
|
727
|
-
logger.debug("URL: %s", request.url)
|
|
728
|
-
|
|
729
|
-
if not catalogs:
|
|
730
|
-
raise HTTPException(
|
|
731
|
-
status_code=404,
|
|
732
|
-
detail="Not found",
|
|
733
|
-
)
|
|
734
|
-
|
|
735
|
-
list_catalog = catalogs.strip("/").split("/")
|
|
736
|
-
response = await get_stac_catalogs(
|
|
737
|
-
request=request,
|
|
738
|
-
url=request.state.url,
|
|
739
|
-
catalogs=tuple(list_catalog),
|
|
740
|
-
provider=provider,
|
|
741
|
-
)
|
|
742
|
-
return ORJSONResponse(response)
|
|
743
|
-
|
|
744
|
-
|
|
745
439
|
@router.api_route(
|
|
746
440
|
methods=["GET", "HEAD"],
|
|
747
441
|
path="/queryables",
|
|
@@ -757,11 +451,9 @@ async def list_queryables(request: Request) -> ORJSONResponse:
|
|
|
757
451
|
operators.
|
|
758
452
|
|
|
759
453
|
:param request: The incoming request object.
|
|
760
|
-
:type request: fastapi.Request
|
|
761
454
|
:returns: A json object containing the list of available queryable terms.
|
|
762
|
-
:rtype: Any
|
|
763
455
|
"""
|
|
764
|
-
logger.
|
|
456
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
765
457
|
additional_params = dict(request.query_params.items())
|
|
766
458
|
provider = additional_params.pop("provider", None)
|
|
767
459
|
queryables = await get_queryables(
|
|
@@ -794,7 +486,7 @@ def get_search(
|
|
|
794
486
|
crunch: Optional[str] = None,
|
|
795
487
|
) -> ORJSONResponse:
|
|
796
488
|
"""Handler for GET /search"""
|
|
797
|
-
logger.
|
|
489
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
798
490
|
|
|
799
491
|
query_params = str(request.query_params)
|
|
800
492
|
|
|
@@ -848,7 +540,7 @@ def get_search(
|
|
|
848
540
|
)
|
|
849
541
|
async def post_search(request: Request) -> ORJSONResponse:
|
|
850
542
|
"""STAC post search"""
|
|
851
|
-
logger.
|
|
543
|
+
logger.info(f"{request.method} {request.state.url}")
|
|
852
544
|
|
|
853
545
|
content_type = request.headers.get("Content-Type")
|
|
854
546
|
|
|
@@ -869,10 +561,12 @@ async def post_search(request: Request) -> ORJSONResponse:
|
|
|
869
561
|
|
|
870
562
|
logger.debug("Body: %s", search_request.model_dump(exclude_none=True))
|
|
871
563
|
|
|
872
|
-
response =
|
|
873
|
-
|
|
874
|
-
|
|
564
|
+
response = await run_in_threadpool(
|
|
565
|
+
search_stac_items,
|
|
566
|
+
request,
|
|
567
|
+
search_request,
|
|
875
568
|
)
|
|
569
|
+
|
|
876
570
|
return ORJSONResponse(content=response, media_type="application/json")
|
|
877
571
|
|
|
878
572
|
|