Skip to content

Commit 959383c

Browse files
authored
Configure whether to open assets URLs during validation, and http headers (#595)
1 parent bde0203 commit 959383c

File tree

2 files changed

+117
-17
lines changed

2 files changed

+117
-17
lines changed

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ jsonschema = "^4.16.0"
2424
PyYAML = "^6.0.1"
2525
Shapely = ">=1.8.4"
2626
more_itertools = ">=8.14,<11.0"
27-
stac-check = "^1.3.1"
28-
stac-validator = "^3.2.0"
27+
stac-check = "^1.5.0"
28+
stac-validator = "^3.5.0"
2929
deepdiff = ">=6.2.3,<9.0.0"
3030

3131
[tool.poetry.dev-dependencies]

src/stac_api_validator/validations.py

Lines changed: 115 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ def stac_validate(
331331
errors: Errors,
332332
context: Context,
333333
method: Method = Method.GET,
334+
open_assets_urls: bool = True,
335+
headers: Optional[dict] = None,
334336
) -> None:
335337
if not body:
336338
errors += f"[{context}] : {method} {url} body was empty when running stac-validate and stac-check"
@@ -350,8 +352,14 @@ def stac_validate(
350352
errors += f"[{context}] : {method} {url} '{body.get('id')}' failed pystac hydration: {e}"
351353

352354
if _type in ["Collection", "Feature"]:
355+
logger.debug(f"stac-validator validation: {url}")
353356
if not (
354-
stac_validator := StacValidate(links=True, assets=True)
357+
stac_validator := StacValidate(
358+
links=True,
359+
assets=True,
360+
assets_open_urls=open_assets_urls,
361+
headers=headers or {},
362+
)
355363
).validate_dict(body):
356364
errors += f"[{context}] : {method} {url} failed stac-validator validation: {stac_validator.message}"
357365

@@ -365,9 +373,12 @@ def stac_check(
365373
warnings: Warnings,
366374
context: Context,
367375
method: Method = Method.GET,
376+
open_assets_urls: bool = True,
377+
headers: Optional[dict] = None,
368378
) -> None:
369379
try:
370-
linter = Linter(url)
380+
logger.debug(f"stac-check validation: {url}")
381+
linter = Linter(url, assets_open_urls=open_assets_urls, headers=headers or {})
371382
if not linter.valid_stac:
372383
errors += f"[{context}] : {method} {url} is not a valid STAC object: {linter.error_msg}"
373384
if msgs := linter.best_practices_msg[1:]: # first msg is a header, so skip
@@ -548,6 +559,7 @@ def validate_api(
548559
query_config: QueryConfig,
549560
transaction_collection: Optional[str],
550561
headers: Optional[Dict[str, str]],
562+
open_assets_urls: bool = True,
551563
) -> Tuple[Warnings, Errors]:
552564
warnings = Warnings()
553565
errors = Errors()
@@ -598,13 +610,17 @@ def validate_api(
598610

599611
if "collections" in ccs_to_validate:
600612
logger.info("Validating STAC API - Collections conformance class.")
601-
validate_collections(landing_page_body, collection, errors, warnings, r_session)
613+
validate_collections(
614+
landing_page_body, collection, errors, warnings, r_session, open_assets_urls
615+
)
602616

603617
conforms_to = landing_page_body.get("conformsTo", [])
604618

605619
if "features" in ccs_to_validate:
606620
logger.info("Validating STAC API - Features conformance class.")
607-
validate_collections(landing_page_body, collection, errors, warnings, r_session)
621+
validate_collections(
622+
landing_page_body, collection, errors, warnings, r_session, open_assets_urls
623+
)
608624
validate_features(
609625
landing_page_body,
610626
conforms_to,
@@ -613,7 +629,8 @@ def validate_api(
613629
warnings,
614630
errors,
615631
r_session,
616-
validate_pagination=validate_pagination,
632+
validate_pagination,
633+
open_assets_urls,
617634
)
618635

619636
if "transaction" in ccs_to_validate:
@@ -664,6 +681,7 @@ def validate_api(
664681
conformance_classes=ccs_to_validate,
665682
r_session=r_session,
666683
validate_pagination=validate_pagination,
684+
open_assets_urls=open_assets_urls,
667685
)
668686

669687
if "item-search#fields" in ccs_to_validate:
@@ -963,6 +981,7 @@ def validate_collections(
963981
errors: Errors,
964982
warnings: Warnings,
965983
r_session: Session,
984+
open_assets_urls: bool = True,
966985
) -> None:
967986
if not (data_link := link_by_rel(root_body["links"], "data")):
968987
errors += f"[{Context.COLLECTIONS}] /: Link[rel=data] must href /collections"
@@ -1019,7 +1038,15 @@ def validate_collections(
10191038
)
10201039
else:
10211040
for body in collections_list:
1022-
stac_validate(collections_url, body, errors, Context.COLLECTIONS)
1041+
stac_validate(
1042+
collections_url,
1043+
body,
1044+
errors,
1045+
Context.COLLECTIONS,
1046+
Method.GET,
1047+
open_assets_urls,
1048+
r_session.headers,
1049+
)
10231050

10241051
collection_url = f"{data_link['href']}/{collection}"
10251052
_, body, resp_headers = retrieve(
@@ -1047,8 +1074,24 @@ def validate_collections(
10471074
if not link_by_rel(body.get("links", []), "parent"):
10481075
errors += f"[{Context.COLLECTIONS}] : {collection_url} does not have parent link"
10491076

1050-
stac_validate(collection_url, body, errors, Context.COLLECTIONS)
1051-
stac_check(collection_url, errors, warnings, Context.COLLECTIONS)
1077+
stac_validate(
1078+
collection_url,
1079+
body,
1080+
errors,
1081+
Context.COLLECTIONS,
1082+
Method.GET,
1083+
open_assets_urls,
1084+
r_session.headers,
1085+
)
1086+
stac_check(
1087+
collection_url,
1088+
errors,
1089+
warnings,
1090+
Context.COLLECTIONS,
1091+
Method.GET,
1092+
open_assets_urls,
1093+
r_session.headers,
1094+
)
10521095

10531096
# todo: collection pagination
10541097

@@ -1062,6 +1105,7 @@ def validate_features(
10621105
errors: Errors,
10631106
r_session: Session,
10641107
validate_pagination: bool,
1108+
open_assets_urls: bool = True,
10651109
) -> None:
10661110
if not geometry:
10671111
errors += f"[{Context.FEATURES}] Geometry parameter required for running Features validations."
@@ -1132,7 +1176,15 @@ def validate_features(
11321176
if not body:
11331177
errors += f"[{Context.FEATURES}] GET {collection_items_url} returned an empty body"
11341178
else:
1135-
stac_validate(collection_items_url, body, errors, Context.FEATURES)
1179+
stac_validate(
1180+
collection_items_url,
1181+
body,
1182+
errors,
1183+
Context.FEATURES,
1184+
Method.GET,
1185+
open_assets_urls,
1186+
r_session.headers,
1187+
)
11361188

11371189
item_url = link_by_rel(body.get("features", [])[0]["links"], "self")[
11381190
"href"
@@ -1147,8 +1199,24 @@ def validate_features(
11471199
r_session=r_session,
11481200
)
11491201

1150-
stac_validate(item_url, body, errors, Context.FEATURES)
1151-
stac_check(item_url, errors, warnings, Context.FEATURES)
1202+
stac_validate(
1203+
item_url,
1204+
body,
1205+
errors,
1206+
Context.FEATURES,
1207+
Method.GET,
1208+
open_assets_urls,
1209+
r_session.headers,
1210+
)
1211+
stac_check(
1212+
item_url,
1213+
errors,
1214+
warnings,
1215+
Context.FEATURES,
1216+
Method.GET,
1217+
open_assets_urls,
1218+
r_session.headers,
1219+
)
11521220

11531221
# Validate Features non-existent item
11541222
if not (collections_link := link_by_rel(root_links, "data")):
@@ -1195,7 +1263,15 @@ def validate_features(
11951263
if not link_by_rel(body.get("links", []), "root"):
11961264
errors += f"[{Context.FEATURES}] GET {collection_items_url} does not have root link"
11971265

1198-
stac_validate(collection_items_url, body, errors, Context.FEATURES)
1266+
stac_validate(
1267+
collection_items_url,
1268+
body,
1269+
errors,
1270+
Context.FEATURES,
1271+
Method.GET,
1272+
open_assets_urls,
1273+
r_session.headers,
1274+
)
11991275

12001276
item = next(iter(body.get("features", [])), None)
12011277

@@ -1233,8 +1309,24 @@ def validate_features(
12331309
if not link_by_rel(body.get("links", []), "parent"):
12341310
errors += f"[{Context.FEATURES}] GET {item_url} does not have parent link"
12351311

1236-
stac_validate(item_url, body, errors, Context.FEATURES)
1237-
stac_check(item_url, errors, warnings, Context.FEATURES)
1312+
stac_validate(
1313+
item_url,
1314+
body,
1315+
errors,
1316+
Context.FEATURES,
1317+
Method.GET,
1318+
open_assets_urls,
1319+
r_session.headers,
1320+
)
1321+
stac_check(
1322+
item_url,
1323+
errors,
1324+
warnings,
1325+
Context.FEATURES,
1326+
Method.GET,
1327+
open_assets_urls,
1328+
r_session.headers,
1329+
)
12381330

12391331
if validate_pagination:
12401332
# Items pagination validation
@@ -1270,6 +1362,7 @@ def validate_item_search(
12701362
conformance_classes: List[str],
12711363
r_session: Session,
12721364
validate_pagination: bool,
1365+
open_assets_urls: bool = True,
12731366
) -> None:
12741367
links = root_body.get("links")
12751368

@@ -1317,7 +1410,14 @@ def validate_item_search(
13171410
)
13181411

13191412
if body:
1320-
stac_validate(search_url, body, errors, Context.ITEM_SEARCH)
1413+
stac_validate(
1414+
search_url,
1415+
body,
1416+
errors,
1417+
Context.ITEM_SEARCH,
1418+
open_assets_urls,
1419+
r_session.headers,
1420+
)
13211421

13221422
validate_item_search_limit(search_url, methods, errors, r_session)
13231423
validate_item_search_bbox_xor_intersects(search_url, methods, errors, r_session)

0 commit comments

Comments
 (0)