Skip to content

Commit 86728e2

Browse files
ndrluissungwy
andauthored
Add list_views to rest catalog (#817)
* Add list_views to rest catalog * Update pyiceberg/catalog/__init__.py Co-authored-by: Sung Yun <[email protected]> --------- Co-authored-by: Sung Yun <[email protected]>
1 parent ba8e9a3 commit 86728e2

File tree

9 files changed

+101
-0
lines changed

9 files changed

+101
-0
lines changed

pyiceberg/catalog/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,22 @@ def list_namespaces(self, namespace: Union[str, Identifier] = ()) -> List[Identi
583583
NoSuchNamespaceError: If a namespace with the given name does not exist.
584584
"""
585585

586+
@abstractmethod
587+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
588+
"""List views under the given namespace in the catalog.
589+
590+
If namespace is not provided, lists all views in the catalog.
591+
592+
Args:
593+
namespace (str | Identifier): Namespace identifier to search.
594+
595+
Returns:
596+
List[Identifier]: list of table identifiers.
597+
598+
Raises:
599+
NoSuchNamespaceError: If a namespace with the given name does not exist.
600+
"""
601+
586602
@abstractmethod
587603
def load_namespace_properties(self, namespace: Union[str, Identifier]) -> Properties:
588604
"""Get properties for a namespace.

pyiceberg/catalog/dynamodb.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ def update_namespace_properties(
528528

529529
return properties_update_summary
530530

531+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
532+
raise NotImplementedError
533+
531534
def _get_iceberg_table_item(self, database_name: str, table_name: str) -> Dict[str, Any]:
532535
try:
533536
return self._get_dynamo_item(identifier=f"{database_name}.{table_name}", namespace=database_name)

pyiceberg/catalog/glue.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,3 +769,6 @@ def update_namespace_properties(
769769
self.glue.update_database(Name=database_name, DatabaseInput=_construct_database_input(database_name, updated_properties))
770770

771771
return properties_update_summary
772+
773+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
774+
raise NotImplementedError

pyiceberg/catalog/hive.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ def register_table(self, identifier: Union[str, Identifier], metadata_location:
389389
"""
390390
raise NotImplementedError
391391

392+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
393+
raise NotImplementedError
394+
392395
def _create_lock_request(self, database_name: str, table_name: str) -> LockRequest:
393396
lock_component: LockComponent = LockComponent(
394397
level=LockLevel.TABLE, type=LockType.EXCLUSIVE, dbname=database_name, tablename=table_name, isTransactional=True

pyiceberg/catalog/noop.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,6 @@ def update_namespace_properties(
113113
self, namespace: Union[str, Identifier], removals: Optional[Set[str]] = None, updates: Properties = EMPTY_DICT
114114
) -> PropertiesUpdateSummary:
115115
raise NotImplementedError
116+
117+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
118+
raise NotImplementedError

pyiceberg/catalog/rest.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class Endpoints:
9696
table_exists: str = "namespaces/{namespace}/tables/{table}"
9797
get_token: str = "oauth/tokens"
9898
rename_table: str = "tables/rename"
99+
list_views: str = "namespaces/{namespace}/views"
99100

100101

101102
AUTHORIZATION_HEADER = "Authorization"
@@ -200,10 +201,19 @@ class ListTableResponseEntry(IcebergBaseModel):
200201
namespace: Identifier = Field()
201202

202203

204+
class ListViewResponseEntry(IcebergBaseModel):
205+
name: str = Field()
206+
namespace: Identifier = Field()
207+
208+
203209
class ListTablesResponse(IcebergBaseModel):
204210
identifiers: List[ListTableResponseEntry] = Field()
205211

206212

213+
class ListViewsResponse(IcebergBaseModel):
214+
identifiers: List[ListViewResponseEntry] = Field()
215+
216+
207217
class ErrorResponseMessage(IcebergBaseModel):
208218
message: str = Field()
209219
type: str = Field()
@@ -719,6 +729,17 @@ def _remove_catalog_name_from_table_request_identifier(self, table_request: Comm
719729
)
720730
return table_request
721731

732+
@retry(**_RETRY_ARGS)
733+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
734+
namespace_tuple = self._check_valid_namespace_identifier(namespace)
735+
namespace_concat = NAMESPACE_SEPARATOR.join(namespace_tuple)
736+
response = self._session.get(self.url(Endpoints.list_views, namespace=namespace_concat))
737+
try:
738+
response.raise_for_status()
739+
except HTTPError as exc:
740+
self._handle_non_200_response(exc, {404: NoSuchNamespaceError})
741+
return [(*view.namespace, view.name) for view in ListViewsResponse(**response.json()).identifiers]
742+
722743
@retry(**_RETRY_ARGS)
723744
def _commit_table(self, table_request: CommitTableRequest) -> CommitTableResponse:
724745
"""Update the table.

pyiceberg/catalog/sql.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,6 @@ def update_namespace_properties(
696696
session.execute(insert_stmt)
697697
session.commit()
698698
return properties_update_summary
699+
700+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
701+
raise NotImplementedError

tests/catalog/test_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ def update_namespace_properties(
256256
removed=list(removed or []), updated=list(updates.keys() if updates else []), missing=list(expected_to_change)
257257
)
258258

259+
def list_views(self, namespace: Optional[Union[str, Identifier]] = None) -> List[Identifier]:
260+
raise NotImplementedError
261+
259262

260263
@pytest.fixture
261264
def catalog(tmp_path: PosixPath) -> InMemoryCatalog:

tests/catalog/test_rest.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,52 @@ def test_list_tables_404(rest_mock: Mocker) -> None:
402402
assert "Namespace does not exist" in str(e.value)
403403

404404

405+
def test_list_views_200(rest_mock: Mocker) -> None:
406+
namespace = "examples"
407+
rest_mock.get(
408+
f"{TEST_URI}v1/namespaces/{namespace}/views",
409+
json={"identifiers": [{"namespace": ["examples"], "name": "fooshare"}]},
410+
status_code=200,
411+
request_headers=TEST_HEADERS,
412+
)
413+
414+
assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace) == [("examples", "fooshare")]
415+
416+
417+
def test_list_views_200_sigv4(rest_mock: Mocker) -> None:
418+
namespace = "examples"
419+
rest_mock.get(
420+
f"{TEST_URI}v1/namespaces/{namespace}/views",
421+
json={"identifiers": [{"namespace": ["examples"], "name": "fooshare"}]},
422+
status_code=200,
423+
request_headers=TEST_HEADERS,
424+
)
425+
426+
assert RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "rest.sigv4-enabled": "true"}).list_views(namespace) == [
427+
("examples", "fooshare")
428+
]
429+
assert rest_mock.called
430+
431+
432+
def test_list_views_404(rest_mock: Mocker) -> None:
433+
namespace = "examples"
434+
rest_mock.get(
435+
f"{TEST_URI}v1/namespaces/{namespace}/views",
436+
json={
437+
"error": {
438+
"message": "Namespace does not exist: personal in warehouse 8bcb0838-50fc-472d-9ddb-8feb89ef5f1e",
439+
"type": "NoSuchNamespaceException",
440+
"code": 404,
441+
}
442+
},
443+
status_code=404,
444+
request_headers=TEST_HEADERS,
445+
)
446+
with pytest.raises(NoSuchNamespaceError) as e:
447+
RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace)
448+
assert "Namespace does not exist" in str(e.value)
449+
450+
405451
def test_list_namespaces_200(rest_mock: Mocker) -> None:
406452
rest_mock.get(
407453
f"{TEST_URI}v1/namespaces",

0 commit comments

Comments
 (0)