stac-fastapi.types 2.5.5__tar.gz → 3.0.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.
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/PKG-INFO +5 -6
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/setup.py +4 -5
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/config.py +4 -11
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/conformance.py +1 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/core.py +123 -138
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/extension.py +1 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/requests.py +1 -3
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/rfc3339.py +37 -5
- stac_fastapi_types-3.0.0/stac_fastapi/types/search.py +190 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/stac.py +11 -18
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/version.py +2 -1
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/PKG-INFO +5 -6
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/requires.txt +4 -5
- stac_fastapi_types-3.0.0/tests/test_limit.py +55 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/tests/test_rfc3339.py +30 -12
- stac_fastapi_types-2.5.5/stac_fastapi/types/search.py +0 -222
- stac_fastapi_types-2.5.5/tests/test_limit.py +0 -22
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/README.md +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/setup.cfg +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/__init__.py +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/errors.py +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi/types/links.py +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/not-zip-safe +0 -0
- {stac_fastapi_types-2.5.5 → stac_fastapi_types-3.0.0}/stac_fastapi.types.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: stac-fastapi.types
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.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
|
|
@@ -18,11 +18,10 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: License :: OSI Approved :: MIT License
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
-
Requires-Dist: fastapi>=0.
|
|
22
|
-
Requires-Dist: attrs
|
|
23
|
-
Requires-Dist: pydantic
|
|
24
|
-
Requires-Dist: stac_pydantic
|
|
25
|
-
Requires-Dist: pystac==1.*
|
|
21
|
+
Requires-Dist: fastapi-slim>=0.111.0
|
|
22
|
+
Requires-Dist: attrs>=23.2.0
|
|
23
|
+
Requires-Dist: pydantic-settings>=2
|
|
24
|
+
Requires-Dist: stac_pydantic~=3.1
|
|
26
25
|
Requires-Dist: iso8601<2.2.0,>=1.0.2
|
|
27
26
|
Provides-Extra: dev
|
|
28
27
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -6,11 +6,10 @@ with open("README.md") as f:
|
|
|
6
6
|
desc = f.read()
|
|
7
7
|
|
|
8
8
|
install_requires = [
|
|
9
|
-
"fastapi>=0.
|
|
10
|
-
"attrs",
|
|
11
|
-
"pydantic
|
|
12
|
-
"stac_pydantic
|
|
13
|
-
"pystac==1.*",
|
|
9
|
+
"fastapi-slim>=0.111.0",
|
|
10
|
+
"attrs>=23.2.0",
|
|
11
|
+
"pydantic-settings>=2",
|
|
12
|
+
"stac_pydantic~=3.1",
|
|
14
13
|
"iso8601>=1.0.2,<2.2.0",
|
|
15
14
|
]
|
|
16
15
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""stac_fastapi.types.config module."""
|
|
2
|
-
from typing import Optional, Set
|
|
3
2
|
|
|
4
|
-
from
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class ApiSettings(BaseSettings):
|
|
@@ -18,10 +19,6 @@ class ApiSettings(BaseSettings):
|
|
|
18
19
|
as distinct columns in the database.
|
|
19
20
|
"""
|
|
20
21
|
|
|
21
|
-
# TODO: Remove `default_includes` attribute so we can use
|
|
22
|
-
# `pydantic.BaseSettings` instead
|
|
23
|
-
default_includes: Optional[Set[str]] = None
|
|
24
|
-
|
|
25
22
|
stac_fastapi_title: str = "stac-fastapi"
|
|
26
23
|
stac_fastapi_description: str = "stac-fastapi"
|
|
27
24
|
stac_fastapi_version: str = "0.1"
|
|
@@ -35,11 +32,7 @@ class ApiSettings(BaseSettings):
|
|
|
35
32
|
openapi_url: str = "/api"
|
|
36
33
|
docs_url: str = "/api.html"
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
"""Model config (https://pydantic-docs.helpmanual.io/usage/model_config/)."""
|
|
40
|
-
|
|
41
|
-
extra = "allow"
|
|
42
|
-
env_file = ".env"
|
|
35
|
+
model_config = SettingsConfigDict(env_file=".env", extra="allow")
|
|
43
36
|
|
|
44
37
|
|
|
45
38
|
class Settings:
|
|
@@ -6,19 +6,30 @@ from urllib.parse import urljoin
|
|
|
6
6
|
|
|
7
7
|
import attr
|
|
8
8
|
from fastapi import Request
|
|
9
|
+
from geojson_pydantic.geometries import Geometry
|
|
10
|
+
from stac_pydantic import Collection, Item, ItemCollection
|
|
11
|
+
from stac_pydantic.api.version import STAC_API_VERSION
|
|
9
12
|
from stac_pydantic.links import Relations
|
|
10
13
|
from stac_pydantic.shared import BBox, MimeTypes
|
|
11
|
-
from stac_pydantic.version import STAC_VERSION
|
|
12
14
|
from starlette.responses import Response
|
|
13
15
|
|
|
14
|
-
from stac_fastapi.types import stac
|
|
16
|
+
from stac_fastapi.types import stac
|
|
15
17
|
from stac_fastapi.types.config import ApiSettings
|
|
16
18
|
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
|
|
17
19
|
from stac_fastapi.types.extension import ApiExtension
|
|
18
20
|
from stac_fastapi.types.requests import get_base_url
|
|
19
21
|
from stac_fastapi.types.rfc3339 import DateTimeType
|
|
20
22
|
from stac_fastapi.types.search import BaseSearchPostRequest
|
|
21
|
-
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"NumType",
|
|
26
|
+
"StacType",
|
|
27
|
+
"BaseTransactionsClient",
|
|
28
|
+
"AsyncBaseTransactionsClient",
|
|
29
|
+
"LandingPageMixin",
|
|
30
|
+
"BaseCoreClient",
|
|
31
|
+
"AsyncBaseCoreClient",
|
|
32
|
+
]
|
|
22
33
|
|
|
23
34
|
NumType = Union[float, int]
|
|
24
35
|
StacType = Dict[str, Any]
|
|
@@ -34,9 +45,9 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
34
45
|
def create_item(
|
|
35
46
|
self,
|
|
36
47
|
collection_id: str,
|
|
37
|
-
item: Union[
|
|
48
|
+
item: Union[Item, ItemCollection],
|
|
38
49
|
**kwargs,
|
|
39
|
-
) -> Optional[Union[
|
|
50
|
+
) -> Optional[Union[stac.Item, Response, None]]:
|
|
40
51
|
"""Create a new item.
|
|
41
52
|
|
|
42
53
|
Called with `POST /collections/{collection_id}/items`.
|
|
@@ -52,8 +63,8 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
52
63
|
|
|
53
64
|
@abc.abstractmethod
|
|
54
65
|
def update_item(
|
|
55
|
-
self, collection_id: str, item_id: str, item:
|
|
56
|
-
) -> Optional[Union[
|
|
66
|
+
self, collection_id: str, item_id: str, item: Item, **kwargs
|
|
67
|
+
) -> Optional[Union[stac.Item, Response]]:
|
|
57
68
|
"""Perform a complete update on an existing item.
|
|
58
69
|
|
|
59
70
|
Called with `PUT /collections/{collection_id}/items`. It is expected
|
|
@@ -73,7 +84,7 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
73
84
|
@abc.abstractmethod
|
|
74
85
|
def delete_item(
|
|
75
86
|
self, item_id: str, collection_id: str, **kwargs
|
|
76
|
-
) -> Optional[Union[
|
|
87
|
+
) -> Optional[Union[stac.Item, Response]]:
|
|
77
88
|
"""Delete an item from a collection.
|
|
78
89
|
|
|
79
90
|
Called with `DELETE /collections/{collection_id}/items/{item_id}`
|
|
@@ -89,8 +100,8 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
89
100
|
|
|
90
101
|
@abc.abstractmethod
|
|
91
102
|
def create_collection(
|
|
92
|
-
self, collection:
|
|
93
|
-
) -> Optional[Union[
|
|
103
|
+
self, collection: Collection, **kwargs
|
|
104
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
94
105
|
"""Create a new collection.
|
|
95
106
|
|
|
96
107
|
Called with `POST /collections`.
|
|
@@ -105,8 +116,8 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
105
116
|
|
|
106
117
|
@abc.abstractmethod
|
|
107
118
|
def update_collection(
|
|
108
|
-
self, collection_id: str, collection:
|
|
109
|
-
) -> Optional[Union[
|
|
119
|
+
self, collection_id: str, collection: Collection, **kwargs
|
|
120
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
110
121
|
"""Perform a complete update on an existing collection.
|
|
111
122
|
|
|
112
123
|
Called with `PUT /collections/{collection_id}`. It is expected that this
|
|
@@ -126,7 +137,7 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
126
137
|
@abc.abstractmethod
|
|
127
138
|
def delete_collection(
|
|
128
139
|
self, collection_id: str, **kwargs
|
|
129
|
-
) -> Optional[Union[
|
|
140
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
130
141
|
"""Delete a collection.
|
|
131
142
|
|
|
132
143
|
Called with `DELETE /collections/{collection_id}`
|
|
@@ -148,9 +159,9 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
148
159
|
async def create_item(
|
|
149
160
|
self,
|
|
150
161
|
collection_id: str,
|
|
151
|
-
item: Union[
|
|
162
|
+
item: Union[Item, ItemCollection],
|
|
152
163
|
**kwargs,
|
|
153
|
-
) -> Optional[Union[
|
|
164
|
+
) -> Optional[Union[stac.Item, Response, None]]:
|
|
154
165
|
"""Create a new item.
|
|
155
166
|
|
|
156
167
|
Called with `POST /collections/{collection_id}/items`.
|
|
@@ -166,8 +177,8 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
166
177
|
|
|
167
178
|
@abc.abstractmethod
|
|
168
179
|
async def update_item(
|
|
169
|
-
self, collection_id: str, item_id: str, item:
|
|
170
|
-
) -> Optional[Union[
|
|
180
|
+
self, collection_id: str, item_id: str, item: Item, **kwargs
|
|
181
|
+
) -> Optional[Union[stac.Item, Response]]:
|
|
171
182
|
"""Perform a complete update on an existing item.
|
|
172
183
|
|
|
173
184
|
Called with `PUT /collections/{collection_id}/items`. It is expected
|
|
@@ -186,7 +197,7 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
186
197
|
@abc.abstractmethod
|
|
187
198
|
async def delete_item(
|
|
188
199
|
self, item_id: str, collection_id: str, **kwargs
|
|
189
|
-
) -> Optional[Union[
|
|
200
|
+
) -> Optional[Union[stac.Item, Response]]:
|
|
190
201
|
"""Delete an item from a collection.
|
|
191
202
|
|
|
192
203
|
Called with `DELETE /collections/{collection_id}/items/{item_id}`
|
|
@@ -202,8 +213,8 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
202
213
|
|
|
203
214
|
@abc.abstractmethod
|
|
204
215
|
async def create_collection(
|
|
205
|
-
self, collection:
|
|
206
|
-
) -> Optional[Union[
|
|
216
|
+
self, collection: Collection, **kwargs
|
|
217
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
207
218
|
"""Create a new collection.
|
|
208
219
|
|
|
209
220
|
Called with `POST /collections`.
|
|
@@ -218,8 +229,8 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
218
229
|
|
|
219
230
|
@abc.abstractmethod
|
|
220
231
|
async def update_collection(
|
|
221
|
-
self, collection_id: str, collection:
|
|
222
|
-
) -> Optional[Union[
|
|
232
|
+
self, collection_id: str, collection: Collection, **kwargs
|
|
233
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
223
234
|
"""Perform a complete update on an existing collection.
|
|
224
235
|
|
|
225
236
|
Called with `PUT /collections/{collection_id}`. It is expected that this item
|
|
@@ -239,7 +250,7 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
239
250
|
@abc.abstractmethod
|
|
240
251
|
async def delete_collection(
|
|
241
252
|
self, collection_id: str, **kwargs
|
|
242
|
-
) -> Optional[Union[
|
|
253
|
+
) -> Optional[Union[stac.Collection, Response]]:
|
|
243
254
|
"""Delete a collection.
|
|
244
255
|
|
|
245
256
|
Called with `DELETE /collections/{collection_id}`
|
|
@@ -257,7 +268,7 @@ class AsyncBaseTransactionsClient(abc.ABC):
|
|
|
257
268
|
class LandingPageMixin(abc.ABC):
|
|
258
269
|
"""Create a STAC landing page (GET /)."""
|
|
259
270
|
|
|
260
|
-
stac_version: str = attr.ib(default=
|
|
271
|
+
stac_version: str = attr.ib(default=STAC_API_VERSION)
|
|
261
272
|
landing_page_id: str = attr.ib(default=api_settings.stac_fastapi_landing_id)
|
|
262
273
|
title: str = attr.ib(default=api_settings.stac_fastapi_title)
|
|
263
274
|
description: str = attr.ib(default=api_settings.stac_fastapi_description)
|
|
@@ -267,8 +278,8 @@ class LandingPageMixin(abc.ABC):
|
|
|
267
278
|
base_url: str,
|
|
268
279
|
conformance_classes: List[str],
|
|
269
280
|
extension_schemas: List[str],
|
|
270
|
-
) ->
|
|
271
|
-
landing_page =
|
|
281
|
+
) -> stac.LandingPage:
|
|
282
|
+
landing_page = stac.LandingPage(
|
|
272
283
|
type="Catalog",
|
|
273
284
|
id=self.landing_page_id,
|
|
274
285
|
title=self.title,
|
|
@@ -278,35 +289,35 @@ class LandingPageMixin(abc.ABC):
|
|
|
278
289
|
links=[
|
|
279
290
|
{
|
|
280
291
|
"rel": Relations.self.value,
|
|
281
|
-
"type": MimeTypes.json,
|
|
292
|
+
"type": MimeTypes.json.value,
|
|
282
293
|
"href": base_url,
|
|
283
294
|
},
|
|
284
295
|
{
|
|
285
296
|
"rel": Relations.root.value,
|
|
286
|
-
"type": MimeTypes.json,
|
|
297
|
+
"type": MimeTypes.json.value,
|
|
287
298
|
"href": base_url,
|
|
288
299
|
},
|
|
289
300
|
{
|
|
290
|
-
"rel":
|
|
291
|
-
"type": MimeTypes.json,
|
|
301
|
+
"rel": Relations.data.value,
|
|
302
|
+
"type": MimeTypes.json.value,
|
|
292
303
|
"href": urljoin(base_url, "collections"),
|
|
293
304
|
},
|
|
294
305
|
{
|
|
295
306
|
"rel": Relations.conformance.value,
|
|
296
|
-
"type": MimeTypes.json,
|
|
307
|
+
"type": MimeTypes.json.value,
|
|
297
308
|
"title": "STAC/OGC conformance classes implemented by this server",
|
|
298
309
|
"href": urljoin(base_url, "conformance"),
|
|
299
310
|
},
|
|
300
311
|
{
|
|
301
312
|
"rel": Relations.search.value,
|
|
302
|
-
"type": MimeTypes.geojson,
|
|
313
|
+
"type": MimeTypes.geojson.value,
|
|
303
314
|
"title": "STAC search",
|
|
304
315
|
"href": urljoin(base_url, "search"),
|
|
305
316
|
"method": "GET",
|
|
306
317
|
},
|
|
307
318
|
{
|
|
308
319
|
"rel": Relations.search.value,
|
|
309
|
-
"type": MimeTypes.geojson,
|
|
320
|
+
"type": MimeTypes.geojson.value,
|
|
310
321
|
"title": "STAC search",
|
|
311
322
|
"href": urljoin(base_url, "search"),
|
|
312
323
|
"method": "POST",
|
|
@@ -314,6 +325,7 @@ class LandingPageMixin(abc.ABC):
|
|
|
314
325
|
],
|
|
315
326
|
stac_extensions=extension_schemas,
|
|
316
327
|
)
|
|
328
|
+
|
|
317
329
|
return landing_page
|
|
318
330
|
|
|
319
331
|
|
|
@@ -356,7 +368,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
356
368
|
|
|
357
369
|
return base_conformance
|
|
358
370
|
|
|
359
|
-
def landing_page(self, **kwargs) ->
|
|
371
|
+
def landing_page(self, **kwargs) -> stac.LandingPage:
|
|
360
372
|
"""Landing page.
|
|
361
373
|
|
|
362
374
|
Called with `GET /`.
|
|
@@ -366,6 +378,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
366
378
|
"""
|
|
367
379
|
request: Request = kwargs["request"]
|
|
368
380
|
base_url = get_base_url(request)
|
|
381
|
+
|
|
369
382
|
landing_page = self._landing_page(
|
|
370
383
|
base_url=base_url,
|
|
371
384
|
conformance_classes=self.conformance_classes(),
|
|
@@ -376,18 +389,35 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
376
389
|
if self.extension_is_enabled("FilterExtension"):
|
|
377
390
|
landing_page["links"].append(
|
|
378
391
|
{
|
|
379
|
-
|
|
380
|
-
"
|
|
381
|
-
# TODO: replace this with MimeTypes.jsonschema,
|
|
382
|
-
"type": "application/schema+json",
|
|
392
|
+
"rel": Relations.queryables.value,
|
|
393
|
+
"type": MimeTypes.jsonschema.value,
|
|
383
394
|
"title": "Queryables",
|
|
384
395
|
"href": urljoin(base_url, "queryables"),
|
|
385
|
-
"method": "GET",
|
|
386
396
|
}
|
|
387
397
|
)
|
|
388
398
|
|
|
399
|
+
# Add Aggregation links
|
|
400
|
+
if self.extension_is_enabled("AggregationExtension"):
|
|
401
|
+
landing_page["links"].extend(
|
|
402
|
+
[
|
|
403
|
+
{
|
|
404
|
+
"rel": "aggregate",
|
|
405
|
+
"type": "application/json",
|
|
406
|
+
"title": "Aggregate",
|
|
407
|
+
"href": urljoin(base_url, "aggregate"),
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
"rel": "aggregations",
|
|
411
|
+
"type": "application/json",
|
|
412
|
+
"title": "Aggregations",
|
|
413
|
+
"href": urljoin(base_url, "aggregations"),
|
|
414
|
+
},
|
|
415
|
+
]
|
|
416
|
+
)
|
|
417
|
+
|
|
389
418
|
# Add Collections links
|
|
390
419
|
collections = self.all_collections(request=kwargs["request"])
|
|
420
|
+
|
|
391
421
|
for collection in collections["collections"]:
|
|
392
422
|
landing_page["links"].append(
|
|
393
423
|
{
|
|
@@ -401,8 +431,8 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
401
431
|
# Add OpenAPI URL
|
|
402
432
|
landing_page["links"].append(
|
|
403
433
|
{
|
|
404
|
-
"rel":
|
|
405
|
-
"type":
|
|
434
|
+
"rel": Relations.service_desc.value,
|
|
435
|
+
"type": MimeTypes.openapi.value,
|
|
406
436
|
"title": "OpenAPI service description",
|
|
407
437
|
"href": str(request.url_for("openapi")),
|
|
408
438
|
}
|
|
@@ -411,16 +441,16 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
411
441
|
# Add human readable service-doc
|
|
412
442
|
landing_page["links"].append(
|
|
413
443
|
{
|
|
414
|
-
"rel":
|
|
415
|
-
"type":
|
|
444
|
+
"rel": Relations.service_doc.value,
|
|
445
|
+
"type": MimeTypes.html.value,
|
|
416
446
|
"title": "OpenAPI service documentation",
|
|
417
447
|
"href": str(request.url_for("swagger_ui_html")),
|
|
418
448
|
}
|
|
419
449
|
)
|
|
420
450
|
|
|
421
|
-
return landing_page
|
|
451
|
+
return stac.LandingPage(**landing_page)
|
|
422
452
|
|
|
423
|
-
def conformance(self, **kwargs) ->
|
|
453
|
+
def conformance(self, **kwargs) -> stac.Conformance:
|
|
424
454
|
"""Conformance classes.
|
|
425
455
|
|
|
426
456
|
Called with `GET /conformance`.
|
|
@@ -428,12 +458,12 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
428
458
|
Returns:
|
|
429
459
|
Conformance classes which the server conforms to.
|
|
430
460
|
"""
|
|
431
|
-
return Conformance(conformsTo=self.conformance_classes())
|
|
461
|
+
return stac.Conformance(conformsTo=self.conformance_classes())
|
|
432
462
|
|
|
433
463
|
@abc.abstractmethod
|
|
434
464
|
def post_search(
|
|
435
465
|
self, search_request: BaseSearchPostRequest, **kwargs
|
|
436
|
-
) ->
|
|
466
|
+
) -> stac.ItemCollection:
|
|
437
467
|
"""Cross catalog search (POST).
|
|
438
468
|
|
|
439
469
|
Called with `POST /search`.
|
|
@@ -452,15 +482,11 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
452
482
|
collections: Optional[List[str]] = None,
|
|
453
483
|
ids: Optional[List[str]] = None,
|
|
454
484
|
bbox: Optional[BBox] = None,
|
|
485
|
+
intersects: Optional[Geometry] = None,
|
|
455
486
|
datetime: Optional[DateTimeType] = None,
|
|
456
487
|
limit: Optional[int] = 10,
|
|
457
|
-
query: Optional[str] = None,
|
|
458
|
-
token: Optional[str] = None,
|
|
459
|
-
fields: Optional[List[str]] = None,
|
|
460
|
-
sortby: Optional[str] = None,
|
|
461
|
-
intersects: Optional[str] = None,
|
|
462
488
|
**kwargs,
|
|
463
|
-
) ->
|
|
489
|
+
) -> stac.ItemCollection:
|
|
464
490
|
"""Cross catalog search (GET).
|
|
465
491
|
|
|
466
492
|
Called with `GET /search`.
|
|
@@ -471,7 +497,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
471
497
|
...
|
|
472
498
|
|
|
473
499
|
@abc.abstractmethod
|
|
474
|
-
def get_item(self, item_id: str, collection_id: str, **kwargs) ->
|
|
500
|
+
def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
|
|
475
501
|
"""Get item by id.
|
|
476
502
|
|
|
477
503
|
Called with `GET /collections/{collection_id}/items/{item_id}`.
|
|
@@ -486,7 +512,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
486
512
|
...
|
|
487
513
|
|
|
488
514
|
@abc.abstractmethod
|
|
489
|
-
def all_collections(self, **kwargs) ->
|
|
515
|
+
def all_collections(self, **kwargs) -> stac.Collections:
|
|
490
516
|
"""Get all available collections.
|
|
491
517
|
|
|
492
518
|
Called with `GET /collections`.
|
|
@@ -497,7 +523,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
497
523
|
...
|
|
498
524
|
|
|
499
525
|
@abc.abstractmethod
|
|
500
|
-
def get_collection(self, collection_id: str, **kwargs) ->
|
|
526
|
+
def get_collection(self, collection_id: str, **kwargs) -> stac.Collection:
|
|
501
527
|
"""Get collection by id.
|
|
502
528
|
|
|
503
529
|
Called with `GET /collections/{collection_id}`.
|
|
@@ -519,7 +545,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
519
545
|
limit: int = 10,
|
|
520
546
|
token: str = None,
|
|
521
547
|
**kwargs,
|
|
522
|
-
) ->
|
|
548
|
+
) -> stac.ItemCollection:
|
|
523
549
|
"""Get all items from a specific collection.
|
|
524
550
|
|
|
525
551
|
Called with `GET /collections/{collection_id}/items`
|
|
@@ -564,7 +590,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
564
590
|
"""Check if an api extension is enabled."""
|
|
565
591
|
return any([type(ext).__name__ == extension for ext in self.extensions])
|
|
566
592
|
|
|
567
|
-
async def landing_page(self, **kwargs) ->
|
|
593
|
+
async def landing_page(self, **kwargs) -> stac.LandingPage:
|
|
568
594
|
"""Landing page.
|
|
569
595
|
|
|
570
596
|
Called with `GET /`.
|
|
@@ -574,6 +600,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
574
600
|
"""
|
|
575
601
|
request: Request = kwargs["request"]
|
|
576
602
|
base_url = get_base_url(request)
|
|
603
|
+
|
|
577
604
|
landing_page = self._landing_page(
|
|
578
605
|
base_url=base_url,
|
|
579
606
|
conformance_classes=self.conformance_classes(),
|
|
@@ -584,18 +611,36 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
584
611
|
if self.extension_is_enabled("FilterExtension"):
|
|
585
612
|
landing_page["links"].append(
|
|
586
613
|
{
|
|
587
|
-
|
|
588
|
-
"
|
|
589
|
-
# TODO: replace this with MimeTypes.jsonschema,
|
|
590
|
-
"type": "application/schema+json",
|
|
614
|
+
"rel": Relations.queryables.value,
|
|
615
|
+
"type": MimeTypes.jsonschema.value,
|
|
591
616
|
"title": "Queryables",
|
|
592
617
|
"href": urljoin(base_url, "queryables"),
|
|
593
618
|
"method": "GET",
|
|
594
619
|
}
|
|
595
620
|
)
|
|
596
621
|
|
|
622
|
+
# Add Aggregation links
|
|
623
|
+
if self.extension_is_enabled("AggregationExtension"):
|
|
624
|
+
landing_page["links"].extend(
|
|
625
|
+
[
|
|
626
|
+
{
|
|
627
|
+
"rel": "aggregate",
|
|
628
|
+
"type": "application/json",
|
|
629
|
+
"title": "Aggregate",
|
|
630
|
+
"href": urljoin(base_url, "aggregate"),
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"rel": "aggregations",
|
|
634
|
+
"type": "application/json",
|
|
635
|
+
"title": "Aggregations",
|
|
636
|
+
"href": urljoin(base_url, "aggregations"),
|
|
637
|
+
},
|
|
638
|
+
]
|
|
639
|
+
)
|
|
640
|
+
|
|
597
641
|
# Add Collections links
|
|
598
642
|
collections = await self.all_collections(request=kwargs["request"])
|
|
643
|
+
|
|
599
644
|
for collection in collections["collections"]:
|
|
600
645
|
landing_page["links"].append(
|
|
601
646
|
{
|
|
@@ -609,26 +654,26 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
609
654
|
# Add OpenAPI URL
|
|
610
655
|
landing_page["links"].append(
|
|
611
656
|
{
|
|
612
|
-
"rel":
|
|
613
|
-
"type":
|
|
657
|
+
"rel": Relations.service_desc.value,
|
|
658
|
+
"type": MimeTypes.openapi.value,
|
|
614
659
|
"title": "OpenAPI service description",
|
|
615
|
-
"href":
|
|
660
|
+
"href": str(request.url_for("openapi")),
|
|
616
661
|
}
|
|
617
662
|
)
|
|
618
663
|
|
|
619
664
|
# Add human readable service-doc
|
|
620
665
|
landing_page["links"].append(
|
|
621
666
|
{
|
|
622
|
-
"rel":
|
|
623
|
-
"type":
|
|
667
|
+
"rel": Relations.service_doc.value,
|
|
668
|
+
"type": MimeTypes.html.value,
|
|
624
669
|
"title": "OpenAPI service documentation",
|
|
625
|
-
"href":
|
|
670
|
+
"href": str(request.url_for("swagger_ui_html")),
|
|
626
671
|
}
|
|
627
672
|
)
|
|
628
673
|
|
|
629
|
-
return landing_page
|
|
674
|
+
return stac.LandingPage(**landing_page)
|
|
630
675
|
|
|
631
|
-
async def conformance(self, **kwargs) ->
|
|
676
|
+
async def conformance(self, **kwargs) -> stac.Conformance:
|
|
632
677
|
"""Conformance classes.
|
|
633
678
|
|
|
634
679
|
Called with `GET /conformance`.
|
|
@@ -636,12 +681,12 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
636
681
|
Returns:
|
|
637
682
|
Conformance classes which the server conforms to.
|
|
638
683
|
"""
|
|
639
|
-
return Conformance(conformsTo=self.conformance_classes())
|
|
684
|
+
return stac.Conformance(conformsTo=self.conformance_classes())
|
|
640
685
|
|
|
641
686
|
@abc.abstractmethod
|
|
642
687
|
async def post_search(
|
|
643
688
|
self, search_request: BaseSearchPostRequest, **kwargs
|
|
644
|
-
) ->
|
|
689
|
+
) -> stac.ItemCollection:
|
|
645
690
|
"""Cross catalog search (POST).
|
|
646
691
|
|
|
647
692
|
Called with `POST /search`.
|
|
@@ -660,15 +705,11 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
660
705
|
collections: Optional[List[str]] = None,
|
|
661
706
|
ids: Optional[List[str]] = None,
|
|
662
707
|
bbox: Optional[BBox] = None,
|
|
708
|
+
intersects: Optional[Geometry] = None,
|
|
663
709
|
datetime: Optional[DateTimeType] = None,
|
|
664
710
|
limit: Optional[int] = 10,
|
|
665
|
-
query: Optional[str] = None,
|
|
666
|
-
token: Optional[str] = None,
|
|
667
|
-
fields: Optional[List[str]] = None,
|
|
668
|
-
sortby: Optional[str] = None,
|
|
669
|
-
intersects: Optional[str] = None,
|
|
670
711
|
**kwargs,
|
|
671
|
-
) ->
|
|
712
|
+
) -> stac.ItemCollection:
|
|
672
713
|
"""Cross catalog search (GET).
|
|
673
714
|
|
|
674
715
|
Called with `GET /search`.
|
|
@@ -679,9 +720,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
679
720
|
...
|
|
680
721
|
|
|
681
722
|
@abc.abstractmethod
|
|
682
|
-
async def get_item(
|
|
683
|
-
self, item_id: str, collection_id: str, **kwargs
|
|
684
|
-
) -> stac_types.Item:
|
|
723
|
+
async def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
|
|
685
724
|
"""Get item by id.
|
|
686
725
|
|
|
687
726
|
Called with `GET /collections/{collection_id}/items/{item_id}`.
|
|
@@ -696,7 +735,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
696
735
|
...
|
|
697
736
|
|
|
698
737
|
@abc.abstractmethod
|
|
699
|
-
async def all_collections(self, **kwargs) ->
|
|
738
|
+
async def all_collections(self, **kwargs) -> stac.Collections:
|
|
700
739
|
"""Get all available collections.
|
|
701
740
|
|
|
702
741
|
Called with `GET /collections`.
|
|
@@ -707,9 +746,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
707
746
|
...
|
|
708
747
|
|
|
709
748
|
@abc.abstractmethod
|
|
710
|
-
async def get_collection(
|
|
711
|
-
self, collection_id: str, **kwargs
|
|
712
|
-
) -> stac_types.Collection:
|
|
749
|
+
async def get_collection(self, collection_id: str, **kwargs) -> stac.Collection:
|
|
713
750
|
"""Get collection by id.
|
|
714
751
|
|
|
715
752
|
Called with `GET /collections/{collection_id}`.
|
|
@@ -731,7 +768,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
731
768
|
limit: int = 10,
|
|
732
769
|
token: str = None,
|
|
733
770
|
**kwargs,
|
|
734
|
-
) ->
|
|
771
|
+
) -> stac.ItemCollection:
|
|
735
772
|
"""Get all items from a specific collection.
|
|
736
773
|
|
|
737
774
|
Called with `GET /collections/{collection_id}/items`
|
|
@@ -745,55 +782,3 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
745
782
|
An ItemCollection.
|
|
746
783
|
"""
|
|
747
784
|
...
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
@attr.s
|
|
751
|
-
class AsyncBaseFiltersClient(abc.ABC):
|
|
752
|
-
"""Defines a pattern for implementing the STAC filter extension."""
|
|
753
|
-
|
|
754
|
-
async def get_queryables(
|
|
755
|
-
self, collection_id: Optional[str] = None, **kwargs
|
|
756
|
-
) -> Dict[str, Any]:
|
|
757
|
-
"""Get the queryables available for the given collection_id.
|
|
758
|
-
|
|
759
|
-
If collection_id is None, returns the intersection of all queryables over all
|
|
760
|
-
collections.
|
|
761
|
-
|
|
762
|
-
This base implementation returns a blank queryable schema. This is not allowed
|
|
763
|
-
under OGC CQL but it is allowed by the STAC API Filter Extension
|
|
764
|
-
https://github.com/radiantearth/stac-api-spec/tree/master/fragments/filter#queryables
|
|
765
|
-
"""
|
|
766
|
-
return {
|
|
767
|
-
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
|
768
|
-
"$id": "https://example.org/queryables",
|
|
769
|
-
"type": "object",
|
|
770
|
-
"title": "Queryables for Example STAC API",
|
|
771
|
-
"description": "Queryable names for the example STAC API Item Search filter.",
|
|
772
|
-
"properties": {},
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
@attr.s
|
|
777
|
-
class BaseFiltersClient(abc.ABC):
|
|
778
|
-
"""Defines a pattern for implementing the STAC filter extension."""
|
|
779
|
-
|
|
780
|
-
def get_queryables(
|
|
781
|
-
self, collection_id: Optional[str] = None, **kwargs
|
|
782
|
-
) -> Dict[str, Any]:
|
|
783
|
-
"""Get the queryables available for the given collection_id.
|
|
784
|
-
|
|
785
|
-
If collection_id is None, returns the intersection of all queryables over all
|
|
786
|
-
collections.
|
|
787
|
-
|
|
788
|
-
This base implementation returns a blank queryable schema. This is not allowed
|
|
789
|
-
under OGC CQL but it is allowed by the STAC API Filter Extension
|
|
790
|
-
https://github.com/stac-api-extensions/filter#queryables
|
|
791
|
-
"""
|
|
792
|
-
return {
|
|
793
|
-
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
|
794
|
-
"$id": "https://example.org/queryables",
|
|
795
|
-
"type": "object",
|
|
796
|
-
"title": "Queryables for Example STAC API",
|
|
797
|
-
"description": "Queryable names for the example STAC API Item Search filter.",
|
|
798
|
-
"properties": {},
|
|
799
|
-
}
|