stac-fastapi-core 6.7.3__py3-none-any.whl → 6.7.5__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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  from datetime import datetime, timezone
4
4
 
5
+ from stac_fastapi.core.utilities import get_bool_env
5
6
  from stac_fastapi.types.rfc3339 import rfc3339_str_to_datetime
6
7
 
7
8
 
@@ -15,27 +16,71 @@ def format_datetime_range(date_str: str) -> str:
15
16
  Returns:
16
17
  str: A string formatted as 'YYYY-MM-DDTHH:MM:SSZ/YYYY-MM-DDTHH:MM:SSZ', with '..' used if any element is None.
17
18
  """
18
-
19
- def normalize(dt):
20
- """Normalize datetime string and preserve millisecond precision."""
21
- dt = dt.strip()
22
- if not dt or dt == "..":
23
- return ".."
24
- dt_obj = rfc3339_str_to_datetime(dt)
25
- dt_utc = dt_obj.astimezone(timezone.utc)
26
- return dt_utc.isoformat(timespec="milliseconds").replace("+00:00", "Z")
27
-
28
- if not isinstance(date_str, str):
29
- return "../.."
30
-
31
- if "/" not in date_str:
32
- return f"{normalize(date_str)}/{normalize(date_str)}"
33
-
34
- try:
35
- start, end = date_str.split("/", 1)
36
- except Exception:
37
- return "../.."
38
- return f"{normalize(start)}/{normalize(end)}"
19
+ use_datetime_nanos = get_bool_env("USE_DATETIME_NANOS", default=True)
20
+
21
+ if use_datetime_nanos:
22
+ MIN_DATE_NANOS = datetime(1970, 1, 1, tzinfo=timezone.utc)
23
+ MAX_DATE_NANOS = datetime(2262, 4, 11, 23, 47, 16, 854775, tzinfo=timezone.utc)
24
+
25
+ def normalize(dt):
26
+ """Normalize datetime string and preserve nano second precision."""
27
+ dt = dt.strip()
28
+ if not dt or dt == "..":
29
+ return ".."
30
+ dt_utc = rfc3339_str_to_datetime(dt).astimezone(timezone.utc)
31
+ if dt_utc < MIN_DATE_NANOS:
32
+ dt_utc = MIN_DATE_NANOS
33
+ if dt_utc > MAX_DATE_NANOS:
34
+ dt_utc = MAX_DATE_NANOS
35
+ return dt_utc.isoformat(timespec="auto").replace("+00:00", "Z")
36
+
37
+ if not isinstance(date_str, str):
38
+ return f"{MIN_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}/{MAX_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}"
39
+
40
+ if "/" not in date_str:
41
+ return f"{normalize(date_str)}/{normalize(date_str)}"
42
+
43
+ try:
44
+ start, end = date_str.split("/", 1)
45
+ except Exception:
46
+ return f"{MIN_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}/{MAX_DATE_NANOS.isoformat(timespec='auto').replace('+00:00','Z')}"
47
+
48
+ normalized_start = normalize(start)
49
+ normalized_end = normalize(end)
50
+
51
+ if normalized_start == "..":
52
+ normalized_start = MIN_DATE_NANOS.isoformat(timespec="auto").replace(
53
+ "+00:00", "Z"
54
+ )
55
+ if normalized_end == "..":
56
+ normalized_end = MAX_DATE_NANOS.isoformat(timespec="auto").replace(
57
+ "+00:00", "Z"
58
+ )
59
+
60
+ return f"{normalized_start}/{normalized_end}"
61
+
62
+ else:
63
+
64
+ def normalize(dt):
65
+ """Normalize datetime string and preserve millisecond precision."""
66
+ dt = dt.strip()
67
+ if not dt or dt == "..":
68
+ return ".."
69
+ dt_obj = rfc3339_str_to_datetime(dt)
70
+ dt_utc = dt_obj.astimezone(timezone.utc)
71
+ return dt_utc.isoformat(timespec="milliseconds").replace("+00:00", "Z")
72
+
73
+ if not isinstance(date_str, str):
74
+ return "../.."
75
+
76
+ if "/" not in date_str:
77
+ return f"{normalize(date_str)}/{normalize(date_str)}"
78
+
79
+ try:
80
+ start, end = date_str.split("/", 1)
81
+ except Exception:
82
+ return "../.."
83
+ return f"{normalize(start)}/{normalize(end)}"
39
84
 
40
85
 
41
86
  # Borrowed from pystac - https://github.com/stac-utils/pystac/blob/f5e4cf4a29b62e9ef675d4a4dac7977b09f53c8f/pystac/utils.py#L370-L394
@@ -2,25 +2,25 @@
2
2
 
3
3
  import json
4
4
  import logging
5
- from typing import List, Optional, Tuple
5
+ from functools import wraps
6
+ from typing import Callable, List, Optional, Tuple, cast
6
7
  from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
7
8
 
8
9
  from pydantic import Field, field_validator
9
10
  from pydantic_settings import BaseSettings
10
11
  from redis import asyncio as aioredis
11
12
  from redis.asyncio.sentinel import Sentinel
13
+ from redis.exceptions import ConnectionError as RedisConnectionError
14
+ from redis.exceptions import TimeoutError as RedisTimeoutError
15
+ from retry import retry # type: ignore
12
16
 
13
17
  logger = logging.getLogger(__name__)
14
18
 
15
19
 
16
- class RedisSentinelSettings(BaseSettings):
17
- """Configuration for connecting to Redis Sentinel."""
20
+ class RedisCommonSettings(BaseSettings):
21
+ """Common configuration for Redis Sentinel and Redis Standalone."""
18
22
 
19
- REDIS_SENTINEL_HOSTS: str = ""
20
- REDIS_SENTINEL_PORTS: str = "26379"
21
- REDIS_SENTINEL_MASTER_NAME: str = "master"
22
23
  REDIS_DB: int = 15
23
-
24
24
  REDIS_MAX_CONNECTIONS: Optional[int] = None
25
25
  REDIS_RETRY_TIMEOUT: bool = True
26
26
  REDIS_DECODE_RESPONSES: bool = True
@@ -28,9 +28,13 @@ class RedisSentinelSettings(BaseSettings):
28
28
  REDIS_HEALTH_CHECK_INTERVAL: int = Field(default=30, gt=0)
29
29
  REDIS_SELF_LINK_TTL: int = 1800
30
30
 
31
+ REDIS_QUERY_RETRIES_NUM: int = Field(default=3, gt=0)
32
+ REDIS_QUERY_INITIAL_DELAY: float = Field(default=1.0, gt=0)
33
+ REDIS_QUERY_BACKOFF: float = Field(default=2.0, gt=1)
34
+
31
35
  @field_validator("REDIS_DB")
32
36
  @classmethod
33
- def validate_db_sentinel(cls, v: int) -> int:
37
+ def validate_db(cls, v: int) -> int:
34
38
  """Validate REDIS_DB is not negative integer."""
35
39
  if v < 0:
36
40
  raise ValueError("REDIS_DB must be a positive integer")
@@ -46,12 +50,20 @@ class RedisSentinelSettings(BaseSettings):
46
50
 
47
51
  @field_validator("REDIS_SELF_LINK_TTL")
48
52
  @classmethod
49
- def validate_self_link_ttl_sentinel(cls, v: int) -> int:
50
- """Validate REDIS_SELF_LINK_TTL is not a negative integer."""
53
+ def validate_self_link_ttl(cls, v: int) -> int:
54
+ """Validate REDIS_SELF_LINK_TTL is negative."""
51
55
  if v < 0:
52
56
  raise ValueError("REDIS_SELF_LINK_TTL must be a positive integer")
53
57
  return v
54
58
 
59
+
60
+ class RedisSentinelSettings(RedisCommonSettings):
61
+ """Configuration for connecting to Redis Sentinel."""
62
+
63
+ REDIS_SENTINEL_HOSTS: str = ""
64
+ REDIS_SENTINEL_PORTS: str = "26379"
65
+ REDIS_SENTINEL_MASTER_NAME: str = "master"
66
+
55
67
  def get_sentinel_hosts(self) -> List[str]:
56
68
  """Parse Redis Sentinel hosts from string to list."""
57
69
  if not self.REDIS_SENTINEL_HOSTS:
@@ -96,19 +108,11 @@ class RedisSentinelSettings(BaseSettings):
96
108
  return [(str(host), int(port)) for host, port in zip(hosts, ports)]
97
109
 
98
110
 
99
- class RedisSettings(BaseSettings):
111
+ class RedisSettings(RedisCommonSettings):
100
112
  """Configuration for connecting Redis."""
101
113
 
102
114
  REDIS_HOST: str = ""
103
115
  REDIS_PORT: int = 6379
104
- REDIS_DB: int = 15
105
-
106
- REDIS_MAX_CONNECTIONS: Optional[int] = None
107
- REDIS_RETRY_TIMEOUT: bool = True
108
- REDIS_DECODE_RESPONSES: bool = True
109
- REDIS_CLIENT_NAME: str = "stac-fastapi-app"
110
- REDIS_HEALTH_CHECK_INTERVAL: int = Field(default=30, gt=0)
111
- REDIS_SELF_LINK_TTL: int = 1800
112
116
 
113
117
  @field_validator("REDIS_PORT")
114
118
  @classmethod
@@ -118,89 +122,93 @@ class RedisSettings(BaseSettings):
118
122
  raise ValueError("REDIS_PORT must be a positive integer")
119
123
  return v
120
124
 
121
- @field_validator("REDIS_DB")
122
- @classmethod
123
- def validate_db_standalone(cls, v: int) -> int:
124
- """Validate REDIS_DB is not a negative integer."""
125
- if v < 0:
126
- raise ValueError("REDIS_DB must be a positive integer")
127
- return v
128
-
129
- @field_validator("REDIS_MAX_CONNECTIONS", mode="before")
130
- @classmethod
131
- def validate_max_connections(cls, v):
132
- """Handle empty/None values for REDIS_MAX_CONNECTIONS."""
133
- if v in ["", "null", "Null", "NULL", "none", "None", "NONE", None]:
134
- return None
135
- return v
136
-
137
- @field_validator("REDIS_SELF_LINK_TTL")
138
- @classmethod
139
- def validate_self_link_ttl_standalone(cls, v: int) -> int:
140
- """Validate REDIS_SELF_LINK_TTL is negative."""
141
- if v < 0:
142
- raise ValueError("REDIS_SELF_LINK_TTL must be a positive integer")
143
- return v
144
-
145
125
 
146
126
  # Configure only one Redis configuration
147
127
  sentinel_settings = RedisSentinelSettings()
148
- standalone_settings = RedisSettings()
128
+ settings: RedisCommonSettings = cast(
129
+ RedisCommonSettings,
130
+ sentinel_settings if sentinel_settings.REDIS_SENTINEL_HOSTS else RedisSettings(),
131
+ )
132
+
133
+
134
+ def redis_retry(func: Callable) -> Callable:
135
+ """Retry with back-off decorator for Redis connections."""
136
+
137
+ @wraps(func)
138
+ @retry(
139
+ exceptions=(RedisConnectionError, RedisTimeoutError),
140
+ tries=settings.REDIS_QUERY_RETRIES_NUM,
141
+ delay=settings.REDIS_QUERY_INITIAL_DELAY,
142
+ backoff=settings.REDIS_QUERY_BACKOFF,
143
+ logger=logger,
144
+ )
145
+ async def wrapper(*args, **kwargs):
146
+ return await func(*args, **kwargs)
149
147
 
148
+ return wrapper
150
149
 
151
- async def connect_redis() -> Optional[aioredis.Redis]:
150
+
151
+ @redis_retry
152
+ async def _connect_redis_internal() -> Optional[aioredis.Redis]:
152
153
  """Return a Redis connection Redis or Redis Sentinel."""
153
- try:
154
- if sentinel_settings.REDIS_SENTINEL_HOSTS:
155
- sentinel_nodes = sentinel_settings.get_sentinel_nodes()
156
- sentinel = Sentinel(
157
- sentinel_nodes,
158
- decode_responses=sentinel_settings.REDIS_DECODE_RESPONSES,
159
- )
154
+ if sentinel_settings.REDIS_SENTINEL_HOSTS:
155
+ sentinel_nodes = settings.get_sentinel_nodes()
156
+ sentinel = Sentinel(
157
+ sentinel_nodes,
158
+ decode_responses=settings.REDIS_DECODE_RESPONSES,
159
+ )
160
160
 
161
- redis = sentinel.master_for(
162
- service_name=sentinel_settings.REDIS_SENTINEL_MASTER_NAME,
163
- db=sentinel_settings.REDIS_DB,
164
- decode_responses=sentinel_settings.REDIS_DECODE_RESPONSES,
165
- retry_on_timeout=sentinel_settings.REDIS_RETRY_TIMEOUT,
166
- client_name=sentinel_settings.REDIS_CLIENT_NAME,
167
- max_connections=sentinel_settings.REDIS_MAX_CONNECTIONS,
168
- health_check_interval=sentinel_settings.REDIS_HEALTH_CHECK_INTERVAL,
169
- )
170
- logger.info("Connected to Redis Sentinel")
171
-
172
- elif standalone_settings.REDIS_HOST:
173
- pool = aioredis.ConnectionPool(
174
- host=standalone_settings.REDIS_HOST,
175
- port=standalone_settings.REDIS_PORT,
176
- db=standalone_settings.REDIS_DB,
177
- max_connections=standalone_settings.REDIS_MAX_CONNECTIONS,
178
- decode_responses=standalone_settings.REDIS_DECODE_RESPONSES,
179
- retry_on_timeout=standalone_settings.REDIS_RETRY_TIMEOUT,
180
- health_check_interval=standalone_settings.REDIS_HEALTH_CHECK_INTERVAL,
181
- )
182
- redis = aioredis.Redis(
183
- connection_pool=pool, client_name=standalone_settings.REDIS_CLIENT_NAME
184
- )
185
- logger.info("Connected to Redis")
186
- else:
187
- logger.warning("No Redis configuration found")
188
- return None
161
+ redis = sentinel.master_for(
162
+ service_name=settings.REDIS_SENTINEL_MASTER_NAME,
163
+ db=settings.REDIS_DB,
164
+ decode_responses=settings.REDIS_DECODE_RESPONSES,
165
+ retry_on_timeout=settings.REDIS_RETRY_TIMEOUT,
166
+ client_name=settings.REDIS_CLIENT_NAME,
167
+ max_connections=settings.REDIS_MAX_CONNECTIONS,
168
+ health_check_interval=settings.REDIS_HEALTH_CHECK_INTERVAL,
169
+ )
170
+ logger.info("Connected to Redis Sentinel")
171
+
172
+ elif settings.REDIS_HOST:
173
+ pool = aioredis.ConnectionPool(
174
+ host=settings.REDIS_HOST,
175
+ port=settings.REDIS_PORT,
176
+ db=settings.REDIS_DB,
177
+ max_connections=settings.REDIS_MAX_CONNECTIONS,
178
+ decode_responses=settings.REDIS_DECODE_RESPONSES,
179
+ retry_on_timeout=settings.REDIS_RETRY_TIMEOUT,
180
+ health_check_interval=settings.REDIS_HEALTH_CHECK_INTERVAL,
181
+ )
182
+ redis = aioredis.Redis(
183
+ connection_pool=pool, client_name=settings.REDIS_CLIENT_NAME
184
+ )
185
+ logger.info("Connected to Redis")
186
+ else:
187
+ logger.warning("No Redis configuration found")
188
+ return None
189
+
190
+ return redis
189
191
 
190
- return redis
191
192
 
193
+ async def connect_redis() -> Optional[aioredis.Redis]:
194
+ """Handle Redis connection."""
195
+ try:
196
+ return await _connect_redis_internal()
197
+ except (
198
+ aioredis.ConnectionError,
199
+ aioredis.TimeoutError,
200
+ ) as e:
201
+ logger.error(f"Redis connection failed after retries: {e}")
192
202
  except aioredis.ConnectionError as e:
193
203
  logger.error(f"Redis connection error: {e}")
194
204
  return None
195
205
  except aioredis.AuthenticationError as e:
196
206
  logger.error(f"Redis authentication error: {e}")
197
207
  return None
198
- except aioredis.TimeoutError as e:
199
- logger.error(f"Redis timeout error: {e}")
200
- return None
201
208
  except Exception as e:
202
209
  logger.error(f"Failed to connect to Redis: {e}")
203
210
  return None
211
+ return None
204
212
 
205
213
 
206
214
  def get_redis_key(url: str, token: str) -> str:
@@ -230,19 +238,21 @@ def build_url_with_token(base_url: str, token: str) -> str:
230
238
  )
231
239
 
232
240
 
241
+ @redis_retry
233
242
  async def save_prev_link(
234
243
  redis: aioredis.Redis, next_url: str, current_url: str, next_token: str
235
244
  ) -> None:
236
245
  """Save the current page as the previous link for the next URL."""
237
246
  if next_url and next_token:
238
247
  if sentinel_settings.REDIS_SENTINEL_HOSTS:
239
- ttl_seconds = sentinel_settings.REDIS_SELF_LINK_TTL
240
- elif standalone_settings.REDIS_HOST:
241
- ttl_seconds = standalone_settings.REDIS_SELF_LINK_TTL
248
+ ttl_seconds = settings.REDIS_SELF_LINK_TTL
249
+ elif settings.REDIS_HOST:
250
+ ttl_seconds = settings.REDIS_SELF_LINK_TTL
242
251
  key = get_redis_key(next_url, next_token)
243
252
  await redis.setex(key, ttl_seconds, current_url)
244
253
 
245
254
 
255
+ @redis_retry
246
256
  async def get_prev_link(
247
257
  redis: aioredis.Redis, current_url: str, current_token: str
248
258
  ) -> Optional[str]:
@@ -2,6 +2,7 @@
2
2
 
3
3
  import abc
4
4
  import logging
5
+ import os
5
6
  from copy import deepcopy
6
7
  from typing import Any, List, Optional
7
8
 
@@ -10,7 +11,7 @@ from starlette.requests import Request
10
11
 
11
12
  from stac_fastapi.core.datetime_utils import now_to_rfc3339_str
12
13
  from stac_fastapi.core.models.links import CollectionLinks
13
- from stac_fastapi.core.utilities import get_bool_env
14
+ from stac_fastapi.core.utilities import get_bool_env, get_excluded_from_items
14
15
  from stac_fastapi.types import stac as stac_types
15
16
  from stac_fastapi.types.links import ItemLinks, resolve_links
16
17
 
@@ -108,7 +109,7 @@ class ItemSerializer(Serializer):
108
109
  else:
109
110
  assets = item.get("assets", {})
110
111
 
111
- return stac_types.Item(
112
+ stac_item = stac_types.Item(
112
113
  type="Feature",
113
114
  stac_version=item.get("stac_version", ""),
114
115
  stac_extensions=item.get("stac_extensions", []),
@@ -121,6 +122,14 @@ class ItemSerializer(Serializer):
121
122
  assets=assets,
122
123
  )
123
124
 
125
+ excluded_fields = os.getenv("EXCLUDED_FROM_ITEMS")
126
+ if excluded_fields:
127
+ for field_path in excluded_fields.split(","):
128
+ if field_path := field_path.strip():
129
+ get_excluded_from_items(stac_item, field_path)
130
+
131
+ return stac_item
132
+
124
133
 
125
134
  class CollectionSerializer(Serializer):
126
135
  """Serialization methods for STAC collections."""
@@ -178,3 +178,20 @@ def dict_deep_update(merge_to: Dict[str, Any], merge_from: Dict[str, Any]) -> No
178
178
  dict_deep_update(merge_to[k], merge_from[k])
179
179
  else:
180
180
  merge_to[k] = v
181
+
182
+
183
+ def get_excluded_from_items(obj: dict, field_path: str) -> None:
184
+ """Remove a field from items.
185
+
186
+ The field is removed in-place from the dictionary if it exists.
187
+ If any intermediate path does not exist or is not a dictionary,
188
+ the function returns without making any changes.
189
+ """
190
+ *path, final = field_path.split(".")
191
+ current = obj
192
+ for part in path:
193
+ current = current.get(part, {})
194
+ if not isinstance(current, dict):
195
+ return
196
+
197
+ current.pop(final, None)
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "6.7.3"
2
+ __version__ = "6.7.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stac_fastapi_core
3
- Version: 6.7.3
3
+ Version: 6.7.5
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
@@ -9,13 +9,11 @@ Classifier: Intended Audience :: Developers
9
9
  Classifier: Intended Audience :: Information Technology
10
10
  Classifier: Intended Audience :: Science/Research
11
11
  Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
13
  Classifier: Programming Language :: Python :: 3.12
16
14
  Classifier: Programming Language :: Python :: 3.13
17
15
  Classifier: Programming Language :: Python :: 3.14
18
- Requires-Python: >=3.9
16
+ Requires-Python: >=3.11
19
17
  Requires-Dist: attrs>=23.2.0
20
18
  Requires-Dist: fastapi~=0.109.0
21
19
  Requires-Dist: geojson-pydantic~=1.0.0
@@ -24,12 +22,14 @@ Requires-Dist: orjson~=3.11.0
24
22
  Requires-Dist: overrides~=7.4.0
25
23
  Requires-Dist: pydantic<3.0.0,>=2.4.1
26
24
  Requires-Dist: pygeofilter~=0.3.1
27
- Requires-Dist: redis==6.4.0
28
25
  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
26
+ Requires-Dist: stac-fastapi-api==6.1.1
27
+ Requires-Dist: stac-fastapi-extensions==6.1.1
28
+ Requires-Dist: stac-fastapi-types==6.1.1
32
29
  Requires-Dist: stac-pydantic~=3.3.0
30
+ Provides-Extra: redis
31
+ Requires-Dist: redis~=6.4.0; extra == 'redis'
32
+ Requires-Dist: retry~=0.9.2; extra == 'redis'
33
33
  Description-Content-Type: text/markdown
34
34
 
35
35
  # stac-fastapi-core
@@ -3,14 +3,14 @@ stac_fastapi/core/base_database_logic.py,sha256=3_XJ_j06ogQHE-Tcjkv5Vye_zNDn9OEU
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
5
  stac_fastapi/core/core.py,sha256=mmUP1da46EiON_TM2HYtxQxw8DHFg-2xEFoSg4P7UgU,50607
6
- stac_fastapi/core/datetime_utils.py,sha256=TrTgbU7AKNC-ic4a3HptfE5XAc9tHR7uJasZyhOuwnc,2633
6
+ stac_fastapi/core/datetime_utils.py,sha256=uhKZVfvsp6Y92__IB6HWn7C6VQU12DMeGuNg-78cb8Y,4534
7
7
  stac_fastapi/core/rate_limit.py,sha256=Gu8dAaJReGsj1L91U6m2tflU6RahpXDRs2-AYSKoybA,1318
8
- stac_fastapi/core/redis_utils.py,sha256=ckfuIjOUk08ofG1ql1eGXcOPG60odzSA86rXGr6h_QA,9859
8
+ stac_fastapi/core/redis_utils.py,sha256=6_lrXfZBi6vCCCibLDdwwHC3lLaXYTEmqQpxOMaCUH4,9689
9
9
  stac_fastapi/core/route_dependencies.py,sha256=hdtuMkv-zY1vg0YxiCz1aKP0SbBcORqDGEKDGgEazW8,5482
10
- stac_fastapi/core/serializers.py,sha256=ZW5hPgq-mftk6zxJeZGur-1Qxn7YGc3fJYFLsd-SYwM,7619
10
+ stac_fastapi/core/serializers.py,sha256=HPA110RLZ17EnKrFf1rvVu5EwQHZto4V912Ofp_ypjA,7951
11
11
  stac_fastapi/core/session.py,sha256=aXqu4LXfVbAAsChMVXd9gAhczA2bZPne6HqPeklAwMY,474
12
- stac_fastapi/core/utilities.py,sha256=xXWO5oJCNDi7_C5jPYlHZD0B-DL-FN66eEUBUSW-cXw,7296
13
- stac_fastapi/core/version.py,sha256=xoqSv7gsqOZ-kd6XSuIGAoUwK4exieNAmUhNJv2SkHs,45
12
+ stac_fastapi/core/utilities.py,sha256=XR_9afK_j8wCydgoXj-CMtRyI8KqgIL3d4HZOE779dU,7807
13
+ stac_fastapi/core/version.py,sha256=WRUWk2u8pozjztw-4p6a3fAkitIN0nGhe4tLZ6P6xUk,45
14
14
  stac_fastapi/core/extensions/__init__.py,sha256=zSIAqou8jnakWPbkh4Ddcx1-oazZVBOs7U2PAakAdU0,291
15
15
  stac_fastapi/core/extensions/aggregation.py,sha256=v1hUHqlYuMqfQ554g3cTp16pUyRYucQxPERbHPAFtf8,1878
16
16
  stac_fastapi/core/extensions/collections_search.py,sha256=xpv51nffMq5a8grNSaLbv2IzeI5JH_pqcoWRbWhzn6Y,14406
@@ -20,6 +20,6 @@ stac_fastapi/core/extensions/query.py,sha256=Xmo8pfZEZKPudZEjjozv3R0wLOP0ayjC9E6
20
20
  stac_fastapi/core/models/__init__.py,sha256=g-D1DiGfmC9Bg27DW9JzkN6fAvscv75wyhyiZ6NzvIk,48
21
21
  stac_fastapi/core/models/links.py,sha256=0dWSEMt3aa7NCISlHwo11zLBeIV1LwXG3JGjrXC3dZI,6672
22
22
  stac_fastapi/core/models/search.py,sha256=7SgAUyzHGXBXSqB4G6cwq9FMwoAS00momb7jvBkjyow,27
23
- stac_fastapi_core-6.7.3.dist-info/METADATA,sha256=7KV0QP5pjo-ilxLyJ3md7wOj67kWW7N2E3jsIf6yPVw,3494
24
- stac_fastapi_core-6.7.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
- stac_fastapi_core-6.7.3.dist-info/RECORD,,
23
+ stac_fastapi_core-6.7.5.dist-info/METADATA,sha256=i2ek_1Z3c6aC7j9G3Atn2bHzZket_Q8Kc5peUi-wvj0,3480
24
+ stac_fastapi_core-6.7.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ stac_fastapi_core-6.7.5.dist-info/RECORD,,