Skip to content

Commit 6259d3c

Browse files
committed
Add list_views to rest catalog
1 parent de2b299 commit 6259d3c

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
@@ -537,6 +537,22 @@ def list_namespaces(self, namespace: Union[str, Identifier] = ()) -> List[Identi
537537
NoSuchNamespaceError: If a namespace with the given name does not exist.
538538
"""
539539

540+
@abstractmethod
541+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
542+
"""List views under the given namespace in the catalog.
543+
544+
If namespace not provided, will list all views in the catalog.
545+
546+
Args:
547+
namespace (str | Identifier): Namespace identifier to search.
548+
549+
Returns:
550+
List[Identifier]: list of table identifiers.
551+
552+
Raises:
553+
NoSuchNamespaceError: If a namespace with the given name does not exist.
554+
"""
555+
540556
@abstractmethod
541557
def load_namespace_properties(self, namespace: Union[str, Identifier]) -> Properties:
542558
"""Get properties for a namespace.

pyiceberg/catalog/dynamodb.py

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

509509
return properties_update_summary
510510

511+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
512+
raise NotImplementedError
513+
511514
def _get_iceberg_table_item(self, database_name: str, table_name: str) -> Dict[str, Any]:
512515
try:
513516
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
@@ -746,3 +746,6 @@ def update_namespace_properties(
746746
self.glue.update_database(Name=database_name, DatabaseInput=_construct_database_input(database_name, updated_properties))
747747

748748
return properties_update_summary
749+
750+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
751+
raise NotImplementedError

pyiceberg/catalog/hive.py

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

396+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
397+
raise NotImplementedError
398+
396399
def _create_lock_request(self, database_name: str, table_name: str) -> LockRequest:
397400
lock_component: LockComponent = LockComponent(
398401
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
@@ -93,6 +93,7 @@ class Endpoints:
9393
table_exists: str = "namespaces/{namespace}/tables/{table}"
9494
get_token: str = "oauth/tokens"
9595
rename_table: str = "tables/rename"
96+
list_views: str = "namespaces/{namespace}/views"
9697

9798

9899
AUTHORIZATION_HEADER = "Authorization"
@@ -196,10 +197,19 @@ class ListTableResponseEntry(IcebergBaseModel):
196197
namespace: Identifier = Field()
197198

198199

200+
class ListViewResponseEntry(IcebergBaseModel):
201+
name: str = Field()
202+
namespace: Identifier = Field()
203+
204+
199205
class ListTablesResponse(IcebergBaseModel):
200206
identifiers: List[ListTableResponseEntry] = Field()
201207

202208

209+
class ListViewsResponse(IcebergBaseModel):
210+
identifiers: List[ListViewResponseEntry] = Field()
211+
212+
203213
class ErrorResponseMessage(IcebergBaseModel):
204214
message: str = Field()
205215
type: str = Field()
@@ -674,6 +684,17 @@ def rename_table(self, from_identifier: Union[str, Identifier], to_identifier: U
674684

675685
return self.load_table(to_identifier)
676686

687+
@retry(**_RETRY_ARGS)
688+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
689+
namespace_tuple = self._check_valid_namespace_identifier(namespace)
690+
namespace_concat = NAMESPACE_SEPARATOR.join(namespace_tuple)
691+
response = self._session.get(self.url(Endpoints.list_views, namespace=namespace_concat))
692+
try:
693+
response.raise_for_status()
694+
except HTTPError as exc:
695+
self._handle_non_200_response(exc, {404: NoSuchNamespaceError})
696+
return [(*view.namespace, view.name) for view in ListViewsResponse(**response.json()).identifiers]
697+
677698
@retry(**_RETRY_ARGS)
678699
def _commit_table(self, table_request: CommitTableRequest) -> CommitTableResponse:
679700
"""Update the table.

pyiceberg/catalog/sql.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,3 +681,6 @@ def update_namespace_properties(
681681
session.execute(insert_stmt)
682682
session.commit()
683683
return properties_update_summary
684+
685+
def list_views(self, namespace: Union[str, Identifier]) -> List[Identifier]:
686+
raise NotImplementedError

tests/catalog/test_base.py

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

263+
def list_views(self, namespace: Optional[Union[str, Identifier]] = None) -> List[Identifier]:
264+
raise NotImplementedError
265+
263266

264267
@pytest.fixture
265268
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
@@ -400,6 +400,52 @@ def test_list_tables_404(rest_mock: Mocker) -> None:
400400
assert "Namespace does not exist" in str(e.value)
401401

402402

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

0 commit comments

Comments
 (0)