Skip to content

Commit b07e6d2

Browse files
authored
Merge branch 'main' into feat/allow-item-search-without-geometry
2 parents 1314dbe + dfc7c21 commit b07e6d2

File tree

5 files changed

+91
-3
lines changed

5 files changed

+91
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- Support stac-check config_file ([#687](https://github.com/stac-utils/stac-api-validator/pull/687))
11+
1012
## [0.6.6] - 2025-07-29
1113

1214
Dependency updates.

src/stac_api_validator/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@
137137
multiple=True,
138138
help="Headers to attach to the main request and dependent pystac requests, curl syntax",
139139
)
140+
@click.option(
141+
"--stac-check-config",
142+
help="Path to a YAML stac-check configuration file",
143+
)
140144
def main(
141145
log_level: str,
142146
root_url: str,
@@ -162,6 +166,7 @@ def main(
162166
query_in_values: Optional[str] = None,
163167
transaction_collection: Optional[str] = None,
164168
headers: Optional[List[str]] = None,
169+
stac_check_config: Optional[str] = None,
165170
) -> int:
166171
"""STAC API Validator."""
167172
logging.basicConfig(stream=sys.stdout, level=log_level)
@@ -202,6 +207,7 @@ def main(
202207
),
203208
transaction_collection=transaction_collection,
204209
headers=processed_headers,
210+
stac_check_config=stac_check_config,
205211
)
206212
except Exception as e:
207213
click.secho(

src/stac_api_validator/validations.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,16 @@ def stac_check(
375375
method: Method = Method.GET,
376376
open_assets_urls: bool = True,
377377
headers: Optional[dict] = None,
378+
config_file: Optional[str] = None,
378379
) -> None:
379380
try:
380381
logger.debug(f"stac-check validation: {url}")
381-
linter = Linter(url, assets_open_urls=open_assets_urls, headers=headers or {})
382+
linter = Linter(
383+
url,
384+
config_file=config_file,
385+
assets_open_urls=open_assets_urls,
386+
headers=headers or {},
387+
)
382388
if not linter.valid_stac:
383389
errors += f"[{context}] : {method} {url} is not a valid STAC object: {linter.error_msg}"
384390
if msgs := linter.best_practices_msg[1:]: # first msg is a header, so skip
@@ -559,6 +565,7 @@ def validate_api(
559565
transaction_collection: Optional[str],
560566
headers: Optional[Dict[str, str]],
561567
open_assets_urls: bool = True,
568+
stac_check_config: Optional[str] = None,
562569
) -> Tuple[Warnings, Errors]:
563570
warnings = Warnings()
564571
errors = Errors()
@@ -610,15 +617,27 @@ def validate_api(
610617
if "collections" in ccs_to_validate:
611618
logger.info("Validating STAC API - Collections conformance class.")
612619
validate_collections(
613-
landing_page_body, collection, errors, warnings, r_session, open_assets_urls
620+
landing_page_body,
621+
collection,
622+
errors,
623+
warnings,
624+
r_session,
625+
open_assets_urls,
626+
stac_check_config,
614627
)
615628

616629
conforms_to = landing_page_body.get("conformsTo", [])
617630

618631
if "features" in ccs_to_validate:
619632
logger.info("Validating STAC API - Features conformance class.")
620633
validate_collections(
621-
landing_page_body, collection, errors, warnings, r_session, open_assets_urls
634+
landing_page_body,
635+
collection,
636+
errors,
637+
warnings,
638+
r_session,
639+
open_assets_urls,
640+
stac_check_config,
622641
)
623642
validate_features(
624643
landing_page_body,
@@ -630,6 +649,7 @@ def validate_api(
630649
r_session,
631650
validate_pagination,
632651
open_assets_urls,
652+
stac_check_config,
633653
)
634654

635655
if "transaction" in ccs_to_validate:
@@ -981,6 +1001,7 @@ def validate_collections(
9811001
warnings: Warnings,
9821002
r_session: Session,
9831003
open_assets_urls: bool = True,
1004+
stac_check_config: Optional[str] = None,
9841005
) -> None:
9851006
if not (data_link := link_by_rel(root_body["links"], "data")):
9861007
errors += f"[{Context.COLLECTIONS}] /: Link[rel=data] must href /collections"
@@ -1090,6 +1111,7 @@ def validate_collections(
10901111
Method.GET,
10911112
open_assets_urls,
10921113
r_session.headers,
1114+
stac_check_config,
10931115
)
10941116

10951117
# todo: collection pagination
@@ -1105,6 +1127,7 @@ def validate_features(
11051127
r_session: Session,
11061128
validate_pagination: bool,
11071129
open_assets_urls: bool = True,
1130+
stac_check_config: Optional[str] = None,
11081131
) -> None:
11091132
if not collection:
11101133
errors += f"[{Context.FEATURES}] Collection parameter required for running Features validations."
@@ -1211,6 +1234,7 @@ def validate_features(
12111234
Method.GET,
12121235
open_assets_urls,
12131236
r_session.headers,
1237+
stac_check_config,
12141238
)
12151239

12161240
# Validate Features non-existent item
@@ -1321,6 +1345,7 @@ def validate_features(
13211345
Method.GET,
13221346
open_assets_urls,
13231347
r_session.headers,
1348+
stac_check_config,
13241349
)
13251350

13261351
if validate_pagination:
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
linting:
2+
# Identifiers should consist of only lowercase characters, numbers, '_', and '-'
3+
searchable_identifiers: true
4+
# Item name '{self.object_id}' should not contain ':' or '/'
5+
percent_encoded: true
6+
# Item file names should match their ids
7+
item_id_file_name: true
8+
# Collections and catalogs should be named collection.json and catalog.json
9+
catalog_id_file_name: true
10+
# A STAC collection should contain a summaries field
11+
check_summaries: true
12+
# Datetime fields should not be set to null
13+
null_datetime: true
14+
# best practices - check unlocated items to make sure bbox field is not set
15+
check_unlocated: true
16+
# best practices - recommend items have a geometry
17+
check_geometry: true
18+
# check to see if there are too many links
19+
bloated_links: true
20+
# best practices - check for bloated metadata in properties
21+
bloated_metadata: true
22+
# best practices - ensure thumbnail is a small file size ["png", "jpeg", "jpg", "webp"]
23+
check_thumbnail: true
24+
# best practices - ensure that links in catalogs and collections include a title field
25+
links_title: true
26+
# best practices - ensure that links in catalogs and collections include self link
27+
links_self: true
28+
29+
# Geometry validation settings [BETA]
30+
geometry_validation:
31+
# Master switch to enable/disable all geometry validation checks
32+
enabled: true
33+
# check if geometry coordinates are potentially ordered incorrectly (longitude, latitude)
34+
geometry_coordinates_order: true
35+
# check if geometry coordinates contain definite errors (latitude > ±90°, longitude > ±180°)
36+
geometry_coordinates_definite_errors: true
37+
# check if bbox matches the bounds of the geometry
38+
bbox_geometry_match: true
39+
# check if a bbox that crosses the antimeridian is correctly formatted
40+
bbox_antimeridian: true
41+
42+
settings:
43+
# number of links before the bloated links warning is shown
44+
max_links: 20
45+
# number of properties before the bloated metadata warning is shown
46+
max_properties: 20

tests/test_validations.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ def sample_item() -> Generator[pystac.Item, None, None]:
4444
yield pystac.Item.from_dict(data)
4545

4646

47+
@pytest.fixture
48+
def stac_check_config() -> Generator[str, None, None]:
49+
current_path = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
50+
51+
yield current_path / "resources" / "stac-check-config.yaml"
52+
53+
4754
@pytest.fixture
4855
def expected_headers(requests_version: str) -> Generator[Dict[str, str], None, None]:
4956
yield {
@@ -91,6 +98,7 @@ def test_validate_api(
9198
request: pytest.FixtureRequest,
9299
r_session: requests.Session,
93100
expected_headers: Dict[str, str],
101+
stac_check_config: str,
94102
) -> None:
95103
if request.config.getoption("typeguard_packages"):
96104
pytest.skip(
@@ -114,6 +122,7 @@ def test_validate_api(
114122
query_config=None,
115123
transaction_collection=None,
116124
headers=headers,
125+
stac_check_config=stac_check_config,
117126
)
118127
assert retrieve_mock.call_count == 1
119128
r_session = retrieve_mock.call_args.args[-1]

0 commit comments

Comments
 (0)