stac-fastapi.api 3.0.0a4__tar.gz → 3.0.0b2__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-3.0.0a4 → stac_fastapi_api-3.0.0b2}/PKG-INFO +1 -1
  2. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/app.py +23 -31
  3. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/models.py +26 -58
  4. stac_fastapi_api-3.0.0b2/stac_fastapi/api/version.py +2 -0
  5. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/PKG-INFO +1 -1
  6. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/tests/test_api.py +9 -0
  7. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/tests/test_app.py +98 -5
  8. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/tests/test_models.py +42 -12
  9. stac_fastapi_api-3.0.0a4/stac_fastapi/api/version.py +0 -2
  10. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/README.md +0 -0
  11. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/setup.cfg +0 -0
  12. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/setup.py +0 -0
  13. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/__init__.py +0 -0
  14. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/config.py +0 -0
  15. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/errors.py +0 -0
  16. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/middleware.py +0 -0
  17. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/openapi.py +0 -0
  18. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi/api/routes.py +0 -0
  19. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
  20. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
  21. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
  22. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/requires.txt +0 -0
  23. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/stac_fastapi.api.egg-info/top_level.txt +0 -0
  24. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/tests/test_app_prefix.py +0 -0
  25. {stac_fastapi_api-3.0.0a4 → stac_fastapi_api-3.0.0b2}/tests/test_middleware.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 3.0.0a4
3
+ Version: 3.0.0b2
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
@@ -18,18 +18,15 @@ 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,
21
22
  CollectionUri,
22
23
  EmptyRequest,
23
24
  GeoJSONResponse,
24
25
  ItemCollectionUri,
25
26
  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
33
30
  from stac_fastapi.types.config import ApiSettings, Settings
34
31
  from stac_fastapi.types.core import AsyncBaseCoreClient, BaseCoreClient
35
32
  from stac_fastapi.types.extension import ApiExtension
@@ -108,7 +105,10 @@ class StacApi:
108
105
  search_post_request_model: Type[BaseSearchPostRequest] = attr.ib(
109
106
  default=BaseSearchPostRequest
110
107
  )
111
- pagination_extension = attr.ib(default=TokenPaginationExtension)
108
+ collections_get_request_model: Type[APIRequest] = attr.ib(default=EmptyRequest)
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)
112
112
  response_class: Type[Response] = attr.ib(default=JSONResponse)
113
113
  middlewares: List[Middleware] = attr.ib(
114
114
  default=attr.Factory(
@@ -211,7 +211,9 @@ 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(self.client.get_item, ItemUri),
214
+ endpoint=create_async_endpoint(
215
+ self.client.get_item, self.item_get_request_model
216
+ ),
215
217
  )
216
218
 
217
219
  def register_post_search(self):
@@ -220,15 +222,12 @@ class StacApi:
220
222
  Returns:
221
223
  None
222
224
  """
223
- fields_ext = self.get_extension(FieldsExtension)
224
225
  self.router.add_api_route(
225
226
  name="Search",
226
227
  path="/search",
227
- response_model=(
228
- (api.ItemCollection if not fields_ext else None)
229
- if self.settings.enable_response_models
230
- else None
231
- ),
228
+ response_model=api.ItemCollection
229
+ if self.settings.enable_response_models
230
+ else None,
232
231
  responses={
233
232
  200: {
234
233
  "content": {
@@ -252,15 +251,12 @@ class StacApi:
252
251
  Returns:
253
252
  None
254
253
  """
255
- fields_ext = self.get_extension(FieldsExtension)
256
254
  self.router.add_api_route(
257
255
  name="Search",
258
256
  path="/search",
259
- response_model=(
260
- (api.ItemCollection if not fields_ext else None)
261
- if self.settings.enable_response_models
262
- else None
263
- ),
257
+ response_model=api.ItemCollection
258
+ if self.settings.enable_response_models
259
+ else None,
264
260
  responses={
265
261
  200: {
266
262
  "content": {
@@ -302,7 +298,9 @@ class StacApi:
302
298
  response_model_exclude_unset=True,
303
299
  response_model_exclude_none=True,
304
300
  methods=["GET"],
305
- endpoint=create_async_endpoint(self.client.all_collections, EmptyRequest),
301
+ endpoint=create_async_endpoint(
302
+ self.client.all_collections, self.collections_get_request_model
303
+ ),
306
304
  )
307
305
 
308
306
  def register_get_collection(self):
@@ -329,7 +327,9 @@ class StacApi:
329
327
  response_model_exclude_unset=True,
330
328
  response_model_exclude_none=True,
331
329
  methods=["GET"],
332
- endpoint=create_async_endpoint(self.client.get_collection, CollectionUri),
330
+ endpoint=create_async_endpoint(
331
+ self.client.get_collection, self.collection_get_request_model
332
+ ),
333
333
  )
334
334
 
335
335
  def register_get_item_collection(self):
@@ -338,16 +338,6 @@ 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
- )
351
341
  self.router.add_api_route(
352
342
  name="Get ItemCollection",
353
343
  path="/collections/{collection_id}/items",
@@ -366,7 +356,9 @@ class StacApi:
366
356
  response_model_exclude_unset=True,
367
357
  response_model_exclude_none=True,
368
358
  methods=["GET"],
369
- endpoint=create_async_endpoint(self.client.item_collection, request_model),
359
+ endpoint=create_async_endpoint(
360
+ self.client.item_collection, self.items_get_request_model
361
+ ),
370
362
  )
371
363
 
372
364
  def register_core(self):
@@ -1,12 +1,12 @@
1
1
  """Api request/response models."""
2
2
 
3
- import importlib.util
4
3
  from typing import List, Optional, Type, Union
5
4
 
6
5
  import attr
7
- from fastapi import Path
6
+ from fastapi import Path, Query
8
7
  from pydantic import BaseModel, create_model
9
8
  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
@@ -18,6 +18,12 @@ from stac_fastapi.types.search import (
18
18
  str_to_interval,
19
19
  )
20
20
 
21
+ try:
22
+ import orjson # noqa
23
+ from fastapi.responses import ORJSONResponse as JSONResponse
24
+ except ImportError: # pragma: nocover
25
+ from starlette.responses import JSONResponse
26
+
21
27
 
22
28
  def create_request_model(
23
29
  model_name="SearchGetRequest",
@@ -80,18 +86,19 @@ def create_post_request_model(
80
86
  )
81
87
 
82
88
 
83
- @attr.s # type:ignore
89
+ @attr.s
84
90
  class CollectionUri(APIRequest):
85
91
  """Get or delete collection."""
86
92
 
87
- collection_id: str = attr.ib(default=Path(..., description="Collection ID"))
93
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
88
94
 
89
95
 
90
96
  @attr.s
91
- class ItemUri(CollectionUri):
97
+ class ItemUri(APIRequest):
92
98
  """Get or delete item."""
93
99
 
94
- item_id: str = attr.ib(default=Path(..., description="Item ID"))
100
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
101
+ item_id: Annotated[str, Path(description="Item ID")] = attr.ib()
95
102
 
96
103
 
97
104
  @attr.s
@@ -102,63 +109,24 @@ class EmptyRequest(APIRequest):
102
109
 
103
110
 
104
111
  @attr.s
105
- class ItemCollectionUri(CollectionUri):
112
+ class ItemCollectionUri(APIRequest):
106
113
  """Get item collection."""
107
114
 
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
-
125
-
126
- class POSTPagination(BaseModel):
127
- """Page based pagination for POST requests."""
128
-
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."""
115
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
116
+ limit: Annotated[int, Query()] = attr.ib(default=10)
117
+ bbox: Annotated[Optional[BBox], Query()] = attr.ib(default=None, converter=str2bbox)
118
+ datetime: Annotated[Optional[DateTimeType], Query()] = attr.ib(
119
+ default=None, converter=str_to_interval
120
+ )
150
121
 
151
- media_type = "application/schema+json"
152
122
 
153
- else:
154
- from starlette.responses import JSONResponse
123
+ class GeoJSONResponse(JSONResponse):
124
+ """JSON with custom, vendor content-type."""
155
125
 
156
- class GeoJSONResponse(JSONResponse):
157
- """JSON with custom, vendor content-type."""
126
+ media_type = "application/geo+json"
158
127
 
159
- media_type = "application/geo+json"
160
128
 
161
- class JSONSchemaResponse(JSONResponse):
162
- """JSON with custom, vendor content-type."""
129
+ class JSONSchemaResponse(JSONResponse):
130
+ """JSON with custom, vendor content-type."""
163
131
 
164
- media_type = "application/schema+json"
132
+ media_type = "application/schema+json"
@@ -0,0 +1,2 @@
1
+ """Library version."""
2
+ __version__ = "3.0.0b2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 3.0.0a4
3
+ Version: 3.0.0b2
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
@@ -2,6 +2,7 @@ from fastapi import Depends, HTTPException, security, status
2
2
  from starlette.testclient import TestClient
3
3
 
4
4
  from stac_fastapi.api.app import StacApi
5
+ from stac_fastapi.api.models import ItemCollectionUri, create_request_model
5
6
  from stac_fastapi.extensions.core import (
6
7
  TokenPaginationExtension,
7
8
  TransactionExtension,
@@ -13,6 +14,13 @@ class TestRouteDependencies:
13
14
  @staticmethod
14
15
  def _build_api(**overrides):
15
16
  settings = config.ApiSettings()
17
+
18
+ items_get_request_model = create_request_model(
19
+ "ItemCollectionURI",
20
+ base_model=ItemCollectionUri,
21
+ mixins=[TokenPaginationExtension().GET],
22
+ )
23
+
16
24
  return StacApi(
17
25
  **{
18
26
  "settings": settings,
@@ -23,6 +31,7 @@ class TestRouteDependencies:
23
31
  ),
24
32
  TokenPaginationExtension(),
25
33
  ],
34
+ "items_get_request_model": items_get_request_model,
26
35
  **overrides,
27
36
  }
28
37
  )
@@ -1,13 +1,21 @@
1
1
  from datetime import datetime
2
2
  from typing import List, Optional, Union
3
3
 
4
+ import attr
4
5
  import pytest
6
+ from fastapi import Path, Query
5
7
  from fastapi.testclient import TestClient
6
8
  from pydantic import ValidationError
7
9
  from stac_pydantic import api
10
+ from typing_extensions import Annotated
8
11
 
9
12
  from stac_fastapi.api import app
10
- from stac_fastapi.api.models import create_get_request_model, create_post_request_model
13
+ from stac_fastapi.api.models import (
14
+ APIRequest,
15
+ JSONResponse,
16
+ create_get_request_model,
17
+ create_post_request_model,
18
+ )
11
19
  from stac_fastapi.extensions.core import FieldsExtension, FilterExtension
12
20
  from stac_fastapi.types import stac
13
21
  from stac_fastapi.types.config import ApiSettings
@@ -200,7 +208,14 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
200
208
  def post_search(
201
209
  self, search_request: BaseSearchPostRequest, **kwargs
202
210
  ) -> stac.ItemCollection:
203
- return {"not": "a proper stac item"}
211
+ resp = {"not": "a proper stac item"}
212
+
213
+ # if `fields` extension is enabled, then we return a JSONResponse
214
+ # to avoid Item validation
215
+ if getattr(search_request, "fields", None):
216
+ return JSONResponse(content=resp)
217
+
218
+ return resp
204
219
 
205
220
  def get_search(
206
221
  self,
@@ -212,7 +227,14 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
212
227
  limit: Optional[int] = 10,
213
228
  **kwargs,
214
229
  ) -> stac.ItemCollection:
215
- return {"not": "a proper stac item"}
230
+ resp = {"not": "a proper stac item"}
231
+
232
+ # if `fields` extension is enabled, then we return a JSONResponse
233
+ # to avoid Item validation
234
+ if "fields" in kwargs:
235
+ return JSONResponse(content=resp)
236
+
237
+ return resp
216
238
 
217
239
  def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
218
240
  raise NotImplementedError
@@ -234,6 +256,7 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
234
256
  ) -> stac.ItemCollection:
235
257
  raise NotImplementedError
236
258
 
259
+ # With FieldsExtension
237
260
  test_app = app.StacApi(
238
261
  settings=ApiSettings(enable_response_models=validate),
239
262
  client=BadCoreClient(),
@@ -258,14 +281,18 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
258
281
  },
259
282
  )
260
283
 
284
+ # With or without validation, /search endpoints will always return 200
285
+ # because we have the `FieldsExtension` enabled, so the endpoint
286
+ # will avoid the model validation (by returning JSONResponse)
261
287
  assert get_search.status_code == 200, get_search.text
262
288
  assert post_search.status_code == 200, post_search.text
263
289
 
290
+ # Without FieldsExtension
264
291
  test_app = app.StacApi(
265
292
  settings=ApiSettings(enable_response_models=validate),
266
293
  client=BadCoreClient(),
267
- search_get_request_model=create_get_request_model([FieldsExtension()]),
268
- search_post_request_model=create_post_request_model([FieldsExtension()]),
294
+ search_get_request_model=create_get_request_model([]),
295
+ search_post_request_model=create_post_request_model([]),
269
296
  extensions=[],
270
297
  )
271
298
 
@@ -284,7 +311,10 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
284
311
  },
285
312
  },
286
313
  )
314
+
287
315
  if validate:
316
+ # NOTE: the `fields` options will be ignored by fastAPI because it's
317
+ # not part of the request model, so the client should not by-pass the validation
288
318
  assert get_search.status_code == 500, (
289
319
  get_search.json()["code"] == "ResponseValidationError"
290
320
  )
@@ -294,3 +324,66 @@ def test_fields_extension(validate, TestCoreClient, item_dict):
294
324
  else:
295
325
  assert get_search.status_code == 200, get_search.text
296
326
  assert post_search.status_code == 200, post_search.text
327
+
328
+
329
+ def test_request_model(AsyncTestCoreClient):
330
+ """Test if request models are passed correctly."""
331
+
332
+ @attr.s
333
+ class CollectionsRequest(APIRequest):
334
+ user: Annotated[str, Query(...)] = attr.ib()
335
+
336
+ @attr.s
337
+ class CollectionRequest(APIRequest):
338
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
339
+ user: Annotated[str, Query(...)] = attr.ib()
340
+
341
+ @attr.s
342
+ class ItemsRequest(APIRequest):
343
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
344
+ user: Annotated[str, Query(...)] = attr.ib()
345
+
346
+ @attr.s
347
+ class ItemRequest(APIRequest):
348
+ collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
349
+ item_id: Annotated[str, Path(description="Item ID")] = attr.ib()
350
+ user: Annotated[str, Query(...)] = attr.ib()
351
+
352
+ test_app = app.StacApi(
353
+ settings=ApiSettings(),
354
+ client=AsyncTestCoreClient(),
355
+ collections_get_request_model=CollectionsRequest,
356
+ collection_get_request_model=CollectionRequest,
357
+ items_get_request_model=ItemsRequest,
358
+ item_get_request_model=ItemRequest,
359
+ extensions=[],
360
+ )
361
+
362
+ with TestClient(test_app.app) as client:
363
+ resp = client.get("/collections")
364
+ assert resp.status_code == 400
365
+
366
+ resp = client.get("/collections", params={"user": "Luke"})
367
+ assert resp.status_code == 200
368
+
369
+ resp = client.get("/collections/test_collection")
370
+ assert resp.status_code == 400
371
+
372
+ resp = client.get("/collections/test_collection", params={"user": "Leia"})
373
+ assert resp.status_code == 200
374
+
375
+ resp = client.get("/collections/test_collection/items")
376
+ assert resp.status_code == 400
377
+
378
+ resp = client.get(
379
+ "/collections/test_collection/items", params={"user": "Obi-Wan"}
380
+ )
381
+ assert resp.status_code == 200
382
+
383
+ resp = client.get("/collections/test_collection/items/test_item")
384
+ assert resp.status_code == 400
385
+
386
+ resp = client.get(
387
+ "/collections/test_collection/items/test_item", params={"user": "Chewbacca"}
388
+ )
389
+ assert resp.status_code == 200
@@ -1,17 +1,20 @@
1
1
  import json
2
2
 
3
3
  import pytest
4
+ from fastapi import Depends, FastAPI, HTTPException
5
+ from fastapi.testclient import TestClient
4
6
  from pydantic import ValidationError
5
7
 
6
8
  from stac_fastapi.api.models import create_get_request_model, create_post_request_model
7
- from stac_fastapi.extensions.core.filter.filter import FilterExtension
8
- from stac_fastapi.extensions.core.sort.sort import SortExtension
9
+ from stac_fastapi.extensions.core import FieldsExtension, FilterExtension, SortExtension
9
10
  from stac_fastapi.types.search import BaseSearchGetRequest, BaseSearchPostRequest
10
11
 
11
12
 
12
13
  def test_create_get_request_model():
13
- extensions = [FilterExtension()]
14
- request_model = create_get_request_model(extensions, BaseSearchGetRequest)
14
+ request_model = create_get_request_model(
15
+ extensions=[FilterExtension(), FieldsExtension()],
16
+ base_model=BaseSearchGetRequest,
17
+ )
15
18
 
16
19
  model = request_model(
17
20
  collections="test1,test2",
@@ -26,13 +29,36 @@ def test_create_get_request_model():
26
29
  datetime="2020-01-01T00:00:00Z",
27
30
  limit=10,
28
31
  filter="test==test",
29
- # FIXME: https://github.com/stac-utils/stac-fastapi/issues/638
30
- # hyphen aliases are not properly working
31
- # **{"filter-crs": "epsg:4326", "filter-lang": "cql2-text"},
32
+ filter_crs="epsg:4326",
33
+ filter_lang="cql2-text",
32
34
  )
33
35
 
34
36
  assert model.collections == ["test1", "test2"]
35
- # assert model.filter_crs == "epsg:4326"
37
+ assert model.filter_crs == "epsg:4326"
38
+
39
+ with pytest.raises(HTTPException):
40
+ request_model(datetime="yo")
41
+
42
+ app = FastAPI()
43
+
44
+ @app.get("/test")
45
+ def route(model=Depends(request_model)):
46
+ return model
47
+
48
+ with TestClient(app) as client:
49
+ resp = client.get(
50
+ "/test",
51
+ params={
52
+ "collections": "test1,test2",
53
+ "filter-crs": "epsg:4326",
54
+ "filter-lang": "cql2-text",
55
+ },
56
+ )
57
+ assert resp.status_code == 200
58
+ response_dict = resp.json()
59
+ assert response_dict["collections"] == ["test1", "test2"]
60
+ assert response_dict["filter_crs"] == "epsg:4326"
61
+ assert response_dict["filter_lang"] == "cql2-text"
36
62
 
37
63
 
38
64
  @pytest.mark.parametrize(
@@ -40,8 +66,10 @@ def test_create_get_request_model():
40
66
  [(None, True), ({"test": "test"}, True), ("test==test", False), ([], False)],
41
67
  )
42
68
  def test_create_post_request_model(filter, passes):
43
- extensions = [FilterExtension()]
44
- request_model = create_post_request_model(extensions, BaseSearchPostRequest)
69
+ request_model = create_post_request_model(
70
+ extensions=[FilterExtension(), FieldsExtension()],
71
+ base_model=BaseSearchPostRequest,
72
+ )
45
73
 
46
74
  if not passes:
47
75
  with pytest.raises(ValidationError):
@@ -78,8 +106,10 @@ def test_create_post_request_model(filter, passes):
78
106
  ],
79
107
  )
80
108
  def test_create_post_request_model_nested_fields(sortby, passes):
81
- extensions = [SortExtension()]
82
- request_model = create_post_request_model(extensions, BaseSearchPostRequest)
109
+ request_model = create_post_request_model(
110
+ extensions=[SortExtension()],
111
+ base_model=BaseSearchPostRequest,
112
+ )
83
113
 
84
114
  if not passes:
85
115
  with pytest.raises(ValidationError):
@@ -1,2 +0,0 @@
1
- """Library version."""
2
- __version__ = "3.0.0a4"