@@ -95,10 +95,17 @@ class Context(Enum):
9595 ITEM_SEARCH = "Item Search"
9696 FEATURES = "Features"
9797 COLLECTIONS = "Collections"
98- ITEM_SEARCH_FILTER = "Item Search - Filter Ext"
99- FEATURES_FILTER = "Features - Filter Ext"
10098 CHILDREN = "Children Ext"
10199 BROWSEABLE = "Browseable Ext"
100+ ITEM_SEARCH_FILTER = "Item Search - Filter Ext"
101+ ITEM_SEARCH_SORT = "Item Search - Sort Ext"
102+ ITEM_SEARCH_FIELDS = "Item Search - Fields Ext"
103+ ITEM_SEARCH_QUERY = "Item Search - Query Ext"
104+ FEATURES_FILTER = "Features - Filter Ext"
105+ FEATURES_SORT = "Features - Sort Ext"
106+ FEATURES_FIELDS = "Features - Fields Ext"
107+ FEATURES_QUERY = "Features - Query Ext"
108+ FEATURES_TXN = "Features - Transaction Ext"
102109
103110 def __str__ (self ) -> str :
104111 return self .value
@@ -148,19 +155,14 @@ def __iadd__(self, x: Union[Tuple[str, str], str]) -> "Warnings":
148155cc_core_regex = re .compile (r"https://api\.stacspec\.org/(.+)/core" )
149156cc_browseable_regex = re .compile (r"https://api\.stacspec\.org/(.+)/browseable" )
150157cc_children_regex = re .compile (r"https://api\.stacspec\.org/(.+)/children" )
151-
152158cc_collections_regex = re .compile (r"https://api\.stacspec\.org/(.+)/collections" )
153-
154159cc_features_regex = re .compile (r"https://api\.stacspec\.org/(.+)/ogcapi-features" )
155160cc_features_transaction_regex = re .compile (
156161 r"https://api\.stacspec\.org/(.+)/ogcapi-features/extensions/transaction"
157162)
158163cc_features_fields_regex = re .compile (
159164 r"https://api\.stacspec\.org/(.+)/ogcapi-features#fields"
160165)
161- cc_features_context_regex = re .compile (
162- r"https://api\.stacspec\.org/(.+)/ogcapi-features#context"
163- )
164166cc_features_sort_regex = re .compile (
165167 r"https://api\.stacspec\.org/(.+)/ogcapi-features#sort"
166168)
@@ -172,13 +174,9 @@ def __iadd__(self, x: Union[Tuple[str, str], str]) -> "Warnings":
172174)
173175
174176cc_item_search_regex = re .compile (r"https://api\.stacspec\.org/(.+)/item-search" )
175-
176177cc_item_search_fields_regex = re .compile (
177178 r"https://api\.stacspec\.org/(.+)/item-search#fields"
178179)
179- cc_item_search_context_regex = re .compile (
180- r"https://api\.stacspec\.org/(.+)/item-search#context"
181- )
182180cc_item_search_sort_regex = re .compile (
183181 r"https://api\.stacspec\.org/(.+)/item-search#sort"
184182)
@@ -333,13 +331,17 @@ def stac_check(
333331 context : Context ,
334332 method : Method = Method .GET ,
335333) -> None :
336- linter = Linter (url )
337- if not linter .valid_stac :
338- errors += (
339- f"[{ context } ] { method } { url } is not a valid STAC object: { linter .error_msg } "
340- )
341- if msgs := linter .best_practices_msg [1 :]: # first msg is a header
342- warnings += f"[{ context } ] { method } { url } has these stac-check recommendations: { '' .join (msgs )} "
334+ try :
335+ linter = Linter (url )
336+ if not linter .valid_stac :
337+ errors += f"[{ context } ] { method } { url } is not a valid STAC object: { linter .error_msg } "
338+ if msgs := linter .best_practices_msg [1 :]: # first msg is a header, so skip
339+ warnings += f"[{ context } ] { method } { url } has these stac-check recommendations: { ',' .join ([x .strip () for x in msgs ])} "
340+ except KeyError as e :
341+ # see https://github.com/stac-utils/stac-check/issues/104
342+ errors += f"[{ Context .CORE } ] Error running stac-check, probably because an item doesn't have a bbox defined, which is okay!: { e } "
343+ except Exception as e :
344+ errors += f"[{ Context .CORE } ] Error while running stac-check: { e } "
343345
344346
345347def retrieve (
@@ -423,7 +425,7 @@ def validate_core_landing_page_body(
423425 ):
424426 warnings += "STAC API Specification v1.0.0-rc.2 is the latest version, but API advertises an older version or older versions."
425427
426- if not any ( cc_core_regex . fullmatch ( x ) for x in conforms_to ):
428+ if not supports ( conforms_to , cc_core_regex ):
427429 errors += ("CORE-4" , "/: STAC API - Core not contained in 'conformsTo'" )
428430
429431 if "browseable" in conformance_classes and not any (
@@ -467,7 +469,7 @@ def validate_core_landing_page_body(
467469 return False
468470
469471 if "item-search" in conformance_classes :
470- if not any ( cc_item_search_regex . fullmatch ( x ) for x in conforms_to ):
472+ if not supports ( conforms_to , cc_item_search_regex ):
471473 errors += (
472474 "CORE-9" ,
473475 "/: Item Search configured for validation, but not contained in 'conformsTo'" ,
@@ -483,12 +485,20 @@ def validate_core_landing_page_body(
483485 )
484486 return False
485487
488+ if "children" in conformance_classes and not any (
489+ cc_children_regex .fullmatch (x ) for x in conforms_to
490+ ):
491+ errors += (
492+ "CORE-6" ,
493+ "/: Children configured for validation, but not contained in 'conformsTo'" ,
494+ )
495+
486496 return True
487497
488498
489499def validate_api (
490500 root_url : str ,
491- conformance_classes : List [str ],
501+ ccs_to_validate : List [str ],
492502 collection : Optional [str ],
493503 geometry : Optional [str ],
494504 auth_bearer_token : Optional [str ],
@@ -520,7 +530,7 @@ def validate_api(
520530 landing_page_headers ,
521531 errors ,
522532 warnings ,
523- conformance_classes ,
533+ ccs_to_validate ,
524534 collection ,
525535 geometry ,
526536 ):
@@ -529,21 +539,21 @@ def validate_api(
529539 logger .info ("Validating STAC API - Core conformance class." )
530540 validate_core (landing_page_body , errors , warnings , r_session )
531541
532- if "browseable" in conformance_classes :
542+ if "browseable" in ccs_to_validate :
533543 logger .info ("Validating STAC API - Browseable conformance class." )
534544 validate_browseable (landing_page_body , errors , warnings , r_session )
535545
536- if "children" in conformance_classes :
546+ if "children" in ccs_to_validate :
537547 logger .info ("Validating STAC API - Children conformance class." )
538548 validate_children (landing_page_body , errors , warnings , r_session )
539549
540- if "collections" in conformance_classes :
550+ if "collections" in ccs_to_validate :
541551 logger .info ("Validating STAC API - Collections conformance class." )
542552 validate_collections (landing_page_body , collection , errors , warnings , r_session )
543553
544554 conforms_to = landing_page_body .get ("conformsTo" , [])
545555
546- if "features" in conformance_classes :
556+ if "features" in ccs_to_validate :
547557 logger .info ("Validating STAC API - Features conformance class." )
548558 validate_collections (landing_page_body , collection , errors , warnings , r_session )
549559 validate_features (
@@ -556,7 +566,7 @@ def validate_api(
556566 r_session ,
557567 )
558568
559- if "item-search" in conformance_classes :
569+ if "item-search" in ccs_to_validate :
560570 logger .info ("Validating STAC API - Item Search conformance class." )
561571 validate_item_search (
562572 root_url = root_url ,
@@ -566,7 +576,7 @@ def validate_api(
566576 warnings = warnings ,
567577 errors = errors ,
568578 geometry = geometry , # type:ignore
569- conformance_classes = conformance_classes ,
579+ conformance_classes = ccs_to_validate ,
570580 r_session = r_session ,
571581 )
572582
@@ -685,6 +695,11 @@ def validate_core(
685695 f"[{ Context .CORE } ] Error while traversing Catalog child/item links to find Items: { e } "
686696 "This can be reproduced with 'list(pystac.Catalog.from_file(root_url).get_all_items())'"
687697 )
698+ except UnicodeEncodeError as e :
699+ # see https://github.com/jjrom/resto/issues/356#issuecomment-1443818163
700+ errors += f"[{ Context .CORE } ] Error while traversing Catalog, a non-ascii character is encoded incorrectly somewhere: { e } "
701+ except Exception as e :
702+ errors += f"[{ Context .CORE } ] Error while traversing Catalog with pystac: { e } "
688703
689704
690705def validate_browseable (
@@ -1101,24 +1116,25 @@ def validate_features(
11011116 r_session = r_session ,
11021117 )
11031118
1104- # Validate Extensions
1105- #
1106- # if any(cc_features_fields_regex.fullmatch(x) for x in conforms_to):
1107- # logger.info("STAC API - Features - Fields extension conformance class found.")
1108- #
1109- # if any(cc_features_context_regex.fullmatch(x) for x in conforms_to):
1110- # logger.info("STAC API - Features - Context extension conformance class found.")
1111- #
1112- # if any(cc_features_sort_regex.fullmatch(x) for x in conforms_to):
1113- # logger.info("STAC API - Features - Sort extension conformance class found.")
1114- #
1115- # if any(cc_features_query_regex.fullmatch(x) for x in conforms_to):
1116- # logger.info("STAC API - Features - Query extension conformance class found.")
1117- #
1118- # if any(cc_features_filter_regex.fullmatch(x) for x in conforms_to):
1119- # logger.info("STAC API - Features - Filter extension conformance class found.")
1120-
1121- if any (cc_features_filter_regex .fullmatch (x ) for x in conforms_to ):
1119+ if supports (conforms_to , cc_features_fields_regex ):
1120+ logger .info ("STAC API - Features - Fields extension conformance class found." )
1121+ logger .info ("STAC API - Features - Fields extension is not yet supported." )
1122+
1123+ if supports (conforms_to , cc_features_transaction_regex ):
1124+ logger .info (
1125+ "STAC API - Features - Transaction extension conformance class found."
1126+ )
1127+ logger .info ("STAC API - Features - Transaction extension is not yet supported." )
1128+
1129+ if supports (conforms_to , cc_features_sort_regex ):
1130+ logger .info ("STAC API - Features - Sort extension conformance class found." )
1131+ logger .info ("STAC API - Features - Sort extension is not yet supported." )
1132+
1133+ if supports (conforms_to , cc_features_query_regex ):
1134+ logger .info ("STAC API - Features - Query extension conformance class found." )
1135+ logger .info ("STAC API - Features - Query extension is not yet supported." )
1136+
1137+ if supports (conforms_to , cc_features_filter_regex ):
11221138 logger .info ("STAC API - Features - Filter Extension conformance class found." )
11231139 validate_features_filter (
11241140 root_body = root_body ,
@@ -1218,18 +1234,16 @@ def validate_item_search(
12181234 r_session = r_session ,
12191235 )
12201236
1221- # if any(cc_item_search_fields_regex.fullmatch(x) for x in conforms_to):
1222- # logger.info("STAC API - Item Search - Fields extension conformance class found.")
1223- #
1224- # if any(cc_item_search_context_regex.fullmatch(x) for x in conforms_to):
1225- # logger.info("STAC API - Item Search - Context extension conformance class found.")
1226- #
1227- # if any(cc_item_search_sort_regex.fullmatch(x) for x in conforms_to):
1228- # logger.info("STAC API - Item Search - Sort extension conformance class found.")
1229- #
1230- # if any(cc_item_search_query_regex.fullmatch(x) for x in conforms_to):
1231- # logger.info("STAC API - Item Search - Query extension conformance class found.")
1232- #
1237+ if supports (conforms_to , cc_item_search_fields_regex ):
1238+ logger .info (
1239+ "STAC API - Item Search - Fields extension conformance class found."
1240+ )
1241+
1242+ if supports (conforms_to , cc_item_search_sort_regex ):
1243+ logger .info ("STAC API - Item Search - Sort extension conformance class found." )
1244+
1245+ if supports (conforms_to , cc_item_search_query_regex ):
1246+ logger .info ("STAC API - Item Search - Query extension conformance class found." )
12331247
12341248 if any (
12351249 x .endswith ("item-search#filter:basic-cql" )
0 commit comments