stac-fastapi-core 4.0.0a1__py3-none-any.whl → 4.1.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/core.py +73 -38
- stac_fastapi/core/database_logic.py +7 -1
- stac_fastapi/core/extensions/query.py +2 -2
- stac_fastapi/core/utilities.py +29 -0
- stac_fastapi/core/version.py +1 -1
- {stac_fastapi_core-4.0.0a1.dist-info → stac_fastapi_core-4.1.0.dist-info}/METADATA +23 -10
- {stac_fastapi_core-4.0.0a1.dist-info → stac_fastapi_core-4.1.0.dist-info}/RECORD +9 -9
- {stac_fastapi_core-4.0.0a1.dist-info → stac_fastapi_core-4.1.0.dist-info}/WHEEL +0 -0
- {stac_fastapi_core-4.0.0a1.dist-info → stac_fastapi_core-4.1.0.dist-info}/top_level.txt +0 -0
stac_fastapi/core/core.py
CHANGED
|
@@ -334,7 +334,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
334
334
|
search=search,
|
|
335
335
|
limit=limit,
|
|
336
336
|
sort=None,
|
|
337
|
-
token=token,
|
|
337
|
+
token=token,
|
|
338
338
|
collection_ids=[collection_id],
|
|
339
339
|
)
|
|
340
340
|
|
|
@@ -633,7 +633,7 @@ class CoreClient(AsyncBaseCoreClient):
|
|
|
633
633
|
items, maybe_count, next_token = await self.database.execute_search(
|
|
634
634
|
search=search,
|
|
635
635
|
limit=limit,
|
|
636
|
-
token=search_request.token,
|
|
636
|
+
token=search_request.token,
|
|
637
637
|
sort=sort,
|
|
638
638
|
collection_ids=search_request.collections,
|
|
639
639
|
)
|
|
@@ -676,43 +676,65 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
676
676
|
@overrides
|
|
677
677
|
async def create_item(
|
|
678
678
|
self, collection_id: str, item: Union[Item, ItemCollection], **kwargs
|
|
679
|
-
) ->
|
|
680
|
-
"""
|
|
679
|
+
) -> Union[stac_types.Item, str]:
|
|
680
|
+
"""
|
|
681
|
+
Create an item or a feature collection of items in the specified collection.
|
|
681
682
|
|
|
682
683
|
Args:
|
|
683
|
-
collection_id (str): The
|
|
684
|
-
item (
|
|
685
|
-
kwargs: Additional keyword arguments
|
|
684
|
+
collection_id (str): The ID of the collection to add the item(s) to.
|
|
685
|
+
item (Union[Item, ItemCollection]): A single item or a collection of items to be added.
|
|
686
|
+
**kwargs: Additional keyword arguments, such as `request` and `refresh`.
|
|
686
687
|
|
|
687
688
|
Returns:
|
|
688
|
-
stac_types.Item: The created item
|
|
689
|
+
Union[stac_types.Item, str]: The created item if a single item is added, or a summary string
|
|
690
|
+
indicating the number of items successfully added and errors if a collection of items is added.
|
|
689
691
|
|
|
690
692
|
Raises:
|
|
691
|
-
|
|
692
|
-
ConflictError: If
|
|
693
|
-
|
|
693
|
+
NotFoundError: If the specified collection is not found in the database.
|
|
694
|
+
ConflictError: If an item with the same ID already exists in the collection.
|
|
694
695
|
"""
|
|
695
|
-
|
|
696
|
-
base_url = str(
|
|
696
|
+
request = kwargs.get("request")
|
|
697
|
+
base_url = str(request.base_url)
|
|
698
|
+
|
|
699
|
+
# Convert Pydantic model to dict for uniform processing
|
|
700
|
+
item_dict = item.model_dump(mode="json")
|
|
697
701
|
|
|
698
|
-
#
|
|
699
|
-
if
|
|
702
|
+
# Handle FeatureCollection (bulk insert)
|
|
703
|
+
if item_dict["type"] == "FeatureCollection":
|
|
700
704
|
bulk_client = BulkTransactionsClient(
|
|
701
705
|
database=self.database, settings=self.settings
|
|
702
706
|
)
|
|
707
|
+
features = item_dict["features"]
|
|
703
708
|
processed_items = [
|
|
704
|
-
bulk_client.preprocess_item(
|
|
709
|
+
bulk_client.preprocess_item(
|
|
710
|
+
feature, base_url, BulkTransactionMethod.INSERT
|
|
711
|
+
)
|
|
712
|
+
for feature in features
|
|
705
713
|
]
|
|
706
|
-
|
|
707
|
-
await self.database.bulk_async(
|
|
708
|
-
collection_id,
|
|
714
|
+
attempted = len(processed_items)
|
|
715
|
+
success, errors = await self.database.bulk_async(
|
|
716
|
+
collection_id,
|
|
717
|
+
processed_items,
|
|
718
|
+
refresh=kwargs.get("refresh", False),
|
|
709
719
|
)
|
|
720
|
+
if errors:
|
|
721
|
+
logger.error(
|
|
722
|
+
f"Bulk async operation encountered errors for collection {collection_id}: {errors} (attempted {attempted})"
|
|
723
|
+
)
|
|
724
|
+
else:
|
|
725
|
+
logger.info(
|
|
726
|
+
f"Bulk async operation succeeded with {success} actions for collection {collection_id}."
|
|
727
|
+
)
|
|
728
|
+
return f"Successfully added {success} Items. {attempted - success} errors occurred."
|
|
710
729
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
730
|
+
# Handle single item
|
|
731
|
+
await self.database.create_item(
|
|
732
|
+
item_dict,
|
|
733
|
+
refresh=kwargs.get("refresh", False),
|
|
734
|
+
base_url=base_url,
|
|
735
|
+
exist_ok=False,
|
|
736
|
+
)
|
|
737
|
+
return ItemSerializer.db_to_stac(item_dict, base_url)
|
|
716
738
|
|
|
717
739
|
@overrides
|
|
718
740
|
async def update_item(
|
|
@@ -735,12 +757,12 @@ class TransactionsClient(AsyncBaseTransactionsClient):
|
|
|
735
757
|
"""
|
|
736
758
|
item = item.model_dump(mode="json")
|
|
737
759
|
base_url = str(kwargs["request"].base_url)
|
|
738
|
-
now = datetime_type.now(timezone.utc).
|
|
760
|
+
now = datetime_type.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
739
761
|
item["properties"]["updated"] = now
|
|
740
762
|
|
|
741
|
-
await self.database.
|
|
742
|
-
|
|
743
|
-
|
|
763
|
+
await self.database.create_item(
|
|
764
|
+
item, refresh=kwargs.get("refresh", False), base_url=base_url, exist_ok=True
|
|
765
|
+
)
|
|
744
766
|
|
|
745
767
|
return ItemSerializer.db_to_stac(item, base_url)
|
|
746
768
|
|
|
@@ -873,7 +895,7 @@ class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
|
873
895
|
The preprocessed item.
|
|
874
896
|
"""
|
|
875
897
|
exist_ok = method == BulkTransactionMethod.UPSERT
|
|
876
|
-
return self.database.
|
|
898
|
+
return self.database.bulk_sync_prep_create_item(
|
|
877
899
|
item=item, base_url=base_url, exist_ok=exist_ok
|
|
878
900
|
)
|
|
879
901
|
|
|
@@ -897,19 +919,32 @@ class BulkTransactionsClient(BaseBulkTransactionsClient):
|
|
|
897
919
|
else:
|
|
898
920
|
base_url = ""
|
|
899
921
|
|
|
900
|
-
processed_items = [
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
922
|
+
processed_items = []
|
|
923
|
+
for item in items.items.values():
|
|
924
|
+
try:
|
|
925
|
+
validated = Item(**item) if not isinstance(item, Item) else item
|
|
926
|
+
processed_items.append(
|
|
927
|
+
self.preprocess_item(
|
|
928
|
+
validated.model_dump(mode="json"), base_url, items.method
|
|
929
|
+
)
|
|
930
|
+
)
|
|
931
|
+
except ValidationError:
|
|
932
|
+
# Immediately raise on the first invalid item (strict mode)
|
|
933
|
+
raise
|
|
904
934
|
|
|
905
|
-
# not a great way to get the collection_id-- should be part of the method signature
|
|
906
935
|
collection_id = processed_items[0]["collection"]
|
|
907
|
-
|
|
908
|
-
self.database.bulk_sync(
|
|
909
|
-
collection_id,
|
|
936
|
+
attempted = len(processed_items)
|
|
937
|
+
success, errors = self.database.bulk_sync(
|
|
938
|
+
collection_id,
|
|
939
|
+
processed_items,
|
|
940
|
+
refresh=kwargs.get("refresh", False),
|
|
910
941
|
)
|
|
942
|
+
if errors:
|
|
943
|
+
logger.error(f"Bulk sync operation encountered errors: {errors}")
|
|
944
|
+
else:
|
|
945
|
+
logger.info(f"Bulk sync operation succeeded with {success} actions.")
|
|
911
946
|
|
|
912
|
-
return f"Successfully added {
|
|
947
|
+
return f"Successfully added/updated {success} Items. {attempted - success} errors occurred."
|
|
913
948
|
|
|
914
949
|
|
|
915
950
|
_DEFAULT_QUERYABLES: Dict[str, Dict[str, Any]] = {
|
|
@@ -96,7 +96,13 @@ ES_MAPPINGS_DYNAMIC_TEMPLATES = [
|
|
|
96
96
|
},
|
|
97
97
|
# Default all other strings not otherwise specified to keyword
|
|
98
98
|
{"strings": {"match_mapping_type": "string", "mapping": {"type": "keyword"}}},
|
|
99
|
-
{"
|
|
99
|
+
{"long_to_double": {"match_mapping_type": "long", "mapping": {"type": "double"}}},
|
|
100
|
+
{
|
|
101
|
+
"double_to_double": {
|
|
102
|
+
"match_mapping_type": "double",
|
|
103
|
+
"mapping": {"type": "double"},
|
|
104
|
+
}
|
|
105
|
+
},
|
|
100
106
|
]
|
|
101
107
|
|
|
102
108
|
ES_ITEMS_MAPPINGS = {
|
|
@@ -10,7 +10,7 @@ from enum import auto
|
|
|
10
10
|
from types import DynamicClassAttribute
|
|
11
11
|
from typing import Any, Callable, Dict, Optional
|
|
12
12
|
|
|
13
|
-
from pydantic import BaseModel,
|
|
13
|
+
from pydantic import BaseModel, model_validator
|
|
14
14
|
from stac_pydantic.utils import AutoValueEnum
|
|
15
15
|
|
|
16
16
|
from stac_fastapi.extensions.core.query import QueryExtension as QueryExtensionBase
|
|
@@ -63,7 +63,7 @@ class QueryExtensionPostRequest(BaseModel):
|
|
|
63
63
|
|
|
64
64
|
query: Optional[Dict[str, Dict[Operator, Any]]] = None
|
|
65
65
|
|
|
66
|
-
@
|
|
66
|
+
@model_validator(mode="before")
|
|
67
67
|
def validate_query_fields(cls, values: Dict) -> Dict:
|
|
68
68
|
"""Validate query fields."""
|
|
69
69
|
...
|
stac_fastapi/core/utilities.py
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
This module contains functions for transforming geospatial coordinates,
|
|
4
4
|
such as converting bounding boxes to polygon representations.
|
|
5
5
|
"""
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
6
8
|
from typing import Any, Dict, List, Optional, Set, Union
|
|
7
9
|
|
|
8
10
|
from stac_fastapi.types.stac import Item
|
|
@@ -10,6 +12,33 @@ from stac_fastapi.types.stac import Item
|
|
|
10
12
|
MAX_LIMIT = 10000
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
def get_bool_env(name: str, default: bool = False) -> bool:
|
|
16
|
+
"""
|
|
17
|
+
Retrieve a boolean value from an environment variable.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
name (str): The name of the environment variable.
|
|
21
|
+
default (bool, optional): The default value to use if the variable is not set or unrecognized. Defaults to False.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
bool: The boolean value parsed from the environment variable.
|
|
25
|
+
"""
|
|
26
|
+
value = os.getenv(name, str(default).lower())
|
|
27
|
+
true_values = ("true", "1", "yes", "y")
|
|
28
|
+
false_values = ("false", "0", "no", "n")
|
|
29
|
+
if value.lower() in true_values:
|
|
30
|
+
return True
|
|
31
|
+
elif value.lower() in false_values:
|
|
32
|
+
return False
|
|
33
|
+
else:
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
logger.warning(
|
|
36
|
+
f"Environment variable '{name}' has unrecognized value '{value}'. "
|
|
37
|
+
f"Expected one of {true_values + false_values}. Using default: {default}"
|
|
38
|
+
)
|
|
39
|
+
return default
|
|
40
|
+
|
|
41
|
+
|
|
13
42
|
def bbox2polygon(b0: float, b1: float, b2: float, b3: float) -> List[List[List[float]]]:
|
|
14
43
|
"""Transform a bounding box represented by its four coordinates `b0`, `b1`, `b2`, and `b3` into a polygon.
|
|
15
44
|
|
stac_fastapi/core/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""library version."""
|
|
2
|
-
__version__ = "4.0
|
|
2
|
+
__version__ = "4.1.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi-core
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.1.0
|
|
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
|
|
@@ -19,9 +19,9 @@ Requires-Dist: fastapi~=0.109.0
|
|
|
19
19
|
Requires-Dist: attrs>=23.2.0
|
|
20
20
|
Requires-Dist: pydantic<3.0.0,>=2.4.1
|
|
21
21
|
Requires-Dist: stac-pydantic~=3.1.0
|
|
22
|
-
Requires-Dist: stac-fastapi.api==5.
|
|
23
|
-
Requires-Dist: stac-fastapi.extensions==5.
|
|
24
|
-
Requires-Dist: stac-fastapi.types==5.
|
|
22
|
+
Requires-Dist: stac-fastapi.api==5.2.0
|
|
23
|
+
Requires-Dist: stac-fastapi.extensions==5.2.0
|
|
24
|
+
Requires-Dist: stac-fastapi.types==5.2.0
|
|
25
25
|
Requires-Dist: orjson~=3.9.0
|
|
26
26
|
Requires-Dist: overrides~=7.4.0
|
|
27
27
|
Requires-Dist: geojson-pydantic~=1.0.0
|
|
@@ -60,8 +60,18 @@ Requires-Dist: slowapi~=0.1.9
|
|
|
60
60
|
- There is [Postman](https://documenter.getpostman.com/view/12888943/2s8ZDSdRHA) documentation here for examples on how to run some of the API routes locally - after starting the elasticsearch backend via the compose.yml file.
|
|
61
61
|
- The `/examples` folder shows an example of running stac-fastapi-elasticsearch from PyPI in docker without needing any code from the repository. There is also a Postman collection here that you can load into Postman for testing the API routes.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
|
|
64
|
+
### Performance Note
|
|
65
|
+
|
|
66
|
+
The `enable_direct_response` option is provided by the stac-fastapi core library (introduced in stac-fastapi 5.2.0) and is available in this project starting from v4.0.0.
|
|
67
|
+
|
|
68
|
+
**You can now control this setting via the `ENABLE_DIRECT_RESPONSE` environment variable.**
|
|
69
|
+
|
|
70
|
+
When enabled (`ENABLE_DIRECT_RESPONSE=true`), endpoints return Starlette Response objects directly, bypassing FastAPI's default serialization for improved performance. **However, all FastAPI dependencies (including authentication, custom status codes, and validation) are disabled for all routes.**
|
|
71
|
+
|
|
72
|
+
This mode is best suited for public or read-only APIs where authentication and custom logic are not required. Default is `false` for safety.
|
|
73
|
+
|
|
74
|
+
See: [issue #347](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/issues/347)
|
|
65
75
|
|
|
66
76
|
|
|
67
77
|
### To install from PyPI:
|
|
@@ -105,8 +115,9 @@ If you wish to use a different version, put the following in a
|
|
|
105
115
|
file named `.env` in the same directory you run Docker Compose from:
|
|
106
116
|
|
|
107
117
|
```shell
|
|
108
|
-
ELASTICSEARCH_VERSION=
|
|
109
|
-
OPENSEARCH_VERSION=2.11.
|
|
118
|
+
ELASTICSEARCH_VERSION=8.11.0
|
|
119
|
+
OPENSEARCH_VERSION=2.11.1
|
|
120
|
+
ENABLE_DIRECT_RESPONSE=false
|
|
110
121
|
```
|
|
111
122
|
The most recent Elasticsearch 7.x versions should also work. See the [opensearch-py docs](https://github.com/opensearch-project/opensearch-py/blob/main/COMPATIBILITY.md) for compatibility information.
|
|
112
123
|
|
|
@@ -131,8 +142,10 @@ You can customize additional settings in your `.env` file:
|
|
|
131
142
|
| `RELOAD` | Enable auto-reload for development. | `true` | Optional |
|
|
132
143
|
| `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
|
|
133
144
|
| `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
|
|
134
|
-
| `ELASTICSEARCH_VERSION`
|
|
135
|
-
| `
|
|
145
|
+
| `ELASTICSEARCH_VERSION` | Version of Elasticsearch to use. | `8.11.0` | Optional |
|
|
146
|
+
| `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
|
|
147
|
+
| `OPENSEARCH_VERSION` | OpenSearch version | `2.11.1` | Optional
|
|
148
|
+
| `RAISE_ON_BULK_ERROR` | Controls whether bulk insert operations raise exceptions on errors. If set to `true`, the operation will stop and raise an exception when an error occurs. If set to `false`, errors will be logged, and the operation will continue. **Note:** STAC Item and ItemCollection validation errors will always raise, regardless of this flag. | `false` | Optional |
|
|
136
149
|
|
|
137
150
|
> [!NOTE]
|
|
138
151
|
> The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, and `ES_VERIFY_CERTS` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
|
|
@@ -2,24 +2,24 @@ stac_fastapi/core/__init__.py,sha256=8izV3IWRGdXmDOK1hIPQAanbWs9EI04PJCGgqG1ZGIs
|
|
|
2
2
|
stac_fastapi/core/base_database_logic.py,sha256=GfxPMtg2gHAZM44haIgi_9J-IG1et_FYA5xRBosJpJA,1608
|
|
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/database_logic.py,sha256=
|
|
5
|
+
stac_fastapi/core/core.py,sha256=6mJBCSufrkOmJwjcLB2HYSmY9ocG946QPyw0zgZK6gI,41334
|
|
6
|
+
stac_fastapi/core/database_logic.py,sha256=KXBNL47QcqYsmtRrHjKaH3r_A4Wl4DUpumSDiuj4uVU,7051
|
|
7
7
|
stac_fastapi/core/datetime_utils.py,sha256=ICUHPgvbH-xumIKdKVtcXogaCNEsxoaVqhWlVoCx-Ug,1481
|
|
8
8
|
stac_fastapi/core/rate_limit.py,sha256=Gu8dAaJReGsj1L91U6m2tflU6RahpXDRs2-AYSKoybA,1318
|
|
9
9
|
stac_fastapi/core/route_dependencies.py,sha256=zefYlfQTMW292vdqmtquW4UswtBHwH5Pm-8UynyZbJQ,5522
|
|
10
10
|
stac_fastapi/core/serializers.py,sha256=pJjpwA6BOHjCXBCmwVTH7MOmTjY9NXF1-i_E0yB60Zg,6228
|
|
11
11
|
stac_fastapi/core/session.py,sha256=Qr080UU_7qKtIv0qZAuOV7oNUQUzT5Yn00h-m_aoCvY,473
|
|
12
|
-
stac_fastapi/core/utilities.py,sha256=
|
|
13
|
-
stac_fastapi/core/version.py,sha256=
|
|
12
|
+
stac_fastapi/core/utilities.py,sha256=493rYGimjWykrkWnRia1Aquc4Jvlyvio21VZcEmdxPo,6743
|
|
13
|
+
stac_fastapi/core/version.py,sha256=3xU5aBmxgAcPizRlef_YROCW9ULsGOA4flSyd9AQog4,45
|
|
14
14
|
stac_fastapi/core/extensions/__init__.py,sha256=2MCo0UoInkgItIM8id-rbeygzn_qUOvTGfr8jFXZjHQ,167
|
|
15
15
|
stac_fastapi/core/extensions/aggregation.py,sha256=H5Yzvs-QH60P8jJm08Ng5FDvMU1o77WoNNZ2mKxUFjI,23062
|
|
16
16
|
stac_fastapi/core/extensions/fields.py,sha256=NCT5XHvfaf297eDPNaIFsIzvJnbbUTpScqF0otdx0NA,1066
|
|
17
17
|
stac_fastapi/core/extensions/filter.py,sha256=F7ECGQO-nDnc8TFmL5FK1TJMeWx5TmKMjwb8b4kRgOc,6394
|
|
18
|
-
stac_fastapi/core/extensions/query.py,sha256=
|
|
18
|
+
stac_fastapi/core/extensions/query.py,sha256=Xmo8pfZEZKPudZEjjozv3R0wLOP0ayjC9E67sBOXqWY,1803
|
|
19
19
|
stac_fastapi/core/models/__init__.py,sha256=g-D1DiGfmC9Bg27DW9JzkN6fAvscv75wyhyiZ6NzvIk,48
|
|
20
20
|
stac_fastapi/core/models/links.py,sha256=3jk4t2wA3RGTq9_BbzFsMKvMbgDBajQy4vKZFSHt7E8,6666
|
|
21
21
|
stac_fastapi/core/models/search.py,sha256=7SgAUyzHGXBXSqB4G6cwq9FMwoAS00momb7jvBkjyow,27
|
|
22
|
-
stac_fastapi_core-4.0.
|
|
23
|
-
stac_fastapi_core-4.0.
|
|
24
|
-
stac_fastapi_core-4.0.
|
|
25
|
-
stac_fastapi_core-4.0.
|
|
22
|
+
stac_fastapi_core-4.1.0.dist-info/METADATA,sha256=GZZtIG4nc-v9LRmEN1XAdcqRMF1IrVMBzfuRDoc9vDc,20524
|
|
23
|
+
stac_fastapi_core-4.1.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
24
|
+
stac_fastapi_core-4.1.0.dist-info/top_level.txt,sha256=vqn-D9-HsRPTTxy0Vk_KkDmTiMES4owwBQ3ydSZYb2s,13
|
|
25
|
+
stac_fastapi_core-4.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|