Skip to content

Commit

Permalink
Merge pull request #232 from stac-utils/collections_validation
Browse files Browse the repository at this point in the history
Validate /collections endpoint
  • Loading branch information
jonhealy1 authored Feb 9, 2024
2 parents 5ff4944 + f3510be commit 41afba7
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 11 deletions.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
repos:
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
rev: 7.0.0
hooks:
- id: flake8
- repo: https://github.com/timothycrosley/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/psf/black
rev: 23.11.0
rev: 24.1.1
hooks:
- id: black
language_version: python3.8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
rev: v1.8.0
hooks:
- id: mypy
exclude: /tests/
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is (loosely) based on [Keep a Changelog](http://keepachangelog.com/)

### Added

- Added ability to validate response from a /collections endpoint [#220](https://github.com/stac-utils/stac-validator/issues/220)
- Added mypy to pre-commit config ([#229](https://github.com/stac-utils/stac-validator/pull/224))

## [v3.3.2] - 2023-11-17
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ Options:
-m, --max-depth INTEGER Maximum depth to traverse when recursing. Omit this
argument to get full recursion. Ignored if
`recursive == False`.
--collections Validate /collections response.
--item-collection Validate item collection response. Can be combined
with --pages. Defaults to one page.
-p, --pages INTEGER Maximum number of pages to validate via --item-
Expand Down
33 changes: 32 additions & 1 deletion stac_validator/stac_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ def item_collection_summary(message: List[Dict[str, Any]]) -> None:
click.secho(f"valid_items: {valid_count}")


def collections_summary(message: List[Dict[str, Any]]) -> None:
"""Prints a summary of the validation results for an item collection response.
Args:
message (List[Dict[str, Any]]): The validation results for the item collection.
Returns:
None
"""
valid_count = 0
for collection in message:
if "valid_stac" in collection and collection["valid_stac"] is True:
valid_count = valid_count + 1
click.secho()
click.secho("--collections summary", bold=True)
click.secho(f"collections_validated: {len(message)}")
click.secho(f"valid_collections: {valid_count}")


@click.command()
@click.argument("stac_file")
@click.option(
Expand Down Expand Up @@ -80,6 +99,11 @@ def item_collection_summary(message: List[Dict[str, Any]]) -> None:
type=int,
help="Maximum depth to traverse when recursing. Omit this argument to get full recursion. Ignored if `recursive == False`.",
)
@click.option(
"--collections",
is_flag=True,
help="Validate /collections response.",
)
@click.option(
"--item-collection",
is_flag=True,
Expand All @@ -102,6 +126,7 @@ def item_collection_summary(message: List[Dict[str, Any]]) -> None:
)
def main(
stac_file: str,
collections: bool,
item_collection: bool,
pages: int,
recursive: bool,
Expand All @@ -120,6 +145,7 @@ def main(
Args:
stac_file (str): Path to the STAC file to be validated.
collections (bool): Validate response from /collections endpoint.
item_collection (bool): Whether to validate item collection responses.
pages (int): Maximum number of pages to validate via `item_collection`.
recursive (bool): Whether to recursively validate all related STAC objects.
Expand All @@ -143,6 +169,7 @@ def main(
valid = True
stac = StacValidate(
stac_file=stac_file,
collections=collections,
item_collection=item_collection,
pages=pages,
recursive=recursive,
Expand All @@ -155,8 +182,10 @@ def main(
verbose=verbose,
log=log_file,
)
if not item_collection:
if not item_collection and not collections:
valid = stac.run()
elif collections:
stac.validate_collections()
else:
stac.validate_item_collection()

Expand All @@ -169,6 +198,8 @@ def main(

if item_collection:
item_collection_summary(message)
elif collections:
collections_summary(message)

sys.exit(0 if valid else 1)

Expand Down
36 changes: 32 additions & 4 deletions stac_validator/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class StacValidate:
Attributes:
stac_file (str): The path or URL to the STAC object to be validated.
collections (bool): Validate response from a /collections endpoint.
item_collection (bool): Whether the STAC object to be validated is an item collection.
pages (int): The maximum number of pages to validate if `item_collection` is True.
recursive (bool): Whether to recursively validate related STAC objects.
Expand All @@ -45,6 +46,7 @@ class StacValidate:
def __init__(
self,
stac_file: Optional[str] = None,
collections: bool = False,
item_collection: bool = False,
pages: Optional[int] = None,
recursive: bool = False,
Expand All @@ -58,6 +60,7 @@ def __init__(
log: str = "",
):
self.stac_file = stac_file
self.collections = collections
self.item_collection = item_collection
self.pages = pages
self.message: List = []
Expand Down Expand Up @@ -392,6 +395,27 @@ def validate_item_collection_dict(self, item_collection: Dict) -> None:
self.schema = ""
self.validate_dict(item)

def validate_collections(self) -> None:
""" "Validate STAC collections from a /collections endpoint.
Raises:
URLError: If there is an issue with the URL used to fetch the item collection.
JSONDecodeError: If the item collection content cannot be parsed as JSON.
ValueError: If the item collection does not conform to the STAC specification.
TypeError: If the item collection content is not a dictionary or JSON object.
FileNotFoundError: If the item collection file cannot be found.
ConnectionError: If there is an issue with the internet connection used to fetch the item collection.
exceptions.SSLError: If there is an issue with the SSL connection used to fetch the item collection.
OSError: If there is an issue with the file system (e.g., read/write permissions) while trying to write to the log file.
Returns:
None
"""
collections = fetch_and_parse_file(str(self.stac_file))
for collection in collections["collections"]:
self.schema = ""
self.validate_dict(collection)

def validate_item_collection(self) -> None:
"""Validate a STAC item collection.
Expand Down Expand Up @@ -429,9 +453,9 @@ def validate_item_collection(self) -> None:
break
except Exception as e:
message = {}
message[
"pagination_error"
] = f"Validating the item collection failed on page {page}: {str(e)}"
message["pagination_error"] = (
f"Validating the item collection failed on page {page}: {str(e)}"
)
self.message.append(message)

def run(self) -> bool:
Expand All @@ -457,7 +481,11 @@ def run(self) -> bool:
"""
message = {}
try:
if self.stac_file is not None and not self.item_collection:
if (
self.stac_file is not None
and not self.item_collection
and not self.collections
):
self.stac_content = fetch_and_parse_file(self.stac_file)

stac_type = get_stac_type(self.stac_content).upper()
Expand Down
55 changes: 55 additions & 0 deletions tests/test_validate_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Description: Test stac-validator on --collections (/collections validation).
"""

from stac_validator import stac_validator


def test_validate_collections_remote():
stac_file = "https://earth-search.aws.element84.com/v0/collections"
stac = stac_validator.StacValidate(stac_file, collections=True)
stac.validate_collections()

assert stac.message == [
{
"version": "1.0.0-beta.2",
"path": "https://earth-search.aws.element84.com/v0/collections",
"schema": [
"https://schemas.stacspec.org/v1.0.0-beta.2/collection-spec/json-schema/collection.json"
],
"valid_stac": True,
"asset_type": "COLLECTION",
"validation_method": "default",
},
{
"version": "1.0.0-beta.2",
"path": "https://earth-search.aws.element84.com/v0/collections",
"schema": [
"https://schemas.stacspec.org/v1.0.0-beta.2/collection-spec/json-schema/collection.json"
],
"valid_stac": True,
"asset_type": "COLLECTION",
"validation_method": "default",
},
{
"version": "1.0.0-beta.2",
"path": "https://earth-search.aws.element84.com/v0/collections",
"schema": [
"https://schemas.stacspec.org/v1.0.0-beta.2/collection-spec/json-schema/collection.json"
],
"valid_stac": True,
"asset_type": "COLLECTION",
"validation_method": "default",
},
{
"version": "1.0.0-beta.2",
"path": "https://earth-search.aws.element84.com/v0/collections",
"schema": [
"https://schemas.stacspec.org/v1.0.0-beta.2/collection-spec/json-schema/collection.json"
],
"valid_stac": True,
"asset_type": "COLLECTION",
"validation_method": "default",
},
]
3 changes: 1 addition & 2 deletions tests/test_validate_item_collection.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""
Description: Test the validator
Description: Test stac-validator on item-collection validation.
"""


from stac_validator import stac_validator


Expand Down

0 comments on commit 41afba7

Please sign in to comment.