Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "tagoio-sdk"
version = "5.1.3"
version = "5.1.4"
description = "Official Python SDK for TagoIO"
authors = [{name = "TagoIO Inc.", email = "contact@tago.io"}]
license = {text = "Apache-2.0"}
Expand Down
2 changes: 2 additions & 0 deletions src/tagoio_sdk/modules/Resources/Resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .Run import Run
from .Secrets import Secrets
from .Service_Authorization import ServiceAuthorization
from .SQL import SQL


class Resources(TagoIOModule):
Expand All @@ -41,6 +42,7 @@ def __init__(self, params: Optional[GenericModuleParams] = None):
self.profile = Profile(params)
self.run = Run(params)
self.secrets = Secrets(params)
self.sql = SQL(params)
self.serviceAuthorization = ServiceAuthorization(params)
self.integration = Integration(params)
self.account = Account(params)
253 changes: 253 additions & 0 deletions src/tagoio_sdk/modules/Resources/SQL.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
from typing import Dict
from typing import List
from typing import Optional
from urllib.parse import quote

from tagoio_sdk.common import Cache
from tagoio_sdk.common.Common_Type import GenericID
from tagoio_sdk.common.tagoio_module import TagoIOModule
from tagoio_sdk.modules.Resources.SQL_Types import SQLCreateInfo
from tagoio_sdk.modules.Resources.SQL_Types import SQLExecuteObj
from tagoio_sdk.modules.Resources.SQL_Types import SQLExecuteResult
from tagoio_sdk.modules.Resources.SQL_Types import SQLInfo
from tagoio_sdk.modules.Resources.SQL_Types import SQLQuery
from tagoio_sdk.modules.Resources.SQL_Types import SQLTablesQuery
from tagoio_sdk.modules.Resources.SQL_Types import SQLTablesResult
from tagoio_sdk.modules.Resources.SQL_Types import SQLVersionInfo
from tagoio_sdk.modules.Utils.dateParser import dateParser
from tagoio_sdk.modules.Utils.dateParser import dateParserList


class SQL(TagoIOModule):
def list(self, queryObj: Optional[SQLQuery] = None) -> List[SQLInfo]:
"""
@description:
Retrieves a list with all TagoSQL queries from the profile.

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
If receive an error "Authorization Denied", check policy **SQL Query** / **Access** in Access Management.
```python
resources = Resources()
result = resources.sql.list({
"page": 1,
"fields": ["id", "name", "tags"],
"amount": 20,
})
print(result) # [{'id': 'query-id-123', 'name': 'My query', ...}]
```
"""
queryObj = queryObj or {}
params = {
"page": queryObj.get("page", 1),
"fields": queryObj.get("fields", ["id", "name", "tags"]),
"filter": queryObj.get("filter", {}),
"amount": queryObj.get("amount", 20),
}
if "orderBy" in queryObj:
params["orderBy"] = f"{queryObj['orderBy'][0]},{queryObj['orderBy'][1]}"

result = self.doRequest(
{
"path": "/sql",
"method": "GET",
"params": params,
}
)
return dateParserList(result, ["created_at", "updated_at"])

def create(self, sqlObj: SQLCreateInfo) -> SQLInfo:
"""
@description:
Creates a new TagoSQL query on the profile. Requires a profile token
(or an analysis token granted **Create** in Access Management).

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
```python
resources = Resources()
result = resources.sql.create({
"name": "Latest temperature",
"query": "SELECT variable, value, time FROM device($1) AS d WHERE variable = 'temperature' ORDER BY time DESC LIMIT 10",
"params": [{"key": "$1", "value": "my-device-id"}],
})
print(result) # {'id': 'query-id-123', 'name': 'Latest temperature', ...}
```
"""
result = self.doRequest(
{
"path": "/sql",
"method": "POST",
"body": sqlObj,
}
)
Cache.clear_cache()
return dateParser(result, ["created_at", "updated_at"])

def info(self, sqlID: GenericID) -> SQLInfo:
"""
@description:
Retrieves detailed information about a specific TagoSQL query.

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
If receive an error "Authorization Denied", check policy **SQL Query** / **Access** in Access Management.
```python
resources = Resources()
result = resources.sql.info("query-id-123")
print(result) # {'id': 'query-id-123', 'name': 'My query', 'query': 'SELECT ...', ...}
```
"""
result = self.doRequest(
{
"path": f"/sql/{sqlID}",
"method": "GET",
}
)
return dateParser(result, ["created_at", "updated_at"])

def edit(self, sqlID: GenericID, sqlObj: SQLCreateInfo) -> SQLInfo:
"""
@description:
Replaces a TagoSQL query. The query is re-validated and its cached
results are dropped; a new version is stored when the query or the
params change.

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
```python
resources = Resources()
result = resources.sql.edit("query-id-123", {
"name": "Latest temperature",
"query": "SELECT variable, value, time FROM device($1) AS d ORDER BY time DESC LIMIT 20",
"params": [{"key": "$1", "value": "my-device-id"}],
})
print(result) # {'id': 'query-id-123', 'version': 2, ...}
```
"""
result = self.doRequest(
{
"path": f"/sql/{sqlID}",
"method": "PUT",
"body": sqlObj,
}
)
Cache.clear_cache()
return dateParser(result, ["created_at", "updated_at"])

def delete(self, sqlID: GenericID) -> Dict[str, str]:
"""
@description:
Deletes a TagoSQL query from the profile.

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
```python
resources = Resources()
result = resources.sql.delete("query-id-123")
print(result) # {'id': 'query-id-123'}
```
"""
result = self.doRequest(
{
"path": f"/sql/{sqlID}",
"method": "DELETE",
}
)
Cache.clear_cache()
return result

def getVersion(self, sqlID: GenericID, version: int) -> SQLVersionInfo:
"""
@description:
Retrieves a historical snapshot (query and params) of a TagoSQL query.
To restore it, send the snapshot's query and params back with `edit`.

@see:
https://docs.tago.io/docs/tagoio/tagosql/queries TagoSQL Queries

@example:
```python
resources = Resources()
result = resources.sql.getVersion("query-id-123", 1)
print(result) # {'query': 'SELECT ...', 'params': [], 'created_at': ...}
```
"""
result = self.doRequest(
{
"path": f"/sql/{sqlID}/version/{version}",
"method": "GET",
}
)
return dateParser(result, ["created_at"])

def execute(
self, sqlID: GenericID, executeObj: Optional[SQLExecuteObj] = None
) -> SQLExecuteResult:
"""
@description:
Executes a TagoSQL query. Params sent here override the saved
defaults per key; `test: True` skips the result cache entirely.

@see:
https://docs.tago.io/docs/tagoio/tagosql/executing-queries Executing Queries

@example:
If receive an error "Authorization Denied", check policy **SQL Query** / **Execute** in Access Management.
```python
resources = Resources()
result = resources.sql.execute("query-id-123", {
"params": [{"key": "$1", "value": "my-device-id"}],
})
print(result) # {'columns': [...], 'rows': [...], 'row_count': 1, ...}
```
"""
return self.doRequest(
{
"path": f"/sql/{sqlID}/execute",
"method": "POST",
"body": executeObj or {},
}
)

def tables(self, queryObj: Optional[SQLTablesQuery] = None) -> SQLTablesResult:
"""
@description:
Retrieves the TagoSQL schema discovery catalog: the virtual table
families with their typed columns, plus the profile's devices and
entities. Pass `entity_id` to resolve one entity's columns.

@see:
https://docs.tago.io/docs/tagoio/tagosql/tables Available Tables

@example:
```python
resources = Resources()
result = resources.sql.tables({"filter": "sensor", "amount": 20})
print(result) # {'tables': [...], 'resources': {'devices': [...], 'entities': [...]}}
```
"""
queryObj = queryObj or {}
# ? doRequest expands object filters into filter[...] pairs; this
# ? endpoint's filter is a plain substring, so it goes on the path.
params = {key: value for key, value in queryObj.items() if key != "filter"}
path = "/sql/tables"
if "filter" in queryObj:
path = f"/sql/tables?filter={quote(str(queryObj['filter']))}"
return self.doRequest(
{
"path": path,
"method": "GET",
"params": params,
}
)
114 changes: 114 additions & 0 deletions src/tagoio_sdk/modules/Resources/SQL_Types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from datetime import datetime
from typing import List
from typing import Literal
from typing import Optional
from typing import TypedDict
from typing import Union

from tagoio_sdk.common.Common_Type import GenericID
from tagoio_sdk.common.Common_Type import Query
from tagoio_sdk.common.Common_Type import TagsObj


class SQLParam(TypedDict):
"""Positional parameter as ``{"key": "$n", "value": "..."}``."""

key: str
value: str


class SQLCreateInfo(TypedDict, total=False):
name: str
description: Optional[str]
query: str
params: Optional[List[SQLParam]]
cache_enabled: Optional[bool]
cache_ttl_seconds: Optional[int]
rate_limit_rpm: Optional[int]
active: Optional[bool]
tags: Optional[List[TagsObj]]


class SQLInfo(SQLCreateInfo):
id: GenericID
version: int
created_at: datetime
updated_at: datetime


class SQLQuery(Query):
fields: Optional[
List[
Literal[
"id",
"name",
"description",
"query",
"params",
"cache_enabled",
"cache_ttl_seconds",
"rate_limit_rpm",
"active",
"tags",
"version",
"created_at",
"updated_at",
]
]
]
filter: Optional[SQLInfo]


class SQLExecuteObj(TypedDict, total=False):
params: Optional[List[SQLParam]]
test: Optional[bool]
after_device: Optional[GenericID]


class SQLColumn(TypedDict):
name: str
type: Literal["string", "number", "timestamp", "boolean", "json"]


class SQLExecuteResult(TypedDict):
columns: List[SQLColumn]
rows: List[dict[str, Union[str, float, bool, None, dict, list]]]
row_count: int
execution_ms: int
served_from_cache: bool


class SQLVersionInfo(TypedDict):
query: str
params: List[SQLParam]
created_at: Optional[datetime]


class SQLTableInfo(TypedDict, total=False):
function: str
label: str
tag_form: Optional[str]
columns: List[SQLColumn]
dynamic: Optional[bool]


class SQLResourceItem(TypedDict):
id: GenericID
name: str


class SQLTablesResources(TypedDict):
devices: List[SQLResourceItem]
entities: List[SQLResourceItem]


class SQLTablesResult(TypedDict):
tables: List[SQLTableInfo]
resources: SQLTablesResources


class SQLTablesQuery(TypedDict, total=False):
filter: Optional[str]
amount: Optional[int]
page: Optional[int]
entity_id: Optional[GenericID]
Loading
Loading