stac-fastapi.api 3.0.0a2__tar.gz → 3.0.0a3__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.0a2 → stac_fastapi_api-3.0.0a3}/PKG-INFO +1 -1
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/app.py +0 -4
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/config.py +1 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/routes.py +15 -1
- stac_fastapi_api-3.0.0a3/stac_fastapi/api/version.py +2 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/PKG-INFO +1 -1
- stac_fastapi_api-3.0.0a3/tests/test_api.py +447 -0
- stac_fastapi_api-3.0.0a2/stac_fastapi/api/version.py +0 -2
- stac_fastapi_api-3.0.0a2/tests/test_api.py +0 -172
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/README.md +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/setup.cfg +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/setup.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/__init__.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/errors.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/middleware.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/models.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi/api/openapi.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/requires.txt +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/top_level.txt +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/tests/test_app.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/tests/test_app_prefix.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/tests/test_middleware.py +0 -0
- {stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/tests/test_models.py +0 -0
|
@@ -458,10 +458,6 @@ class StacApi:
|
|
|
458
458
|
self.client.title = self.title
|
|
459
459
|
self.client.description = self.description
|
|
460
460
|
|
|
461
|
-
fields_ext = self.get_extension(FieldsExtension)
|
|
462
|
-
if fields_ext:
|
|
463
|
-
self.settings.default_includes = fields_ext.default_includes
|
|
464
|
-
|
|
465
461
|
Settings.set(self.settings)
|
|
466
462
|
self.app.state.settings = self.settings
|
|
467
463
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Route factories."""
|
|
2
2
|
|
|
3
|
+
import copy
|
|
3
4
|
import functools
|
|
4
5
|
import inspect
|
|
5
6
|
import warnings
|
|
@@ -100,15 +101,28 @@ def add_route_dependencies(
|
|
|
100
101
|
Allows a developer to add dependencies to a route after the route has been
|
|
101
102
|
defined.
|
|
102
103
|
|
|
104
|
+
"*" can be used for path or method to match all allowed routes.
|
|
105
|
+
|
|
103
106
|
Returns:
|
|
104
107
|
None
|
|
105
108
|
"""
|
|
106
109
|
for scope in scopes:
|
|
110
|
+
_scope = copy.deepcopy(scope)
|
|
107
111
|
for route in routes:
|
|
108
|
-
|
|
112
|
+
if scope["path"] == "*":
|
|
113
|
+
_scope["path"] = route.path
|
|
114
|
+
|
|
115
|
+
if scope["method"] == "*":
|
|
116
|
+
_scope["method"] = list(route.methods)[0]
|
|
117
|
+
|
|
118
|
+
match, _ = route.matches({"type": "http", **_scope})
|
|
109
119
|
if match != Match.FULL:
|
|
110
120
|
continue
|
|
111
121
|
|
|
122
|
+
# Ignore paths without dependants, e.g. /api, /api.html, /docs/oauth2-redirect
|
|
123
|
+
if not hasattr(route, "dependant"):
|
|
124
|
+
continue
|
|
125
|
+
|
|
112
126
|
# Mimicking how APIRoute handles dependencies:
|
|
113
127
|
# https://github.com/tiangolo/fastapi/blob/1760da0efa55585c19835d81afa8ca386036c325/fastapi/routing.py#L408-L412
|
|
114
128
|
for depends in dependencies[::-1]:
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
from fastapi import Depends, HTTPException, security, status
|
|
2
|
+
from starlette.testclient import TestClient
|
|
3
|
+
|
|
4
|
+
from stac_fastapi.api.app import StacApi
|
|
5
|
+
from stac_fastapi.extensions.core import (
|
|
6
|
+
TokenPaginationExtension,
|
|
7
|
+
TransactionExtension,
|
|
8
|
+
)
|
|
9
|
+
from stac_fastapi.types import config, core
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestRouteDependencies:
|
|
13
|
+
@staticmethod
|
|
14
|
+
def _build_api(**overrides):
|
|
15
|
+
settings = config.ApiSettings()
|
|
16
|
+
return StacApi(
|
|
17
|
+
**{
|
|
18
|
+
"settings": settings,
|
|
19
|
+
"client": DummyCoreClient(),
|
|
20
|
+
"extensions": [
|
|
21
|
+
TransactionExtension(
|
|
22
|
+
client=DummyTransactionsClient(), settings=settings
|
|
23
|
+
),
|
|
24
|
+
TokenPaginationExtension(),
|
|
25
|
+
],
|
|
26
|
+
**overrides,
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _assert_dependency_applied(api, routes):
|
|
32
|
+
with TestClient(api.app) as client:
|
|
33
|
+
for route in routes:
|
|
34
|
+
response = getattr(client, route["method"].lower())(route["path"])
|
|
35
|
+
assert (
|
|
36
|
+
response.status_code == 401
|
|
37
|
+
), "Unauthenticated requests should be rejected"
|
|
38
|
+
assert response.json() == {"detail": "Not authenticated"}
|
|
39
|
+
|
|
40
|
+
path = route["path"].format(
|
|
41
|
+
collectionId="test_collection", itemId="test_item"
|
|
42
|
+
)
|
|
43
|
+
response = client.request(
|
|
44
|
+
method=route["method"].lower(),
|
|
45
|
+
url=path,
|
|
46
|
+
auth=("bob", "dobbs"),
|
|
47
|
+
content=route["payload"],
|
|
48
|
+
headers={"content-type": "application/json"},
|
|
49
|
+
)
|
|
50
|
+
assert (
|
|
51
|
+
200 <= response.status_code < 300
|
|
52
|
+
), "Authenticated requests should be accepted"
|
|
53
|
+
assert response.json() == "dummy response"
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _assert_dependency_not_applied(api, routes):
|
|
57
|
+
with TestClient(api.app) as client:
|
|
58
|
+
for route in routes:
|
|
59
|
+
path = route["path"].format(
|
|
60
|
+
collectionId="test_collection", itemId="test_item"
|
|
61
|
+
)
|
|
62
|
+
response = client.request(
|
|
63
|
+
method=route["method"].lower(),
|
|
64
|
+
url=path,
|
|
65
|
+
content=route["payload"],
|
|
66
|
+
headers={"content-type": "application/json"},
|
|
67
|
+
)
|
|
68
|
+
assert (
|
|
69
|
+
200 <= response.status_code < 300
|
|
70
|
+
), "Authenticated requests should be accepted"
|
|
71
|
+
assert response.json() == "dummy response"
|
|
72
|
+
|
|
73
|
+
def test_openapi_content_type(self):
|
|
74
|
+
api = self._build_api()
|
|
75
|
+
with TestClient(api.app) as client:
|
|
76
|
+
response = client.get(api.settings.openapi_url)
|
|
77
|
+
assert (
|
|
78
|
+
response.headers["content-type"]
|
|
79
|
+
== "application/vnd.oai.openapi+json;version=3.0"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def test_build_api_with_route_dependencies(self, collection, item):
|
|
83
|
+
routes = [
|
|
84
|
+
{"path": "/collections", "method": "POST", "payload": collection},
|
|
85
|
+
{
|
|
86
|
+
"path": "/collections/{collectionId}",
|
|
87
|
+
"method": "PUT",
|
|
88
|
+
"payload": collection,
|
|
89
|
+
},
|
|
90
|
+
{"path": "/collections/{collectionId}", "method": "DELETE", "payload": ""},
|
|
91
|
+
{
|
|
92
|
+
"path": "/collections/{collectionId}/items",
|
|
93
|
+
"method": "POST",
|
|
94
|
+
"payload": item,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
98
|
+
"method": "PUT",
|
|
99
|
+
"payload": item,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
103
|
+
"method": "DELETE",
|
|
104
|
+
"payload": "",
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
dependencies = [Depends(must_be_bob)]
|
|
108
|
+
api = self._build_api(route_dependencies=[(routes, dependencies)])
|
|
109
|
+
self._assert_dependency_applied(api, routes)
|
|
110
|
+
|
|
111
|
+
def test_add_route_dependencies_after_building_api(self, collection, item):
|
|
112
|
+
routes = [
|
|
113
|
+
{"path": "/collections", "method": "POST", "payload": collection},
|
|
114
|
+
{
|
|
115
|
+
"path": "/collections/{collectionId}",
|
|
116
|
+
"method": "PUT",
|
|
117
|
+
"payload": collection,
|
|
118
|
+
},
|
|
119
|
+
{"path": "/collections/{collectionId}", "method": "DELETE", "payload": ""},
|
|
120
|
+
{
|
|
121
|
+
"path": "/collections/{collectionId}/items",
|
|
122
|
+
"method": "POST",
|
|
123
|
+
"payload": item,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
127
|
+
"method": "PUT",
|
|
128
|
+
"payload": item,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
132
|
+
"method": "DELETE",
|
|
133
|
+
"payload": "",
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
api = self._build_api()
|
|
137
|
+
api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)])
|
|
138
|
+
self._assert_dependency_applied(api, routes)
|
|
139
|
+
|
|
140
|
+
def test_build_api_with_default_route_dependencies(self, collection, item):
|
|
141
|
+
routes = [{"path": "*", "method": "*"}]
|
|
142
|
+
test_routes = [
|
|
143
|
+
{"path": "/collections", "method": "POST", "payload": collection},
|
|
144
|
+
{
|
|
145
|
+
"path": "/collections/{collectionId}",
|
|
146
|
+
"method": "PUT",
|
|
147
|
+
"payload": collection,
|
|
148
|
+
},
|
|
149
|
+
{"path": "/collections/{collectionId}", "method": "DELETE", "payload": ""},
|
|
150
|
+
{
|
|
151
|
+
"path": "/collections/{collectionId}/items",
|
|
152
|
+
"method": "POST",
|
|
153
|
+
"payload": item,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
157
|
+
"method": "PUT",
|
|
158
|
+
"payload": item,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
162
|
+
"method": "DELETE",
|
|
163
|
+
"payload": "",
|
|
164
|
+
},
|
|
165
|
+
]
|
|
166
|
+
dependencies = [Depends(must_be_bob)]
|
|
167
|
+
api = self._build_api(route_dependencies=[(routes, dependencies)])
|
|
168
|
+
self._assert_dependency_applied(api, test_routes)
|
|
169
|
+
|
|
170
|
+
def test_build_api_with_default_path_route_dependencies(self, collection, item):
|
|
171
|
+
routes = [{"path": "*", "method": "POST"}]
|
|
172
|
+
test_routes = [
|
|
173
|
+
{
|
|
174
|
+
"path": "/collections",
|
|
175
|
+
"method": "POST",
|
|
176
|
+
"payload": collection,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"path": "/collections/{collectionId}/items",
|
|
180
|
+
"method": "POST",
|
|
181
|
+
"payload": item,
|
|
182
|
+
},
|
|
183
|
+
]
|
|
184
|
+
test_not_routes = [
|
|
185
|
+
{
|
|
186
|
+
"path": "/collections/{collectionId}",
|
|
187
|
+
"method": "PUT",
|
|
188
|
+
"payload": collection,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"path": "/collections/{collectionId}",
|
|
192
|
+
"method": "DELETE",
|
|
193
|
+
"payload": "",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
197
|
+
"method": "PUT",
|
|
198
|
+
"payload": item,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
202
|
+
"method": "DELETE",
|
|
203
|
+
"payload": "",
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
dependencies = [Depends(must_be_bob)]
|
|
207
|
+
api = self._build_api(route_dependencies=[(routes, dependencies)])
|
|
208
|
+
self._assert_dependency_applied(api, test_routes)
|
|
209
|
+
self._assert_dependency_not_applied(api, test_not_routes)
|
|
210
|
+
|
|
211
|
+
def test_build_api_with_default_method_route_dependencies(self, collection, item):
|
|
212
|
+
routes = [
|
|
213
|
+
{
|
|
214
|
+
"path": "/collections/{collectionId}",
|
|
215
|
+
"method": "*",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
219
|
+
"method": "*",
|
|
220
|
+
},
|
|
221
|
+
]
|
|
222
|
+
test_routes = [
|
|
223
|
+
{
|
|
224
|
+
"path": "/collections/{collectionId}",
|
|
225
|
+
"method": "PUT",
|
|
226
|
+
"payload": collection,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"path": "/collections/{collectionId}",
|
|
230
|
+
"method": "DELETE",
|
|
231
|
+
"payload": "",
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
235
|
+
"method": "PUT",
|
|
236
|
+
"payload": item,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
240
|
+
"method": "DELETE",
|
|
241
|
+
"payload": "",
|
|
242
|
+
},
|
|
243
|
+
]
|
|
244
|
+
test_not_routes = [
|
|
245
|
+
{
|
|
246
|
+
"path": "/collections",
|
|
247
|
+
"method": "POST",
|
|
248
|
+
"payload": collection,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"path": "/collections/{collectionId}/items",
|
|
252
|
+
"method": "POST",
|
|
253
|
+
"payload": item,
|
|
254
|
+
},
|
|
255
|
+
]
|
|
256
|
+
dependencies = [Depends(must_be_bob)]
|
|
257
|
+
api = self._build_api(route_dependencies=[(routes, dependencies)])
|
|
258
|
+
self._assert_dependency_applied(api, test_routes)
|
|
259
|
+
self._assert_dependency_not_applied(api, test_not_routes)
|
|
260
|
+
|
|
261
|
+
def test_add_default_route_dependencies_after_building_api(self, collection, item):
|
|
262
|
+
routes = [{"path": "*", "method": "*"}]
|
|
263
|
+
test_routes = [
|
|
264
|
+
{
|
|
265
|
+
"path": "/collections",
|
|
266
|
+
"method": "POST",
|
|
267
|
+
"payload": collection,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"path": "/collections/{collectionId}",
|
|
271
|
+
"method": "PUT",
|
|
272
|
+
"payload": collection,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"path": "/collections/{collectionId}",
|
|
276
|
+
"method": "DELETE",
|
|
277
|
+
"payload": "",
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"path": "/collections/{collectionId}/items",
|
|
281
|
+
"method": "POST",
|
|
282
|
+
"payload": item,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
286
|
+
"method": "PUT",
|
|
287
|
+
"payload": item,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
291
|
+
"method": "DELETE",
|
|
292
|
+
"payload": "",
|
|
293
|
+
},
|
|
294
|
+
]
|
|
295
|
+
api = self._build_api()
|
|
296
|
+
api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)])
|
|
297
|
+
self._assert_dependency_applied(api, test_routes)
|
|
298
|
+
|
|
299
|
+
def test_add_default_path_route_dependencies_after_building_api(
|
|
300
|
+
self, collection, item
|
|
301
|
+
):
|
|
302
|
+
routes = [{"path": "*", "method": "POST"}]
|
|
303
|
+
test_routes = [
|
|
304
|
+
{
|
|
305
|
+
"path": "/collections",
|
|
306
|
+
"method": "POST",
|
|
307
|
+
"payload": collection,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"path": "/collections/{collectionId}/items",
|
|
311
|
+
"method": "POST",
|
|
312
|
+
"payload": item,
|
|
313
|
+
},
|
|
314
|
+
]
|
|
315
|
+
test_not_routes = [
|
|
316
|
+
{
|
|
317
|
+
"path": "/collections/{collectionId}",
|
|
318
|
+
"method": "PUT",
|
|
319
|
+
"payload": collection,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"path": "/collections/{collectionId}",
|
|
323
|
+
"method": "DELETE",
|
|
324
|
+
"payload": "",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
328
|
+
"method": "PUT",
|
|
329
|
+
"payload": item,
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
333
|
+
"method": "DELETE",
|
|
334
|
+
"payload": "",
|
|
335
|
+
},
|
|
336
|
+
]
|
|
337
|
+
api = self._build_api()
|
|
338
|
+
api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)])
|
|
339
|
+
self._assert_dependency_applied(api, test_routes)
|
|
340
|
+
self._assert_dependency_not_applied(api, test_not_routes)
|
|
341
|
+
|
|
342
|
+
def test_add_default_method_route_dependencies_after_building_api(
|
|
343
|
+
self, collection, item
|
|
344
|
+
):
|
|
345
|
+
routes = [
|
|
346
|
+
{
|
|
347
|
+
"path": "/collections/{collectionId}",
|
|
348
|
+
"method": "*",
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
352
|
+
"method": "*",
|
|
353
|
+
},
|
|
354
|
+
]
|
|
355
|
+
test_routes = [
|
|
356
|
+
{
|
|
357
|
+
"path": "/collections/{collectionId}",
|
|
358
|
+
"method": "PUT",
|
|
359
|
+
"payload": collection,
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"path": "/collections/{collectionId}",
|
|
363
|
+
"method": "DELETE",
|
|
364
|
+
"payload": "",
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
368
|
+
"method": "PUT",
|
|
369
|
+
"payload": item,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"path": "/collections/{collectionId}/items/{itemId}",
|
|
373
|
+
"method": "DELETE",
|
|
374
|
+
"payload": "",
|
|
375
|
+
},
|
|
376
|
+
]
|
|
377
|
+
test_not_routes = [
|
|
378
|
+
{
|
|
379
|
+
"path": "/collections",
|
|
380
|
+
"method": "POST",
|
|
381
|
+
"payload": collection,
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"path": "/collections/{collectionId}/items",
|
|
385
|
+
"method": "POST",
|
|
386
|
+
"payload": item,
|
|
387
|
+
},
|
|
388
|
+
]
|
|
389
|
+
api = self._build_api()
|
|
390
|
+
api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)])
|
|
391
|
+
self._assert_dependency_applied(api, test_routes)
|
|
392
|
+
self._assert_dependency_not_applied(api, test_not_routes)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class DummyCoreClient(core.BaseCoreClient):
|
|
396
|
+
def all_collections(self, *args, **kwargs):
|
|
397
|
+
...
|
|
398
|
+
|
|
399
|
+
def get_collection(self, *args, **kwargs):
|
|
400
|
+
...
|
|
401
|
+
|
|
402
|
+
def get_item(self, *args, **kwargs):
|
|
403
|
+
...
|
|
404
|
+
|
|
405
|
+
def get_search(self, *args, **kwargs):
|
|
406
|
+
...
|
|
407
|
+
|
|
408
|
+
def post_search(self, *args, **kwargs):
|
|
409
|
+
...
|
|
410
|
+
|
|
411
|
+
def item_collection(self, *args, **kwargs):
|
|
412
|
+
...
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class DummyTransactionsClient(core.BaseTransactionsClient):
|
|
416
|
+
"""Defines a pattern for implementing the STAC transaction extension."""
|
|
417
|
+
|
|
418
|
+
def create_item(self, *args, **kwargs):
|
|
419
|
+
return "dummy response"
|
|
420
|
+
|
|
421
|
+
def update_item(self, *args, **kwargs):
|
|
422
|
+
return "dummy response"
|
|
423
|
+
|
|
424
|
+
def delete_item(self, *args, **kwargs):
|
|
425
|
+
return "dummy response"
|
|
426
|
+
|
|
427
|
+
def create_collection(self, *args, **kwargs):
|
|
428
|
+
return "dummy response"
|
|
429
|
+
|
|
430
|
+
def update_collection(self, *args, **kwargs):
|
|
431
|
+
return "dummy response"
|
|
432
|
+
|
|
433
|
+
def delete_collection(self, *args, **kwargs):
|
|
434
|
+
return "dummy response"
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def must_be_bob(
|
|
438
|
+
credentials: security.HTTPBasicCredentials = Depends(security.HTTPBasic()),
|
|
439
|
+
):
|
|
440
|
+
if credentials.username == "bob":
|
|
441
|
+
return True
|
|
442
|
+
|
|
443
|
+
raise HTTPException(
|
|
444
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
445
|
+
detail="You're not Bob",
|
|
446
|
+
headers={"WWW-Authenticate": "Basic"},
|
|
447
|
+
)
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
from fastapi import Depends, HTTPException, security, status
|
|
2
|
-
from starlette.testclient import TestClient
|
|
3
|
-
|
|
4
|
-
from stac_fastapi.api.app import StacApi
|
|
5
|
-
from stac_fastapi.extensions.core import TokenPaginationExtension, TransactionExtension
|
|
6
|
-
from stac_fastapi.types import config, core
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestRouteDependencies:
|
|
10
|
-
@staticmethod
|
|
11
|
-
def _build_api(**overrides):
|
|
12
|
-
settings = config.ApiSettings()
|
|
13
|
-
return StacApi(
|
|
14
|
-
**{
|
|
15
|
-
"settings": settings,
|
|
16
|
-
"client": DummyCoreClient(),
|
|
17
|
-
"extensions": [
|
|
18
|
-
TransactionExtension(
|
|
19
|
-
client=DummyTransactionsClient(), settings=settings
|
|
20
|
-
),
|
|
21
|
-
TokenPaginationExtension(),
|
|
22
|
-
],
|
|
23
|
-
**overrides,
|
|
24
|
-
}
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
@staticmethod
|
|
28
|
-
def _assert_dependency_applied(api, routes):
|
|
29
|
-
with TestClient(api.app) as client:
|
|
30
|
-
for route in routes:
|
|
31
|
-
response = getattr(client, route["method"].lower())(route["path"])
|
|
32
|
-
assert (
|
|
33
|
-
response.status_code == 401
|
|
34
|
-
), "Unauthenticated requests should be rejected"
|
|
35
|
-
assert response.json() == {"detail": "Not authenticated"}
|
|
36
|
-
|
|
37
|
-
path = route["path"].format(
|
|
38
|
-
collectionId="test_collection", itemId="test_item"
|
|
39
|
-
)
|
|
40
|
-
response = client.request(
|
|
41
|
-
method=route["method"].lower(),
|
|
42
|
-
url=path,
|
|
43
|
-
auth=("bob", "dobbs"),
|
|
44
|
-
content=route["payload"],
|
|
45
|
-
headers={"content-type": "application/json"},
|
|
46
|
-
)
|
|
47
|
-
assert (
|
|
48
|
-
200 <= response.status_code < 300
|
|
49
|
-
), "Authenticated requests should be accepted"
|
|
50
|
-
assert response.json() == "dummy response"
|
|
51
|
-
|
|
52
|
-
def test_openapi_content_type(self):
|
|
53
|
-
api = self._build_api()
|
|
54
|
-
with TestClient(api.app) as client:
|
|
55
|
-
response = client.get(api.settings.openapi_url)
|
|
56
|
-
assert (
|
|
57
|
-
response.headers["content-type"]
|
|
58
|
-
== "application/vnd.oai.openapi+json;version=3.0"
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
def test_build_api_with_route_dependencies(self, collection, item):
|
|
62
|
-
routes = [
|
|
63
|
-
{"path": "/collections", "method": "POST", "payload": collection},
|
|
64
|
-
{
|
|
65
|
-
"path": "/collections/{collectionId}",
|
|
66
|
-
"method": "PUT",
|
|
67
|
-
"payload": collection,
|
|
68
|
-
},
|
|
69
|
-
{"path": "/collections/{collectionId}", "method": "DELETE", "payload": ""},
|
|
70
|
-
{
|
|
71
|
-
"path": "/collections/{collectionId}/items",
|
|
72
|
-
"method": "POST",
|
|
73
|
-
"payload": item,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"path": "/collections/{collectionId}/items/{itemId}",
|
|
77
|
-
"method": "PUT",
|
|
78
|
-
"payload": item,
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"path": "/collections/{collectionId}/items/{itemId}",
|
|
82
|
-
"method": "DELETE",
|
|
83
|
-
"payload": "",
|
|
84
|
-
},
|
|
85
|
-
]
|
|
86
|
-
dependencies = [Depends(must_be_bob)]
|
|
87
|
-
api = self._build_api(route_dependencies=[(routes, dependencies)])
|
|
88
|
-
self._assert_dependency_applied(api, routes)
|
|
89
|
-
|
|
90
|
-
def test_add_route_dependencies_after_building_api(self, collection, item):
|
|
91
|
-
routes = [
|
|
92
|
-
{"path": "/collections", "method": "POST", "payload": collection},
|
|
93
|
-
{
|
|
94
|
-
"path": "/collections/{collectionId}",
|
|
95
|
-
"method": "PUT",
|
|
96
|
-
"payload": collection,
|
|
97
|
-
},
|
|
98
|
-
{"path": "/collections/{collectionId}", "method": "DELETE", "payload": ""},
|
|
99
|
-
{
|
|
100
|
-
"path": "/collections/{collectionId}/items",
|
|
101
|
-
"method": "POST",
|
|
102
|
-
"payload": item,
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"path": "/collections/{collectionId}/items/{itemId}",
|
|
106
|
-
"method": "PUT",
|
|
107
|
-
"payload": item,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
"path": "/collections/{collectionId}/items/{itemId}",
|
|
111
|
-
"method": "DELETE",
|
|
112
|
-
"payload": "",
|
|
113
|
-
},
|
|
114
|
-
]
|
|
115
|
-
api = self._build_api()
|
|
116
|
-
api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)])
|
|
117
|
-
self._assert_dependency_applied(api, routes)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class DummyCoreClient(core.BaseCoreClient):
|
|
121
|
-
def all_collections(self, *args, **kwargs):
|
|
122
|
-
...
|
|
123
|
-
|
|
124
|
-
def get_collection(self, *args, **kwargs):
|
|
125
|
-
...
|
|
126
|
-
|
|
127
|
-
def get_item(self, *args, **kwargs):
|
|
128
|
-
...
|
|
129
|
-
|
|
130
|
-
def get_search(self, *args, **kwargs):
|
|
131
|
-
...
|
|
132
|
-
|
|
133
|
-
def post_search(self, *args, **kwargs):
|
|
134
|
-
...
|
|
135
|
-
|
|
136
|
-
def item_collection(self, *args, **kwargs):
|
|
137
|
-
...
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
class DummyTransactionsClient(core.BaseTransactionsClient):
|
|
141
|
-
"""Defines a pattern for implementing the STAC transaction extension."""
|
|
142
|
-
|
|
143
|
-
def create_item(self, *args, **kwargs):
|
|
144
|
-
return "dummy response"
|
|
145
|
-
|
|
146
|
-
def update_item(self, *args, **kwargs):
|
|
147
|
-
return "dummy response"
|
|
148
|
-
|
|
149
|
-
def delete_item(self, *args, **kwargs):
|
|
150
|
-
return "dummy response"
|
|
151
|
-
|
|
152
|
-
def create_collection(self, *args, **kwargs):
|
|
153
|
-
return "dummy response"
|
|
154
|
-
|
|
155
|
-
def update_collection(self, *args, **kwargs):
|
|
156
|
-
return "dummy response"
|
|
157
|
-
|
|
158
|
-
def delete_collection(self, *args, **kwargs):
|
|
159
|
-
return "dummy response"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def must_be_bob(
|
|
163
|
-
credentials: security.HTTPBasicCredentials = Depends(security.HTTPBasic()),
|
|
164
|
-
):
|
|
165
|
-
if credentials.username == "bob":
|
|
166
|
-
return True
|
|
167
|
-
|
|
168
|
-
raise HTTPException(
|
|
169
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
170
|
-
detail="You're not Bob",
|
|
171
|
-
headers={"WWW-Authenticate": "Basic"},
|
|
172
|
-
)
|
|
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.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/requires.txt
RENAMED
|
File without changes
|
{stac_fastapi_api-3.0.0a2 → stac_fastapi_api-3.0.0a3}/stac_fastapi.api.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|