Skip to content

Commit db339c7

Browse files
committed
Add full page for catalogs to support API functionality
1 parent 713ce06 commit db339c7

File tree

5 files changed

+134
-51
lines changed

5 files changed

+134
-51
lines changed

stac_fastapi/pgstac/stac_fastapi/pgstac/core.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from stac_fastapi.pgstac.types.search import PgstacSearch
1818
from stac_fastapi.types.core import AsyncBaseCoreClient
1919
from stac_fastapi.types.errors import InvalidQueryParameter, NotFoundError
20-
from stac_fastapi.types.hierarchy import browseable_catalog
20+
from stac_fastapi.types.hierarchy import browseable_catalog_page
2121
from stac_fastapi.types.stac import (
2222
Children,
2323
Collection,
@@ -117,9 +117,17 @@ async def get_root_children(self, **kwargs) -> Children:
117117
"""
118118
request: Request = kwargs["request"]
119119
base_url = str(request.base_url)
120+
extension_schemas = [
121+
schema.schema_href for schema in self.extensions if schema.schema_href
122+
]
120123
catalog_children = [
121-
browseable_catalog(child, base_url, child["catalog_id"]).dict(
122-
exclude_unset=True
124+
browseable_catalog_page(
125+
child,
126+
base_url,
127+
child["catalog_id"],
128+
self.stac_version,
129+
self.conformance_classes(),
130+
extension_schemas,
123131
)
124132
for child in self.hierarchy_definition["children"]
125133
if "catalog_id" in child

stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/core.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from stac_fastapi.types.config import Settings
2929
from stac_fastapi.types.core import BaseCoreClient
3030
from stac_fastapi.types.errors import NotFoundError
31-
from stac_fastapi.types.hierarchy import browseable_catalog
31+
from stac_fastapi.types.hierarchy import browseable_catalog_page
3232
from stac_fastapi.types.search import BaseSearchPostRequest
3333
from stac_fastapi.types.stac import (
3434
Children,
@@ -115,10 +115,18 @@ def get_root_children(self, **kwargs) -> Children:
115115
"""
116116
request: Request = kwargs["request"]
117117
base_url = str(request.base_url)
118+
extension_schemas = [
119+
schema.schema_href for schema in self.extensions if schema.schema_href
120+
]
118121
catalog_children = [
119-
browseable_catalog(child, base_url, child["catalog_id"]).dict(
120-
exclude_unset=True
121-
)
122+
browseable_catalog_page(
123+
child,
124+
base_url,
125+
child["catalog_id"],
126+
self.stac_version,
127+
self.conformance_classes(),
128+
extension_schemas,
129+
).dict(exclude_unset=True)
122130
for child in self.hierarchy_definition["children"]
123131
if "catalog_id" in child
124132
]

stac_fastapi/testdata/joplin/hierarchy.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
["joplin", "f7f164c9-cfdf-436d-a3f0-69864c38ba2a"],
1414
["joplin", "f734401c-2df0-4694-a353-cdd3ea760cdc"]
1515
]
16+
}, {
17+
"collection_id": "joplin",
18+
"children": [],
19+
"items": []
1620
}],
1721
"items": [
1822
["joplin", "fe916452-ba6f-4631-9154-c249924a122d"],

stac_fastapi/types/stac_fastapi/types/core.py

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from stac_fastapi.types.extension import ApiExtension
2121
from stac_fastapi.types.hierarchy import (
2222
BrowseableNode,
23-
browseable_catalog,
2423
browseable_catalog_link,
24+
browseable_catalog_page,
2525
browseable_collection_link,
2626
browseable_item_link,
2727
)
@@ -364,16 +364,16 @@ def landing_page(self, **kwargs) -> stac_types.LandingPage:
364364
)
365365

366366
# Add Collections links
367-
collections = self.all_collections(request=kwargs["request"])
368-
for collection in collections["collections"]:
369-
landing_page["links"].append(
370-
{
371-
"rel": Relations.child.value,
372-
"type": MimeTypes.json.value,
373-
"title": collection.get("title") or collection.get("id"),
374-
"href": urljoin(base_url, f"collections/{collection['id']}"),
375-
}
376-
)
367+
# collections = self.all_collections(request=kwargs["request"])
368+
# for collection in collections["collections"]:
369+
# landing_page["links"].append(
370+
# {
371+
# "rel": Relations.child.value,
372+
# "type": MimeTypes.json.value,
373+
# "title": collection.get("title") or collection.get("id"),
374+
# "href": urljoin(base_url, f"collections/{collection['id']}"),
375+
# }
376+
# )
377377

378378
# Add links for browseable and children conformance
379379
if self.hierarchy_definition is not None:
@@ -550,8 +550,18 @@ def get_catalog(self, catalog_path: str, **kwargs) -> stac_types.Catalog:
550550
for node in remaining_hierarchy["children"]
551551
if node["catalog_id"] == fork
552552
)
553-
return browseable_catalog(remaining_hierarchy, base_url, catalog_path).dict(
554-
exclude_unset=True
553+
554+
extension_schemas = [
555+
schema.schema_href for schema in self.extensions if schema.schema_href
556+
]
557+
558+
return browseable_catalog_page(
559+
remaining_hierarchy,
560+
base_url,
561+
catalog_path,
562+
self.stac_version,
563+
self.conformance_classes(),
564+
extension_schemas,
555565
)
556566

557567
@abc.abstractmethod
@@ -625,17 +635,17 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:
625635
extension_schemas=extension_schemas,
626636
)
627637

628-
# Add Collections links
629-
collections = await self.all_collections(request=kwargs["request"])
630-
for collection in collections["collections"]:
631-
landing_page["links"].append(
632-
{
633-
"rel": Relations.child.value,
634-
"type": MimeTypes.json.value,
635-
"title": collection.get("title") or collection.get("id"),
636-
"href": urljoin(base_url, f"collections/{collection['id']}"),
637-
}
638-
)
638+
# # Add Collections links
639+
# collections = await self.all_collections(request=kwargs["request"])
640+
# for collection in collections["collections"]:
641+
# landing_page["links"].append(
642+
# {
643+
# "rel": Relations.child.value,
644+
# "type": MimeTypes.json.value,
645+
# "title": collection.get("title") or collection.get("id"),
646+
# "href": urljoin(base_url, f"collections/{collection['id']}"),
647+
# }
648+
# )
639649

640650
# Add links for children and browseable conformance
641651
if self.hierarchy_definition is not None:
@@ -656,7 +666,9 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:
656666
)
657667
if "catalog_id" in child:
658668
landing_page["links"].append(
659-
browseable_catalog_link(child, base_url, child["catalog_id"])
669+
browseable_catalog_link(
670+
child, base_url, child["catalog_id"], ""
671+
)
660672
)
661673
for item in self.hierarchy_definition["items"]:
662674
landing_page["links"].append(browseable_item_link(item, base_url))
@@ -813,8 +825,18 @@ async def get_catalog(self, catalog_path: str, **kwargs) -> stac_types.Catalog:
813825
for node in remaining_hierarchy["children"]
814826
if node["catalog_id"] == fork
815827
)
816-
return browseable_catalog(remaining_hierarchy, base_url, catalog_path).dict(
817-
exclude_unset=True
828+
829+
extension_schemas = [
830+
schema.schema_href for schema in self.extensions if schema.schema_href
831+
]
832+
833+
return browseable_catalog_page(
834+
remaining_hierarchy,
835+
base_url,
836+
catalog_path,
837+
self.stac_version,
838+
self.conformance_classes(),
839+
extension_schemas,
818840
)
819841

820842
@abc.abstractmethod

stac_fastapi/types/stac_fastapi/types/hierarchy.py

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
from typing import List, Optional, Tuple, TypedDict, Union
1010
from urllib.parse import urljoin
1111

12-
from stac_pydantic import Catalog
1312
from stac_pydantic.links import Relations
1413
from stac_pydantic.shared import MimeTypes
15-
from stac_pydantic.version import STAC_VERSION
14+
15+
from stac_fastapi.types import stac as stac_types
1616

1717
ItemPath = Tuple[str, str]
18-
NodeType = str
1918

2019

2120
class BrowseableNode(TypedDict):
@@ -57,7 +56,7 @@ def browseable_collection_link(node: BrowseableNode, base_url: str) -> str:
5756
"rel": Relations.child.value,
5857
"type": MimeTypes.json,
5958
"title": node.get("title") or node.get("collection_id"),
60-
"href": urljoin([base_url, f"collections/{node['collection_id']}"]),
59+
"href": urljoin(base_url, f"collections/{node['collection_id']}"),
6160
}
6261

6362

@@ -70,22 +69,31 @@ def browseable_item_link(item_path: ItemPath, base_url: str):
7069
}
7170

7271

73-
def browseable_catalog(node: CatalogNode, base_url: str, catalog_path: str) -> Catalog:
74-
"""Generate a catalog based on a CatalogNode in a BrowseableNode tree."""
72+
def browseable_catalog_page(
73+
catalog_node: CatalogNode,
74+
base_url: str,
75+
catalog_path: str,
76+
stac_version: str,
77+
conformance_classes: List[str],
78+
extension_schemas: List[str],
79+
) -> stac_types.LandingPage:
80+
"""Generate a STAC API landing page/catalog."""
7581
catalog_links = [
7682
browseable_catalog_link(
7783
child, base_url, "/".join([catalog_path.strip("/"), child["catalog_id"]])
7884
)
79-
for child in node["children"]
85+
for child in catalog_node["children"]
8086
if "catalog_id" in child
8187
]
8288
collection_links = [
8389
browseable_collection_link(child, base_url)
84-
for child in node["children"]
90+
for child in catalog_node["children"]
8591
if "collection_id" in child
8692
]
8793
children_links = catalog_links + collection_links
88-
item_links = [browseable_item_link(item, base_url) for item in node["items"]]
94+
item_links = [
95+
browseable_item_link(item, base_url) for item in catalog_node["items"]
96+
]
8997

9098
split_catalog_path = catalog_path.split("/")
9199
if len(split_catalog_path) > 1:
@@ -96,26 +104,59 @@ def browseable_catalog(node: CatalogNode, base_url: str, catalog_path: str) -> C
96104
parent_href = base_url
97105

98106
standard_links = [
107+
{
108+
"rel": Relations.self.value,
109+
"type": MimeTypes.json,
110+
"href": urljoin(base_url, f"/catalogs/{catalog_path.strip('/')}"),
111+
},
99112
{
100113
"rel": Relations.root.value,
101114
"type": MimeTypes.json,
102115
"href": base_url,
103116
},
104117
{
105-
"rel": Relations.self.value,
118+
"rel": "data",
106119
"type": MimeTypes.json,
107-
"href": urljoin(base_url, f"/catalogs/{catalog_path.strip('/')}"),
120+
"href": urljoin(
121+
base_url, f"/catalogs/{catalog_path.strip('/')}/collections"
122+
),
123+
},
124+
{
125+
"rel": Relations.conformance.value,
126+
"type": MimeTypes.json,
127+
"title": "STAC/WFS3 conformance classes implemented by this api",
128+
"href": urljoin(
129+
base_url, f"/catalogs/{catalog_path.strip('/')}/conformance"
130+
),
131+
},
132+
{
133+
"rel": Relations.search.value,
134+
"type": MimeTypes.geojson,
135+
"title": "STAC search",
136+
"href": urljoin(base_url, f"/catalogs/{catalog_path.strip('/')}/search"),
137+
"method": "GET",
138+
},
139+
{
140+
"rel": Relations.search.value,
141+
"type": MimeTypes.json,
142+
"title": "STAC search",
143+
"href": urljoin(base_url, f"/catalogs/{catalog_path.strip('/')}/search"),
144+
"method": "POST",
108145
},
109146
{"rel": Relations.parent.value, "type": MimeTypes.json, "href": parent_href},
110147
]
111-
return Catalog(
148+
149+
catalog_page = stac_types.LandingPage(
112150
type="Catalog",
113-
id=node["catalog_id"],
114-
description=node.get("description")
115-
or f"Generated description for {node['catalog_id']}",
116-
stac_version=STAC_VERSION,
117-
links=children_links + item_links + standard_links,
151+
id=catalog_node["catalog_id"],
152+
title=catalog_node["catalog_id"],
153+
description=catalog_node["description"],
154+
stac_version=stac_version,
155+
conformsTo=conformance_classes,
156+
links=standard_links + children_links + item_links,
157+
stac_extensions=extension_schemas,
118158
)
159+
return catalog_page
119160

120161

121162
def parse_hierarchy(d: dict) -> BrowseableNode:

0 commit comments

Comments
 (0)