stac-fastapi.api 5.1.1__tar.gz → 5.2.0__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.
Files changed (25) hide show
  1. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/PKG-INFO +9 -6
  2. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/setup.py +9 -2
  3. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/app.py +74 -66
  4. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/middleware.py +2 -2
  5. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/routes.py +36 -4
  6. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/version.py +1 -1
  7. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi.api.egg-info/PKG-INFO +9 -6
  8. stac_fastapi_api-5.2.0/stac_fastapi.api.egg-info/requires.txt +21 -0
  9. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/tests/test_app.py +74 -0
  10. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/tests/test_app_prefix.py +3 -0
  11. stac_fastapi_api-5.1.1/stac_fastapi.api.egg-info/requires.txt +0 -18
  12. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/README.md +0 -0
  13. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/setup.cfg +0 -0
  14. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/__init__.py +0 -0
  15. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/config.py +0 -0
  16. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/errors.py +0 -0
  17. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/models.py +0 -0
  18. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi/api/openapi.py +0 -0
  19. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
  20. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
  21. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
  22. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/stac_fastapi.api.egg-info/top_level.txt +0 -0
  23. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/tests/test_api.py +0 -0
  24. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/tests/test_middleware.py +0 -0
  25. {stac_fastapi_api-5.1.1 → stac_fastapi_api-5.2.0}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: stac-fastapi.api
3
- Version: 5.1.1
3
+ Version: 5.2.0
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.9
20
20
  Description-Content-Type: text/markdown
21
21
  Requires-Dist: brotli_asgi
22
- Requires-Dist: stac-fastapi.types~=5.0
22
+ Requires-Dist: stac-fastapi.types~=5.2
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: httpx; extra == "dev"
25
25
  Requires-Dist: pytest; extra == "dev"
@@ -30,9 +30,12 @@ Requires-Dist: requests; extra == "dev"
30
30
  Provides-Extra: benchmark
31
31
  Requires-Dist: pytest-benchmark; extra == "benchmark"
32
32
  Provides-Extra: docs
33
- Requires-Dist: mkdocs; extra == "docs"
34
- Requires-Dist: mkdocs-material; extra == "docs"
35
- Requires-Dist: pdocs; extra == "docs"
33
+ Requires-Dist: black>=23.10.1; extra == "docs"
34
+ Requires-Dist: mkdocs>=1.4.3; extra == "docs"
35
+ Requires-Dist: mkdocs-jupyter>=0.24.5; extra == "docs"
36
+ Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
37
+ Requires-Dist: griffe-inherited-docstrings>=1.0.0; extra == "docs"
38
+ Requires-Dist: mkdocstrings[python]>=0.25.1; extra == "docs"
36
39
  Dynamic: author
37
40
  Dynamic: author-email
38
41
  Dynamic: classifier
@@ -7,7 +7,7 @@ with open("README.md") as f:
7
7
 
8
8
  install_requires = [
9
9
  "brotli_asgi",
10
- "stac-fastapi.types~=5.0",
10
+ "stac-fastapi.types~=5.2",
11
11
  ]
12
12
 
13
13
  extra_reqs = {
@@ -22,7 +22,14 @@ extra_reqs = {
22
22
  "benchmark": [
23
23
  "pytest-benchmark",
24
24
  ],
25
- "docs": ["mkdocs", "mkdocs-material", "pdocs"],
25
+ "docs": [
26
+ "black>=23.10.1",
27
+ "mkdocs>=1.4.3",
28
+ "mkdocs-jupyter>=0.24.5",
29
+ "mkdocs-material[imaging]>=9.5",
30
+ "griffe-inherited-docstrings>=1.0.0",
31
+ "mkdocstrings[python]>=0.25.1",
32
+ ],
26
33
  }
27
34
 
28
35
 
@@ -1,7 +1,7 @@
1
1
  """Fastapi app creation."""
2
2
 
3
3
 
4
- from typing import Dict, List, Optional, Tuple, Type, Union
4
+ from typing import Awaitable, Callable, Dict, List, Optional, Tuple, Type, Union
5
5
 
6
6
  import attr
7
7
  from brotli_asgi import BrotliMiddleware
@@ -25,7 +25,12 @@ from stac_fastapi.api.models import (
25
25
  ItemUri,
26
26
  )
27
27
  from stac_fastapi.api.openapi import update_openapi
28
- from stac_fastapi.api.routes import Scope, add_route_dependencies, create_async_endpoint
28
+ from stac_fastapi.api.routes import (
29
+ Scope,
30
+ add_direct_response,
31
+ add_route_dependencies,
32
+ create_async_endpoint,
33
+ )
29
34
  from stac_fastapi.types.config import ApiSettings, Settings
30
35
  from stac_fastapi.types.core import AsyncBaseCoreClient, BaseCoreClient
31
36
  from stac_fastapi.types.extension import ApiExtension
@@ -62,6 +67,10 @@ class StacApi:
62
67
  specified routes. This is useful
63
68
  for applying custom auth requirements to routes defined elsewhere in
64
69
  the application.
70
+ health_check:
71
+ A Callable which return application's `health` information.
72
+ Defaults to `def health: return {"status": "UP"}`
73
+
65
74
  """
66
75
 
67
76
  settings: ApiSettings = attr.ib()
@@ -123,6 +132,9 @@ class StacApi:
123
132
  )
124
133
  )
125
134
  route_dependencies: List[Tuple[List[Scope], List[Depends]]] = attr.ib(default=[])
135
+ health_check: Union[Callable[[], Dict], Callable[[], Awaitable[Dict]]] = attr.ib(
136
+ default=lambda: {"status": "UP"}
137
+ )
126
138
 
127
139
  def get_extension(self, extension: Type[ApiExtension]) -> Optional[ApiExtension]:
128
140
  """Get an extension.
@@ -138,12 +150,8 @@ class StacApi:
138
150
  return ext
139
151
  return None
140
152
 
141
- def register_landing_page(self):
142
- """Register landing page (GET /).
143
-
144
- Returns:
145
- None
146
- """
153
+ def register_landing_page(self) -> None:
154
+ """Register landing page (GET /)."""
147
155
  self.router.add_api_route(
148
156
  name="Landing Page",
149
157
  path="/",
@@ -165,12 +173,8 @@ class StacApi:
165
173
  endpoint=create_async_endpoint(self.client.landing_page, EmptyRequest),
166
174
  )
167
175
 
168
- def register_conformance_classes(self):
169
- """Register conformance classes (GET /conformance).
170
-
171
- Returns:
172
- None
173
- """
176
+ def register_conformance_classes(self) -> None:
177
+ """Register conformance classes (GET /conformance)."""
174
178
  self.router.add_api_route(
175
179
  name="Conformance Classes",
176
180
  path="/conformance",
@@ -192,12 +196,8 @@ class StacApi:
192
196
  endpoint=create_async_endpoint(self.client.conformance, EmptyRequest),
193
197
  )
194
198
 
195
- def register_get_item(self):
196
- """Register get item endpoint (GET /collections/{collection_id}/items/{item_id}).
197
-
198
- Returns:
199
- None
200
- """
199
+ def register_get_item(self) -> None:
200
+ """Register get item endpoint (GET /collections/{collection_id}/items/{item_id}).""" # noqa: E501
201
201
  self.router.add_api_route(
202
202
  name="Get Item",
203
203
  path="/collections/{collection_id}/items/{item_id}",
@@ -219,12 +219,8 @@ class StacApi:
219
219
  ),
220
220
  )
221
221
 
222
- def register_post_search(self):
223
- """Register search endpoint (POST /search).
224
-
225
- Returns:
226
- None
227
- """
222
+ def register_post_search(self) -> None:
223
+ """Register search endpoint (POST /search)."""
228
224
  self.router.add_api_route(
229
225
  name="Search",
230
226
  path="/search",
@@ -248,12 +244,8 @@ class StacApi:
248
244
  ),
249
245
  )
250
246
 
251
- def register_get_search(self):
252
- """Register search endpoint (GET /search).
253
-
254
- Returns:
255
- None
256
- """
247
+ def register_get_search(self) -> None:
248
+ """Register search endpoint (GET /search)."""
257
249
  self.router.add_api_route(
258
250
  name="Search",
259
251
  path="/search",
@@ -277,12 +269,8 @@ class StacApi:
277
269
  ),
278
270
  )
279
271
 
280
- def register_get_collections(self):
281
- """Register get collections endpoint (GET /collections).
282
-
283
- Returns:
284
- None
285
- """
272
+ def register_get_collections(self) -> None:
273
+ """Register get collections endpoint (GET /collections)."""
286
274
  self.router.add_api_route(
287
275
  name="Get Collections",
288
276
  path="/collections",
@@ -306,12 +294,8 @@ class StacApi:
306
294
  ),
307
295
  )
308
296
 
309
- def register_get_collection(self):
310
- """Register get collection endpoint (GET /collection/{collection_id}).
311
-
312
- Returns:
313
- None
314
- """
297
+ def register_get_collection(self) -> None:
298
+ """Register get collection endpoint (GET /collection/{collection_id})."""
315
299
  self.router.add_api_route(
316
300
  name="Get Collection",
317
301
  path="/collections/{collection_id}",
@@ -335,12 +319,8 @@ class StacApi:
335
319
  ),
336
320
  )
337
321
 
338
- def register_get_item_collection(self):
339
- """Register get item collection endpoint (GET /collection/{collection_id}/items).
340
-
341
- Returns:
342
- None
343
- """
322
+ def register_get_item_collection(self) -> None:
323
+ """Register get item collection endpoint (GET /collection/{collection_id}/items).""" # noqa: E501
344
324
  self.router.add_api_route(
345
325
  name="Get ItemCollection",
346
326
  path="/collections/{collection_id}/items",
@@ -364,7 +344,7 @@ class StacApi:
364
344
  ),
365
345
  )
366
346
 
367
- def register_core(self):
347
+ def register_core(self) -> None:
368
348
  """Register core STAC endpoints.
369
349
 
370
350
  GET /
@@ -378,8 +358,6 @@ class StacApi:
378
358
 
379
359
  Injects application logic (StacApi.client) into the API layer.
380
360
 
381
- Returns:
382
- None
383
361
  """
384
362
  self.register_landing_page()
385
363
  self.register_conformance_classes()
@@ -390,41 +368,68 @@ class StacApi:
390
368
  self.register_get_collection()
391
369
  self.register_get_item_collection()
392
370
 
393
- def add_health_check(self):
371
+ def add_health_check(self) -> None:
394
372
  """Add a health check."""
373
+
395
374
  mgmt_router = APIRouter(prefix=self.app.state.router_prefix)
396
375
 
397
- @mgmt_router.get("/_mgmt/ping")
398
376
  async def ping():
399
- """Liveliness/readiness probe."""
377
+ """Liveliness probe."""
400
378
  return {"message": "PONG"}
401
379
 
380
+ mgmt_router.add_api_route(
381
+ name="Ping",
382
+ path="/_mgmt/ping",
383
+ response_model=Dict,
384
+ responses={
385
+ 200: {
386
+ "content": {
387
+ MimeTypes.json.value: {},
388
+ },
389
+ },
390
+ },
391
+ response_class=self.response_class,
392
+ methods=["GET"],
393
+ endpoint=ping,
394
+ )
395
+
396
+ mgmt_router.add_api_route(
397
+ name="Health",
398
+ path="/_mgmt/health",
399
+ response_model=Dict,
400
+ responses={
401
+ 200: {
402
+ "content": {
403
+ MimeTypes.json.value: {},
404
+ },
405
+ },
406
+ },
407
+ response_class=self.response_class,
408
+ methods=["GET"],
409
+ endpoint=self.health_check,
410
+ )
402
411
  self.app.include_router(mgmt_router, tags=["Liveliness/Readiness"])
403
412
 
404
413
  def add_route_dependencies(
405
- self, scopes: List[Scope], dependencies=List[Depends]
414
+ self, scopes: List[Scope], dependencies: List[Depends]
406
415
  ) -> None:
407
416
  """Add custom dependencies to routes.
408
417
 
409
418
  Args:
410
- scopes: list of scopes. Each scope should be a dict with a `path`
411
- and `method` property.
412
- dependencies: list of [FastAPI
413
- dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/)
419
+ scopes: list of Scope.
420
+ Each scope should be a dict with a `path` and `method` property.
421
+ dependencies: list of fastapi.Depends
422
+ [FastAPI dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/)
414
423
  to apply to each scope.
415
424
 
416
- Returns:
417
- None
418
425
  """
419
426
  return add_route_dependencies(self.app.router.routes, scopes, dependencies)
420
427
 
421
- def __attrs_post_init__(self):
428
+ def __attrs_post_init__(self) -> None:
422
429
  """Post-init hook.
423
430
 
424
431
  Responsible for setting up the application upon instantiation of the class.
425
432
 
426
- Returns:
427
- None
428
433
  """
429
434
  # inject settings
430
435
  self.client.extensions = self.extensions
@@ -463,3 +468,6 @@ class StacApi:
463
468
  # customize route dependencies
464
469
  for scopes, dependencies in self.route_dependencies:
465
470
  self.add_route_dependencies(scopes=scopes, dependencies=dependencies)
471
+
472
+ if self.app.state.settings.enable_direct_response:
473
+ add_direct_response(self.app)
@@ -82,7 +82,7 @@ class ProxyHeaderMiddleware:
82
82
 
83
83
  await self.app(scope, receive, send)
84
84
 
85
- def _get_forwarded_url_parts(self, scope: Scope) -> Tuple[str]:
85
+ def _get_forwarded_url_parts(self, scope: Scope) -> Tuple[str, str, str]:
86
86
  proto = scope.get("scheme", "http")
87
87
  header_host = self._get_header_value_by_name(scope, "host")
88
88
  if header_host is None:
@@ -127,7 +127,7 @@ class ProxyHeaderMiddleware:
127
127
  @staticmethod
128
128
  def _replace_header_value_by_name(
129
129
  scope: Scope, header_name: str, new_value: str
130
- ) -> List[Tuple[str]]:
130
+ ) -> List[Tuple[str, str]]:
131
131
  return [
132
132
  (name, value)
133
133
  for name, value in scope["headers"]
@@ -5,13 +5,15 @@ import functools
5
5
  import inspect
6
6
  from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, Union
7
7
 
8
- from fastapi import Depends, params
9
- from fastapi.dependencies.utils import get_parameterless_sub_dependant
8
+ from fastapi import Depends, FastAPI, params
9
+ from fastapi.datastructures import DefaultPlaceholder
10
+ from fastapi.dependencies.utils import get_dependant, get_parameterless_sub_dependant
11
+ from fastapi.routing import APIRoute
10
12
  from pydantic import BaseModel
11
13
  from starlette.concurrency import run_in_threadpool
12
14
  from starlette.requests import Request
13
15
  from starlette.responses import Response
14
- from starlette.routing import BaseRoute, Match
16
+ from starlette.routing import BaseRoute, Match, request_response
15
17
  from starlette.status import HTTP_204_NO_CONTENT
16
18
 
17
19
  from stac_fastapi.api.models import APIRequest
@@ -86,7 +88,7 @@ class Scope(TypedDict, total=False):
86
88
 
87
89
 
88
90
  def add_route_dependencies(
89
- routes: List[BaseRoute], scopes: List[Scope], dependencies=List[params.Depends]
91
+ routes: List[BaseRoute], scopes: List[Scope], dependencies: List[params.Depends]
90
92
  ) -> None:
91
93
  """Add dependencies to routes.
92
94
 
@@ -131,3 +133,33 @@ def add_route_dependencies(
131
133
  # https://github.com/tiangolo/fastapi/blob/58ab733f19846b4875c5b79bfb1f4d1cb7f4823f/fastapi/applications.py#L337-L360
132
134
  # https://github.com/tiangolo/fastapi/blob/58ab733f19846b4875c5b79bfb1f4d1cb7f4823f/fastapi/routing.py#L677-L678
133
135
  route.dependencies.extend(dependencies)
136
+
137
+
138
+ def add_direct_response(app: FastAPI) -> None:
139
+ """
140
+ Setup FastAPI application's endpoints to return Response Object directly, avoiding
141
+ Pydantic validation and FastAPI (slow) serialization.
142
+
143
+ ref: https://gist.github.com/Zaczero/00f3a2679ebc0a25eb938ed82bc63553
144
+ """
145
+
146
+ def wrap_endpoint(endpoint: Callable, cls: Type[Response]):
147
+ @functools.wraps(endpoint)
148
+ async def wrapper(*args, **kwargs):
149
+ content = await endpoint(*args, **kwargs)
150
+ return content if isinstance(content, Response) else cls(content)
151
+
152
+ return wrapper
153
+
154
+ for route in app.routes:
155
+ if not isinstance(route, APIRoute):
156
+ continue
157
+
158
+ response_class = route.response_class
159
+ if isinstance(response_class, DefaultPlaceholder):
160
+ response_class = response_class.value
161
+
162
+ if issubclass(response_class, Response):
163
+ route.endpoint = wrap_endpoint(route.endpoint, response_class)
164
+ route.dependant = get_dependant(path=route.path_format, call=route.endpoint)
165
+ route.app = request_response(route.get_route_handler())
@@ -1,3 +1,3 @@
1
1
  """Library version."""
2
2
 
3
- __version__ = "5.1.1"
3
+ __version__ = "5.2.0"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: stac-fastapi.api
3
- Version: 5.1.1
3
+ Version: 5.2.0
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.9
20
20
  Description-Content-Type: text/markdown
21
21
  Requires-Dist: brotli_asgi
22
- Requires-Dist: stac-fastapi.types~=5.0
22
+ Requires-Dist: stac-fastapi.types~=5.2
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: httpx; extra == "dev"
25
25
  Requires-Dist: pytest; extra == "dev"
@@ -30,9 +30,12 @@ Requires-Dist: requests; extra == "dev"
30
30
  Provides-Extra: benchmark
31
31
  Requires-Dist: pytest-benchmark; extra == "benchmark"
32
32
  Provides-Extra: docs
33
- Requires-Dist: mkdocs; extra == "docs"
34
- Requires-Dist: mkdocs-material; extra == "docs"
35
- Requires-Dist: pdocs; extra == "docs"
33
+ Requires-Dist: black>=23.10.1; extra == "docs"
34
+ Requires-Dist: mkdocs>=1.4.3; extra == "docs"
35
+ Requires-Dist: mkdocs-jupyter>=0.24.5; extra == "docs"
36
+ Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
37
+ Requires-Dist: griffe-inherited-docstrings>=1.0.0; extra == "docs"
38
+ Requires-Dist: mkdocstrings[python]>=0.25.1; extra == "docs"
36
39
  Dynamic: author
37
40
  Dynamic: author-email
38
41
  Dynamic: classifier
@@ -0,0 +1,21 @@
1
+ brotli_asgi
2
+ stac-fastapi.types~=5.2
3
+
4
+ [benchmark]
5
+ pytest-benchmark
6
+
7
+ [dev]
8
+ httpx
9
+ pytest
10
+ pytest-cov
11
+ pytest-asyncio
12
+ pre-commit
13
+ requests
14
+
15
+ [docs]
16
+ black>=23.10.1
17
+ mkdocs>=1.4.3
18
+ mkdocs-jupyter>=0.24.5
19
+ mkdocs-material[imaging]>=9.5
20
+ griffe-inherited-docstrings>=1.0.0
21
+ mkdocstrings[python]>=0.25.1
@@ -6,6 +6,7 @@ from fastapi import Path, Query
6
6
  from fastapi.testclient import TestClient
7
7
  from pydantic import ValidationError
8
8
  from stac_pydantic import api
9
+ from starlette.requests import Request
9
10
  from typing_extensions import Annotated
10
11
 
11
12
  from stac_fastapi.api import app
@@ -108,6 +109,31 @@ def test_client_invalid_response_type(validate, TestCoreClient, item_dict):
108
109
  assert item.status_code == 200, item.text
109
110
 
110
111
 
112
+ def test_client_response_by_pass(TestCoreClient, item_dict):
113
+ """Check with `enable_direct_response` option."""
114
+
115
+ class InValidResponseClient(TestCoreClient):
116
+ def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
117
+ item_dict.pop("bbox", None)
118
+ item_dict.pop("geometry", None)
119
+ return item_dict
120
+
121
+ test_app = app.StacApi(
122
+ settings=ApiSettings(
123
+ enable_response_models=False,
124
+ enable_direct_response=True,
125
+ ),
126
+ client=InValidResponseClient(),
127
+ )
128
+
129
+ with TestClient(test_app.app) as client:
130
+ item = client.get("/collections/test/items/test")
131
+
132
+ assert item.json()
133
+ assert item.status_code == 200
134
+ assert item.headers["content-type"] == "application/geo+json"
135
+
136
+
111
137
  def test_client_openapi(TestCoreClient):
112
138
  """Test if response models are all documented with OpenAPI."""
113
139
 
@@ -504,3 +530,51 @@ def test_client_datetime_input_params():
504
530
  assert post_search.json() == "2020-01-01T00:00:00.00001Z"
505
531
  assert post_search_zero.status_code == 200, post_search_zero.text
506
532
  assert post_search_zero.json() == "2020-01-01T00:00:00.0000Z"
533
+
534
+
535
+ def test_mgmt_endpoints(AsyncTestCoreClient):
536
+ """Test ping/health endpoints."""
537
+
538
+ test_app = app.StacApi(
539
+ settings=ApiSettings(),
540
+ client=AsyncTestCoreClient(),
541
+ )
542
+
543
+ with TestClient(test_app.app) as client:
544
+ resp = client.get("/_mgmt/ping")
545
+ assert resp.status_code == 200
546
+ assert resp.json() == {"message": "PONG"}
547
+
548
+ resp = client.get("/_mgmt/health")
549
+ assert resp.status_code == 200
550
+ assert resp.json() == {"status": "UP"}
551
+
552
+ def health_check(request: Request):
553
+ return {
554
+ "status": "UP",
555
+ "database": {
556
+ "status": "UP",
557
+ "version": "0.1.0",
558
+ },
559
+ }
560
+
561
+ test_app = app.StacApi(
562
+ settings=ApiSettings(),
563
+ client=AsyncTestCoreClient(),
564
+ health_check=health_check,
565
+ )
566
+
567
+ with TestClient(test_app.app) as client:
568
+ resp = client.get("/_mgmt/ping")
569
+ assert resp.status_code == 200
570
+ assert resp.json() == {"message": "PONG"}
571
+
572
+ resp = client.get("/_mgmt/health")
573
+ assert resp.status_code == 200
574
+ assert resp.json() == {
575
+ "status": "UP",
576
+ "database": {
577
+ "status": "UP",
578
+ "version": "0.1.0",
579
+ },
580
+ }
@@ -34,6 +34,9 @@ def test_api_prefix(TestCoreClient, prefix):
34
34
  )
35
35
 
36
36
  with TestClient(api.app, base_url="http://stac.io") as client:
37
+ ping = client.get(f"{prefix}/_mgmt/ping")
38
+ assert ping.status_code == 200, ping.json() == {"message": "PONG"}
39
+
37
40
  landing = client.get(f"{prefix}/")
38
41
  assert landing.status_code == 200, landing.json()
39
42
 
@@ -1,18 +0,0 @@
1
- brotli_asgi
2
- stac-fastapi.types~=5.0
3
-
4
- [benchmark]
5
- pytest-benchmark
6
-
7
- [dev]
8
- httpx
9
- pytest
10
- pytest-cov
11
- pytest-asyncio
12
- pre-commit
13
- requests
14
-
15
- [docs]
16
- mkdocs
17
- mkdocs-material
18
- pdocs