Skip to content

Commit 7cbf7e8

Browse files
authored
feat: Implement create/get/delete/list table metadata methods (#28)
1 parent 17a08ea commit 7cbf7e8

15 files changed

+412
-19
lines changed

docs/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@
5757

5858
# Configuration for https://autodoc-pydantic.readthedocs.io
5959
autodoc_pydantic_model_show_config_summary = False
60+
autodoc_pydantic_field_show_alias = False

mypy.ini

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ strict_equality=True
1313

1414
[mypy-tests.*]
1515
disallow_untyped_calls=True
16-
disallow_incomplete_defs=True
1716
disallow_untyped_decorators=True
1817

1918
strict_equality=True

nisystemlink/clients/core/_uplink/_base_client.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# mypy: disable-error-code = misc
22

3-
from typing import Optional
3+
from typing import Dict, Optional, Type
44

55
from nisystemlink.clients import core
66
from requests import JSONDecodeError, Response
7-
from uplink import Consumer, response_handler
7+
from uplink import Consumer, dumps, response_handler
8+
9+
from ._json_model import JsonModel
810

911

1012
@response_handler
@@ -36,6 +38,12 @@ def _handle_http_status(response: Response) -> Optional[Response]:
3638
raise core.ApiException(msg, http_status_code=response.status_code)
3739

3840

41+
@dumps.to_json(JsonModel)
42+
def _deserialize_model(model_cls: Type[JsonModel], model_instance: JsonModel) -> Dict:
43+
"""Turns a :class:`.JsonModel` instance into a dictionary for serialization."""
44+
return model_instance.dict(by_alias=True, exclude_unset=True)
45+
46+
3947
class BaseClient(Consumer):
4048
"""Base class for SystemLink clients, built on top of `Uplink <https://github.com/prkumar/uplink>`_."""
4149

@@ -45,6 +53,10 @@ def __init__(self, configuration: core.HttpConfiguration):
4553
Args:
4654
configuration: Defines the web server to connect to and information about how to connect.
4755
"""
48-
super().__init__(base_url=configuration.server_uri, hooks=[_handle_http_status])
56+
super().__init__(
57+
base_url=configuration.server_uri,
58+
converter=_deserialize_model,
59+
hooks=[_handle_http_status],
60+
)
4961
if configuration.api_keys:
5062
self.session.headers.update(configuration.api_keys)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Optional
2+
3+
from ._json_model import JsonModel
4+
5+
6+
class PagedResult(JsonModel):
7+
continuation_token: Optional[str]
8+
"""A token which allows the user to resume a query at the next item in the matching results.
9+
10+
When querying, a token will be returned if a query may be
11+
continued. To obtain the next page of results, pass the token to the service
12+
on a subsequent request. The service will respond with a new continuation
13+
token. To paginate results, continue sending requests with the newest
14+
continuation token provided by the service, until this value is null.
15+
"""

nisystemlink/clients/dataframe/_data_frame_client.py

+73-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
"""Implementation of DataFrameClient."""
55

6-
from typing import Optional
6+
from typing import List, Optional
77

88
from nisystemlink.clients import core
99
from nisystemlink.clients.core._uplink._base_client import BaseClient
10-
from uplink import get, returns
10+
from uplink import Body, delete, get, json, post, Query, returns
1111

1212
from . import models
1313

@@ -28,8 +28,77 @@ def __init__(self, configuration: Optional[core.HttpConfiguration] = None):
2828

2929
super().__init__(configuration)
3030

31-
@returns.json()
3231
@get(_BASE_PATH)
3332
def api_info(self) -> models.ApiInfo:
3433
"""Returns information about available API operations."""
35-
pass
34+
...
35+
36+
@get(
37+
_BASE_PATH + "/tables",
38+
args=(
39+
Query("take"),
40+
Query("id"),
41+
Query("orderBy"),
42+
Query("orderByDescending"),
43+
Query("continuationToken"),
44+
Query("workspace"),
45+
),
46+
)
47+
def list_tables(
48+
self,
49+
take: Optional[int] = None,
50+
id: Optional[List[str]] = None,
51+
order_by: Optional[models.OrderBy] = None,
52+
order_by_descending: Optional[bool] = None,
53+
continuation_token: Optional[str] = None,
54+
workspace: Optional[List[str]] = None,
55+
) -> models.PagedTables:
56+
"""Lists available tables on the SystemLink DataFrame service.
57+
58+
Args:
59+
take: Limits the returned list to the specified number of results. Defaults to 1000.
60+
id: List of table IDs to filter by.
61+
order_by: The sort order of the returned list of tables.
62+
order_by_descending: Whether to sort descending instead of ascending. Defaults to false.
63+
continuation_token: The token used to paginate results.
64+
workspace: List of workspace IDs to filter by.
65+
66+
Returns:
67+
models.PagedTables: The list of tables with a continuation token.
68+
"""
69+
...
70+
71+
@json
72+
@returns.json(key="id")
73+
@post(_BASE_PATH + "/tables", args=(Body,))
74+
def create_table(self, table: models.CreateTableRequest) -> str:
75+
"""Create a new table with the provided metadata and column definitions.
76+
77+
Args:
78+
table: The request create the table.
79+
80+
Returns:
81+
The ID of the newly created table.
82+
"""
83+
...
84+
85+
@get(_BASE_PATH + "/tables/{id}")
86+
def get_table_metadata(self, id: str) -> models.TableMetadata:
87+
"""Retrieves the metadata and column information for a single table identified by its ID.
88+
89+
Args:
90+
id (str): Unique ID of a DataFrame table.
91+
92+
Returns:
93+
models.TableMetadata: The metadata for the table.
94+
"""
95+
...
96+
97+
@delete(_BASE_PATH + "/tables/{id}")
98+
def delete_table(self, id: str) -> None:
99+
"""Deletes a table.
100+
101+
Args:
102+
id (str): Unique ID of a DataFrame table.
103+
"""
104+
...
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
from ._api_info import ApiInfo, Operation, OperationsV1
2+
from ._create_table_request import CreateTableRequest
3+
from ._column import Column
4+
from ._column_type import ColumnType
5+
from ._data_type import DataType
6+
from ._order_by import OrderBy
7+
from ._paged_tables import PagedTables
8+
from ._table_metadata import TableMetadata
29

310
# flake8: noqa

nisystemlink/clients/dataframe/models/_api_info.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,33 @@
44
class Operation(JsonModel):
55
"""Represents an operation that can be performed on a data frame."""
66

7-
available: bool #: Whether or not the operation is available to the caller (e.g. due to permissions).
8-
version: int #: The version of the available operation.
7+
available: bool
8+
"""Whether or not the operation is available to the caller (e.g. due to permissions)."""
9+
10+
version: int
11+
"""The version of the available operation."""
912

1013

1114
class OperationsV1(JsonModel):
1215
"""The operations available in the routes provided by the v1 HTTP API."""
1316

14-
create_tables: Operation #: The ability to create new DataFrame tables.
15-
delete_tables: Operation #: The ability to delete tables and all of their data.
16-
modify_metadata: Operation #: The ability to modify metadata for tables.
17-
list_tables: Operation #: The ability to locate and read metadata for tables.
18-
read_data: Operation #: The ability to query and read data from tables.
19-
write_data: Operation #: The ability to append rows of data to tables.
17+
create_tables: Operation
18+
"""The ability to create new DataFrame tables."""
19+
20+
delete_tables: Operation
21+
"""The ability to delete tables and all of their data."""
22+
23+
modify_metadata: Operation
24+
"""The ability to modify metadata for tables."""
25+
26+
list_tables: Operation
27+
"""The ability to locate and read metadata for tables."""
28+
29+
read_data: Operation
30+
"""The ability to query and read data from tables."""
31+
32+
write_data: Operation
33+
"""The ability to append rows of data to tables."""
2034

2135

2236
class ApiInfo(JsonModel):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import Dict, Optional
2+
3+
from nisystemlink.clients.core._uplink._json_model import JsonModel
4+
5+
from ._column_type import ColumnType
6+
from ._data_type import DataType
7+
8+
9+
class Column(JsonModel):
10+
"""Defines a single column in a table."""
11+
12+
name: str
13+
"""The column name, which must be unique across all columns in the table."""
14+
15+
data_type: DataType
16+
"""The data type of the column."""
17+
18+
column_type: ColumnType = ColumnType.Normal
19+
"""The column type. Defaults to ColumnType.Normal."""
20+
21+
properties: Optional[Dict[str, str]] = None
22+
"""User-defined properties associated with the column."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from enum import Enum
2+
3+
4+
class ColumnType(str, Enum):
5+
"""Represents the different column types for a table column."""
6+
7+
Normal = "NORMAL"
8+
"""The column has no special properties. This is the default."""
9+
10+
Index = "INDEX"
11+
"""The column provides a unique value per row. Each table must provide
12+
exactly one INDEX column. The column's :class:`.DataType` must be INT32,
13+
INT64, or TIMESTAMP."""
14+
15+
Nullable = "NULLABLE"
16+
"""Rows may contain null values for this column. When appending rows,
17+
NULLABLE columns may be left out entirely, in which case all rows being
18+
appended will use null values for that column."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import Dict, List, Optional
2+
3+
from nisystemlink.clients.core._uplink._json_model import JsonModel
4+
5+
from ._column import Column
6+
7+
8+
class CreateTableRequest(JsonModel):
9+
"""Contains information needed to create a table, including its properties and column definitions."""
10+
11+
columns: List[Column]
12+
"""The list of columns in the table. Exactly one column must have a :class:`.ColumnType` of INDEX."""
13+
14+
name: Optional[str] = None
15+
"""The name to associate with the table. When not specified, a name will be
16+
assigned from the table's ID."""
17+
18+
properties: Optional[Dict[str, str]] = None
19+
"""User-defined properties to associate with the table."""
20+
21+
workspace: Optional[str] = None
22+
"""The workspace to create the table in. Uses the default workspace when not specified."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from enum import Enum
2+
3+
4+
class DataType(str, Enum):
5+
"""Represents the different data types for a table column."""
6+
7+
Bool = "BOOL"
8+
"""32-bit IEEE 754 floating-point number."""
9+
10+
Float32 = "FLOAT32"
11+
"""32-bit IEEE 754 floating-point number."""
12+
13+
Float64 = "FLOAT64"
14+
"""64-bit IEEE 754 floating-point number."""
15+
16+
Int32 = "INT32"
17+
"""32-bit signed integers."""
18+
19+
Int64 = "INT64"
20+
"""64-bit signed integers."""
21+
22+
String = "STRING"
23+
"""Arbitrary string data."""
24+
25+
Timestamp = "TIMESTAMP"
26+
"""Date and time represented in UTC with millisecond precision."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Literal
2+
3+
# TODO: Migrate to Enum when this change is released: https://github.com/prkumar/uplink/pull/282
4+
OrderBy = Literal[
5+
"CREATED_AT", "METADATA_MODIFIED_AT", "NAME", "NUMBER_OF_ROWS", "ROWS_MODIFIED_AT"
6+
]
7+
"""Possible options for sorting when querying tables.
8+
9+
* ``CREATED_AT``: The date and time the table was created.
10+
* ``METADATA_MODIFIED_AT``: The date and time the table's metadata properties were modified.
11+
* ``NAME``: The name of the table.
12+
* ``NUMBER_OF_ROWS``: The number of rows of data in the table.
13+
* ``ROWS_MODIFIED_AT``: Date and time rows were most recently appended to the table.
14+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import List
2+
3+
from nisystemlink.clients.core._uplink._paged_result import PagedResult
4+
5+
from ._table_metadata import TableMetadata
6+
7+
8+
class PagedTables(PagedResult):
9+
"""The response for a table query containing the matched tables."""
10+
11+
tables: List[TableMetadata]
12+
"""The list of tables returned by the query."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from datetime import datetime
2+
from typing import Dict, List
3+
4+
from nisystemlink.clients.core._uplink._json_model import JsonModel
5+
6+
from ._column import Column
7+
8+
9+
class TableMetadata(JsonModel):
10+
"""Contains information about a table, including its properties and column definitions."""
11+
12+
columns: List[Column]
13+
"""The list of columns in the table."""
14+
15+
created_at: datetime
16+
"""The date and time the table was created."""
17+
18+
id: str
19+
"""The table's unique identifier."""
20+
21+
metadata_modified_at: datetime
22+
"""The date and time the table's metadata was last modified."""
23+
24+
metadata_revision: int
25+
"""The table's metadata revision number, incremented each time the metadata is modified."""
26+
27+
name: str
28+
"""The name associated with the table."""
29+
30+
properties: Dict[str, str]
31+
"""User-defined properties associated with the table."""
32+
33+
row_count: int
34+
"""The number of rows in the table."""
35+
36+
rows_modified_at: datetime
37+
"""The date and time the table's data was last modified."""
38+
39+
supports_append: bool
40+
"""Whether the table supports appending additional rows of data."""
41+
42+
workspace: str
43+
"""The workspace the table belongs to."""

0 commit comments

Comments
 (0)