stac-fastapi-core 6.5.1__py3-none-any.whl → 6.7.0__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.
- stac_fastapi/core/base_database_logic.py +14 -2
- stac_fastapi/core/core.py +102 -48
- stac_fastapi/core/extensions/collections_search.py +200 -10
- stac_fastapi/core/redis_utils.py +301 -0
- stac_fastapi/core/serializers.py +6 -0
- stac_fastapi/core/utilities.py +1 -9
- stac_fastapi/core/version.py +1 -1
- stac_fastapi_core-6.7.0.dist-info/METADATA +66 -0
- {stac_fastapi_core-6.5.1.dist-info → stac_fastapi_core-6.7.0.dist-info}/RECORD +10 -10
- {stac_fastapi_core-6.5.1.dist-info → stac_fastapi_core-6.7.0.dist-info}/WHEEL +1 -2
- stac_fastapi_core-6.5.1.dist-info/METADATA +0 -726
- stac_fastapi_core-6.5.1.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Utilities for connecting to and managing Redis connections."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from typing import List, Optional, Tuple
|
|
6
|
+
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
|
7
|
+
|
|
8
|
+
from pydantic import field_validator
|
|
9
|
+
from pydantic_settings import BaseSettings
|
|
10
|
+
from redis import asyncio as aioredis
|
|
11
|
+
from redis.asyncio.sentinel import Sentinel
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RedisSentinelSettings(BaseSettings):
|
|
17
|
+
"""Configuration for connecting to Redis Sentinel."""
|
|
18
|
+
|
|
19
|
+
REDIS_SENTINEL_HOSTS: str = ""
|
|
20
|
+
REDIS_SENTINEL_PORTS: str = "26379"
|
|
21
|
+
REDIS_SENTINEL_MASTER_NAME: str = "master"
|
|
22
|
+
REDIS_DB: int = 15
|
|
23
|
+
|
|
24
|
+
REDIS_MAX_CONNECTIONS: int = 10
|
|
25
|
+
REDIS_RETRY_TIMEOUT: bool = True
|
|
26
|
+
REDIS_DECODE_RESPONSES: bool = True
|
|
27
|
+
REDIS_CLIENT_NAME: str = "stac-fastapi-app"
|
|
28
|
+
REDIS_HEALTH_CHECK_INTERVAL: int = 30
|
|
29
|
+
REDIS_SELF_LINK_TTL: int = 1800
|
|
30
|
+
|
|
31
|
+
@field_validator("REDIS_DB")
|
|
32
|
+
@classmethod
|
|
33
|
+
def validate_db_sentinel(cls, v: int) -> int:
|
|
34
|
+
"""Validate REDIS_DB is not negative integer."""
|
|
35
|
+
if v < 0:
|
|
36
|
+
raise ValueError("REDIS_DB must be a positive integer")
|
|
37
|
+
return v
|
|
38
|
+
|
|
39
|
+
@field_validator("REDIS_MAX_CONNECTIONS")
|
|
40
|
+
@classmethod
|
|
41
|
+
def validate_max_connections_sentinel(cls, v: int) -> int:
|
|
42
|
+
"""Validate REDIS_MAX_CONNECTIONS is at least 1."""
|
|
43
|
+
if v < 1:
|
|
44
|
+
raise ValueError("REDIS_MAX_CONNECTIONS must be at least 1")
|
|
45
|
+
return v
|
|
46
|
+
|
|
47
|
+
@field_validator("REDIS_HEALTH_CHECK_INTERVAL")
|
|
48
|
+
@classmethod
|
|
49
|
+
def validate_health_check_interval_sentinel(cls, v: int) -> int:
|
|
50
|
+
"""Validate REDIS_HEALTH_CHECK_INTERVAL is not negative integer."""
|
|
51
|
+
if v < 0:
|
|
52
|
+
raise ValueError("REDIS_HEALTH_CHECK_INTERVAL must be a positive integer")
|
|
53
|
+
return v
|
|
54
|
+
|
|
55
|
+
@field_validator("REDIS_SELF_LINK_TTL")
|
|
56
|
+
@classmethod
|
|
57
|
+
def validate_self_link_ttl_sentinel(cls, v: int) -> int:
|
|
58
|
+
"""Validate REDIS_SELF_LINK_TTL is not a negative integer."""
|
|
59
|
+
if v < 0:
|
|
60
|
+
raise ValueError("REDIS_SELF_LINK_TTL must be a positive integer")
|
|
61
|
+
return v
|
|
62
|
+
|
|
63
|
+
def get_sentinel_hosts(self) -> List[str]:
|
|
64
|
+
"""Parse Redis Sentinel hosts from string to list."""
|
|
65
|
+
if not self.REDIS_SENTINEL_HOSTS:
|
|
66
|
+
return []
|
|
67
|
+
|
|
68
|
+
if self.REDIS_SENTINEL_HOSTS.strip().startswith("["):
|
|
69
|
+
return json.loads(self.REDIS_SENTINEL_HOSTS)
|
|
70
|
+
else:
|
|
71
|
+
return [
|
|
72
|
+
h.strip() for h in self.REDIS_SENTINEL_HOSTS.split(",") if h.strip()
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
def get_sentinel_ports(self) -> List[int]:
|
|
76
|
+
"""Parse Redis Sentinel ports from string to list of integers."""
|
|
77
|
+
if not self.REDIS_SENTINEL_PORTS:
|
|
78
|
+
return [26379]
|
|
79
|
+
|
|
80
|
+
if self.REDIS_SENTINEL_PORTS.strip().startswith("["):
|
|
81
|
+
return json.loads(self.REDIS_SENTINEL_PORTS)
|
|
82
|
+
else:
|
|
83
|
+
ports_str_list = [
|
|
84
|
+
p.strip() for p in self.REDIS_SENTINEL_PORTS.split(",") if p.strip()
|
|
85
|
+
]
|
|
86
|
+
return [int(port) for port in ports_str_list]
|
|
87
|
+
|
|
88
|
+
def get_sentinel_nodes(self) -> List[Tuple[str, int]]:
|
|
89
|
+
"""Get list of (host, port) tuples for Sentinel connection."""
|
|
90
|
+
hosts = self.get_sentinel_hosts()
|
|
91
|
+
ports = self.get_sentinel_ports()
|
|
92
|
+
|
|
93
|
+
if not hosts:
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
if len(ports) == 1 and len(hosts) > 1:
|
|
97
|
+
ports = ports * len(hosts)
|
|
98
|
+
|
|
99
|
+
if len(hosts) != len(ports):
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"Mismatch between hosts ({len(hosts)}) and ports ({len(ports)})"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return [(str(host), int(port)) for host, port in zip(hosts, ports)]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RedisSettings(BaseSettings):
|
|
108
|
+
"""Configuration for connecting Redis."""
|
|
109
|
+
|
|
110
|
+
REDIS_HOST: str = ""
|
|
111
|
+
REDIS_PORT: int = 6379
|
|
112
|
+
REDIS_DB: int = 15
|
|
113
|
+
|
|
114
|
+
REDIS_MAX_CONNECTIONS: int = 10
|
|
115
|
+
REDIS_RETRY_TIMEOUT: bool = True
|
|
116
|
+
REDIS_DECODE_RESPONSES: bool = True
|
|
117
|
+
REDIS_CLIENT_NAME: str = "stac-fastapi-app"
|
|
118
|
+
REDIS_HEALTH_CHECK_INTERVAL: int = 30
|
|
119
|
+
REDIS_SELF_LINK_TTL: int = 1800
|
|
120
|
+
|
|
121
|
+
@field_validator("REDIS_PORT")
|
|
122
|
+
@classmethod
|
|
123
|
+
def validate_port_standalone(cls, v: int) -> int:
|
|
124
|
+
"""Validate REDIS_PORT is not a negative integer."""
|
|
125
|
+
if v < 0:
|
|
126
|
+
raise ValueError("REDIS_PORT must be a positive integer")
|
|
127
|
+
return v
|
|
128
|
+
|
|
129
|
+
@field_validator("REDIS_DB")
|
|
130
|
+
@classmethod
|
|
131
|
+
def validate_db_standalone(cls, v: int) -> int:
|
|
132
|
+
"""Validate REDIS_DB is not a negative integer."""
|
|
133
|
+
if v < 0:
|
|
134
|
+
raise ValueError("REDIS_DB must be a positive integer")
|
|
135
|
+
return v
|
|
136
|
+
|
|
137
|
+
@field_validator("REDIS_MAX_CONNECTIONS")
|
|
138
|
+
@classmethod
|
|
139
|
+
def validate_max_connections_standalone(cls, v: int) -> int:
|
|
140
|
+
"""Validate REDIS_MAX_CONNECTIONS is at least 1."""
|
|
141
|
+
if v < 1:
|
|
142
|
+
raise ValueError("REDIS_MAX_CONNECTIONS must be at least 1")
|
|
143
|
+
return v
|
|
144
|
+
|
|
145
|
+
@field_validator("REDIS_HEALTH_CHECK_INTERVAL")
|
|
146
|
+
@classmethod
|
|
147
|
+
def validate_health_check_interval_standalone(cls, v: int) -> int:
|
|
148
|
+
"""Validate REDIS_HEALTH_CHECK_INTERVAL is not a negative."""
|
|
149
|
+
if v < 0:
|
|
150
|
+
raise ValueError("REDIS_HEALTH_CHECK_INTERVAL must be a positive integer")
|
|
151
|
+
return v
|
|
152
|
+
|
|
153
|
+
@field_validator("REDIS_SELF_LINK_TTL")
|
|
154
|
+
@classmethod
|
|
155
|
+
def validate_self_link_ttl_standalone(cls, v: int) -> int:
|
|
156
|
+
"""Validate REDIS_SELF_LINK_TTL is negative."""
|
|
157
|
+
if v < 0:
|
|
158
|
+
raise ValueError("REDIS_SELF_LINK_TTL must be a positive integer")
|
|
159
|
+
return v
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Configure only one Redis configuration
|
|
163
|
+
sentinel_settings = RedisSentinelSettings()
|
|
164
|
+
standalone_settings = RedisSettings()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def connect_redis() -> Optional[aioredis.Redis]:
|
|
168
|
+
"""Return a Redis connection Redis or Redis Sentinel."""
|
|
169
|
+
try:
|
|
170
|
+
if sentinel_settings.REDIS_SENTINEL_HOSTS:
|
|
171
|
+
sentinel_nodes = sentinel_settings.get_sentinel_nodes()
|
|
172
|
+
sentinel = Sentinel(
|
|
173
|
+
sentinel_nodes,
|
|
174
|
+
decode_responses=sentinel_settings.REDIS_DECODE_RESPONSES,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
redis = sentinel.master_for(
|
|
178
|
+
service_name=sentinel_settings.REDIS_SENTINEL_MASTER_NAME,
|
|
179
|
+
db=sentinel_settings.REDIS_DB,
|
|
180
|
+
decode_responses=sentinel_settings.REDIS_DECODE_RESPONSES,
|
|
181
|
+
retry_on_timeout=sentinel_settings.REDIS_RETRY_TIMEOUT,
|
|
182
|
+
client_name=sentinel_settings.REDIS_CLIENT_NAME,
|
|
183
|
+
max_connections=sentinel_settings.REDIS_MAX_CONNECTIONS,
|
|
184
|
+
health_check_interval=sentinel_settings.REDIS_HEALTH_CHECK_INTERVAL,
|
|
185
|
+
)
|
|
186
|
+
logger.info("Connected to Redis Sentinel")
|
|
187
|
+
|
|
188
|
+
elif standalone_settings.REDIS_HOST:
|
|
189
|
+
pool = aioredis.ConnectionPool(
|
|
190
|
+
host=standalone_settings.REDIS_HOST,
|
|
191
|
+
port=standalone_settings.REDIS_PORT,
|
|
192
|
+
db=standalone_settings.REDIS_DB,
|
|
193
|
+
max_connections=standalone_settings.REDIS_MAX_CONNECTIONS,
|
|
194
|
+
decode_responses=standalone_settings.REDIS_DECODE_RESPONSES,
|
|
195
|
+
retry_on_timeout=standalone_settings.REDIS_RETRY_TIMEOUT,
|
|
196
|
+
health_check_interval=standalone_settings.REDIS_HEALTH_CHECK_INTERVAL,
|
|
197
|
+
)
|
|
198
|
+
redis = aioredis.Redis(
|
|
199
|
+
connection_pool=pool, client_name=standalone_settings.REDIS_CLIENT_NAME
|
|
200
|
+
)
|
|
201
|
+
logger.info("Connected to Redis")
|
|
202
|
+
else:
|
|
203
|
+
logger.warning("No Redis configuration found")
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
return redis
|
|
207
|
+
|
|
208
|
+
except aioredis.ConnectionError as e:
|
|
209
|
+
logger.error(f"Redis connection error: {e}")
|
|
210
|
+
return None
|
|
211
|
+
except aioredis.AuthenticationError as e:
|
|
212
|
+
logger.error(f"Redis authentication error: {e}")
|
|
213
|
+
return None
|
|
214
|
+
except aioredis.TimeoutError as e:
|
|
215
|
+
logger.error(f"Redis timeout error: {e}")
|
|
216
|
+
return None
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.error(f"Failed to connect to Redis: {e}")
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def get_redis_key(url: str, token: str) -> str:
|
|
223
|
+
"""Create Redis key using URL path and token."""
|
|
224
|
+
parsed = urlparse(url)
|
|
225
|
+
return f"nav:{parsed.path}:{token}"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def build_url_with_token(base_url: str, token: str) -> str:
|
|
229
|
+
"""Build URL with token parameter."""
|
|
230
|
+
parsed = urlparse(base_url)
|
|
231
|
+
query_params = parse_qs(parsed.query)
|
|
232
|
+
|
|
233
|
+
query_params["token"] = [token]
|
|
234
|
+
|
|
235
|
+
new_query = urlencode(query_params, doseq=True)
|
|
236
|
+
|
|
237
|
+
return urlunparse(
|
|
238
|
+
(
|
|
239
|
+
parsed.scheme,
|
|
240
|
+
parsed.netloc,
|
|
241
|
+
parsed.path,
|
|
242
|
+
parsed.params,
|
|
243
|
+
new_query,
|
|
244
|
+
parsed.fragment,
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
async def save_prev_link(
|
|
250
|
+
redis: aioredis.Redis, next_url: str, current_url: str, next_token: str
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Save the current page as the previous link for the next URL."""
|
|
253
|
+
if next_url and next_token:
|
|
254
|
+
if sentinel_settings.REDIS_SENTINEL_HOSTS:
|
|
255
|
+
ttl_seconds = sentinel_settings.REDIS_SELF_LINK_TTL
|
|
256
|
+
elif standalone_settings.REDIS_HOST:
|
|
257
|
+
ttl_seconds = standalone_settings.REDIS_SELF_LINK_TTL
|
|
258
|
+
key = get_redis_key(next_url, next_token)
|
|
259
|
+
await redis.setex(key, ttl_seconds, current_url)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
async def get_prev_link(
|
|
263
|
+
redis: aioredis.Redis, current_url: str, current_token: str
|
|
264
|
+
) -> Optional[str]:
|
|
265
|
+
"""Get the previous page link for the current token."""
|
|
266
|
+
if not current_url or not current_token:
|
|
267
|
+
return None
|
|
268
|
+
key = get_redis_key(current_url, current_token)
|
|
269
|
+
return await redis.get(key)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def redis_pagination_links(
|
|
273
|
+
current_url: str, token: str, next_token: str, links: list
|
|
274
|
+
) -> None:
|
|
275
|
+
"""Handle Redis pagination."""
|
|
276
|
+
redis = await connect_redis()
|
|
277
|
+
if not redis:
|
|
278
|
+
logger.warning("Redis connection failed.")
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
if next_token:
|
|
283
|
+
next_url = build_url_with_token(current_url, next_token)
|
|
284
|
+
await save_prev_link(redis, next_url, current_url, next_token)
|
|
285
|
+
|
|
286
|
+
if token:
|
|
287
|
+
prev_link = await get_prev_link(redis, current_url, token)
|
|
288
|
+
if prev_link:
|
|
289
|
+
links.insert(
|
|
290
|
+
0,
|
|
291
|
+
{
|
|
292
|
+
"rel": "previous",
|
|
293
|
+
"type": "application/json",
|
|
294
|
+
"method": "GET",
|
|
295
|
+
"href": prev_link,
|
|
296
|
+
},
|
|
297
|
+
)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
logger.warning(f"Redis pagination operation failed: {e}")
|
|
300
|
+
finally:
|
|
301
|
+
await redis.close()
|
stac_fastapi/core/serializers.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Serializers."""
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
+
import logging
|
|
4
5
|
from copy import deepcopy
|
|
5
6
|
from typing import Any, List, Optional
|
|
6
7
|
|
|
@@ -13,6 +14,8 @@ from stac_fastapi.core.utilities import get_bool_env
|
|
|
13
14
|
from stac_fastapi.types import stac as stac_types
|
|
14
15
|
from stac_fastapi.types.links import ItemLinks, resolve_links
|
|
15
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
@attr.s
|
|
18
21
|
class Serializer(abc.ABC):
|
|
@@ -168,6 +171,9 @@ class CollectionSerializer(Serializer):
|
|
|
168
171
|
# Avoid modifying the input dict in-place ... doing so breaks some tests
|
|
169
172
|
collection = deepcopy(collection)
|
|
170
173
|
|
|
174
|
+
# Remove internal bbox_shape field (not part of STAC spec)
|
|
175
|
+
collection.pop("bbox_shape", None)
|
|
176
|
+
|
|
171
177
|
# Set defaults
|
|
172
178
|
collection_id = collection.get("id")
|
|
173
179
|
collection.setdefault("type", "Collection")
|
stac_fastapi/core/utilities.py
CHANGED
|
@@ -10,15 +10,7 @@ from typing import Any, Dict, List, Optional, Set, Union
|
|
|
10
10
|
|
|
11
11
|
from stac_fastapi.types.stac import Item
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
def get_max_limit():
|
|
15
|
-
"""
|
|
16
|
-
Retrieve a MAX_LIMIT value from an environment variable.
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
int: The int value parsed from the environment variable.
|
|
20
|
-
"""
|
|
21
|
-
return int(os.getenv("ENV_MAX_LIMIT", 10000))
|
|
13
|
+
MAX_LIMIT = 10000
|
|
22
14
|
|
|
23
15
|
|
|
24
16
|
def get_bool_env(name: str, default: Union[bool, str] = False) -> bool:
|
stac_fastapi/core/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "6.
|
|
2
|
+
__version__ = "6.7.0"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stac_fastapi_core
|
|
3
|
+
Version: 6.7.0
|
|
4
|
+
Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
|
|
5
|
+
Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: Elasticsearch,FastAPI,Opensearch,STAC,STAC-API,stac-fastapi
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Intended Audience :: Information Technology
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Requires-Dist: attrs>=23.2.0
|
|
20
|
+
Requires-Dist: fastapi~=0.109.0
|
|
21
|
+
Requires-Dist: geojson-pydantic~=1.0.0
|
|
22
|
+
Requires-Dist: jsonschema~=4.0.0
|
|
23
|
+
Requires-Dist: orjson~=3.11.0
|
|
24
|
+
Requires-Dist: overrides~=7.4.0
|
|
25
|
+
Requires-Dist: pydantic<3.0.0,>=2.4.1
|
|
26
|
+
Requires-Dist: pygeofilter~=0.3.1
|
|
27
|
+
Requires-Dist: redis==6.4.0
|
|
28
|
+
Requires-Dist: slowapi~=0.1.9
|
|
29
|
+
Requires-Dist: stac-fastapi-api==6.0.0
|
|
30
|
+
Requires-Dist: stac-fastapi-extensions==6.0.0
|
|
31
|
+
Requires-Dist: stac-fastapi-types==6.0.0
|
|
32
|
+
Requires-Dist: stac-pydantic~=3.3.0
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# stac-fastapi-core
|
|
36
|
+
|
|
37
|
+
<p align="left">
|
|
38
|
+
<img src="https://raw.githubusercontent.com/stac-utils/stac-fastapi-elasticsearch-opensearch/refs/heads/main/assets/sfeos.png" width=1000>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
[](https://pepy.tech/project/stac-fastapi-core)
|
|
42
|
+
[](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/graphs/contributors)
|
|
43
|
+
[](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/stargazers)
|
|
44
|
+
[](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/network/members)
|
|
45
|
+
[](https://pypi.org/project/stac-fastapi-elasticsearch/)
|
|
46
|
+
[](https://github.com/radiantearth/stac-spec/tree/v1.1.0)
|
|
47
|
+
[](https://github.com/stac-utils/stac-fastapi)
|
|
48
|
+
|
|
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
|
+
|
|
51
|
+
## Package Information
|
|
52
|
+
|
|
53
|
+
- **Package name**: stac-fastapi-core
|
|
54
|
+
- **Description**: Core functionality for STAC API implementations.
|
|
55
|
+
- **Documentation**: [https://stac-utils.github.io/stac-fastapi-elasticsearch-opensearch/](https://stac-utils.github.io/stac-fastapi-elasticsearch-opensearch/)
|
|
56
|
+
- **Source**: [GitHub Repository](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/)
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install stac-fastapi-core
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
For detailed usage and examples, please refer to the [main documentation](https://stac-utils.github.io/stac-fastapi-elasticsearch-opensearch/).
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
stac_fastapi/core/__init__.py,sha256=8izV3IWRGdXmDOK1hIPQAanbWs9EI04PJCGgqG1ZGIs,20
|
|
2
|
-
stac_fastapi/core/base_database_logic.py,sha256=
|
|
2
|
+
stac_fastapi/core/base_database_logic.py,sha256=3_XJ_j06ogQHE-Tcjkv5Vye_zNDn9OEU9lNYU03am1k,4618
|
|
3
3
|
stac_fastapi/core/base_settings.py,sha256=R3_Sx7n5XpGMs3zAwFJD7y008WvGU_uI2xkaabm82Kg,239
|
|
4
4
|
stac_fastapi/core/basic_auth.py,sha256=RhFv3RVSHF6OaqnaaU2DO4ncJ_S5nB1q8UNpnVJJsrk,2155
|
|
5
|
-
stac_fastapi/core/core.py,sha256=
|
|
5
|
+
stac_fastapi/core/core.py,sha256=0_H0QKQN-fkvzAidqGt8MvYfO2f_OF82pXXYs9HpQ_4,50387
|
|
6
6
|
stac_fastapi/core/datetime_utils.py,sha256=TrTgbU7AKNC-ic4a3HptfE5XAc9tHR7uJasZyhOuwnc,2633
|
|
7
7
|
stac_fastapi/core/rate_limit.py,sha256=Gu8dAaJReGsj1L91U6m2tflU6RahpXDRs2-AYSKoybA,1318
|
|
8
|
+
stac_fastapi/core/redis_utils.py,sha256=hoghKhq7eHkqttU6T_Yq6uSWPPfgFjz9HrN8nMLJqNo,10440
|
|
8
9
|
stac_fastapi/core/route_dependencies.py,sha256=hdtuMkv-zY1vg0YxiCz1aKP0SbBcORqDGEKDGgEazW8,5482
|
|
9
|
-
stac_fastapi/core/serializers.py,sha256=
|
|
10
|
+
stac_fastapi/core/serializers.py,sha256=ZW5hPgq-mftk6zxJeZGur-1Qxn7YGc3fJYFLsd-SYwM,7619
|
|
10
11
|
stac_fastapi/core/session.py,sha256=aXqu4LXfVbAAsChMVXd9gAhczA2bZPne6HqPeklAwMY,474
|
|
11
|
-
stac_fastapi/core/utilities.py,sha256=
|
|
12
|
-
stac_fastapi/core/version.py,sha256=
|
|
12
|
+
stac_fastapi/core/utilities.py,sha256=xXWO5oJCNDi7_C5jPYlHZD0B-DL-FN66eEUBUSW-cXw,7296
|
|
13
|
+
stac_fastapi/core/version.py,sha256=GMc7YzxyWeUVpr_RMVlweeoH2lCLxOWemF-FOkqKXx8,45
|
|
13
14
|
stac_fastapi/core/extensions/__init__.py,sha256=zSIAqou8jnakWPbkh4Ddcx1-oazZVBOs7U2PAakAdU0,291
|
|
14
15
|
stac_fastapi/core/extensions/aggregation.py,sha256=v1hUHqlYuMqfQ554g3cTp16pUyRYucQxPERbHPAFtf8,1878
|
|
15
|
-
stac_fastapi/core/extensions/collections_search.py,sha256=
|
|
16
|
+
stac_fastapi/core/extensions/collections_search.py,sha256=xpv51nffMq5a8grNSaLbv2IzeI5JH_pqcoWRbWhzn6Y,14406
|
|
16
17
|
stac_fastapi/core/extensions/fields.py,sha256=NCT5XHvfaf297eDPNaIFsIzvJnbbUTpScqF0otdx0NA,1066
|
|
17
18
|
stac_fastapi/core/extensions/filter.py,sha256=-NQGME7rR_ereuDx-LAa1M5JhEXFaKiTtkH2asraYHE,2998
|
|
18
19
|
stac_fastapi/core/extensions/query.py,sha256=Xmo8pfZEZKPudZEjjozv3R0wLOP0ayjC9E67sBOXqWY,1803
|
|
19
20
|
stac_fastapi/core/models/__init__.py,sha256=g-D1DiGfmC9Bg27DW9JzkN6fAvscv75wyhyiZ6NzvIk,48
|
|
20
21
|
stac_fastapi/core/models/links.py,sha256=0dWSEMt3aa7NCISlHwo11zLBeIV1LwXG3JGjrXC3dZI,6672
|
|
21
22
|
stac_fastapi/core/models/search.py,sha256=7SgAUyzHGXBXSqB4G6cwq9FMwoAS00momb7jvBkjyow,27
|
|
22
|
-
stac_fastapi_core-6.
|
|
23
|
-
stac_fastapi_core-6.
|
|
24
|
-
stac_fastapi_core-6.
|
|
25
|
-
stac_fastapi_core-6.5.1.dist-info/RECORD,,
|
|
23
|
+
stac_fastapi_core-6.7.0.dist-info/METADATA,sha256=K8U8uKNxI9D5JM_ZIqaOV0wi0TLPgSwK9Qv4ca2VCb4,3494
|
|
24
|
+
stac_fastapi_core-6.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
25
|
+
stac_fastapi_core-6.7.0.dist-info/RECORD,,
|