22
33from pathlib import Path
44import attr
5- from typing import Any , Dict , Optional , Union
5+ from typing import Any , Dict , List , Optional , Union
6+
7+ from pystac_client import Client
68
79from superstac .enums import CatalogOutputFormat
810from superstac .exceptions import (
@@ -29,7 +31,6 @@ def register_catalog(
2931 name : str ,
3032 url : str ,
3133 is_private : Optional [bool ] = False ,
32- summary : Optional [str ] = None ,
3334 auth : Optional [AuthInfo ] = None ,
3435 ) -> CatalogEntry :
3536 """Register a single STAC catalog in state.
@@ -48,54 +49,120 @@ def register_catalog(
4849 CatalogEntry: The registered STAC catalog.
4950 """
5051 logger .info (f"Registering catalog: { name } " )
51- logger .debug (
52- f"Params - url: { url } , is_private: { is_private } , summary: { summary } , auth: { auth } "
53- )
52+ logger .debug (f"Params - url: { url } , is_private: { is_private } , auth: { auth } " )
5453 if is_private and auth is None :
5554 logger .error (
5655 f"Private catalog '{ name } ' requires authentication but none was provided."
5756 )
5857 raise InvalidCatalogSchemaError (
5958 f"Authentication parameters is required for private catalogs. If this is a mistake, you can set 'is_private' to False or provide the { AuthInfo .__annotations__ } parameters."
6059 )
61-
62- logger .info (f"Catalog '{ name } ' registered successfully." )
63- self .catalogs [name ] = CatalogEntry (
60+ client = None
61+ try :
62+ # todo - add auth parameters and config...
63+ client = Client .open (url )
64+ metadata = {
65+ "client" : client ,
66+ "catalog" : client .get_root (),
67+ "is_available" : True ,
68+ }
69+ logger .info (f"Catalog '{ name } ' is reachable and valid." )
70+ except Exception as e :
71+ logger .warning (f"Catalog '{ name } ' could not be reached or parsed: { e } " )
72+ metadata = {
73+ "client" : None ,
74+ "catalog" : None ,
75+ "is_available" : False ,
76+ }
77+ entry = CatalogEntry (
6478 name = name ,
6579 url = url ,
66- summary = summary ,
6780 is_private = is_private ,
6881 auth = AuthInfo (** auth .__dict__ ) if auth and not is_private else None ,
82+ ** metadata ,
6983 )
70- return self .catalogs [name ]
84+ self .catalogs [name ] = entry
85+ logger .info (f"Catalog '{ name } ' registered successfully." )
86+ return entry
7187
72- def get_available_catalogs (
73- self , format : Union [str , CatalogOutputFormat ] = CatalogOutputFormat .DICT
88+ def get_catalogs (
89+ self ,
90+ format : Union [str , CatalogOutputFormat ] = CatalogOutputFormat .DICT ,
91+ available : bool = False ,
7492 ) -> list [Union [dict [str , Any ], str ]]:
75- """Get the available STAC catalogs.
93+ """Get the STAC catalogs.
94+
95+ Args:
96+ format (Union[str, CatalogOutputFormat]): Output format, dict or json string.
97+ available (bool): If True, return only available catalogs; else return all.
7698
7799 Raises:
78100 ValueError: When an invalid format is provided.
79101
80102 Returns:
81- list[CatalogEntry]: The list of all available STAC catalogs .
103+ list[CatalogEntry]: List of catalogs in the requested format .
82104 """
83- logger .info ("Retrieving available catalogs." )
105+ logger .info (f "Retrieving { ' available' if available else 'all' } catalogs." )
84106 if isinstance (format , str ):
85107 try :
86108 format = CatalogOutputFormat (format .lower ())
87109 except ValueError :
88110 logger .error (f"Invalid output format: { format } " )
89111 raise ValueError (f"Invalid format: { format } " )
90112
91- available = [
113+ catalogs = [
92114 c .as_dict () if format == CatalogOutputFormat .DICT else c .as_json ()
93115 for c in self .catalogs .values ()
94- if c .is_available
116+ if ( c .is_available if available else True )
95117 ]
96118
97- logger .info (f"{ len (available )} catalogs available in format '{ format .value } '." )
98- return available
119+ logger .info (f"{ len (catalogs )} catalogs retrieved in format '{ format .value } '." )
120+ return catalogs
121+
122+ def get_all_collections (self , available : bool = False ) -> Dict [str , List [str ]]:
123+ """
124+ Returns a dictionary mapping catalog names to a list of collection IDs
125+ available in each catalog.
126+
127+ Args:
128+ available (bool): If True, only include catalogs that are available.
129+
130+ Returns:
131+ Dict[str, List[str]]: Catalog name -> list of collection IDs
132+ """
133+ logger .info (f"Getting collections with available={ available } " )
134+ collections = {}
135+ for name , entry in self .catalogs .items ():
136+ if (entry .is_available if available else True ) and entry .catalog :
137+ logger .debug (
138+ f"Processing catalog '{ name } ' (available={ entry .is_available } )"
139+ )
140+ try :
141+ all_collections = entry .catalog .get_all_collections ()
142+ if all_collections :
143+ collection_ids = []
144+ for c in all_collections :
145+ if hasattr (c , "id" ):
146+ collection_ids .append (c .id )
147+ elif isinstance (c , dict ) and "id" in c :
148+ collection_ids .append (c ["id" ])
149+ else :
150+ collection_ids .append (str (c ))
151+ collections [name ] = collection_ids
152+ logger .info (
153+ f"Found { len (collection_ids )} collections in catalog '{ name } '"
154+ )
155+ else :
156+ logger .info (f"No collections found in catalog '{ name } '" )
157+ except Exception as e :
158+ logger .warning (
159+ f"Failed to get collections for catalog '{ name } ': { e } "
160+ )
161+ continue
162+ else :
163+ logger .debug (f"Skipping catalog '{ name } ' due to availability filter" )
164+ logger .info (f"Total catalogs with collections returned: { len (collections )} " )
165+ return collections
99166
100167 def load_catalogs_from_config (
101168 self , config : Union [str , Path , None ] = None
@@ -155,7 +222,6 @@ def load_catalogs_from_config(
155222 name = name ,
156223 url = spec .get ("url" ),
157224 is_private = spec .get ("is_private" , False ),
158- summary = spec .get ("summary" ),
159225 auth = AuthInfo (** spec ["auth" ]) if "auth" in spec else None ,
160226 )
161227 except Exception as e :
0 commit comments