stac-fastapi.api 3.0.0__tar.gz → 3.0.0a1__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.0 → stac_fastapi_api-3.0.0a1}/PKG-INFO +2 -2
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/setup.py +1 -1
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/app.py +41 -27
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/config.py +1 -3
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/models.py +60 -34
- stac_fastapi_api-3.0.0a1/stac_fastapi/api/openapi.py +87 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/routes.py +9 -15
- stac_fastapi_api-3.0.0a1/stac_fastapi/api/version.py +2 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/PKG-INFO +2 -2
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/requires.txt +1 -1
- stac_fastapi_api-3.0.0a1/tests/test_api.py +172 -0
- stac_fastapi_api-3.0.0a1/tests/test_app.py +192 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/tests/test_middleware.py +0 -30
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/tests/test_models.py +12 -42
- stac_fastapi_api-3.0.0/stac_fastapi/api/openapi.py +0 -37
- stac_fastapi_api-3.0.0/stac_fastapi/api/version.py +0 -3
- stac_fastapi_api-3.0.0/tests/test_api.py +0 -456
- stac_fastapi_api-3.0.0/tests/test_app.py +0 -389
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/README.md +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/setup.cfg +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/__init__.py +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/errors.py +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi/api/middleware.py +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/stac_fastapi.api.egg-info/top_level.txt +0 -0
- {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a1}/tests/test_app_prefix.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi.api
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a1
|
|
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
|
|
@@ -19,7 +19,7 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: brotli_asgi
|
|
22
|
-
Requires-Dist: stac-fastapi.types
|
|
22
|
+
Requires-Dist: stac-fastapi.types
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: httpx; extra == "dev"
|
|
25
25
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -18,15 +18,18 @@ from starlette.responses import JSONResponse, Response
|
|
|
18
18
|
from stac_fastapi.api.errors import DEFAULT_STATUS_CODES, add_exception_handlers
|
|
19
19
|
from stac_fastapi.api.middleware import CORSMiddleware, ProxyHeaderMiddleware
|
|
20
20
|
from stac_fastapi.api.models import (
|
|
21
|
-
APIRequest,
|
|
22
21
|
CollectionUri,
|
|
23
22
|
EmptyRequest,
|
|
24
23
|
GeoJSONResponse,
|
|
25
24
|
ItemCollectionUri,
|
|
26
25
|
ItemUri,
|
|
26
|
+
create_request_model,
|
|
27
27
|
)
|
|
28
28
|
from stac_fastapi.api.openapi import update_openapi
|
|
29
29
|
from stac_fastapi.api.routes import Scope, add_route_dependencies, create_async_endpoint
|
|
30
|
+
|
|
31
|
+
# TODO: make this module not depend on `stac_fastapi.extensions`
|
|
32
|
+
from stac_fastapi.extensions.core import FieldsExtension, TokenPaginationExtension
|
|
30
33
|
from stac_fastapi.types.config import ApiSettings, Settings
|
|
31
34
|
from stac_fastapi.types.core import AsyncBaseCoreClient, BaseCoreClient
|
|
32
35
|
from stac_fastapi.types.extension import ApiExtension
|
|
@@ -105,10 +108,7 @@ class StacApi:
|
|
|
105
108
|
search_post_request_model: Type[BaseSearchPostRequest] = attr.ib(
|
|
106
109
|
default=BaseSearchPostRequest
|
|
107
110
|
)
|
|
108
|
-
|
|
109
|
-
collection_get_request_model: Type[APIRequest] = attr.ib(default=CollectionUri)
|
|
110
|
-
items_get_request_model: Type[APIRequest] = attr.ib(default=ItemCollectionUri)
|
|
111
|
-
item_get_request_model: Type[APIRequest] = attr.ib(default=ItemUri)
|
|
111
|
+
pagination_extension = attr.ib(default=TokenPaginationExtension)
|
|
112
112
|
response_class: Type[Response] = attr.ib(default=JSONResponse)
|
|
113
113
|
middlewares: List[Middleware] = attr.ib(
|
|
114
114
|
default=attr.Factory(
|
|
@@ -211,9 +211,7 @@ class StacApi:
|
|
|
211
211
|
response_model_exclude_unset=True,
|
|
212
212
|
response_model_exclude_none=True,
|
|
213
213
|
methods=["GET"],
|
|
214
|
-
endpoint=create_async_endpoint(
|
|
215
|
-
self.client.get_item, self.item_get_request_model
|
|
216
|
-
),
|
|
214
|
+
endpoint=create_async_endpoint(self.client.get_item, ItemUri),
|
|
217
215
|
)
|
|
218
216
|
|
|
219
217
|
def register_post_search(self):
|
|
@@ -222,12 +220,15 @@ class StacApi:
|
|
|
222
220
|
Returns:
|
|
223
221
|
None
|
|
224
222
|
"""
|
|
223
|
+
fields_ext = self.get_extension(FieldsExtension)
|
|
225
224
|
self.router.add_api_route(
|
|
226
225
|
name="Search",
|
|
227
226
|
path="/search",
|
|
228
|
-
response_model=
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
response_model=(
|
|
228
|
+
(api.ItemCollection if not fields_ext else None)
|
|
229
|
+
if self.settings.enable_response_models
|
|
230
|
+
else None
|
|
231
|
+
),
|
|
231
232
|
responses={
|
|
232
233
|
200: {
|
|
233
234
|
"content": {
|
|
@@ -251,12 +252,15 @@ class StacApi:
|
|
|
251
252
|
Returns:
|
|
252
253
|
None
|
|
253
254
|
"""
|
|
255
|
+
fields_ext = self.get_extension(FieldsExtension)
|
|
254
256
|
self.router.add_api_route(
|
|
255
257
|
name="Search",
|
|
256
258
|
path="/search",
|
|
257
|
-
response_model=
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
response_model=(
|
|
260
|
+
(api.ItemCollection if not fields_ext else None)
|
|
261
|
+
if self.settings.enable_response_models
|
|
262
|
+
else None
|
|
263
|
+
),
|
|
260
264
|
responses={
|
|
261
265
|
200: {
|
|
262
266
|
"content": {
|
|
@@ -298,9 +302,7 @@ class StacApi:
|
|
|
298
302
|
response_model_exclude_unset=True,
|
|
299
303
|
response_model_exclude_none=True,
|
|
300
304
|
methods=["GET"],
|
|
301
|
-
endpoint=create_async_endpoint(
|
|
302
|
-
self.client.all_collections, self.collections_get_request_model
|
|
303
|
-
),
|
|
305
|
+
endpoint=create_async_endpoint(self.client.all_collections, EmptyRequest),
|
|
304
306
|
)
|
|
305
307
|
|
|
306
308
|
def register_get_collection(self):
|
|
@@ -327,9 +329,7 @@ class StacApi:
|
|
|
327
329
|
response_model_exclude_unset=True,
|
|
328
330
|
response_model_exclude_none=True,
|
|
329
331
|
methods=["GET"],
|
|
330
|
-
endpoint=create_async_endpoint(
|
|
331
|
-
self.client.get_collection, self.collection_get_request_model
|
|
332
|
-
),
|
|
332
|
+
endpoint=create_async_endpoint(self.client.get_collection, CollectionUri),
|
|
333
333
|
)
|
|
334
334
|
|
|
335
335
|
def register_get_item_collection(self):
|
|
@@ -338,6 +338,16 @@ class StacApi:
|
|
|
338
338
|
Returns:
|
|
339
339
|
None
|
|
340
340
|
"""
|
|
341
|
+
pagination_extension = self.get_extension(self.pagination_extension)
|
|
342
|
+
if pagination_extension is not None:
|
|
343
|
+
mixins = [pagination_extension.GET]
|
|
344
|
+
else:
|
|
345
|
+
mixins = None
|
|
346
|
+
request_model = create_request_model(
|
|
347
|
+
"ItemCollectionURI",
|
|
348
|
+
base_model=ItemCollectionUri,
|
|
349
|
+
mixins=mixins,
|
|
350
|
+
)
|
|
341
351
|
self.router.add_api_route(
|
|
342
352
|
name="Get ItemCollection",
|
|
343
353
|
path="/collections/{collection_id}/items",
|
|
@@ -356,9 +366,7 @@ class StacApi:
|
|
|
356
366
|
response_model_exclude_unset=True,
|
|
357
367
|
response_model_exclude_none=True,
|
|
358
368
|
methods=["GET"],
|
|
359
|
-
endpoint=create_async_endpoint(
|
|
360
|
-
self.client.item_collection, self.items_get_request_model
|
|
361
|
-
),
|
|
369
|
+
endpoint=create_async_endpoint(self.client.item_collection, request_model),
|
|
362
370
|
)
|
|
363
371
|
|
|
364
372
|
def register_core(self):
|
|
@@ -431,6 +439,11 @@ class StacApi:
|
|
|
431
439
|
"""
|
|
432
440
|
return add_route_dependencies(self.app.router.routes, scopes, dependencies)
|
|
433
441
|
|
|
442
|
+
def add_middleware(self, middleware: Middleware):
|
|
443
|
+
"""Add a middleware class to the application."""
|
|
444
|
+
self.app.user_middleware.insert(0, middleware)
|
|
445
|
+
self.app.middleware_stack = self.app.build_middleware_stack()
|
|
446
|
+
|
|
434
447
|
def __attrs_post_init__(self):
|
|
435
448
|
"""Post-init hook.
|
|
436
449
|
|
|
@@ -445,6 +458,10 @@ class StacApi:
|
|
|
445
458
|
self.client.title = self.title
|
|
446
459
|
self.client.description = self.description
|
|
447
460
|
|
|
461
|
+
fields_ext = self.get_extension(FieldsExtension)
|
|
462
|
+
if fields_ext:
|
|
463
|
+
self.settings.default_includes = fields_ext.default_includes
|
|
464
|
+
|
|
448
465
|
Settings.set(self.settings)
|
|
449
466
|
self.app.state.settings = self.settings
|
|
450
467
|
|
|
@@ -470,11 +487,8 @@ class StacApi:
|
|
|
470
487
|
self.app.openapi = self.customize_openapi
|
|
471
488
|
|
|
472
489
|
# add middlewares
|
|
473
|
-
if self.middlewares and self.app.middleware_stack is not None:
|
|
474
|
-
raise RuntimeError("Cannot add middleware after an application has started")
|
|
475
|
-
|
|
476
490
|
for middleware in self.middlewares:
|
|
477
|
-
self.
|
|
491
|
+
self.add_middleware(middleware)
|
|
478
492
|
|
|
479
493
|
# customize route dependencies
|
|
480
494
|
for scopes, dependencies in self.route_dependencies:
|
|
@@ -12,14 +12,12 @@ class ApiExtensions(enum.Enum):
|
|
|
12
12
|
Ref: https://github.com/stac-api-extensions
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
context = "context"
|
|
15
16
|
fields = "fields"
|
|
16
17
|
filter = "filter"
|
|
17
18
|
query = "query"
|
|
18
19
|
sort = "sort"
|
|
19
20
|
transaction = "transaction"
|
|
20
|
-
aggregation = "aggregation"
|
|
21
|
-
collection_search = "collection-search"
|
|
22
|
-
free_text = "free-text"
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
class AddOns(enum.Enum):
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Api request/response models."""
|
|
2
2
|
|
|
3
|
+
import importlib.util
|
|
3
4
|
from typing import List, Optional, Type, Union
|
|
4
5
|
|
|
5
6
|
import attr
|
|
6
|
-
from fastapi import Path
|
|
7
|
+
from fastapi import Path
|
|
7
8
|
from pydantic import BaseModel, create_model
|
|
8
9
|
from stac_pydantic.shared import BBox
|
|
9
|
-
from typing_extensions import Annotated
|
|
10
10
|
|
|
11
11
|
from stac_fastapi.types.extension import ApiExtension
|
|
12
12
|
from stac_fastapi.types.rfc3339 import DateTimeType
|
|
@@ -14,17 +14,10 @@ from stac_fastapi.types.search import (
|
|
|
14
14
|
APIRequest,
|
|
15
15
|
BaseSearchGetRequest,
|
|
16
16
|
BaseSearchPostRequest,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
_datetime_converter,
|
|
17
|
+
str2bbox,
|
|
18
|
+
str_to_interval,
|
|
20
19
|
)
|
|
21
20
|
|
|
22
|
-
try:
|
|
23
|
-
import orjson # noqa
|
|
24
|
-
from fastapi.responses import ORJSONResponse as JSONResponse
|
|
25
|
-
except ImportError: # pragma: nocover
|
|
26
|
-
from starlette.responses import JSONResponse
|
|
27
|
-
|
|
28
21
|
|
|
29
22
|
def create_request_model(
|
|
30
23
|
model_name="SearchGetRequest",
|
|
@@ -87,19 +80,18 @@ def create_post_request_model(
|
|
|
87
80
|
)
|
|
88
81
|
|
|
89
82
|
|
|
90
|
-
@attr.s
|
|
83
|
+
@attr.s # type:ignore
|
|
91
84
|
class CollectionUri(APIRequest):
|
|
92
85
|
"""Get or delete collection."""
|
|
93
86
|
|
|
94
|
-
collection_id:
|
|
87
|
+
collection_id: str = attr.ib(default=Path(..., description="Collection ID"))
|
|
95
88
|
|
|
96
89
|
|
|
97
90
|
@attr.s
|
|
98
|
-
class ItemUri(
|
|
91
|
+
class ItemUri(CollectionUri):
|
|
99
92
|
"""Get or delete item."""
|
|
100
93
|
|
|
101
|
-
|
|
102
|
-
item_id: Annotated[str, Path(description="Item ID")] = attr.ib()
|
|
94
|
+
item_id: str = attr.ib(default=Path(..., description="Item ID"))
|
|
103
95
|
|
|
104
96
|
|
|
105
97
|
@attr.s
|
|
@@ -110,29 +102,63 @@ class EmptyRequest(APIRequest):
|
|
|
110
102
|
|
|
111
103
|
|
|
112
104
|
@attr.s
|
|
113
|
-
class ItemCollectionUri(
|
|
105
|
+
class ItemCollectionUri(CollectionUri):
|
|
114
106
|
"""Get item collection."""
|
|
115
107
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
108
|
+
limit: int = attr.ib(default=10)
|
|
109
|
+
bbox: Optional[BBox] = attr.ib(default=None, converter=str2bbox)
|
|
110
|
+
datetime: Optional[DateTimeType] = attr.ib(default=None, converter=str_to_interval)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class POSTTokenPagination(BaseModel):
|
|
114
|
+
"""Token pagination model for POST requests."""
|
|
115
|
+
|
|
116
|
+
token: Optional[str] = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@attr.s
|
|
120
|
+
class GETTokenPagination(APIRequest):
|
|
121
|
+
"""Token pagination for GET requests."""
|
|
122
|
+
|
|
123
|
+
token: Optional[str] = attr.ib(default=None)
|
|
124
|
+
|
|
127
125
|
|
|
126
|
+
class POSTPagination(BaseModel):
|
|
127
|
+
"""Page based pagination for POST requests."""
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
page: Optional[str] = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@attr.s
|
|
133
|
+
class GETPagination(APIRequest):
|
|
134
|
+
"""Page based pagination for GET requests."""
|
|
135
|
+
|
|
136
|
+
page: Optional[str] = attr.ib(default=None)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# Test for ORJSON and use it rather than stdlib JSON where supported
|
|
140
|
+
if importlib.util.find_spec("orjson") is not None:
|
|
141
|
+
from fastapi.responses import ORJSONResponse
|
|
142
|
+
|
|
143
|
+
class GeoJSONResponse(ORJSONResponse):
|
|
144
|
+
"""JSON with custom, vendor content-type."""
|
|
145
|
+
|
|
146
|
+
media_type = "application/geo+json"
|
|
147
|
+
|
|
148
|
+
class JSONSchemaResponse(ORJSONResponse):
|
|
149
|
+
"""JSON with custom, vendor content-type."""
|
|
150
|
+
|
|
151
|
+
media_type = "application/schema+json"
|
|
152
|
+
|
|
153
|
+
else:
|
|
154
|
+
from starlette.responses import JSONResponse
|
|
131
155
|
|
|
132
|
-
|
|
156
|
+
class GeoJSONResponse(JSONResponse):
|
|
157
|
+
"""JSON with custom, vendor content-type."""
|
|
133
158
|
|
|
159
|
+
media_type = "application/geo+json"
|
|
134
160
|
|
|
135
|
-
class JSONSchemaResponse(JSONResponse):
|
|
136
|
-
|
|
161
|
+
class JSONSchemaResponse(JSONResponse):
|
|
162
|
+
"""JSON with custom, vendor content-type."""
|
|
137
163
|
|
|
138
|
-
|
|
164
|
+
media_type = "application/schema+json"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""openapi."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from fastapi import FastAPI
|
|
6
|
+
from fastapi.openapi.utils import get_openapi
|
|
7
|
+
from starlette.requests import Request
|
|
8
|
+
from starlette.responses import JSONResponse, Response
|
|
9
|
+
from starlette.routing import Route, request_response
|
|
10
|
+
|
|
11
|
+
from stac_fastapi.api.config import ApiExtensions
|
|
12
|
+
from stac_fastapi.types.config import ApiSettings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VndOaiResponse(JSONResponse):
|
|
16
|
+
"""JSON with custom, vendor content-type."""
|
|
17
|
+
|
|
18
|
+
media_type = "application/vnd.oai.openapi+json;version=3.0"
|
|
19
|
+
|
|
20
|
+
def __init__(self, *args, **kwargs):
|
|
21
|
+
"""Init function with deprecation warning."""
|
|
22
|
+
warnings.warn(
|
|
23
|
+
"VndOaiResponse is deprecated and will be removed in v3.0",
|
|
24
|
+
DeprecationWarning,
|
|
25
|
+
)
|
|
26
|
+
super().__init__(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def update_openapi(app: FastAPI) -> FastAPI:
|
|
30
|
+
"""Update OpenAPI response content-type.
|
|
31
|
+
|
|
32
|
+
This function modifies the openapi route to comply with the STAC API spec's required
|
|
33
|
+
content-type response header.
|
|
34
|
+
"""
|
|
35
|
+
# Find the route for the openapi_url in the app
|
|
36
|
+
openapi_route: Route = next(
|
|
37
|
+
route for route in app.router.routes if route.path == app.openapi_url
|
|
38
|
+
)
|
|
39
|
+
# Store the old endpoint function so we can call it from the patched function
|
|
40
|
+
old_endpoint = openapi_route.endpoint
|
|
41
|
+
|
|
42
|
+
# Create a patched endpoint function that modifies the content type of the response
|
|
43
|
+
async def patched_openapi_endpoint(req: Request) -> Response:
|
|
44
|
+
# Get the response from the old endpoint function
|
|
45
|
+
response: JSONResponse = await old_endpoint(req)
|
|
46
|
+
# Update the content type header in place
|
|
47
|
+
response.headers["content-type"] = "application/vnd.oai.openapi+json;version=3.0"
|
|
48
|
+
# Return the updated response
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
# When a Route is accessed the `handle` function calls `self.app`. Which is
|
|
52
|
+
# the endpoint function wrapped with `request_response`. So we need to wrap
|
|
53
|
+
# our patched function and replace the existing app with it.
|
|
54
|
+
openapi_route.app = request_response(patched_openapi_endpoint)
|
|
55
|
+
|
|
56
|
+
# return the patched app
|
|
57
|
+
return app
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def config_openapi(app: FastAPI, settings: ApiSettings):
|
|
61
|
+
"""Config openapi."""
|
|
62
|
+
warnings.warn(
|
|
63
|
+
"config_openapi is deprecated and will be removed in v3.0",
|
|
64
|
+
DeprecationWarning,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def custom_openapi():
|
|
68
|
+
"""Config openapi."""
|
|
69
|
+
if app.openapi_schema:
|
|
70
|
+
return app.openapi_schema
|
|
71
|
+
|
|
72
|
+
openapi_schema = get_openapi(
|
|
73
|
+
title="Arturo STAC API", version="0.1", routes=app.routes
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if settings.api_extension_is_enabled(ApiExtensions.fields):
|
|
77
|
+
openapi_schema["paths"]["/search"]["get"]["responses"]["200"]["content"][
|
|
78
|
+
"application/json"
|
|
79
|
+
]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}
|
|
80
|
+
openapi_schema["paths"]["/search"]["post"]["responses"]["200"]["content"][
|
|
81
|
+
"application/json"
|
|
82
|
+
]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}
|
|
83
|
+
|
|
84
|
+
app.openapi_schema = openapi_schema
|
|
85
|
+
return app.openapi_schema
|
|
86
|
+
|
|
87
|
+
app.openapi = custom_openapi
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Route factories."""
|
|
2
2
|
|
|
3
|
-
import copy
|
|
4
3
|
import functools
|
|
5
4
|
import inspect
|
|
5
|
+
import warnings
|
|
6
6
|
from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, Union
|
|
7
7
|
|
|
8
8
|
from fastapi import Depends, params
|
|
@@ -37,12 +37,19 @@ def sync_to_async(func):
|
|
|
37
37
|
def create_async_endpoint(
|
|
38
38
|
func: Callable,
|
|
39
39
|
request_model: Union[Type[APIRequest], Type[BaseModel], Dict],
|
|
40
|
+
response_class: Optional[Type[Response]] = None,
|
|
40
41
|
):
|
|
41
42
|
"""Wrap a function in a coroutine which may be used to create a FastAPI endpoint.
|
|
42
43
|
|
|
43
44
|
Synchronous functions are executed asynchronously using a background thread.
|
|
44
45
|
"""
|
|
45
46
|
|
|
47
|
+
if response_class:
|
|
48
|
+
warnings.warn(
|
|
49
|
+
"`response_class` option is deprecated, please set the Response class directly in the endpoint.", # noqa: E501
|
|
50
|
+
DeprecationWarning,
|
|
51
|
+
)
|
|
52
|
+
|
|
46
53
|
if not inspect.iscoroutinefunction(func):
|
|
47
54
|
func = sync_to_async(func)
|
|
48
55
|
|
|
@@ -93,28 +100,15 @@ def add_route_dependencies(
|
|
|
93
100
|
Allows a developer to add dependencies to a route after the route has been
|
|
94
101
|
defined.
|
|
95
102
|
|
|
96
|
-
"*" can be used for path or method to match all allowed routes.
|
|
97
|
-
|
|
98
103
|
Returns:
|
|
99
104
|
None
|
|
100
105
|
"""
|
|
101
106
|
for scope in scopes:
|
|
102
|
-
_scope = copy.deepcopy(scope)
|
|
103
107
|
for route in routes:
|
|
104
|
-
|
|
105
|
-
_scope["path"] = route.path
|
|
106
|
-
|
|
107
|
-
if scope["method"] == "*":
|
|
108
|
-
_scope["method"] = list(route.methods)[0]
|
|
109
|
-
|
|
110
|
-
match, _ = route.matches({"type": "http", **_scope})
|
|
108
|
+
match, _ = route.matches({"type": "http", **scope})
|
|
111
109
|
if match != Match.FULL:
|
|
112
110
|
continue
|
|
113
111
|
|
|
114
|
-
# Ignore paths without dependants, e.g. /api, /api.html, /docs/oauth2-redirect
|
|
115
|
-
if not hasattr(route, "dependant"):
|
|
116
|
-
continue
|
|
117
|
-
|
|
118
112
|
# Mimicking how APIRoute handles dependencies:
|
|
119
113
|
# https://github.com/tiangolo/fastapi/blob/1760da0efa55585c19835d81afa8ca386036c325/fastapi/routing.py#L408-L412
|
|
120
114
|
for depends in dependencies[::-1]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi.api
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a1
|
|
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
|
|
@@ -19,7 +19,7 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: brotli_asgi
|
|
22
|
-
Requires-Dist: stac-fastapi.types
|
|
22
|
+
Requires-Dist: stac-fastapi.types
|
|
23
23
|
Provides-Extra: dev
|
|
24
24
|
Requires-Dist: httpx; extra == "dev"
|
|
25
25
|
Requires-Dist: pytest; extra == "dev"
|