stac-fastapi-core 6.7.5__tar.gz → 6.8.0__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-6.7.5 → stac_fastapi_core-6.8.0}/PKG-INFO +2 -2
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/README.md +1 -1
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/base_database_logic.py +40 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/core.py +49 -4
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/datetime_utils.py +4 -1
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/__init__.py +2 -0
- stac_fastapi_core-6.8.0/stac_fastapi/core/extensions/catalogs.py +995 -0
- stac_fastapi_core-6.8.0/stac_fastapi/core/models/__init__.py +27 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/models/links.py +9 -2
- stac_fastapi_core-6.8.0/stac_fastapi/core/queryables.py +105 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/serializers.py +147 -1
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/version.py +1 -1
- stac_fastapi_core-6.7.5/stac_fastapi/core/models/__init__.py +0 -1
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/.gitignore +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/pyproject.toml +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/pytest.ini +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/__init__.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/base_settings.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/basic_auth.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/aggregation.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/collections_search.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/fields.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/filter.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/query.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/models/search.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/rate_limit.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/redis_utils.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/route_dependencies.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/session.py +0 -0
- {stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/utilities.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stac_fastapi_core
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.8.0
|
|
4
4
|
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
5
|
Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
6
|
License: MIT
|
|
@@ -44,7 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
[](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/network/members)
|
|
45
45
|
[](https://pypi.org/project/stac-fastapi-elasticsearch/)
|
|
46
46
|
[](https://github.com/radiantearth/stac-spec/tree/v1.1.0)
|
|
47
|
-
[](https://github.com/stac-utils/stac-fastapi)
|
|
48
48
|
|
|
49
49
|
Core functionality for stac-fastapi. For full documentation, please see the [main README](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/blob/main/README.md).
|
|
50
50
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/network/members)
|
|
11
11
|
[](https://pypi.org/project/stac-fastapi-elasticsearch/)
|
|
12
12
|
[](https://github.com/radiantearth/stac-spec/tree/v1.1.0)
|
|
13
|
-
[](https://github.com/stac-utils/stac-fastapi)
|
|
14
14
|
|
|
15
15
|
Core functionality for stac-fastapi. For full documentation, please see the [main README](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/blob/main/README.md).
|
|
16
16
|
|
{stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/base_database_logic.py
RENAMED
|
@@ -138,3 +138,43 @@ class BaseDatabaseLogic(abc.ABC):
|
|
|
138
138
|
) -> None:
|
|
139
139
|
"""Delete a collection from the database."""
|
|
140
140
|
pass
|
|
141
|
+
|
|
142
|
+
@abc.abstractmethod
|
|
143
|
+
async def get_queryables_mapping(self, collection_id: str = "*") -> Dict[str, Any]:
|
|
144
|
+
"""Retrieve mapping of Queryables for search."""
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
async def get_all_catalogs(
|
|
148
|
+
self,
|
|
149
|
+
token: Optional[str],
|
|
150
|
+
limit: int,
|
|
151
|
+
request: Any = None,
|
|
152
|
+
sort: Optional[List[Dict[str, Any]]] = None,
|
|
153
|
+
) -> Tuple[List[Dict[str, Any]], Optional[str], Optional[int]]:
|
|
154
|
+
"""Retrieve a list of catalogs from the database, supporting pagination.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
token (Optional[str]): The pagination token.
|
|
158
|
+
limit (int): The number of results to return.
|
|
159
|
+
request (Any, optional): The FastAPI request object. Defaults to None.
|
|
160
|
+
sort (Optional[List[Dict[str, Any]]], optional): Optional sort parameter. Defaults to None.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A tuple of (catalogs, next pagination token if any, optional count).
|
|
164
|
+
"""
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
@abc.abstractmethod
|
|
168
|
+
async def create_catalog(self, catalog: Dict, refresh: bool = False) -> None:
|
|
169
|
+
"""Create a catalog in the database."""
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
@abc.abstractmethod
|
|
173
|
+
async def find_catalog(self, catalog_id: str) -> Dict:
|
|
174
|
+
"""Find a catalog in the database."""
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
@abc.abstractmethod
|
|
178
|
+
async def delete_catalog(self, catalog_id: str, refresh: bool = False) -> None:
|
|
179
|
+
"""Delete a catalog from the database."""
|
|
180
|
+
pass
|
|
@@ -24,8 +24,15 @@ from stac_fastapi.core.base_database_logic import BaseDatabaseLogic
|
|
|
24
24
|
from stac_fastapi.core.base_settings import ApiBaseSettings
|
|
25
25
|
from stac_fastapi.core.datetime_utils import format_datetime_range
|
|
26
26
|
from stac_fastapi.core.models.links import PagingLinks
|
|
27
|
-
from stac_fastapi.core.
|
|
28
|
-
|
|
27
|
+
from stac_fastapi.core.queryables import (
|
|
28
|
+
QueryablesCache,
|
|
29
|
+
get_properties_from_cql2_filter,
|
|
30
|
+
)
|
|
31
|
+
from stac_fastapi.core.serializers import (
|
|
32
|
+
CatalogSerializer,
|
|
33
|
+
CollectionSerializer,
|
|
34
|
+
ItemSerializer,
|
|
35
|
+
)
|
|
29
36
|
from stac_fastapi.core.session import Session
|
|
30
37
|
from stac_fastapi.core.utilities import filter_fields, get_bool_env
|
|
31
38
|
from stac_fastapi.extensions.core.transaction import AsyncBaseTransactionsClient
|
|
@@ -82,12 +89,28 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
82
89
|
collection_serializer: Type[CollectionSerializer] = attr.ib(
|
|
83
90
|
default=CollectionSerializer
|
|
84
91
|
)
|
|
92
|
+
catalog_serializer: Type[CatalogSerializer] = attr.ib(default=CatalogSerializer)
|
|
85
93
|
post_request_model = attr.ib(default=BaseSearchPostRequest)
|
|
86
94
|
stac_version: str = attr.ib(default=STAC_VERSION)
|
|
87
95
|
landing_page_id: str = attr.ib(default="stac-fastapi")
|
|
88
96
|
title: str = attr.ib(default="stac-fastapi")
|
|
89
97
|
description: str = attr.ib(default="stac-fastapi")
|
|
90
98
|
|
|
99
|
+
def __attrs_post_init__(self):
|
|
100
|
+
"""Initialize the queryables cache."""
|
|
101
|
+
self.queryables_cache = QueryablesCache(self.database)
|
|
102
|
+
|
|
103
|
+
def extension_is_enabled(self, extension_name: str) -> bool:
|
|
104
|
+
"""Check if an extension is enabled by checking self.extensions.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
extension_name: Name of the extension class to check for.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
True if the extension is in self.extensions, False otherwise.
|
|
111
|
+
"""
|
|
112
|
+
return any(ext.__class__.__name__ == extension_name for ext in self.extensions)
|
|
113
|
+
|
|
91
114
|
def _landing_page(
|
|
92
115
|
self,
|
|
93
116
|
base_url: str,
|
|
@@ -151,6 +174,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
151
174
|
API landing page, serving as an entry point to the API.
|
|
152
175
|
"""
|
|
153
176
|
request: Request = kwargs["request"]
|
|
177
|
+
|
|
154
178
|
base_url = get_base_url(request)
|
|
155
179
|
landing_page = self._landing_page(
|
|
156
180
|
base_url=base_url,
|
|
@@ -208,6 +232,16 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
208
232
|
]
|
|
209
233
|
)
|
|
210
234
|
|
|
235
|
+
if self.extension_is_enabled("CatalogsExtension"):
|
|
236
|
+
landing_page["links"].append(
|
|
237
|
+
{
|
|
238
|
+
"rel": "catalogs",
|
|
239
|
+
"type": "application/json",
|
|
240
|
+
"title": "Catalogs",
|
|
241
|
+
"href": urljoin(base_url, "catalogs"),
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
|
|
211
245
|
# Add OpenAPI URL
|
|
212
246
|
landing_page["links"].append(
|
|
213
247
|
{
|
|
@@ -426,6 +460,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
426
460
|
]
|
|
427
461
|
|
|
428
462
|
if redis_enable:
|
|
463
|
+
from stac_fastapi.core.redis_utils import redis_pagination_links
|
|
464
|
+
|
|
429
465
|
await redis_pagination_links(
|
|
430
466
|
current_url=str(request.url),
|
|
431
467
|
token=token,
|
|
@@ -758,7 +794,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
758
794
|
|
|
759
795
|
body_limit = None
|
|
760
796
|
try:
|
|
761
|
-
if request.method == "POST" and request.body():
|
|
797
|
+
if request.method == "POST" and await request.body():
|
|
762
798
|
body_data = await request.json()
|
|
763
799
|
body_limit = body_data.get("limit")
|
|
764
800
|
except Exception:
|
|
@@ -790,9 +826,10 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
790
826
|
search=search, collection_ids=search_request.collections
|
|
791
827
|
)
|
|
792
828
|
|
|
829
|
+
datetime_parsed = format_datetime_range(date_str=search_request.datetime)
|
|
793
830
|
try:
|
|
794
831
|
search, datetime_search = self.database.apply_datetime_filter(
|
|
795
|
-
search=search, datetime=
|
|
832
|
+
search=search, datetime=datetime_parsed
|
|
796
833
|
)
|
|
797
834
|
except (ValueError, TypeError) as e:
|
|
798
835
|
# Handle invalid interval formats if return_date fails
|
|
@@ -815,6 +852,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
815
852
|
)
|
|
816
853
|
|
|
817
854
|
if hasattr(search_request, "query") and getattr(search_request, "query"):
|
|
855
|
+
query_fields = set(getattr(search_request, "query").keys())
|
|
856
|
+
await self.queryables_cache.validate(query_fields)
|
|
818
857
|
for field_name, expr in getattr(search_request, "query").items():
|
|
819
858
|
field = "properties__" + field_name
|
|
820
859
|
for op, value in expr.items():
|
|
@@ -833,7 +872,11 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
833
872
|
|
|
834
873
|
if cql2_filter is not None:
|
|
835
874
|
try:
|
|
875
|
+
query_fields = get_properties_from_cql2_filter(cql2_filter)
|
|
876
|
+
await self.queryables_cache.validate(query_fields)
|
|
836
877
|
search = await self.database.apply_cql2_filter(search, cql2_filter)
|
|
878
|
+
except HTTPException:
|
|
879
|
+
raise
|
|
837
880
|
except Exception as e:
|
|
838
881
|
raise HTTPException(
|
|
839
882
|
status_code=400, detail=f"Error with cql2 filter: {e}"
|
|
@@ -903,6 +946,8 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
903
946
|
links.extend(collection_links)
|
|
904
947
|
|
|
905
948
|
if redis_enable:
|
|
949
|
+
from stac_fastapi.core.redis_utils import redis_pagination_links
|
|
950
|
+
|
|
906
951
|
await redis_pagination_links(
|
|
907
952
|
current_url=str(request.url),
|
|
908
953
|
token=token_param,
|
|
@@ -32,7 +32,10 @@ def format_datetime_range(date_str: str) -> str:
|
|
|
32
32
|
dt_utc = MIN_DATE_NANOS
|
|
33
33
|
if dt_utc > MAX_DATE_NANOS:
|
|
34
34
|
dt_utc = MAX_DATE_NANOS
|
|
35
|
-
|
|
35
|
+
dt_normalized = dt_utc.isoformat(timespec="auto").replace("+00:00", "Z")
|
|
36
|
+
if "." not in dt_normalized:
|
|
37
|
+
dt_normalized = dt_normalized.replace("Z", ".0Z")
|
|
38
|
+
return dt_normalized
|
|
36
39
|
|
|
37
40
|
if not isinstance(date_str, str):
|
|
38
41
|
return f"{MIN_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}/{MAX_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}"
|
{stac_fastapi_core-6.7.5 → stac_fastapi_core-6.8.0}/stac_fastapi/core/extensions/__init__.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""elasticsearch extensions modifications."""
|
|
2
2
|
|
|
3
|
+
from .catalogs import CatalogsExtension
|
|
3
4
|
from .collections_search import CollectionsSearchEndpointExtension
|
|
4
5
|
from .query import Operator, QueryableTypes, QueryExtension
|
|
5
6
|
|
|
@@ -8,4 +9,5 @@ __all__ = [
|
|
|
8
9
|
"QueryableTypes",
|
|
9
10
|
"QueryExtension",
|
|
10
11
|
"CollectionsSearchEndpointExtension",
|
|
12
|
+
"CatalogsExtension",
|
|
11
13
|
]
|