stac-fastapi.api 3.0.3__tar.gz → 3.0.5__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_api-3.0.3 → stac_fastapi_api-3.0.5}/PKG-INFO +14 -2
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/setup.py +1 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/app.py +3 -2
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/errors.py +2 -1
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/version.py +1 -1
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/PKG-INFO +14 -2
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/tests/test_app.py +69 -4
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/tests/test_app_prefix.py +64 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/tests/test_models.py +10 -7
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/README.md +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/setup.cfg +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/__init__.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/config.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/middleware.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/models.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/openapi.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi/api/routes.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/requires.txt +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/top_level.txt +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/tests/test_api.py +0 -0
- {stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/tests/test_middleware.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: stac-fastapi.api
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.5
|
|
4
4
|
Summary: An implementation of STAC API based on the FastAPI framework.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi
|
|
6
6
|
Author: Arturo Engineering
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
19
|
Classifier: License :: OSI Approved :: MIT License
|
|
19
20
|
Requires-Python: >=3.8
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
@@ -33,3 +34,14 @@ Provides-Extra: docs
|
|
|
33
34
|
Requires-Dist: mkdocs; extra == "docs"
|
|
34
35
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
35
36
|
Requires-Dist: pdocs; extra == "docs"
|
|
37
|
+
Dynamic: author
|
|
38
|
+
Dynamic: author-email
|
|
39
|
+
Dynamic: classifier
|
|
40
|
+
Dynamic: description-content-type
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: keywords
|
|
43
|
+
Dynamic: license
|
|
44
|
+
Dynamic: provides-extra
|
|
45
|
+
Dynamic: requires-dist
|
|
46
|
+
Dynamic: requires-python
|
|
47
|
+
Dynamic: summary
|
|
@@ -41,6 +41,7 @@ setup(
|
|
|
41
41
|
"Programming Language :: Python :: 3.10",
|
|
42
42
|
"Programming Language :: Python :: 3.11",
|
|
43
43
|
"Programming Language :: Python :: 3.12",
|
|
44
|
+
"Programming Language :: Python :: 3.13",
|
|
44
45
|
"License :: OSI Approved :: MIT License",
|
|
45
46
|
],
|
|
46
47
|
keywords="STAC FastAPI COG",
|
|
@@ -10,8 +10,8 @@ from fastapi.openapi.utils import get_openapi
|
|
|
10
10
|
from fastapi.params import Depends
|
|
11
11
|
from stac_pydantic import api
|
|
12
12
|
from stac_pydantic.api.collections import Collections
|
|
13
|
-
from stac_pydantic.api.version import STAC_API_VERSION
|
|
14
13
|
from stac_pydantic.shared import MimeTypes
|
|
14
|
+
from stac_pydantic.version import STAC_VERSION
|
|
15
15
|
from starlette.middleware import Middleware
|
|
16
16
|
from starlette.responses import JSONResponse, Response
|
|
17
17
|
|
|
@@ -77,6 +77,7 @@ class StacApi:
|
|
|
77
77
|
openapi_url=self.settings.openapi_url,
|
|
78
78
|
docs_url=self.settings.docs_url,
|
|
79
79
|
redoc_url=None,
|
|
80
|
+
root_path=self.settings.root_path,
|
|
80
81
|
),
|
|
81
82
|
takes_self=True,
|
|
82
83
|
),
|
|
@@ -93,7 +94,7 @@ class StacApi:
|
|
|
93
94
|
lambda self: self.settings.stac_fastapi_version, takes_self=True
|
|
94
95
|
)
|
|
95
96
|
)
|
|
96
|
-
stac_version: str = attr.ib(default=
|
|
97
|
+
stac_version: str = attr.ib(default=STAC_VERSION)
|
|
97
98
|
description: str = attr.ib(
|
|
98
99
|
default=attr.Factory(
|
|
99
100
|
lambda self: self.settings.stac_fastapi_description, takes_self=True
|
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
from typing import Callable, Dict, Type, TypedDict
|
|
5
5
|
|
|
6
6
|
from fastapi import FastAPI
|
|
7
|
+
from fastapi.encoders import jsonable_encoder
|
|
7
8
|
from fastapi.exceptions import RequestValidationError, ResponseValidationError
|
|
8
9
|
from starlette import status
|
|
9
10
|
from starlette.requests import Request
|
|
@@ -88,7 +89,7 @@ def add_exception_handlers(
|
|
|
88
89
|
request: Request, exc: RequestValidationError
|
|
89
90
|
) -> JSONResponse:
|
|
90
91
|
return JSONResponse(
|
|
91
|
-
content=
|
|
92
|
+
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
|
|
92
93
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
93
94
|
)
|
|
94
95
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: stac-fastapi.api
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.5
|
|
4
4
|
Summary: An implementation of STAC API based on the FastAPI framework.
|
|
5
5
|
Home-page: https://github.com/stac-utils/stac-fastapi
|
|
6
6
|
Author: Arturo Engineering
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
19
|
Classifier: License :: OSI Approved :: MIT License
|
|
19
20
|
Requires-Python: >=3.8
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
@@ -33,3 +34,14 @@ Provides-Extra: docs
|
|
|
33
34
|
Requires-Dist: mkdocs; extra == "docs"
|
|
34
35
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
35
36
|
Requires-Dist: pdocs; extra == "docs"
|
|
37
|
+
Dynamic: author
|
|
38
|
+
Dynamic: author-email
|
|
39
|
+
Dynamic: classifier
|
|
40
|
+
Dynamic: description-content-type
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: keywords
|
|
43
|
+
Dynamic: license
|
|
44
|
+
Dynamic: provides-extra
|
|
45
|
+
Dynamic: requires-dist
|
|
46
|
+
Dynamic: requires-python
|
|
47
|
+
Dynamic: summary
|
|
@@ -165,13 +165,11 @@ def test_filter_extension(validate, TestCoreClient, item_dict):
|
|
|
165
165
|
type="FeatureCollection", features=[stac.Item(**item_dict)]
|
|
166
166
|
)
|
|
167
167
|
|
|
168
|
-
post_request_model = create_post_request_model([FilterExtension()])
|
|
169
|
-
|
|
170
168
|
test_app = app.StacApi(
|
|
171
169
|
settings=ApiSettings(enable_response_models=validate),
|
|
172
|
-
client=FilterClient(
|
|
170
|
+
client=FilterClient(),
|
|
173
171
|
search_get_request_model=create_get_request_model([FilterExtension()]),
|
|
174
|
-
search_post_request_model=
|
|
172
|
+
search_post_request_model=create_post_request_model([FilterExtension()]),
|
|
175
173
|
extensions=[FilterExtension()],
|
|
176
174
|
)
|
|
177
175
|
|
|
@@ -387,3 +385,70 @@ def test_request_model(AsyncTestCoreClient):
|
|
|
387
385
|
"/collections/test_collection/items/test_item", params={"user": "Chewbacca"}
|
|
388
386
|
)
|
|
389
387
|
assert resp.status_code == 200
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def test_client_datetime_input_params():
|
|
391
|
+
"""Test all endpoints. Verify input models."""
|
|
392
|
+
|
|
393
|
+
class FakeClient(BaseCoreClient):
|
|
394
|
+
def post_search(self, search_request: BaseSearchPostRequest, **kwargs):
|
|
395
|
+
return search_request.datetime
|
|
396
|
+
|
|
397
|
+
def get_search(
|
|
398
|
+
self,
|
|
399
|
+
collections: Optional[List[str]] = None,
|
|
400
|
+
ids: Optional[List[str]] = None,
|
|
401
|
+
bbox: Optional[List[NumType]] = None,
|
|
402
|
+
intersects: Optional[str] = None,
|
|
403
|
+
datetime: Optional[Union[str, datetime]] = None,
|
|
404
|
+
limit: Optional[int] = 10,
|
|
405
|
+
**kwargs,
|
|
406
|
+
):
|
|
407
|
+
return datetime
|
|
408
|
+
|
|
409
|
+
def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
|
|
410
|
+
raise NotImplementedError
|
|
411
|
+
|
|
412
|
+
def all_collections(self, **kwargs) -> stac.Collections:
|
|
413
|
+
raise NotImplementedError
|
|
414
|
+
|
|
415
|
+
def get_collection(self, collection_id: str, **kwargs) -> stac.Collection:
|
|
416
|
+
raise NotImplementedError
|
|
417
|
+
|
|
418
|
+
def item_collection(
|
|
419
|
+
self,
|
|
420
|
+
collection_id: str,
|
|
421
|
+
bbox: Optional[List[Union[float, int]]] = None,
|
|
422
|
+
datetime: Optional[Union[str, datetime]] = None,
|
|
423
|
+
limit: int = 10,
|
|
424
|
+
token: str = None,
|
|
425
|
+
**kwargs,
|
|
426
|
+
) -> stac.ItemCollection:
|
|
427
|
+
raise NotImplementedError
|
|
428
|
+
|
|
429
|
+
test_app = app.StacApi(
|
|
430
|
+
settings=ApiSettings(enable_response_models=False),
|
|
431
|
+
client=FakeClient(),
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
with TestClient(test_app.app) as client:
|
|
435
|
+
get_search = client.get(
|
|
436
|
+
"/search",
|
|
437
|
+
params={
|
|
438
|
+
"collections": ["test"],
|
|
439
|
+
"datetime": "2020-01-01T00:00:00.00001Z",
|
|
440
|
+
},
|
|
441
|
+
)
|
|
442
|
+
post_search = client.post(
|
|
443
|
+
"/search",
|
|
444
|
+
json={
|
|
445
|
+
"collections": ["test"],
|
|
446
|
+
"datetime": "2020-01-01T00:00:00.00001Z",
|
|
447
|
+
},
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
assert get_search.status_code == 200, get_search.text
|
|
451
|
+
assert get_search.json() == "2020-01-01T00:00:00.000010+00:00"
|
|
452
|
+
|
|
453
|
+
assert post_search.status_code == 200, post_search.text
|
|
454
|
+
assert post_search.json() == "2020-01-01T00:00:00.00001Z"
|
|
@@ -144,3 +144,67 @@ def test_async_api_prefix(AsyncTestCoreClient, prefix):
|
|
|
144
144
|
|
|
145
145
|
resp = client.get(prefix + expected_path)
|
|
146
146
|
assert resp.status_code == 200
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@pytest.mark.parametrize("prefix", ["", "/a_prefix"])
|
|
150
|
+
def test_api_prefix_with_root_path(TestCoreClient, prefix):
|
|
151
|
+
api_settings = ApiSettings(
|
|
152
|
+
openapi_url=f"{prefix}/api", docs_url=f"{prefix}/api.html", root_path="/api/v1"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
api = StacApi(
|
|
156
|
+
settings=api_settings,
|
|
157
|
+
client=TestCoreClient(),
|
|
158
|
+
router=APIRouter(prefix=prefix),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
prefix = "/api/v1" + prefix
|
|
162
|
+
with TestClient(api.app, base_url="http://stac.io", root_path="/api/v1") as client:
|
|
163
|
+
landing = client.get(f"{prefix}/")
|
|
164
|
+
assert landing.status_code == 200, landing.json()
|
|
165
|
+
|
|
166
|
+
service_doc = client.get(f"{prefix}/api.html")
|
|
167
|
+
assert service_doc.status_code == 200, service_doc.text
|
|
168
|
+
|
|
169
|
+
service_desc = client.get(f"{prefix}/api")
|
|
170
|
+
assert service_desc.status_code == 200, service_desc.json()
|
|
171
|
+
|
|
172
|
+
conformance = client.get(f"{prefix}/conformance")
|
|
173
|
+
assert conformance.status_code == 200, conformance.json()
|
|
174
|
+
|
|
175
|
+
# NOTE: The collections/collection/items/item links do not have the prefix
|
|
176
|
+
# because they are created in the fixtures
|
|
177
|
+
collections = client.get(f"{prefix}/collections")
|
|
178
|
+
assert collections.status_code == 200, collections.json()
|
|
179
|
+
collection_id = collections.json()["collections"][0]["id"]
|
|
180
|
+
|
|
181
|
+
collection = client.get(f"{prefix}/collections/{collection_id}")
|
|
182
|
+
assert collection.status_code == 200, collection.json()
|
|
183
|
+
|
|
184
|
+
items = client.get(f"{prefix}/collections/{collection_id}/items")
|
|
185
|
+
assert items.status_code == 200, items.json()
|
|
186
|
+
|
|
187
|
+
item_id = items.json()["features"][0]["id"]
|
|
188
|
+
item = client.get(f"{prefix}/collections/{collection_id}/items/{item_id}")
|
|
189
|
+
assert item.status_code == 200, item.json()
|
|
190
|
+
|
|
191
|
+
link_tests = [
|
|
192
|
+
("root", "application/json", "/"),
|
|
193
|
+
("conformance", "application/json", "/conformance"),
|
|
194
|
+
("data", "application/json", "/collections"),
|
|
195
|
+
("search", "application/geo+json", "/search"),
|
|
196
|
+
("service-doc", "text/html", "/api.html"),
|
|
197
|
+
("service-desc", "application/vnd.oai.openapi+json;version=3.0", "/api"),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
for rel_type, expected_media_type, expected_path in link_tests:
|
|
201
|
+
link = get_link(landing.json(), rel_type)
|
|
202
|
+
|
|
203
|
+
assert link is not None, f"Missing {rel_type} link in landing page"
|
|
204
|
+
assert link.get("type") == expected_media_type
|
|
205
|
+
|
|
206
|
+
link_path = urllib.parse.urlsplit(link.get("href")).path
|
|
207
|
+
assert link_path == prefix + expected_path
|
|
208
|
+
|
|
209
|
+
resp = client.get(prefix + expected_path)
|
|
210
|
+
assert resp.status_code == 200
|
|
@@ -26,7 +26,7 @@ def test_create_get_request_model():
|
|
|
26
26
|
"coordinates": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],
|
|
27
27
|
}
|
|
28
28
|
),
|
|
29
|
-
datetime="2020-01-01T00:00:
|
|
29
|
+
datetime="2020-01-01T00:00:00.00001Z",
|
|
30
30
|
limit=10,
|
|
31
31
|
filter="test==test",
|
|
32
32
|
filter_crs="epsg:4326",
|
|
@@ -35,6 +35,8 @@ def test_create_get_request_model():
|
|
|
35
35
|
|
|
36
36
|
assert model.collections == ["test1", "test2"]
|
|
37
37
|
assert model.filter_crs == "epsg:4326"
|
|
38
|
+
d = model.datetime
|
|
39
|
+
assert d.microsecond == 10
|
|
38
40
|
|
|
39
41
|
with pytest.raises(HTTPException):
|
|
40
42
|
request_model(datetime="yo")
|
|
@@ -62,10 +64,10 @@ def test_create_get_request_model():
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
@pytest.mark.parametrize(
|
|
65
|
-
"
|
|
67
|
+
"filter_val,passes",
|
|
66
68
|
[(None, True), ({"test": "test"}, True), ([], False)],
|
|
67
69
|
)
|
|
68
|
-
def test_create_post_request_model(
|
|
70
|
+
def test_create_post_request_model(filter_val, passes):
|
|
69
71
|
request_model = create_post_request_model(
|
|
70
72
|
extensions=[FilterExtension(), FieldsExtension()],
|
|
71
73
|
base_model=BaseSearchPostRequest,
|
|
@@ -73,21 +75,22 @@ def test_create_post_request_model(filter, passes):
|
|
|
73
75
|
|
|
74
76
|
if not passes:
|
|
75
77
|
with pytest.raises(ValidationError):
|
|
76
|
-
model = request_model(filter=
|
|
78
|
+
model = request_model(filter=filter_val)
|
|
77
79
|
else:
|
|
78
80
|
model = request_model(
|
|
79
81
|
collections=["test1", "test2"],
|
|
80
82
|
ids=["test1", "test2"],
|
|
81
83
|
bbox=[0, 0, 1, 1],
|
|
82
|
-
datetime="2020-01-01T00:00:
|
|
84
|
+
datetime="2020-01-01T00:00:00.00001Z",
|
|
83
85
|
limit=10,
|
|
84
|
-
filter=
|
|
86
|
+
filter=filter_val,
|
|
85
87
|
**{"filter-crs": "epsg:4326", "filter-lang": "cql2-json"},
|
|
86
88
|
)
|
|
87
89
|
|
|
88
90
|
assert model.collections == ["test1", "test2"]
|
|
89
91
|
assert model.filter_crs == "epsg:4326"
|
|
90
|
-
assert model.filter ==
|
|
92
|
+
assert model.filter == filter_val
|
|
93
|
+
assert model.datetime == "2020-01-01T00:00:00.00001Z"
|
|
91
94
|
|
|
92
95
|
|
|
93
96
|
@pytest.mark.parametrize(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac_fastapi_api-3.0.3 → stac_fastapi_api-3.0.5}/stac_fastapi.api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|