Skip to content

Commit d4e229d

Browse files
committed
update for stac-fastapi 5.0
1 parent c009e32 commit d4e229d

File tree

8 files changed

+71
-220
lines changed

8 files changed

+71
-220
lines changed

docker-compose.nginx.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3'
21
services:
32
nginx:
43
image: nginx

nginx.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ http {
1717
proxy_redirect off;
1818
}
1919
}
20-
}
20+
}

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
"orjson",
1111
"pydantic",
1212
"stac_pydantic==3.1.*",
13-
"stac-fastapi.api~=4.0",
14-
"stac-fastapi.extensions~=4.0",
15-
"stac-fastapi.types~=4.0",
13+
"stac-fastapi.api~=5.0",
14+
"stac-fastapi.extensions~=5.0",
15+
"stac-fastapi.types~=5.0",
1616
"asyncpg",
1717
"buildpg",
1818
"brotli_asgi",

stac_fastapi/pgstac/app.py

Lines changed: 32 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,30 @@
2020
create_post_request_model,
2121
create_request_model,
2222
)
23-
from stac_fastapi.api.openapi import update_openapi
2423
from stac_fastapi.extensions.core import (
24+
CollectionSearchExtension,
25+
CollectionSearchFilterExtension,
2526
FieldsExtension,
2627
FreeTextExtension,
28+
ItemCollectionFilterExtension,
2729
OffsetPaginationExtension,
30+
SearchFilterExtension,
2831
SortExtension,
2932
TokenPaginationExtension,
3033
TransactionExtension,
3134
)
32-
from stac_fastapi.extensions.core.collection_search import CollectionSearchExtension
33-
from stac_fastapi.extensions.core.collection_search.request import (
34-
BaseCollectionSearchGetRequest,
35-
)
35+
from stac_fastapi.extensions.core.fields import FieldsConformanceClasses
36+
from stac_fastapi.extensions.core.free_text import FreeTextConformanceClasses
37+
from stac_fastapi.extensions.core.query import QueryConformanceClasses
38+
from stac_fastapi.extensions.core.sort import SortConformanceClasses
3639
from stac_fastapi.extensions.third_party import BulkTransactionExtension
3740
from starlette.middleware import Middleware
3841

3942
from stac_fastapi.pgstac.config import Settings
4043
from stac_fastapi.pgstac.core import CoreCrudClient
4144
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
4245
from stac_fastapi.pgstac.extensions import QueryExtension
43-
from stac_fastapi.pgstac.extensions.filter import (
44-
CollectionSearchFilterExtension,
45-
FiltersClient,
46-
ItemCollectionFilterExtension,
47-
SearchFilterExtension,
48-
)
46+
from stac_fastapi.pgstac.extensions.filter import FiltersClient
4947
from stac_fastapi.pgstac.transactions import BulkTransactionsClient, TransactionsClient
5048
from stac_fastapi.pgstac.types.search import PgstacSearch
5149

@@ -72,41 +70,25 @@
7270

7371
# collection_search extensions
7472
cs_extensions_map = {
75-
"query": QueryExtension(
76-
conformance_classes=[
77-
"https://api.stacspec.org/v1.0.0-rc.1/collection-search#query"
78-
]
79-
),
80-
"sort": SortExtension(
81-
conformance_classes=[
82-
"https://api.stacspec.org/v1.0.0-rc.1/collection-search#sort"
83-
]
84-
),
85-
"fields": FieldsExtension(
86-
conformance_classes=[
87-
"https://api.stacspec.org/v1.0.0-rc.1/collection-search#fields"
88-
]
89-
),
73+
"query": QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]),
74+
"sort": SortExtension(conformance_classes=[SortConformanceClasses.COLLECTIONS]),
75+
"fields": FieldsExtension(conformance_classes=[FieldsConformanceClasses.COLLECTIONS]),
9076
"filter": CollectionSearchFilterExtension(client=FiltersClient()),
9177
"free_text": FreeTextExtension(
92-
conformance_classes=[
93-
"https://api.stacspec.org/v1.0.0-rc.1/collection-search#free-text",
94-
],
78+
conformance_classes=[FreeTextConformanceClasses.COLLECTIONS],
9579
),
9680
"pagination": OffsetPaginationExtension(),
9781
}
9882

9983
# item_collection extensions
10084
itm_col_extensions_map = {
10185
"query": QueryExtension(
102-
conformance_classes=["https://api.stacspec.org/v1.0.0/ogcapi-features#query"],
86+
conformance_classes=[QueryConformanceClasses.ITEMS],
10387
),
10488
"sort": SortExtension(
105-
conformance_classes=["https://api.stacspec.org/v1.0.0/ogcapi-features#sort"],
106-
),
107-
"fields": FieldsExtension(
108-
conformance_classes=["https://api.stacspec.org/v1.0.0/ogcapi-features#fields"],
89+
conformance_classes=[SortConformanceClasses.ITEMS],
10990
),
91+
"fields": FieldsExtension(conformance_classes=[FieldsConformanceClasses.ITEMS]),
11092
"filter": ItemCollectionFilterExtension(client=FiltersClient()),
11193
"pagination": TokenPaginationExtension(),
11294
}
@@ -160,26 +142,13 @@
160142
# /collections model
161143
collections_get_request_model = EmptyRequest
162144
if "collection_search" in enabled_extensions:
163-
mixins = []
164-
mixing_conformances = [
165-
"https://api.stacspec.org/v1.0.0-rc.1/collection-search",
166-
"http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/simple-query",
145+
cs_extensions = [
146+
extension
147+
for key, extension in cs_extensions_map.items()
148+
if key in enabled_extensions
167149
]
168-
for key, extension in cs_extensions_map.items():
169-
if key not in enabled_extensions:
170-
continue
171-
mixins.append(extension.GET)
172-
mixing_conformances.extend(extension.conformance_classes)
173-
174-
collections_get_request_model = create_request_model(
175-
model_name="CollectionsGetRequest",
176-
base_model=BaseCollectionSearchGetRequest,
177-
mixins=mixins,
178-
request_type="GET",
179-
)
180-
collection_search_extension = CollectionSearchExtension(
181-
GET=collections_get_request_model, conformance_classes=mixing_conformances
182-
)
150+
collection_search_extension = CollectionSearchExtension.from_extensions(cs_extensions)
151+
collections_get_request_model = collection_search_extension.GET
183152
application_extensions.append(collection_search_extension)
184153

185154

@@ -191,17 +160,17 @@ async def lifespan(app: FastAPI):
191160
await close_db_connection(app)
192161

193162

194-
fastapp = FastAPI(
195-
openapi_url=settings.openapi_url,
196-
docs_url=settings.docs_url,
197-
redoc_url=None,
198-
root_path=settings.root_path,
199-
lifespan=lifespan,
200-
)
201-
202-
203163
api = StacApi(
204-
app=update_openapi(fastapp),
164+
app=FastAPI(
165+
openapi_url=settings.openapi_url,
166+
docs_url=settings.docs_url,
167+
redoc_url=None,
168+
root_path=settings.root_path,
169+
title=settings.stac_fastapi_title,
170+
version=settings.stac_fastapi_version,
171+
description=settings.stac_fastapi_description,
172+
lifespan=lifespan,
173+
),
205174
settings=settings,
206175
extensions=application_extensions,
207176
client=CoreCrudClient(pgstac_search_model=post_request_model),

stac_fastapi/pgstac/core.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ async def all_collections( # noqa: C901
123123
collection_id=coll["id"], request=request
124124
).get_links(extra_links=coll.get("links"))
125125

126-
if self.extension_is_enabled("FilterExtension"):
126+
if self.extension_is_enabled(
127+
"FilterExtension"
128+
) or self.extension_is_enabled("ItemCollectionFilterExtension"):
127129
coll["links"].append(
128130
{
129131
"rel": Relations.queryables.value,
@@ -178,7 +180,9 @@ async def get_collection(
178180
collection_id=collection_id, request=request
179181
).get_links(extra_links=collection.get("links"))
180182

181-
if self.extension_is_enabled("FilterExtension"):
183+
if self.extension_is_enabled("FilterExtension") or self.extension_is_enabled(
184+
"ItemCollectionFilterExtension"
185+
):
182186
base_url = get_base_url(request)
183187
collection["links"].append(
184188
{
Lines changed: 2 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
"""Get Queryables."""
22

3-
from typing import Any, Dict, List, Optional, Type
3+
from typing import Any, Dict, Optional
44

5-
import attr
65
from buildpg import render
7-
from fastapi import APIRouter, FastAPI, Request
8-
from stac_fastapi.api.models import CollectionUri, EmptyRequest, JSONSchemaResponse
9-
from stac_fastapi.api.routes import create_async_endpoint
10-
from stac_fastapi.extensions.core import FilterExtension
11-
from stac_fastapi.extensions.core.collection_search.collection_search import (
12-
ConformanceClasses as CollectionSearchConformanceClasses,
13-
)
6+
from fastapi import Request
147
from stac_fastapi.extensions.core.filter.client import AsyncBaseFiltersClient
15-
from stac_fastapi.extensions.core.filter.filter import FilterConformanceClasses
16-
from stac_fastapi.extensions.core.filter.request import (
17-
FilterExtensionGetRequest,
18-
FilterExtensionPostRequest,
19-
)
208
from stac_fastapi.types.errors import NotFoundError
21-
from starlette.responses import Response
229

2310

2411
class FiltersClient(AsyncBaseFiltersClient):
@@ -53,124 +40,3 @@ async def get_queryables(
5340

5441
queryables["$id"] = str(request.url)
5542
return queryables
56-
57-
58-
@attr.s
59-
class SearchFilterExtension(FilterExtension):
60-
"""Item Search Filter Extension."""
61-
62-
GET = FilterExtensionGetRequest
63-
POST = FilterExtensionPostRequest
64-
65-
client: FiltersClient = attr.ib(factory=FiltersClient)
66-
conformance_classes: List[str] = attr.ib(
67-
default=[
68-
FilterConformanceClasses.FILTER,
69-
FilterConformanceClasses.ITEM_SEARCH_FILTER,
70-
FilterConformanceClasses.BASIC_CQL2,
71-
FilterConformanceClasses.CQL2_JSON,
72-
FilterConformanceClasses.CQL2_TEXT,
73-
]
74-
)
75-
router: APIRouter = attr.ib(factory=APIRouter)
76-
response_class: Type[Response] = attr.ib(default=JSONSchemaResponse)
77-
78-
def register(self, app: FastAPI) -> None:
79-
"""Register the extension with a FastAPI application.
80-
81-
Args:
82-
app: target FastAPI application.
83-
84-
Returns:
85-
None
86-
"""
87-
self.router.prefix = app.state.router_prefix
88-
self.router.add_api_route(
89-
name="Queryables",
90-
path="/queryables",
91-
methods=["GET"],
92-
responses={
93-
200: {
94-
"content": {
95-
"application/schema+json": {},
96-
},
97-
# TODO: add output model in stac-pydantic
98-
},
99-
},
100-
response_class=self.response_class,
101-
endpoint=create_async_endpoint(self.client.get_queryables, EmptyRequest),
102-
)
103-
app.include_router(self.router, tags=["Filter Extension"])
104-
105-
106-
@attr.s
107-
class ItemCollectionFilterExtension(FilterExtension):
108-
"""Item Collection Filter Extension."""
109-
110-
GET = FilterExtensionGetRequest
111-
POST = FilterExtensionPostRequest
112-
113-
client: FiltersClient = attr.ib(factory=FiltersClient)
114-
conformance_classes: List[str] = attr.ib(
115-
default=[
116-
FilterConformanceClasses.FILTER,
117-
FilterConformanceClasses.FEATURES_FILTER,
118-
FilterConformanceClasses.BASIC_CQL2,
119-
FilterConformanceClasses.CQL2_JSON,
120-
FilterConformanceClasses.CQL2_TEXT,
121-
]
122-
)
123-
router: APIRouter = attr.ib(factory=APIRouter)
124-
response_class: Type[Response] = attr.ib(default=JSONSchemaResponse)
125-
126-
def register(self, app: FastAPI) -> None:
127-
"""Register the extension with a FastAPI application.
128-
129-
Args:
130-
app: target FastAPI application.
131-
132-
Returns:
133-
None
134-
"""
135-
self.router.add_api_route(
136-
name="Collection Queryables",
137-
path="/collections/{collection_id}/queryables",
138-
methods=["GET"],
139-
responses={
140-
200: {
141-
"content": {
142-
"application/schema+json": {},
143-
},
144-
# TODO: add output model in stac-pydantic
145-
},
146-
},
147-
response_class=self.response_class,
148-
endpoint=create_async_endpoint(self.client.get_queryables, CollectionUri),
149-
)
150-
app.include_router(self.router, tags=["Filter Extension"])
151-
152-
153-
@attr.s
154-
class CollectionSearchFilterExtension(FilterExtension):
155-
"""Collection Search Filter Extension."""
156-
157-
GET = FilterExtensionGetRequest
158-
POST = FilterExtensionPostRequest
159-
160-
client: FiltersClient = attr.ib(factory=FiltersClient)
161-
conformance_classes: List[str] = attr.ib(
162-
default=[CollectionSearchConformanceClasses.FILTER]
163-
)
164-
router: APIRouter = attr.ib(factory=APIRouter)
165-
response_class: Type[Response] = attr.ib(default=JSONSchemaResponse)
166-
167-
def register(self, app: FastAPI) -> None:
168-
"""Register the extension with a FastAPI application.
169-
170-
Args:
171-
app: target FastAPI application.
172-
173-
Returns:
174-
None
175-
"""
176-
pass

tests/api/test_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
FieldsExtension,
1818
TransactionExtension,
1919
)
20+
from stac_fastapi.extensions.core.fields import FieldsConformanceClasses
2021
from stac_fastapi.types import stac as stac_types
2122

2223
from stac_fastapi.pgstac.core import CoreCrudClient, Settings
@@ -733,7 +734,7 @@ async def get_collection(
733734

734735
collection_search_extension = CollectionSearchExtension.from_extensions(
735736
extensions=[
736-
FieldsExtension(),
737+
FieldsExtension(conformance_classes=[FieldsConformanceClasses.COLLECTIONS]),
737738
]
738739
)
739740

0 commit comments

Comments
 (0)