Skip to content

Commit cc76952

Browse files
committed
update from main
1 parent e60590d commit cc76952

File tree

9 files changed

+731
-59
lines changed

9 files changed

+731
-59
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""eoapi.stac.api: custom StacAPI class."""
2+
3+
from typing import Type
4+
5+
import attr
6+
from stac_fastapi.api import app
7+
from stac_fastapi.api.models import APIRequest, GeoJSONResponse
8+
from stac_fastapi.api.routes import create_async_endpoint
9+
from stac_pydantic import api
10+
from stac_pydantic.api.collections import Collections
11+
from stac_pydantic.shared import MimeTypes
12+
13+
from .extensions import HTMLorJSONGetRequest
14+
15+
16+
@attr.s
17+
class StacApi(app.StacApi):
18+
"""Custom StacAPI."""
19+
20+
landing_get_model: Type[APIRequest] = attr.ib(default=HTMLorJSONGetRequest)
21+
conformance_get_model: Type[APIRequest] = attr.ib(default=HTMLorJSONGetRequest)
22+
23+
def register_landing_page(self):
24+
"""Register landing page (GET /).
25+
26+
Returns:
27+
None
28+
"""
29+
self.router.add_api_route(
30+
name="Landing Page",
31+
path="/",
32+
response_model=(
33+
api.LandingPage if self.settings.enable_response_models else None
34+
),
35+
responses={
36+
200: {
37+
"content": {
38+
MimeTypes.json.value: {},
39+
MimeTypes.html.value: {},
40+
},
41+
"model": api.LandingPage,
42+
},
43+
},
44+
response_class=self.response_class,
45+
response_model_exclude_unset=False,
46+
response_model_exclude_none=True,
47+
methods=["GET"],
48+
endpoint=create_async_endpoint(
49+
self.client.landing_page, self.landing_get_model
50+
),
51+
)
52+
53+
def register_conformance_classes(self):
54+
"""Register conformance classes (GET /conformance).
55+
56+
Returns:
57+
None
58+
"""
59+
self.router.add_api_route(
60+
name="Conformance Classes",
61+
path="/conformance",
62+
response_model=(
63+
api.Conformance if self.settings.enable_response_models else None
64+
),
65+
responses={
66+
200: {
67+
"content": {
68+
MimeTypes.json.value: {},
69+
MimeTypes.html.value: {},
70+
},
71+
"model": api.Conformance,
72+
},
73+
},
74+
response_class=self.response_class,
75+
response_model_exclude_unset=True,
76+
response_model_exclude_none=True,
77+
methods=["GET"],
78+
endpoint=create_async_endpoint(
79+
self.client.conformance, self.conformance_get_model
80+
),
81+
)
82+
83+
def register_get_collections(self):
84+
"""Register get collections endpoint (GET /collections).
85+
86+
Returns:
87+
None
88+
"""
89+
self.router.add_api_route(
90+
name="Get Collections",
91+
path="/collections",
92+
response_model=(
93+
Collections if self.settings.enable_response_models else None
94+
),
95+
responses={
96+
200: {
97+
"content": {
98+
MimeTypes.json.value: {},
99+
MimeTypes.html.value: {},
100+
},
101+
"model": Collections,
102+
},
103+
},
104+
response_class=self.response_class,
105+
response_model_exclude_unset=True,
106+
response_model_exclude_none=True,
107+
methods=["GET"],
108+
endpoint=create_async_endpoint(
109+
self.client.all_collections, self.collections_get_request_model
110+
),
111+
)
112+
113+
def register_get_collection(self):
114+
"""Register get collection endpoint (GET /collection/{collection_id}).
115+
116+
Returns:
117+
None
118+
"""
119+
self.router.add_api_route(
120+
name="Get Collection",
121+
path="/collections/{collection_id}",
122+
response_model=api.Collection
123+
if self.settings.enable_response_models
124+
else None,
125+
responses={
126+
200: {
127+
"content": {
128+
MimeTypes.json.value: {},
129+
MimeTypes.html.value: {},
130+
},
131+
"model": api.Collection,
132+
},
133+
},
134+
response_class=self.response_class,
135+
response_model_exclude_unset=True,
136+
response_model_exclude_none=True,
137+
methods=["GET"],
138+
endpoint=create_async_endpoint(
139+
self.client.get_collection, self.collection_get_request_model
140+
),
141+
)
142+
143+
def register_get_item_collection(self):
144+
"""Register get item collection endpoint (GET /collection/{collection_id}/items).
145+
146+
Returns:
147+
None
148+
"""
149+
self.router.add_api_route(
150+
name="Get ItemCollection",
151+
path="/collections/{collection_id}/items",
152+
response_model=(
153+
api.ItemCollection if self.settings.enable_response_models else None
154+
),
155+
responses={
156+
200: {
157+
"content": {
158+
MimeTypes.geojson.value: {},
159+
MimeTypes.html.value: {},
160+
},
161+
"model": api.ItemCollection,
162+
},
163+
},
164+
response_class=GeoJSONResponse,
165+
response_model_exclude_unset=True,
166+
response_model_exclude_none=True,
167+
methods=["GET"],
168+
endpoint=create_async_endpoint(
169+
self.client.item_collection, self.items_get_request_model
170+
),
171+
)
172+
173+
def register_get_search(self):
174+
"""Register search endpoint (GET /search).
175+
176+
Returns:
177+
None
178+
"""
179+
self.router.add_api_route(
180+
name="Search",
181+
path="/search",
182+
response_model=api.ItemCollection
183+
if self.settings.enable_response_models
184+
else None,
185+
responses={
186+
200: {
187+
"content": {
188+
MimeTypes.geojson.value: {},
189+
MimeTypes.html.value: {},
190+
},
191+
"model": api.ItemCollection,
192+
},
193+
},
194+
response_class=GeoJSONResponse,
195+
response_model_exclude_unset=True,
196+
response_model_exclude_none=True,
197+
methods=["GET"],
198+
endpoint=create_async_endpoint(
199+
self.client.get_search, self.search_get_request_model
200+
),
201+
)

runtimes/eoapi/stac/eoapi/stac/app.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
from eoapi.auth_utils import OpenIdConnectAuth, OpenIdConnectSettings
88
from fastapi import FastAPI
99
from fastapi.responses import ORJSONResponse
10-
from stac_fastapi.api.app import StacApi
1110
from stac_fastapi.api.models import (
11+
CollectionUri,
1212
ItemCollectionUri,
13+
ItemUri,
1314
create_get_request_model,
1415
create_post_request_model,
1516
create_request_model,
@@ -19,9 +20,7 @@
1920
CollectionSearchFilterExtension,
2021
FieldsExtension,
2122
FreeTextExtension,
22-
ItemCollectionFilterExtension,
2323
OffsetPaginationExtension,
24-
SearchFilterExtension,
2524
SortExtension,
2625
TokenPaginationExtension,
2726
)
@@ -31,7 +30,6 @@
3130
from stac_fastapi.extensions.core.sort import SortConformanceClasses
3231
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
3332
from stac_fastapi.pgstac.extensions import QueryExtension
34-
from stac_fastapi.pgstac.extensions.filter import FiltersClient
3533
from stac_fastapi.pgstac.types.search import PgstacSearch
3634
from starlette.middleware import Middleware
3735
from starlette.middleware.cors import CORSMiddleware
@@ -41,9 +39,16 @@
4139
from starlette_cramjam.middleware import CompressionMiddleware
4240

4341
from . import __version__ as eoapi_devseed_version
44-
from .client import PgSTACClient
42+
from .api import StacApi
43+
from .client import FiltersClient, PgSTACClient
4544
from .config import Settings
46-
from .extension import TiTilerExtension
45+
from .extensions import (
46+
HTMLorGeoOutputExtension,
47+
HTMLorJSONOutputExtension,
48+
ItemCollectionFilterExtension,
49+
SearchFilterExtension,
50+
TiTilerExtension,
51+
)
4752
from .logs import init_logging
4853

4954
jinja2_env = jinja2.Environment(
@@ -77,8 +82,9 @@
7782
QueryExtension(),
7883
SortExtension(),
7984
FieldsExtension(),
80-
SearchFilterExtension(client=FiltersClient()),
85+
SearchFilterExtension(client=FiltersClient()), # type: ignore
8186
TokenPaginationExtension(),
87+
HTMLorGeoOutputExtension(),
8288
]
8389

8490
# collection_search extensions
@@ -91,6 +97,7 @@
9197
conformance_classes=[FreeTextConformanceClasses.COLLECTIONS],
9298
),
9399
OffsetPaginationExtension(),
100+
HTMLorJSONOutputExtension(),
94101
]
95102

96103
# item_collection extensions
@@ -102,8 +109,9 @@
102109
conformance_classes=[SortConformanceClasses.ITEMS],
103110
),
104111
FieldsExtension(conformance_classes=[FieldsConformanceClasses.ITEMS]),
105-
ItemCollectionFilterExtension(client=FiltersClient()),
112+
ItemCollectionFilterExtension(client=FiltersClient()), # type: ignore
106113
TokenPaginationExtension(),
114+
HTMLorGeoOutputExtension(),
107115
]
108116

109117
# Request Models
@@ -128,6 +136,22 @@
128136
collections_get_model = collection_search_extension.GET
129137
application_extensions.append(collection_search_extension)
130138

139+
# /collections/{collectionId} model
140+
collection_get_model = create_request_model(
141+
model_name="CollectionUri",
142+
base_model=CollectionUri,
143+
extensions=[HTMLorJSONOutputExtension()],
144+
request_type="GET",
145+
)
146+
147+
# /collections/{collectionId}/items/itemId model
148+
item_get_model = create_request_model(
149+
model_name="ItemUri",
150+
base_model=ItemUri,
151+
extensions=[HTMLorGeoOutputExtension()],
152+
request_type="GET",
153+
)
154+
131155

132156
@asynccontextmanager
133157
async def lifespan(app: FastAPI):
@@ -173,10 +197,12 @@ async def lifespan(app: FastAPI):
173197
description=settings.stac_fastapi_description,
174198
pgstac_search_model=search_post_model,
175199
),
200+
item_get_request_model=item_get_model,
176201
items_get_request_model=items_get_model,
202+
collection_get_request_model=collection_get_model,
203+
collections_get_request_model=collections_get_model,
177204
search_get_request_model=search_get_model,
178205
search_post_request_model=search_post_model,
179-
collections_get_request_model=collections_get_model,
180206
response_class=ORJSONResponse,
181207
middlewares=middlewares,
182208
)

0 commit comments

Comments
 (0)