1414from stac_fastapi .types import stac as stac_types
1515from stac_fastapi .types .conformance import BASE_CONFORMANCE_CLASSES
1616from stac_fastapi .types .extension import ApiExtension
17+ from stac_fastapi .types .hierarchy import (
18+ BrowsableNode ,
19+ CatalogNode ,
20+ CollectionNode ,
21+ browsable_catalog ,
22+ browsable_child_link ,
23+ browsable_item_link ,
24+ )
1725from stac_fastapi .types .search import BaseSearchPostRequest
1826from stac_fastapi .types .stac import Conformance
1927
@@ -302,6 +310,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
302310 )
303311 extensions : List [ApiExtension ] = attr .ib (default = attr .Factory (list ))
304312 post_request_model = attr .ib (default = BaseSearchPostRequest )
313+ hierarchy_definition : Optional [BrowsableNode ] = attr .ib (default = None )
305314
306315 def conformance_classes (self ) -> List [str ]:
307316 """Generate conformance classes by adding extension conformance to base conformance classes."""
@@ -360,6 +369,20 @@ def landing_page(self, **kwargs) -> stac_types.LandingPage:
360369 }
361370 )
362371
372+ # Add links for browsable conformance
373+ if self .hierarchy_definition is not None :
374+ for child in self .hierarchy_definition ["children" ]:
375+ if isinstance (child , CollectionNode ):
376+ landing_page ["links" ].append (
377+ browsable_child_link (child , urljoin (base_url , "collections" ))
378+ )
379+ if isinstance (child , CatalogNode ):
380+ landing_page ["links" ].append (
381+ browsable_child_link (child , urljoin (base_url , "catalogs" ))
382+ )
383+ for item in self .hierarchy_definition ["items" ]:
384+ landing_page ["links" ].append (browsable_item_link (item , base_url ))
385+
363386 # Add OpenAPI URL
364387 landing_page ["links" ].append (
365388 {
@@ -487,6 +510,24 @@ def get_collection_children(
487510 """
488511 ...
489512
513+ def get_catalog (self , catalog_path : str , ** kwargs ) -> stac_types .Catalog :
514+ """Get collection by id.
515+
516+ Called with `GET /catalogs/{catalog_path}`.
517+
518+ Args:
519+ catalog_path: The full path of the catalog in the browsable hierarchy.
520+
521+ Returns:
522+ Catalog.
523+ """
524+ split_path = catalog_path .split ("/" )
525+ remaining_hierarchy = self .hierarchy_definition
526+ for fork in split_path :
527+ remaining_hierarchy = remaining_hierarchy [fork ]
528+ print ("TEST" , remaining_hierarchy )
529+ return browsable_catalog (catalog_path )
530+
490531 @abc .abstractmethod
491532 def item_collection (
492533 self , collection_id : str , limit : int = 10 , token : str = None , ** kwargs
@@ -519,6 +560,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
519560 )
520561 extensions : List [ApiExtension ] = attr .ib (default = attr .Factory (list ))
521562 post_request_model = attr .ib (default = BaseSearchPostRequest )
563+ hierarchy_definition : Optional [BrowsableNode ] = attr .ib (default = None )
522564
523565 def conformance_classes (self ) -> List [str ]:
524566 """Generate conformance classes by adding extension conformance to base conformance classes."""
@@ -552,6 +594,8 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:
552594 conformance_classes = self .conformance_classes (),
553595 extension_schemas = extension_schemas ,
554596 )
597+
598+ # Add Collections links
555599 collections = await self .all_collections (request = kwargs ["request" ])
556600 for collection in collections ["collections" ]:
557601 landing_page ["links" ].append (
@@ -563,6 +607,20 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:
563607 }
564608 )
565609
610+ # Add links for browsable conformance
611+ if self .hierarchy_definition is not None :
612+ for child in self .hierarchy_definition ["children" ]:
613+ if "collection_id" in child :
614+ landing_page ["links" ].append (
615+ browsable_child_link (child , urljoin (base_url , "collections" ))
616+ )
617+ if "catalog_id" in child :
618+ landing_page ["links" ].append (
619+ browsable_child_link (child , urljoin (base_url , "catalogs" ))
620+ )
621+ for item in self .hierarchy_definition ["items" ]:
622+ landing_page ["links" ].append (browsable_item_link (item , base_url ))
623+
566624 # Add OpenAPI URL
567625 landing_page ["links" ].append (
568626 {
@@ -694,6 +752,29 @@ async def get_collection_children(
694752 """
695753 ...
696754
755+ async def get_catalog (self , catalog_path : str , ** kwargs ) -> stac_types .Catalog :
756+ """Get collection by id.
757+
758+ Called with `GET /catalogs/{catalog_path}`.
759+
760+ Args:
761+ catalog_path: The full path of the catalog in the browsable hierarchy.
762+
763+ Returns:
764+ Catalog.
765+ """
766+ request : Request = kwargs ["request" ]
767+ base_url = str (request .base_url )
768+ split_path = catalog_path .split ("/" )
769+ remaining_hierarchy = self .hierarchy_definition
770+ for fork in split_path :
771+ remaining_hierarchy = next (
772+ node
773+ for node in remaining_hierarchy ["children" ]
774+ if node ["catalog_id" ] == fork
775+ )
776+ return browsable_catalog (remaining_hierarchy , base_url ).dict (exclude_unset = True )
777+
697778 @abc .abstractmethod
698779 async def item_collection (
699780 self , collection_id : str , limit : int = 10 , token : str = None , ** kwargs
0 commit comments