Skip to content

Commit

Permalink
Add more endpoints to APILayoutStrategy (#1335)
Browse files Browse the repository at this point in the history
* allow id as input for APILayoutStrategy

* add extra href methods

* update documentation

* add more tests

* typo

* changlog

* revert linting of migrate.py

* revert linting of creating-a-landsat-stac.ipynb

* fix notebook

* Update CHANGELOG.md

* add default stac api version

---------

Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
  • Loading branch information
thomas-maschler and gadomski committed May 3, 2024
1 parent 1f0409a commit 3432392
Show file tree
Hide file tree
Showing 7 changed files with 2,722 additions and 2,542 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## [Unreleased]

- Allow object ID as input for getting APILayoutStrategy hrefs and add `items`, `collections`, `search`, `conformance`, `service_desc` and `service_doc` href methods. ([#1335](https://github.com/stac-utils/pystac/pull/1335))


## [v1.10.0] - 2024-03-28

### Added
Expand Down
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ a template.
* :class:`pystac.layout.BestPracticesLayoutStrategy`: Layout strategy that represents
the catalog layout described in the :stac-spec:`STAC Best Practices documentation
<best-practices.md>`.
* :class:`pystac.layout.APILayoutStrategy`: Layout strategy that represents
the catalog layout described in
the :stac-api-spec:`STAC API documentation <overview.md#endpoints>`.
* :class:`pystac.layout.TemplateLayoutStrategy`: Layout strategy that can take strings
to be supplied to a :class:`~pystac.layout.LayoutTemplate` to derive paths.
* :class:`pystac.layout.CustomLayoutStrategy`: Layout strategy that allows users to
Expand Down
5 changes: 5 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
"v{}/%s".format(STACVersion.DEFAULT_STAC_VERSION),
"%s path",
),
"stac-api-spec": (
"https://github.com/radiantearth/stac-api-spec/tree/"
"v{}/%s".format(STACVersion.DEFAULT_STAC_API_VERSION),
"%s path",
),
"stac-ext": ("https://github.com/stac-extensions/%s", "%s extension"),
}

Expand Down
5,068 changes: 2,534 additions & 2,534 deletions docs/tutorials/creating-a-landsat-stac.ipynb

Large diffs are not rendered by default.

137 changes: 129 additions & 8 deletions pystac/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,25 +550,146 @@ class APILayoutStrategy(HrefLayoutStrategy):
``./collections/${collection}/items/${id}``.
"""

def get_catalog_href(self, cat: Catalog, parent_dir: str, is_root: bool) -> str:
def get_catalog_href(
self, cat: Catalog | str, parent_dir: str, is_root: bool
) -> str:
"""
Generate a catalog href based on the provided parameters.
Parameters:
cat (Catalog | str): The catalog object or its ID.
parent_dir (str): The parent directory for the catalog.
is_root (bool): A flag indicating whether the catalog is a root catalog.
Returns:
str: The generated catalog href.
"""

if not isinstance(cat, str):
cat = cat.id

if is_root:
cat_href = parent_dir
else:
cat_href = posixpath.join(parent_dir, f"{cat.id}")
cat_href = posixpath.join(parent_dir, cat)

return cat_href

def get_collections_href(self, parent_dir: str) -> str:
"""
Generate a collections href based on the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the collections.
Returns:
str: The generated collections href.
"""
return posixpath.join(parent_dir, "collections")

def get_collection_href(
self, col: Collection, parent_dir: str, is_root: bool
self, col: Collection | str, parent_dir: str, is_root: bool
) -> str:
"""
Generate a collection href based on the provided parameters.
Parameters:
col (Collection | str): The collection object or its ID.
parent_dir (str): The parent directory for the collection.
is_root (bool): A flag indicating whether the collection is a root collection.
Returns:
str: The generated collection href.
Raises:
ValueError: If the collection is set as root.
"""

if not isinstance(col, str):
col = col.id

if is_root:
raise ValueError("Collections cannot be root")

col_root = posixpath.join(parent_dir, "collections")
col_root = self.get_collections_href(parent_dir)

return posixpath.join(col_root, f"{col.id}")
return posixpath.join(col_root, col)

def get_item_href(self, item: Item, parent_dir: str) -> str:
item_root = posixpath.join(parent_dir, "items")
def get_items_href(self, parent_dir: str) -> str:
"""
Generate an items href based on the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the items.
Returns:
str: The generated items href.
"""
return posixpath.join(parent_dir, "items")

return posixpath.join(item_root, f"{item.id}")
def get_item_href(self, item: Item | str, parent_dir: str) -> str:
"""
Generate an item href based on the provided parameters.
Parameters:
item (Item | str): The item object or its ID.
parent_dir (str): The parent directory for the item.
Returns:
str: The generated item href.
"""
if not isinstance(item, str):
item = item.id

item_root = self.get_items_href(parent_dir)

return posixpath.join(item_root, item)

def get_search_href(self, parent_dir: str) -> str:
"""
Generate a search href based on the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the search.
Returns:
str: The generated search href.
"""
return posixpath.join(parent_dir, "search")

def get_conformance_href(self, parent_dir: str) -> str:
"""
Generate a conformance href based on the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the conformance.
Returns:
str: The generated conformance href.
"""
return posixpath.join(parent_dir, "conformance")

def get_service_desc_href(self, parent_dir: str) -> str:
"""
Generate an API service description href based on the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the API.
Returns:
str: The generated API href.
"""
return posixpath.join(parent_dir, "api")

def get_service_doc_href(self, parent_dir: str) -> str:
"""
Generate an API service documentation href based on
the provided parent directory.
Parameters:
parent_dir (str): The parent directory for the API.
Returns:
str: The generated API href.
"""
return posixpath.join(parent_dir, "api.html")
3 changes: 3 additions & 0 deletions pystac/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class STACVersion:
DEFAULT_STAC_VERSION = "1.0.0"
"""Latest STAC version supported by PySTAC"""

DEFAULT_STAC_API_VERSION = "1.0.0"
"""Latest STAC API version supported by PySTAC"""

# Version that holds a user-set STAC version to use.
_override_version: Optional[str] = None

Expand Down
45 changes: 45 additions & 0 deletions tests/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,25 @@ def test_produces_layout_for_root_catalog(self) -> None:
)
self.assertEqual(href, "http://example.com")

def test_produces_layout_for_root_catalog_str(self) -> None:
cat = pystac.Catalog(id="test", description="test desc")
href = self.strategy.get_catalog_href(
cat.id, parent_dir="http://example.com", is_root=True
)
self.assertEqual(href, "http://example.com")

def test_produces_layout_for_child_catalog(self) -> None:
cat = pystac.Catalog(id="test", description="test desc")
href = self.strategy.get_href(cat, parent_dir="http://example.com")
self.assertEqual(href, "http://example.com/test")

def test_produces_layout_for_child_catalog_str(self) -> None:
cat = pystac.Catalog(id="test", description="test desc")
href = self.strategy.get_catalog_href(
cat.id, parent_dir="http://example.com", is_root=False
)
self.assertEqual(href, "http://example.com/test")

def test_cannot_produce_layout_for_root_collection(self) -> None:
collection = TestCases.case_8()
with pytest.raises(ValueError):
Expand All @@ -475,6 +489,13 @@ def test_produces_layout_for_child_collection(self) -> None:
href = self.strategy.get_href(collection, parent_dir="http://example.com")
self.assertEqual(href, f"http://example.com/collections/{collection.id}")

def test_produces_layout_for_child_collection_str(self) -> None:
collection = TestCases.case_8()
href = self.strategy.get_collection_href(
collection.id, parent_dir="http://example.com", is_root=False
)
self.assertEqual(href, f"http://example.com/collections/{collection.id}")

def test_produces_layout_for_item(self) -> None:
collection = TestCases.case_8()
col_href = self.strategy.get_href(collection, parent_dir="http://example.com")
Expand All @@ -483,6 +504,14 @@ def test_produces_layout_for_item(self) -> None:
expected = f"http://example.com/collections/{collection.id}/items/{item.id}"
self.assertEqual(href, expected)

def test_produces_layout_for_item_str(self) -> None:
collection = TestCases.case_8()
col_href = self.strategy.get_href(collection, parent_dir="http://example.com")
item = next(collection.get_items(recursive=True))
href = self.strategy.get_item_href(item.id, parent_dir=col_href)
expected = f"http://example.com/collections/{collection.id}/items/{item.id}"
self.assertEqual(href, expected)

def test_produces_normalized_layout(self) -> None:
cat = pystac.Catalog(id="test_catalog", description="Test Catalog")
col = pystac.Collection(
Expand Down Expand Up @@ -530,3 +559,19 @@ def test_produces_normalized_layout(self) -> None:
item.self_href
== "http://example.com/collections/test_collection/items/test_item"
)

def test_produces_layout_for_search(self) -> None:
href = self.strategy.get_search_href(parent_dir="http://example.com")
self.assertEqual(href, "http://example.com/search")

def test_produces_layout_for_conformance(self) -> None:
href = self.strategy.get_conformance_href(parent_dir="http://example.com")
self.assertEqual(href, "http://example.com/conformance")

def test_produces_layout_for_service_description(self) -> None:
href = self.strategy.get_service_desc_href(parent_dir="http://example.com")
self.assertEqual(href, "http://example.com/api")

def test_produces_layout_for_service_doc(self) -> None:
href = self.strategy.get_service_doc_href(parent_dir="http://example.com")
self.assertEqual(href, "http://example.com/api.html")

0 comments on commit 3432392

Please sign in to comment.