stac-fastapi-core 6.7.5__py3-none-any.whl → 6.8.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 +40 -0
- stac_fastapi/core/core.py +49 -4
- stac_fastapi/core/datetime_utils.py +4 -1
- stac_fastapi/core/extensions/__init__.py +2 -0
- stac_fastapi/core/extensions/catalogs.py +995 -0
- stac_fastapi/core/models/__init__.py +27 -1
- stac_fastapi/core/models/links.py +9 -2
- stac_fastapi/core/queryables.py +105 -0
- stac_fastapi/core/serializers.py +147 -1
- stac_fastapi/core/version.py +1 -1
- {stac_fastapi_core-6.7.5.dist-info → stac_fastapi_core-6.8.0.dist-info}/METADATA +2 -2
- {stac_fastapi_core-6.7.5.dist-info → stac_fastapi_core-6.8.0.dist-info}/RECORD +13 -11
- {stac_fastapi_core-6.7.5.dist-info → stac_fastapi_core-6.8.0.dist-info}/WHEEL +1 -1
|
@@ -1 +1,27 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""STAC models."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Catalog(BaseModel):
|
|
9
|
+
"""STAC Catalog model."""
|
|
10
|
+
|
|
11
|
+
type: str = "Catalog"
|
|
12
|
+
stac_version: str
|
|
13
|
+
id: str
|
|
14
|
+
title: Optional[str] = None
|
|
15
|
+
description: Optional[str] = None
|
|
16
|
+
links: List[Dict[str, Any]]
|
|
17
|
+
stac_extensions: Optional[List[str]] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PartialCatalog(BaseModel):
|
|
21
|
+
"""Partial STAC Catalog model for updates."""
|
|
22
|
+
|
|
23
|
+
id: str
|
|
24
|
+
title: Optional[str] = None
|
|
25
|
+
description: Optional[str] = None
|
|
26
|
+
links: Optional[List[Dict[str, Any]]] = None
|
|
27
|
+
stac_extensions: Optional[List[str]] = None
|
|
@@ -113,6 +113,7 @@ class CollectionLinks(BaseLinks):
|
|
|
113
113
|
|
|
114
114
|
collection_id: str = attr.ib()
|
|
115
115
|
extensions: List[str] = attr.ib(default=attr.Factory(list))
|
|
116
|
+
parent_url: Optional[str] = attr.ib(default=None, kw_only=True)
|
|
116
117
|
|
|
117
118
|
def link_self(self) -> Dict:
|
|
118
119
|
"""Return the self link."""
|
|
@@ -123,8 +124,14 @@ class CollectionLinks(BaseLinks):
|
|
|
123
124
|
)
|
|
124
125
|
|
|
125
126
|
def link_parent(self) -> Dict[str, Any]:
|
|
126
|
-
"""Create the `parent` link.
|
|
127
|
-
|
|
127
|
+
"""Create the `parent` link.
|
|
128
|
+
|
|
129
|
+
The parent link represents the structural parent (the path the user is traversing):
|
|
130
|
+
- If accessed via /catalogs/{id}/collections/{id}, parent is the catalog
|
|
131
|
+
- If accessed via /collections/{id}, parent is the root landing page
|
|
132
|
+
"""
|
|
133
|
+
parent_href = self.parent_url if self.parent_url else self.base_url
|
|
134
|
+
return dict(rel=Relations.parent, type=MimeTypes.json.value, href=parent_href)
|
|
128
135
|
|
|
129
136
|
def link_items(self) -> Dict[str, Any]:
|
|
130
137
|
"""Create the `items` link."""
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""A module for managing queryable attributes."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Dict, List, Set
|
|
7
|
+
|
|
8
|
+
from fastapi import HTTPException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class QueryablesCache:
|
|
12
|
+
"""A thread-safe, time-based cache for queryable properties."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, database_logic: Any):
|
|
15
|
+
"""
|
|
16
|
+
Initialize the QueryablesCache.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
database_logic: An instance of a class with a `get_queryables_mapping` method.
|
|
20
|
+
"""
|
|
21
|
+
self._db_logic = database_logic
|
|
22
|
+
self._cache: Dict[str, List[str]] = {}
|
|
23
|
+
self._all_queryables: Set[str] = set()
|
|
24
|
+
self._last_updated: float = 0
|
|
25
|
+
self._lock = asyncio.Lock()
|
|
26
|
+
self.validation_enabled: bool = False
|
|
27
|
+
self.cache_ttl: int = 1800 # How often to refresh cache (in seconds)
|
|
28
|
+
self.reload_settings()
|
|
29
|
+
|
|
30
|
+
def reload_settings(self):
|
|
31
|
+
"""Reload settings from environment variables."""
|
|
32
|
+
self.validation_enabled = (
|
|
33
|
+
os.getenv("VALIDATE_QUERYABLES", "false").lower() == "true"
|
|
34
|
+
)
|
|
35
|
+
self.cache_ttl = int(os.getenv("QUERYABLES_CACHE_TTL", "1800"))
|
|
36
|
+
|
|
37
|
+
async def _update_cache(self):
|
|
38
|
+
"""Update the cache with the latest queryables from the database."""
|
|
39
|
+
if not self.validation_enabled:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
async with self._lock:
|
|
43
|
+
if (time.time() - self._last_updated < self.cache_ttl) and self._cache:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
queryables_mapping = await self._db_logic.get_queryables_mapping()
|
|
47
|
+
all_queryables_set = set(queryables_mapping.keys())
|
|
48
|
+
|
|
49
|
+
self._all_queryables = all_queryables_set
|
|
50
|
+
|
|
51
|
+
self._cache = {"*": list(all_queryables_set)}
|
|
52
|
+
self._last_updated = time.time()
|
|
53
|
+
|
|
54
|
+
async def get_all_queryables(self) -> Set[str]:
|
|
55
|
+
"""
|
|
56
|
+
Return a set of all queryable attributes across all collections.
|
|
57
|
+
|
|
58
|
+
This method will update the cache if it's stale or has been cleared.
|
|
59
|
+
"""
|
|
60
|
+
if not self.validation_enabled:
|
|
61
|
+
return set()
|
|
62
|
+
|
|
63
|
+
if (time.time() - self._last_updated >= self.cache_ttl) or not self._cache:
|
|
64
|
+
await self._update_cache()
|
|
65
|
+
return self._all_queryables
|
|
66
|
+
|
|
67
|
+
async def validate(self, fields: Set[str]) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Validate if the provided fields are queryable.
|
|
70
|
+
|
|
71
|
+
Raises HTTPException if invalid fields are found.
|
|
72
|
+
"""
|
|
73
|
+
if not self.validation_enabled:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
allowed_fields = await self.get_all_queryables()
|
|
77
|
+
invalid_fields = fields - allowed_fields
|
|
78
|
+
if invalid_fields:
|
|
79
|
+
raise HTTPException(
|
|
80
|
+
status_code=400,
|
|
81
|
+
detail=f"Invalid query fields: {', '.join(sorted(invalid_fields))}. "
|
|
82
|
+
"These fields are not defined in the collection's queryables. "
|
|
83
|
+
"Use the /queryables endpoint to see available fields.",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_properties_from_cql2_filter(cql2_filter: Dict[str, Any]) -> Set[str]:
|
|
88
|
+
"""Recursively extract property names from a CQL2 filter.
|
|
89
|
+
|
|
90
|
+
Property names are normalized by stripping the 'properties.' prefix
|
|
91
|
+
if present, to match queryables stored without the prefix.
|
|
92
|
+
"""
|
|
93
|
+
props: Set[str] = set()
|
|
94
|
+
if "op" in cql2_filter and "args" in cql2_filter:
|
|
95
|
+
for arg in cql2_filter["args"]:
|
|
96
|
+
if isinstance(arg, dict):
|
|
97
|
+
if "op" in arg:
|
|
98
|
+
props.update(get_properties_from_cql2_filter(arg))
|
|
99
|
+
elif "property" in arg:
|
|
100
|
+
prop_name = arg["property"]
|
|
101
|
+
# Strip 'properties.' prefix if present
|
|
102
|
+
if prop_name.startswith("properties."):
|
|
103
|
+
prop_name = prop_name[11:]
|
|
104
|
+
props.add(prop_name)
|
|
105
|
+
return props
|
stac_fastapi/core/serializers.py
CHANGED
|
@@ -10,6 +10,7 @@ import attr
|
|
|
10
10
|
from starlette.requests import Request
|
|
11
11
|
|
|
12
12
|
from stac_fastapi.core.datetime_utils import now_to_rfc3339_str
|
|
13
|
+
from stac_fastapi.core.models import Catalog
|
|
13
14
|
from stac_fastapi.core.models.links import CollectionLinks
|
|
14
15
|
from stac_fastapi.core.utilities import get_bool_env, get_excluded_from_items
|
|
15
16
|
from stac_fastapi.types import stac as stac_types
|
|
@@ -180,8 +181,9 @@ class CollectionSerializer(Serializer):
|
|
|
180
181
|
# Avoid modifying the input dict in-place ... doing so breaks some tests
|
|
181
182
|
collection = deepcopy(collection)
|
|
182
183
|
|
|
183
|
-
# Remove internal
|
|
184
|
+
# Remove internal fields (not part of STAC spec)
|
|
184
185
|
collection.pop("bbox_shape", None)
|
|
186
|
+
collection.pop("parent_ids", None)
|
|
185
187
|
|
|
186
188
|
# Set defaults
|
|
187
189
|
collection_id = collection.get("id")
|
|
@@ -208,6 +210,7 @@ class CollectionSerializer(Serializer):
|
|
|
208
210
|
original_links = collection.get("links")
|
|
209
211
|
if original_links:
|
|
210
212
|
collection_links += resolve_links(original_links, str(request.base_url))
|
|
213
|
+
|
|
211
214
|
collection["links"] = collection_links
|
|
212
215
|
|
|
213
216
|
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
@@ -225,3 +228,146 @@ class CollectionSerializer(Serializer):
|
|
|
225
228
|
|
|
226
229
|
# Return the stac_types.Collection object
|
|
227
230
|
return stac_types.Collection(**collection)
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def db_to_stac_in_catalog(
|
|
234
|
+
cls,
|
|
235
|
+
collection: dict,
|
|
236
|
+
request: Request,
|
|
237
|
+
catalog_id: str,
|
|
238
|
+
extensions: Optional[List[str]] = [],
|
|
239
|
+
) -> stac_types.Collection:
|
|
240
|
+
"""Transform database model to STAC collection within a catalog context.
|
|
241
|
+
|
|
242
|
+
This method is used when a collection is accessed via /catalogs/{id}/collections/{id}.
|
|
243
|
+
It sets the structural parent to the catalog and injects a catalog link.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
collection (dict): The collection data in dictionary form, extracted from the database.
|
|
247
|
+
request: the API request
|
|
248
|
+
catalog_id: The ID of the parent catalog (sets structural parent)
|
|
249
|
+
extensions: A list of the extension class names (`ext.__name__`) or all enabled STAC API extensions.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
stac_types.Collection: The STAC collection object with catalog context.
|
|
253
|
+
"""
|
|
254
|
+
# Avoid modifying the input dict in-place
|
|
255
|
+
collection = deepcopy(collection)
|
|
256
|
+
|
|
257
|
+
# Remove internal fields (not part of STAC spec)
|
|
258
|
+
collection.pop("bbox_shape", None)
|
|
259
|
+
|
|
260
|
+
# Set defaults
|
|
261
|
+
collection_id = collection.get("id")
|
|
262
|
+
collection.setdefault("type", "Collection")
|
|
263
|
+
collection.setdefault("stac_extensions", [])
|
|
264
|
+
collection.setdefault("stac_version", "")
|
|
265
|
+
collection.setdefault("title", "")
|
|
266
|
+
collection.setdefault("description", "")
|
|
267
|
+
collection.setdefault("keywords", [])
|
|
268
|
+
collection.setdefault("license", "")
|
|
269
|
+
collection.setdefault("providers", [])
|
|
270
|
+
collection.setdefault("summaries", {})
|
|
271
|
+
collection.setdefault(
|
|
272
|
+
"extent", {"spatial": {"bbox": []}, "temporal": {"interval": []}}
|
|
273
|
+
)
|
|
274
|
+
collection.setdefault("assets", {})
|
|
275
|
+
|
|
276
|
+
# Determine the structural parent URL
|
|
277
|
+
# When accessed via /catalogs/{id}/collections/{id}, the parent is the catalog
|
|
278
|
+
base_url = str(request.base_url)
|
|
279
|
+
parent_url = f"{base_url}catalogs/{catalog_id}"
|
|
280
|
+
|
|
281
|
+
# Create the collection links using CollectionLinks with catalog as parent
|
|
282
|
+
collection_links = CollectionLinks(
|
|
283
|
+
collection_id=collection_id,
|
|
284
|
+
request=request,
|
|
285
|
+
extensions=extensions,
|
|
286
|
+
parent_url=parent_url,
|
|
287
|
+
).create_links()
|
|
288
|
+
|
|
289
|
+
# Add any additional links from the collection dictionary
|
|
290
|
+
original_links = collection.get("links")
|
|
291
|
+
if original_links:
|
|
292
|
+
collection_links += resolve_links(original_links, str(request.base_url))
|
|
293
|
+
|
|
294
|
+
# Inject catalog link for consistency (same as parent in this context)
|
|
295
|
+
catalog_link = {
|
|
296
|
+
"rel": "catalog",
|
|
297
|
+
"type": "application/json",
|
|
298
|
+
"href": parent_url,
|
|
299
|
+
"title": catalog_id,
|
|
300
|
+
}
|
|
301
|
+
collection_links.append(catalog_link)
|
|
302
|
+
|
|
303
|
+
collection["links"] = collection_links
|
|
304
|
+
|
|
305
|
+
if get_bool_env("STAC_INDEX_ASSETS"):
|
|
306
|
+
collection["assets"] = {
|
|
307
|
+
a.pop("es_key"): a for a in collection.get("assets", [])
|
|
308
|
+
}
|
|
309
|
+
collection["item_assets"] = {
|
|
310
|
+
i.pop("es_key"): i for i in collection.get("item_assets", [])
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
else:
|
|
314
|
+
collection["assets"] = collection.get("assets", {})
|
|
315
|
+
if item_assets := collection.get("item_assets"):
|
|
316
|
+
collection["item_assets"] = item_assets
|
|
317
|
+
|
|
318
|
+
# Return the stac_types.Collection object
|
|
319
|
+
return stac_types.Collection(**collection)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class CatalogSerializer(Serializer):
|
|
323
|
+
"""Serialization methods for STAC catalogs."""
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def stac_to_db(cls, catalog: Catalog, request: Request) -> Catalog:
|
|
327
|
+
"""
|
|
328
|
+
Transform STAC Catalog to database-ready STAC catalog.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
catalog: the STAC Catalog object to be transformed
|
|
332
|
+
request: the API request
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Catalog: The database-ready STAC Catalog object.
|
|
336
|
+
"""
|
|
337
|
+
catalog = deepcopy(catalog)
|
|
338
|
+
catalog.links = resolve_links(catalog.links, str(request.base_url))
|
|
339
|
+
return catalog
|
|
340
|
+
|
|
341
|
+
@classmethod
|
|
342
|
+
def db_to_stac(
|
|
343
|
+
cls, catalog: dict, request: Request, extensions: Optional[List[str]] = []
|
|
344
|
+
) -> Catalog:
|
|
345
|
+
"""Transform database model to STAC catalog.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
catalog (dict): The catalog data in dictionary form, extracted from the database.
|
|
349
|
+
request (Request): the API request
|
|
350
|
+
extensions: A list of the extension class names (`ext.__name__`) or all enabled STAC API extensions.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Catalog: The STAC catalog object.
|
|
354
|
+
"""
|
|
355
|
+
# Avoid modifying the input dict in-place
|
|
356
|
+
catalog = deepcopy(catalog)
|
|
357
|
+
|
|
358
|
+
# Set defaults
|
|
359
|
+
catalog.setdefault("type", "Catalog")
|
|
360
|
+
catalog.setdefault("stac_extensions", [])
|
|
361
|
+
catalog.setdefault("stac_version", "")
|
|
362
|
+
catalog.setdefault("title", "")
|
|
363
|
+
catalog.setdefault("description", "")
|
|
364
|
+
|
|
365
|
+
# Create the catalog links - for now, just resolve existing links
|
|
366
|
+
original_links = catalog.get("links", [])
|
|
367
|
+
if original_links:
|
|
368
|
+
catalog["links"] = resolve_links(original_links, str(request.base_url))
|
|
369
|
+
else:
|
|
370
|
+
catalog["links"] = []
|
|
371
|
+
|
|
372
|
+
# Return the Catalog object
|
|
373
|
+
return Catalog(**catalog)
|
stac_fastapi/core/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "6.
|
|
2
|
+
__version__ = "6.8.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
|
|
|
@@ -1,25 +1,27 @@
|
|
|
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=JL7DRcDdqeaLbSPPGcIUMs7q6I3Gm_E5XCOwFG458Io,6053
|
|
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=
|
|
6
|
-
stac_fastapi/core/datetime_utils.py,sha256=
|
|
5
|
+
stac_fastapi/core/core.py,sha256=QvwZbXi5PssZXBXxyggLsYHfvzj57NidloI4uVXenzM,52238
|
|
6
|
+
stac_fastapi/core/datetime_utils.py,sha256=QygF2mJFfI_zqCwmSIec3HYqrsVsn3nUcaRQx3CD7Zw,4683
|
|
7
|
+
stac_fastapi/core/queryables.py,sha256=0gKdxlmCVaIj3ODpmyIfzLChEB1nNKXPZhA3K9ApfL0,3755
|
|
7
8
|
stac_fastapi/core/rate_limit.py,sha256=Gu8dAaJReGsj1L91U6m2tflU6RahpXDRs2-AYSKoybA,1318
|
|
8
9
|
stac_fastapi/core/redis_utils.py,sha256=6_lrXfZBi6vCCCibLDdwwHC3lLaXYTEmqQpxOMaCUH4,9689
|
|
9
10
|
stac_fastapi/core/route_dependencies.py,sha256=hdtuMkv-zY1vg0YxiCz1aKP0SbBcORqDGEKDGgEazW8,5482
|
|
10
|
-
stac_fastapi/core/serializers.py,sha256=
|
|
11
|
+
stac_fastapi/core/serializers.py,sha256=l5EWZvlGjlfsZ3S4wHjWKD8sJBf83zd2dkursu18fV4,13345
|
|
11
12
|
stac_fastapi/core/session.py,sha256=aXqu4LXfVbAAsChMVXd9gAhczA2bZPne6HqPeklAwMY,474
|
|
12
13
|
stac_fastapi/core/utilities.py,sha256=XR_9afK_j8wCydgoXj-CMtRyI8KqgIL3d4HZOE779dU,7807
|
|
13
|
-
stac_fastapi/core/version.py,sha256=
|
|
14
|
-
stac_fastapi/core/extensions/__init__.py,sha256=
|
|
14
|
+
stac_fastapi/core/version.py,sha256=ib-H9zXMsXhMlZkHut5gkqf27FPYcv0_wBkKUIXnJbA,45
|
|
15
|
+
stac_fastapi/core/extensions/__init__.py,sha256=oaK-UJDQSEISdQ8VtM0ESxpsv7Hx1HbAdmMnh6MTFD4,356
|
|
15
16
|
stac_fastapi/core/extensions/aggregation.py,sha256=v1hUHqlYuMqfQ554g3cTp16pUyRYucQxPERbHPAFtf8,1878
|
|
17
|
+
stac_fastapi/core/extensions/catalogs.py,sha256=s6yrubil90fCrav5Xgg0bUiEeq7dTU6EPC3SBy055fQ,38935
|
|
16
18
|
stac_fastapi/core/extensions/collections_search.py,sha256=xpv51nffMq5a8grNSaLbv2IzeI5JH_pqcoWRbWhzn6Y,14406
|
|
17
19
|
stac_fastapi/core/extensions/fields.py,sha256=NCT5XHvfaf297eDPNaIFsIzvJnbbUTpScqF0otdx0NA,1066
|
|
18
20
|
stac_fastapi/core/extensions/filter.py,sha256=-NQGME7rR_ereuDx-LAa1M5JhEXFaKiTtkH2asraYHE,2998
|
|
19
21
|
stac_fastapi/core/extensions/query.py,sha256=Xmo8pfZEZKPudZEjjozv3R0wLOP0ayjC9E67sBOXqWY,1803
|
|
20
|
-
stac_fastapi/core/models/__init__.py,sha256=
|
|
21
|
-
stac_fastapi/core/models/links.py,sha256=
|
|
22
|
+
stac_fastapi/core/models/__init__.py,sha256=sUsEB7umGZVYXjT4EHqLwm8p2wevtRBdig2Ioj2ZdVQ,631
|
|
23
|
+
stac_fastapi/core/models/links.py,sha256=5KEZKisFN34U4UuOzSQnDy0QdsUOT2VRuuY36vs-FGw,7074
|
|
22
24
|
stac_fastapi/core/models/search.py,sha256=7SgAUyzHGXBXSqB4G6cwq9FMwoAS00momb7jvBkjyow,27
|
|
23
|
-
stac_fastapi_core-6.
|
|
24
|
-
stac_fastapi_core-6.
|
|
25
|
-
stac_fastapi_core-6.
|
|
25
|
+
stac_fastapi_core-6.8.0.dist-info/METADATA,sha256=OTeCtPe2s5cQCqoACfM5bddnUvL92ux6tU_jks6q5EU,3480
|
|
26
|
+
stac_fastapi_core-6.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
27
|
+
stac_fastapi_core-6.8.0.dist-info/RECORD,,
|