stac-fastapi.core 2.1.0__tar.gz → 2.3.1__tar.gz
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.
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/PKG-INFO +5 -4
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/setup.py +4 -3
- stac_fastapi_core-2.3.1/stac_fastapi/core/basic_auth.py +115 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/core.py +60 -53
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/models/links.py +2 -2
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/serializers.py +19 -5
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/types/core.py +1 -1
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/version.py +1 -1
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/PKG-INFO +5 -4
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/SOURCES.txt +1 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/requires.txt +4 -3
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/README.md +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/setup.cfg +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/__init__.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/base_database_logic.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/base_settings.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/datetime_utils.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/extensions/__init__.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/extensions/filter.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/extensions/query.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/models/__init__.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/models/search.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/session.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/utilities.py +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/dependency_links.txt +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/not-zip-safe +0 -0
- {stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi.core
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -18,13 +18,14 @@ Requires-Dist: fastapi
|
|
|
18
18
|
Requires-Dist: attrs
|
|
19
19
|
Requires-Dist: pydantic[dotenv]<2
|
|
20
20
|
Requires-Dist: stac_pydantic==2.0.*
|
|
21
|
-
Requires-Dist: stac-fastapi.types==2.
|
|
22
|
-
Requires-Dist: stac-fastapi.api==2.
|
|
23
|
-
Requires-Dist: stac-fastapi.extensions==2.
|
|
21
|
+
Requires-Dist: stac-fastapi.types==2.5.5.post1
|
|
22
|
+
Requires-Dist: stac-fastapi.api==2.5.5.post1
|
|
23
|
+
Requires-Dist: stac-fastapi.extensions==2.5.5.post1
|
|
24
24
|
Requires-Dist: pystac[validation]
|
|
25
25
|
Requires-Dist: orjson
|
|
26
26
|
Requires-Dist: overrides
|
|
27
27
|
Requires-Dist: geojson-pydantic
|
|
28
28
|
Requires-Dist: pygeofilter==0.2.1
|
|
29
|
+
Requires-Dist: typing_extensions==4.4.0
|
|
29
30
|
|
|
30
31
|
# stac-fastapi core library for Elasticsearch and Opensearch backends
|
|
@@ -10,14 +10,15 @@ install_requires = [
|
|
|
10
10
|
"attrs",
|
|
11
11
|
"pydantic[dotenv]<2",
|
|
12
12
|
"stac_pydantic==2.0.*",
|
|
13
|
-
"stac-fastapi.types==2.
|
|
14
|
-
"stac-fastapi.api==2.
|
|
15
|
-
"stac-fastapi.extensions==2.
|
|
13
|
+
"stac-fastapi.types==2.5.5.post1",
|
|
14
|
+
"stac-fastapi.api==2.5.5.post1",
|
|
15
|
+
"stac-fastapi.extensions==2.5.5.post1",
|
|
16
16
|
"pystac[validation]",
|
|
17
17
|
"orjson",
|
|
18
18
|
"overrides",
|
|
19
19
|
"geojson-pydantic",
|
|
20
20
|
"pygeofilter==0.2.1",
|
|
21
|
+
"typing_extensions==4.4.0",
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
setup(
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Basic Authentication Module."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import secrets
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from fastapi import Depends, HTTPException, Request, status
|
|
10
|
+
from fastapi.routing import APIRoute
|
|
11
|
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
|
12
|
+
from typing_extensions import Annotated
|
|
13
|
+
|
|
14
|
+
from stac_fastapi.api.app import StacApi
|
|
15
|
+
|
|
16
|
+
_LOGGER = logging.getLogger("uvicorn.default")
|
|
17
|
+
_SECURITY = HTTPBasic()
|
|
18
|
+
_BASIC_AUTH: Dict[str, Any] = {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def has_access(
|
|
22
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(_SECURITY)]
|
|
23
|
+
) -> str:
|
|
24
|
+
"""Check if the provided credentials match the expected \
|
|
25
|
+
username and password stored in environment variables for basic authentication.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
request (Request): The FastAPI request object.
|
|
29
|
+
credentials (HTTPBasicCredentials): The HTTP basic authentication credentials.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
str: The username if authentication is successful.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
HTTPException: If authentication fails due to incorrect username or password.
|
|
36
|
+
"""
|
|
37
|
+
global _BASIC_AUTH
|
|
38
|
+
|
|
39
|
+
users = _BASIC_AUTH.get("users")
|
|
40
|
+
user: Dict[str, Any] = next(
|
|
41
|
+
(u for u in users if u.get("username") == credentials.username), {}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if not user:
|
|
45
|
+
raise HTTPException(
|
|
46
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
47
|
+
detail="Incorrect username or password",
|
|
48
|
+
headers={"WWW-Authenticate": "Basic"},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Compare the provided username and password with the correct ones using compare_digest
|
|
52
|
+
if not secrets.compare_digest(
|
|
53
|
+
credentials.username.encode("utf-8"), user.get("username").encode("utf-8")
|
|
54
|
+
) or not secrets.compare_digest(
|
|
55
|
+
credentials.password.encode("utf-8"), user.get("password").encode("utf-8")
|
|
56
|
+
):
|
|
57
|
+
raise HTTPException(
|
|
58
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
59
|
+
detail="Incorrect username or password",
|
|
60
|
+
headers={"WWW-Authenticate": "Basic"},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
permissions = user.get("permissions", [])
|
|
64
|
+
path = request.url.path
|
|
65
|
+
method = request.method
|
|
66
|
+
|
|
67
|
+
if permissions == "*":
|
|
68
|
+
return credentials.username
|
|
69
|
+
for permission in permissions:
|
|
70
|
+
if permission["path"] == path and method in permission.get("method", []):
|
|
71
|
+
return credentials.username
|
|
72
|
+
|
|
73
|
+
raise HTTPException(
|
|
74
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
75
|
+
detail=f"Insufficient permissions for [{method} {path}]",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def apply_basic_auth(api: StacApi) -> None:
|
|
80
|
+
"""Apply basic authentication to the provided FastAPI application \
|
|
81
|
+
based on environment variables for username, password, and endpoints.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
api (StacApi): The FastAPI application.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
HTTPException: If there are issues with the configuration or format
|
|
88
|
+
of the environment variables.
|
|
89
|
+
"""
|
|
90
|
+
global _BASIC_AUTH
|
|
91
|
+
|
|
92
|
+
basic_auth_json_str = os.environ.get("BASIC_AUTH")
|
|
93
|
+
if not basic_auth_json_str:
|
|
94
|
+
_LOGGER.info("Basic authentication disabled.")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
_BASIC_AUTH = json.loads(basic_auth_json_str)
|
|
99
|
+
except json.JSONDecodeError as exception:
|
|
100
|
+
_LOGGER.error(f"Invalid JSON format for BASIC_AUTH. {exception=}")
|
|
101
|
+
raise
|
|
102
|
+
public_endpoints = _BASIC_AUTH.get("public_endpoints", [])
|
|
103
|
+
users = _BASIC_AUTH.get("users")
|
|
104
|
+
if not users:
|
|
105
|
+
raise Exception("Invalid JSON format for BASIC_AUTH. Key 'users' undefined.")
|
|
106
|
+
|
|
107
|
+
app = api.app
|
|
108
|
+
for route in app.routes:
|
|
109
|
+
if isinstance(route, APIRoute):
|
|
110
|
+
for method in route.methods:
|
|
111
|
+
endpoint = {"path": route.path, "method": method}
|
|
112
|
+
if endpoint not in public_endpoints:
|
|
113
|
+
api.add_route_dependencies([endpoint], [Depends(has_access)])
|
|
114
|
+
|
|
115
|
+
_LOGGER.info("Basic authentication enabled.")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Core client."""
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
4
|
from datetime import datetime as datetime_type
|
|
@@ -15,7 +15,7 @@ from pydantic import ValidationError
|
|
|
15
15
|
from pygeofilter.backends.cql2_json import to_cql2
|
|
16
16
|
from pygeofilter.parsers.cql2_text import parse as parse_cql2_text
|
|
17
17
|
from stac_pydantic.links import Relations
|
|
18
|
-
from stac_pydantic.shared import MimeTypes
|
|
18
|
+
from stac_pydantic.shared import BBox, MimeTypes
|
|
19
19
|
from stac_pydantic.version import STAC_VERSION
|
|
20
20
|
|
|
21
21
|
from stac_fastapi.core.base_database_logic import BaseDatabaseLogic
|
|
@@ -37,8 +37,8 @@ from stac_fastapi.types import stac as stac_types
|
|
|
37
37
|
from stac_fastapi.types.config import Settings
|
|
38
38
|
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
|
|
39
39
|
from stac_fastapi.types.extension import ApiExtension
|
|
40
|
-
from stac_fastapi.types.links import CollectionLinks
|
|
41
40
|
from stac_fastapi.types.requests import get_base_url
|
|
41
|
+
from stac_fastapi.types.rfc3339 import DateTimeType
|
|
42
42
|
from stac_fastapi.types.search import BaseSearchPostRequest
|
|
43
43
|
from stac_fastapi.types.stac import Collection, Collections, Item, ItemCollection
|
|
44
44
|
|
|
@@ -245,8 +245,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
245
245
|
async def item_collection(
|
|
246
246
|
self,
|
|
247
247
|
collection_id: str,
|
|
248
|
-
bbox: Optional[
|
|
249
|
-
datetime:
|
|
248
|
+
bbox: Optional[BBox] = None,
|
|
249
|
+
datetime: Optional[DateTimeType] = None,
|
|
250
250
|
limit: int = 10,
|
|
251
251
|
token: str = None,
|
|
252
252
|
**kwargs,
|
|
@@ -255,8 +255,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
255
255
|
|
|
256
256
|
Args:
|
|
257
257
|
collection_id (str): The identifier of the collection to read items from.
|
|
258
|
-
bbox (Optional[
|
|
259
|
-
datetime (
|
|
258
|
+
bbox (Optional[BBox]): The bounding box to filter items by.
|
|
259
|
+
datetime (Optional[DateTimeType]): The datetime range to filter items by.
|
|
260
260
|
limit (int): The maximum number of items to return. The default value is 10.
|
|
261
261
|
token (str): A token used for pagination.
|
|
262
262
|
request (Request): The incoming request.
|
|
@@ -350,53 +350,64 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
350
350
|
return self.item_serializer.db_to_stac(item, base_url)
|
|
351
351
|
|
|
352
352
|
@staticmethod
|
|
353
|
-
def _return_date(
|
|
353
|
+
def _return_date(
|
|
354
|
+
interval: Optional[Union[DateTimeType, str]]
|
|
355
|
+
) -> Dict[str, Optional[str]]:
|
|
354
356
|
"""
|
|
355
|
-
Convert a date interval
|
|
357
|
+
Convert a date interval.
|
|
356
358
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
assigned default values to encompass the entire possible date range.
|
|
359
|
+
(which may be a datetime, a tuple of one or two datetimes a string
|
|
360
|
+
representing a datetime or range, or None) into a dictionary for filtering
|
|
361
|
+
search results with Elasticsearch.
|
|
362
|
+
|
|
363
|
+
This function ensures the output dictionary contains 'gte' and 'lte' keys,
|
|
364
|
+
even if they are set to None, to prevent KeyError in the consuming logic.
|
|
364
365
|
|
|
365
366
|
Args:
|
|
366
|
-
|
|
367
|
+
interval (Optional[Union[DateTimeType, str]]): The date interval, which might be a single datetime,
|
|
368
|
+
a tuple with one or two datetimes, a string, or None.
|
|
367
369
|
|
|
368
370
|
Returns:
|
|
369
|
-
dict: A dictionary representing the date interval for use in filtering search results
|
|
371
|
+
dict: A dictionary representing the date interval for use in filtering search results,
|
|
372
|
+
always containing 'gte' and 'lte' keys.
|
|
370
373
|
"""
|
|
371
|
-
|
|
372
|
-
if len(intervals) == 1:
|
|
373
|
-
datetime = f"{intervals[0][0:19]}Z"
|
|
374
|
-
return {"eq": datetime}
|
|
375
|
-
else:
|
|
376
|
-
start_date = intervals[0]
|
|
377
|
-
end_date = intervals[1]
|
|
378
|
-
if ".." not in intervals:
|
|
379
|
-
start_date = f"{start_date[0:19]}Z"
|
|
380
|
-
end_date = f"{end_date[0:19]}Z"
|
|
381
|
-
elif start_date != "..":
|
|
382
|
-
start_date = f"{start_date[0:19]}Z"
|
|
383
|
-
end_date = "2200-12-01T12:31:12Z"
|
|
384
|
-
elif end_date != "..":
|
|
385
|
-
start_date = "1900-10-01T00:00:00Z"
|
|
386
|
-
end_date = f"{end_date[0:19]}Z"
|
|
387
|
-
else:
|
|
388
|
-
start_date = "1900-10-01T00:00:00Z"
|
|
389
|
-
end_date = "2200-12-01T12:31:12Z"
|
|
374
|
+
result: Dict[str, Optional[str]] = {"gte": None, "lte": None}
|
|
390
375
|
|
|
391
|
-
|
|
376
|
+
if interval is None:
|
|
377
|
+
return result
|
|
378
|
+
|
|
379
|
+
if isinstance(interval, str):
|
|
380
|
+
if "/" in interval:
|
|
381
|
+
parts = interval.split("/")
|
|
382
|
+
result["gte"] = parts[0] if parts[0] != ".." else None
|
|
383
|
+
result["lte"] = (
|
|
384
|
+
parts[1] if len(parts) > 1 and parts[1] != ".." else None
|
|
385
|
+
)
|
|
386
|
+
else:
|
|
387
|
+
converted_time = interval if interval != ".." else None
|
|
388
|
+
result["gte"] = result["lte"] = converted_time
|
|
389
|
+
return result
|
|
390
|
+
|
|
391
|
+
if isinstance(interval, datetime_type):
|
|
392
|
+
datetime_iso = interval.isoformat()
|
|
393
|
+
result["gte"] = result["lte"] = datetime_iso
|
|
394
|
+
elif isinstance(interval, tuple):
|
|
395
|
+
start, end = interval
|
|
396
|
+
# Ensure datetimes are converted to UTC and formatted with 'Z'
|
|
397
|
+
if start:
|
|
398
|
+
result["gte"] = start.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
399
|
+
if end:
|
|
400
|
+
result["lte"] = end.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
401
|
+
|
|
402
|
+
return result
|
|
392
403
|
|
|
393
404
|
async def get_search(
|
|
394
405
|
self,
|
|
395
406
|
request: Request,
|
|
396
407
|
collections: Optional[List[str]] = None,
|
|
397
408
|
ids: Optional[List[str]] = None,
|
|
398
|
-
bbox: Optional[
|
|
399
|
-
datetime: Optional[
|
|
409
|
+
bbox: Optional[BBox] = None,
|
|
410
|
+
datetime: Optional[DateTimeType] = None,
|
|
400
411
|
limit: Optional[int] = 10,
|
|
401
412
|
query: Optional[str] = None,
|
|
402
413
|
token: Optional[str] = None,
|
|
@@ -412,8 +423,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
412
423
|
Args:
|
|
413
424
|
collections (Optional[List[str]]): List of collection IDs to search in.
|
|
414
425
|
ids (Optional[List[str]]): List of item IDs to search for.
|
|
415
|
-
bbox (Optional[
|
|
416
|
-
datetime (Optional[
|
|
426
|
+
bbox (Optional[BBox]): Bounding box to search in.
|
|
427
|
+
datetime (Optional[DateTimeType]): Filter items based on the datetime field.
|
|
417
428
|
limit (Optional[int]): Maximum number of results to return.
|
|
418
429
|
query (Optional[str]): Query string to filter the results.
|
|
419
430
|
token (Optional[str]): Access token to use when searching the catalog.
|
|
@@ -460,7 +471,6 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
460
471
|
"direction": "desc" if sort[0] == "-" else "asc",
|
|
461
472
|
}
|
|
462
473
|
)
|
|
463
|
-
print(sort_param)
|
|
464
474
|
base_args["sortby"] = sort_param
|
|
465
475
|
|
|
466
476
|
if filter:
|
|
@@ -667,7 +677,7 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
667
677
|
else:
|
|
668
678
|
item = await self.database.prep_create_item(item=item, base_url=base_url)
|
|
669
679
|
await self.database.create_item(item, refresh=kwargs.get("refresh", False))
|
|
670
|
-
return item
|
|
680
|
+
return ItemSerializer.db_to_stac(item, base_url)
|
|
671
681
|
|
|
672
682
|
@overrides
|
|
673
683
|
async def update_item(
|
|
@@ -731,10 +741,9 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
731
741
|
ConflictError: If the collection already exists.
|
|
732
742
|
"""
|
|
733
743
|
base_url = str(kwargs["request"].base_url)
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
)
|
|
737
|
-
collection["links"] = collection_links
|
|
744
|
+
collection = self.database.collection_serializer.stac_to_db(
|
|
745
|
+
collection, base_url
|
|
746
|
+
)
|
|
738
747
|
await self.database.create_collection(collection=collection)
|
|
739
748
|
|
|
740
749
|
return CollectionSerializer.db_to_stac(collection, base_url)
|
|
@@ -767,11 +776,9 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
767
776
|
"collection_id", collection["id"]
|
|
768
777
|
)
|
|
769
778
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
)
|
|
773
|
-
collection["links"] = collection_links
|
|
774
|
-
|
|
779
|
+
collection = self.database.collection_serializer.stac_to_db(
|
|
780
|
+
collection, base_url
|
|
781
|
+
)
|
|
775
782
|
await self.database.update_collection(
|
|
776
783
|
collection_id=collection_id, collection=collection
|
|
777
784
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""link helpers."""
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
|
4
|
-
from urllib.parse import ParseResult, parse_qs,
|
|
4
|
+
from urllib.parse import ParseResult, parse_qs, urlencode, urljoin, urlparse
|
|
5
5
|
|
|
6
6
|
import attr
|
|
7
7
|
from stac_pydantic.links import Relations
|
|
@@ -20,7 +20,7 @@ def merge_params(url: str, newparams: Dict) -> str:
|
|
|
20
20
|
u = urlparse(url)
|
|
21
21
|
params = parse_qs(u.query)
|
|
22
22
|
params.update(newparams)
|
|
23
|
-
param_string =
|
|
23
|
+
param_string = urlencode(params, True)
|
|
24
24
|
|
|
25
25
|
href = ParseResult(
|
|
26
26
|
scheme=u.scheme,
|
|
@@ -60,11 +60,7 @@ class ItemSerializer(Serializer):
|
|
|
60
60
|
Returns:
|
|
61
61
|
stac_types.Item: The database-ready STAC item object.
|
|
62
62
|
"""
|
|
63
|
-
item_links =
|
|
64
|
-
collection_id=stac_data["collection"],
|
|
65
|
-
item_id=stac_data["id"],
|
|
66
|
-
base_url=base_url,
|
|
67
|
-
).create_links()
|
|
63
|
+
item_links = resolve_links(stac_data.get("links", []), base_url)
|
|
68
64
|
stac_data["links"] = item_links
|
|
69
65
|
|
|
70
66
|
now = now_to_rfc3339_str()
|
|
@@ -111,6 +107,24 @@ class ItemSerializer(Serializer):
|
|
|
111
107
|
class CollectionSerializer(Serializer):
|
|
112
108
|
"""Serialization methods for STAC collections."""
|
|
113
109
|
|
|
110
|
+
@classmethod
|
|
111
|
+
def stac_to_db(
|
|
112
|
+
cls, collection: stac_types.Collection, base_url: str
|
|
113
|
+
) -> stac_types.Collection:
|
|
114
|
+
"""
|
|
115
|
+
Transform STAC Collection to database-ready STAC collection.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
stac_data: the STAC Collection object to be transformed
|
|
119
|
+
base_url: the base URL for the STAC API
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
stac_types.Collection: The database-ready STAC Collection object.
|
|
123
|
+
"""
|
|
124
|
+
collection = deepcopy(collection)
|
|
125
|
+
collection["links"] = resolve_links(collection.get("links", []), base_url)
|
|
126
|
+
return collection
|
|
127
|
+
|
|
114
128
|
@classmethod
|
|
115
129
|
def db_to_stac(cls, collection: dict, base_url: str) -> stac_types.Collection:
|
|
116
130
|
"""Transform database model to STAC collection.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "2.1
|
|
2
|
+
__version__ = "2.3.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi.core
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -18,13 +18,14 @@ Requires-Dist: fastapi
|
|
|
18
18
|
Requires-Dist: attrs
|
|
19
19
|
Requires-Dist: pydantic[dotenv]<2
|
|
20
20
|
Requires-Dist: stac_pydantic==2.0.*
|
|
21
|
-
Requires-Dist: stac-fastapi.types==2.
|
|
22
|
-
Requires-Dist: stac-fastapi.api==2.
|
|
23
|
-
Requires-Dist: stac-fastapi.extensions==2.
|
|
21
|
+
Requires-Dist: stac-fastapi.types==2.5.5.post1
|
|
22
|
+
Requires-Dist: stac-fastapi.api==2.5.5.post1
|
|
23
|
+
Requires-Dist: stac-fastapi.extensions==2.5.5.post1
|
|
24
24
|
Requires-Dist: pystac[validation]
|
|
25
25
|
Requires-Dist: orjson
|
|
26
26
|
Requires-Dist: overrides
|
|
27
27
|
Requires-Dist: geojson-pydantic
|
|
28
28
|
Requires-Dist: pygeofilter==0.2.1
|
|
29
|
+
Requires-Dist: typing_extensions==4.4.0
|
|
29
30
|
|
|
30
31
|
# stac-fastapi core library for Elasticsearch and Opensearch backends
|
|
@@ -10,6 +10,7 @@ stac_fastapi.core.egg-info/top_level.txt
|
|
|
10
10
|
stac_fastapi/core/__init__.py
|
|
11
11
|
stac_fastapi/core/base_database_logic.py
|
|
12
12
|
stac_fastapi/core/base_settings.py
|
|
13
|
+
stac_fastapi/core/basic_auth.py
|
|
13
14
|
stac_fastapi/core/core.py
|
|
14
15
|
stac_fastapi/core/datetime_utils.py
|
|
15
16
|
stac_fastapi/core/serializers.py
|
|
@@ -2,11 +2,12 @@ fastapi
|
|
|
2
2
|
attrs
|
|
3
3
|
pydantic[dotenv]<2
|
|
4
4
|
stac_pydantic==2.0.*
|
|
5
|
-
stac-fastapi.types==2.
|
|
6
|
-
stac-fastapi.api==2.
|
|
7
|
-
stac-fastapi.extensions==2.
|
|
5
|
+
stac-fastapi.types==2.5.5.post1
|
|
6
|
+
stac-fastapi.api==2.5.5.post1
|
|
7
|
+
stac-fastapi.extensions==2.5.5.post1
|
|
8
8
|
pystac[validation]
|
|
9
9
|
orjson
|
|
10
10
|
overrides
|
|
11
11
|
geojson-pydantic
|
|
12
12
|
pygeofilter==0.2.1
|
|
13
|
+
typing_extensions==4.4.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/base_database_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi/core/extensions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{stac-fastapi.core-2.1.0 → stac_fastapi_core-2.3.1}/stac_fastapi.core.egg-info/top_level.txt
RENAMED
|
File without changes
|