Skip to content

Commit

Permalink
Add list_tables method
Browse files Browse the repository at this point in the history
  • Loading branch information
mure committed Nov 16, 2022
1 parent 18a3cb6 commit 20f3bbf
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 30 deletions.
15 changes: 15 additions & 0 deletions nisystemlink/clients/core/_uplink/_paged_result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Optional

from ._json_model import JsonModel


class PagedResult(JsonModel):
continuation_token: Optional[str]
"""A token which allows the user to resume a query at the next item in the matching results.
When querying, a token will be returned if a query may be
continued. To obtain the next page of results, pass the token to the service
on a subsequent request.The service will respond with a new continuation
token. To paginate results, continue sending requests with the newest
continuation token provided by the service, until this value is null.
"""
39 changes: 37 additions & 2 deletions nisystemlink/clients/dataframe/_data_frame_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

"""Implementation of DataFrameClient."""

from typing import Optional
from typing import List, Optional

from nisystemlink.clients import core
from nisystemlink.clients.core._uplink._base_client import BaseClient
from uplink import Body, delete, get, json, post, returns
from uplink import Body, delete, get, json, post, Query, returns

from . import models

Expand All @@ -33,6 +33,41 @@ def api_info(self) -> models.ApiInfo:
"""Returns information about available API operations."""
...

@get(
_BASE_PATH + "/tables",
args=(
Query("take"),
Query("id"),
Query("orderBy"),
Query("orderByDescending"),
Query("continuationToken"),
Query("workspace"),
),
)
def list_tables(
self,
take: Optional[int] = None,
id: Optional[List[str]] = None,
order_by: Optional[models.OrderBy] = None,
order_by_descending: Optional[bool] = None,
continuation_token: Optional[str] = None,
workspace: Optional[str] = None,
) -> models.PagedTables:
"""Lists available tables on the SystemLink DataFrame service.
Args:
take: Limits the returned list to the specified number of results. Defaults to 1000.
id: List of table IDs to filter by.
order_by: The sort order of the returned list of tables.
order_by_descending: Whether to sort descending instead of ascending. Defaults to false.
continuation_token: The token used to paginate results.
workspace: List of workspace IDs to filter by.
Returns:
models.PagedTables: The list of tables with a continuation token.
"""
...

@json
@returns.json(key="id")
@post(_BASE_PATH + "/tables", args=(Body,))
Expand Down
2 changes: 2 additions & 0 deletions nisystemlink/clients/dataframe/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from ._column import Column
from ._column_type import ColumnType
from ._data_type import DataType
from ._order_by import OrderBy
from ._paged_tables import PagedTables
from ._table_metadata import TableMetadata

# flake8: noqa
2 changes: 1 addition & 1 deletion nisystemlink/clients/dataframe/models/_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Column(JsonModel):
data_type: DataType
"""The data type of the column."""

column_type: Optional[ColumnType] = ColumnType.Normal
column_type: ColumnType = ColumnType.Normal
"""The column type. Defaults to ColumnType.Normal."""

properties: Optional[Dict[str, str]] = None
Expand Down
14 changes: 14 additions & 0 deletions nisystemlink/clients/dataframe/models/_order_by.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Literal


OrderBy = Literal[
"CREATED_AT", "METADATA_MODIFIED_AT", "NAME", "NUMBER_OF_ROWS", "ROWS_MODIFIED_AT"
]
"""Possible options for sorting when querying tables.
* ``CREATED_AT``: The date and time the table was created.
* ``METADATA_MODIFIED_AT``: The date and time the table's metadata properties were modified.
* ``NAME``: The name of the table.
* ``NUMBER_OF_ROWS``: The number of rows of data in the table.
* ``ROWS_MODIFIED_AT``: Date and time rows were most recently appended to the table.
"""
12 changes: 12 additions & 0 deletions nisystemlink/clients/dataframe/models/_paged_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import List

from nisystemlink.clients.core._uplink._paged_result import PagedResult

from ._table_metadata import TableMetadata


class PagedTables(PagedResult):
"""The response for a table query containing the matched tables."""

tables: List[TableMetadata]
"""The list of tables returned by the query."""
128 changes: 101 additions & 27 deletions tests/integration/dataframe/test_dataframe.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,76 @@
# -*- coding: utf-8 -*-
from typing import Iterator
from datetime import datetime
from typing import List

import pytest # type: ignore
from nisystemlink.clients.core import ApiException
from nisystemlink.clients.dataframe import DataFrameClient
from nisystemlink.clients.dataframe import models


@pytest.mark.enterprise
@pytest.mark.integration
class TestDataFrame:
@pytest.fixture
def client(self, enterprise_config):
return DataFrameClient(enterprise_config)
@pytest.fixture(scope="class")
def client(enterprise_config):
"""Fixture to create a DataFrameClient instance."""
return DataFrameClient(enterprise_config)

@pytest.fixture
def table_id(self, client: DataFrameClient) -> Iterator[str]:
id = client.create_table(
models.CreateTableRequest(
columns=[
models.Column(
name="time",
data_type=models.DataType.Timestamp,
column_type=models.ColumnType.Index,
properties={"cat": "meow"},
),
models.Column(name="value", data_type=models.DataType.Int32),
],
name="Python API test table (delete me)",
properties={"dog": "woof"},

@pytest.fixture(scope="class")
def create_table(client: DataFrameClient):
"""Fixture to return a factory that creates tables."""
tables = []

def _create_table(table: models.CreateTableRequest) -> str:
id = client.create_table(table)
print(f"Created table {id}")
tables.append(id)
return id

yield _create_table

for id in tables:
client.delete_table(id)
print(f"Deleted table {id}")


@pytest.fixture(scope="class")
def test_tables(create_table):
"""Fixture to create a set of test tables."""
ids = []
for i in range(1, 4):
ids.append(
create_table(
models.CreateTableRequest(
columns=[
models.Column(
name="time",
data_type=models.DataType.Timestamp,
column_type=models.ColumnType.Index,
properties={"cat": "meow"},
),
models.Column(name="value", data_type=models.DataType.Int32),
],
name=f"Python API test table {i} (delete me)",
properties={"dog": "woof"},
)
)
)
yield id
client.delete_table(id)
return ids


@pytest.mark.enterprise
@pytest.mark.integration
class TestDataFrame:
def test__api_info__returns(self, client):
response = client.api_info()

assert len(response.dict()) != 0

def test__create_table__metadata_is_corect(
self, client: DataFrameClient, table_id: str
self, client: DataFrameClient, test_tables: List[str]
):
table_metadata = client.get_table_metadata(table_id)
table_metadata = client.get_table_metadata(test_tables[0])

assert table_metadata.name == "Python API test table (delete me)"
assert table_metadata.name == "Python API test table 1 (delete me)"
assert table_metadata.properties == {"dog": "woof"}
assert table_metadata.columns == [
models.Column(
Expand All @@ -59,3 +86,50 @@ def test__create_table__metadata_is_corect(
properties={},
),
]

def test__get_table__correct_timestamp(self, client: DataFrameClient, create_table):
id = create_table(
models.CreateTableRequest(
columns=[
models.Column(
name="index",
data_type=models.DataType.Int32,
column_type=models.ColumnType.Index,
)
]
)
)
table = client.get_table_metadata(id)

now = datetime.now().timestamp()
# Assert that timestamp is within 10 seconds of now
assert table.created_at.timestamp() == pytest.approx(now, abs=10)

def test__get_table_invalid_id__raises(self, client: DataFrameClient):
with pytest.raises(ApiException, match="invalid table ID"):
client.get_table_metadata("invalid_id")

def test__list_tables__returns(
self, client: DataFrameClient, test_tables: List[str]
):
take = len(test_tables) - 1
first_page = client.list_tables(
take=take,
id=test_tables,
order_by="NAME",
order_by_descending=True,
)

assert len(first_page.tables) == take
assert first_page.tables[0].id == test_tables[-1] # Asserts descending order
assert first_page.continuation_token is not None

second_page = client.list_tables(
id=test_tables,
order_by="NAME",
order_by_descending=True,
continuation_token=first_page.continuation_token,
)

assert len(second_page.tables) == 1
assert second_page.continuation_token is None

0 comments on commit 20f3bbf

Please sign in to comment.