diff --git a/docs/source/Resources/Devices/Devices_Type.rst b/docs/source/Resources/Devices/Devices_Type.rst index adc54c3..2b2f5d8 100644 --- a/docs/source/Resources/Devices/Devices_Type.rst +++ b/docs/source/Resources/Devices/Devices_Type.rst @@ -485,3 +485,77 @@ ListDeviceTokenQuery ]] | [Optional] Tuple with a field and an order + +.. _DeviceChunkData: + +DeviceChunkData +--------------- + + Chunk information from an immutable device. + + **Attributes:** + + | **id**: str + | Chunk ID in format 'from_to' (Unix timestamps). + + | **from_date**: str + | Start date of the chunk (ISO 8601). + + | **to_date**: str + | End date of the chunk (ISO 8601). + + | **amount**: int or float + | Amount of data records in the chunk. + + +.. _DeviceDataBackup: + +DeviceDataBackup +---------------- + + Parameters for device data backup operation. + + **Attributes:** + + | **deviceID**: :ref:`GenericID` + | Device ID. + + | **file_address**: str + | File path in TagoIO Files where backup will be saved. + | Can use template variables: $DEVICE$, $CHUNK$, $FROM$, $TO$, $TIMESTAMP$. + + | **headers**: Optional[bool] + | [Optional] Include CSV headers in the exported file. + + +.. _DeviceDataBackupResponse: + +DeviceDataBackupResponse +------------------------ + + Response from device data backup operation. + + **Attributes:** + + | **file_address**: str + | Final file path where the backup was saved. + + | **chunk_id**: Optional[str] + | [Optional] Chunk ID if backup was for a specific chunk. + + +.. _DeviceDataRestore: + +DeviceDataRestore +----------------- + + Parameters for device data restore operation. + + **Attributes:** + + | **deviceID**: :ref:`GenericID` + | Device ID. + + | **file_address**: str + | File path in TagoIO Files to restore data from (CSV format). + diff --git a/docs/source/Resources/Devices/index.rst b/docs/source/Resources/Devices/index.rst index 996834b..cf019d5 100644 --- a/docs/source/Resources/Devices/index.rst +++ b/docs/source/Resources/Devices/index.rst @@ -3,283 +3,699 @@ Manage devices in account. -======= +====== +amount +====== + +Gets the amount of data stored in a device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + **Parameters:** + + | **deviceID**: :ref:`GenericID` + | Device ID + + **Returns:** + + Union[int, float] + +.. code-block:: + :caption: **Example:** + + resources = Resources() + amount = resources.devices.amount("device-id-123") + print(amount) # 15234 + + +====== create -======= +====== + +Creates a new device in the account with specified configuration and returns device credentials. + + **See:** -Generates and retrieves a new action from the Device + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview + https://help.tago.io/portal/en/kb/articles/481-device-types Device Types **Parameters:** | **deviceObj**: :ref:`DeviceCreateInfo` | Object data to create new device + **Returns:** + + :ref:`DeviceCreateResponse` + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.create({ + "name": "Temperature Sensor", + "network": "network-id-123", + "connector": "connector-id-456", + "type": "immutable", + "chunk_period": "month", + "chunk_retention": 6, + "active": True + }) + print(result) # {'device_id': '...', 'bucket_id': '...', 'token': '...'} + + +========== +dataBackup +========== + +Schedule to export the device's data to TagoIO Files. +For mutable devices, exports all data. For immutable devices with chunkID, exports specific chunk. + + **See:** + + https://help.tago.io/portal/en/kb/articles/55-data-export Data Export + https://help.tago.io/portal/en/kb/articles/device-data#Backing_Up_Data Backing Up Data + + **Parameters:** + + | **params**: :ref:`DeviceDataBackup` + | Parameters for device data backup operation + + | **chunkID**: Optional[:ref:`GenericID`] + | [Optional] Chunk ID if backup is for a specific chunk + + **Returns:** + + :ref:`DeviceDataBackupResponse` + +.. code-block:: + :caption: **Example:** + + resources = Resources() + import time + timestamp = int(time.time()) + result = resources.devices.dataBackup({ + "deviceID": "device-id-123", + "file_address": f"/backups/device-id-123/{timestamp}", + "headers": True + }) + print(result) # {'file_address': 'backups/device-id-123/1736433519380.csv'} + + +=========== +dataRestore +=========== + +Restores data to a device from a CSV file in TagoIO Files. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data#Importing Importing Device Data + https://api.docs.tago.io/#7ebca255-6c38-43d3-97d0-9b62155f202e Import Data API + + **Parameters:** + + | **params**: :ref:`DeviceDataRestore` + | Parameters for device data restore operation + + **Returns:** + + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.dataRestore({ + "deviceID": "device-id-123", + "file_address": "/backups/backup.csv" + }) + print(result) # Data import added to the queue successfully! + + ====== delete ====== -Deletes an device from the account +Permanently deletes a device from the account along with all its data. + + **See:** + + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID + **Returns:** -====== + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.delete("device-id-123") + print(result) # Successfully Removed + + +=========== +deleteChunk +=========== + +Deletes a chunk from an immutable device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/chunk-management#Delete_chunks Delete Chunks + + **Parameters:** + + | **deviceID**: :ref:`GenericID` + | Device ID + + | **chunkID**: :ref:`GenericID` + | Chunk ID + + **Returns:** + + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.deleteChunk("device-id-123", "chunk-id-123") + print(result) # Chunk chunk-id-123 deleted + + +================ +deleteDeviceData +================ + +Deletes data from a device based on specified query parameters. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + **Parameters:** + + | **deviceID**: :ref:`GenericID` + | Device ID + + | **queryParam**: Optional[:ref:`DataQuery`] + | [Optional] Query parameters to filter the results + + **Returns:** + + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + # Delete specific records by ID + result = resources.devices.deleteDeviceData("device-id-123", { + "ids": ["record-id-1", "record-id-2"] + }) + # Delete by variable + result = resources.devices.deleteDeviceData("device-id-123", { + "variables": ["old_sensor"], + "qty": 100 + }) + print(result) # Successfully Removed + + +==== edit -====== +==== -Modify any property of the device +Modifies any property of an existing device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID | **deviceObj**: :ref:`DeviceEditInfo` | Device object with fields to replace + **Returns:** -================ + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.edit("device-id-123", { + "name": "Updated Sensor Name", + "active": False, + "tags": [{"key": "location", "value": "warehouse"}] + }) + print(result) # Successfully Updated + + +============== +editDeviceData +============== + +Modifies existing data records in a device. Requires the data record ID. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + **Parameters:** + + | **deviceID**: :ref:`GenericID` + | Device ID + + | **updatedData**: Union[DataEdit, List[DataEdit]] + | An array or one object with data to be updated + + **Returns:** + + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.editDeviceData("device-id-123", { + "id": "data-record-id", + "value": 30.0, + "unit": "°F" + }) + # Edit multiple records + result = resources.devices.editDeviceData("device-id-123", [ + {"id": "record-1-id", "value": 25.5}, + {"id": "record-2-id", "value": 65} + ]) + print(result) # Device Data Updated + + +=============== emptyDeviceData -================ +=============== + +Permanently removes all data from a device. This operation cannot be undone. + + **See:** -Empty all data in a device. + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID + **Returns:** -================ -getDeviceData -================ + str -Get data from all variables in the device. +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.emptyDeviceData("device-id-123") + print(result) # All data has been removed + + +======== +getChunk +======== + +Retrieves chunk information from an immutable device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/chunk-management Chunk Management **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID - | *Optional* **queryParams**: :ref:`DataQuery` - | Query parameters to filter the results. + **Returns:** + + List[:ref:`DeviceChunkData`] .. code-block:: :caption: **Example:** - from tagoio_sdk import Resources + resources = Resources() + chunks = resources.devices.getChunk("device-id-123") + print(chunks) # [{'amount': 0, 'id': 'chunk-id-123', 'from_date': '2025-01-09T00:00:00.000+00:00', ...}] + + +============= +getDeviceData +============= + +Retrieves data from all variables in the device with optional query filters. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + **Parameters:** + + | **deviceID**: :ref:`GenericID` + | Device ID + + | **queryParams**: Optional[:ref:`DataQuery`] + | [Optional] Query parameters to filter the results + + **Returns:** + + List[:ref:`CommonData`] + +.. code-block:: + :caption: **Example:** resources = Resources() - resources.devices.getDeviceData("myDeviceId"); + # Get all data + data = resources.devices.getDeviceData("device-id-123") + # Get specific variable data + temp_data = resources.devices.getDeviceData("device-id-123", { + "variables": ["temperature"], + "qty": 10 + }) + print(temp_data) # [{'variable': 'temperature', 'value': 25.5, 'time': '...', ...}] + -===== +==== info -===== +==== + +Retrieves detailed information about a specific device. + + **See:** -Get Info of the Device + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID + **Returns:** + + :ref:`DeviceInfo` + +.. code-block:: + :caption: **Example:** + + resources = Resources() + device_info = resources.devices.info("device-id-123") + print(device_info) # {'id': '...', 'name': '...', 'type': 'mutable', ...} + ========== listDevice ========== -Retrieves a list with all devices from the account +Retrieves a list of all devices from the account with optional filtering and pagination. + + **See:** + + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview **Parameters:** - | *Optional* **queryObj**: :ref:`DeviceQuery` - | Search query params + | **queryObj**: Optional[:ref:`DeviceQuery`] + | [Optional] Search query params + + **Returns:** + + List[:ref:`DeviceListItem`] .. code-block:: - :caption: **Default queryObj:** + :caption: **Example:** - queryObj: { + resources = Resources() + devices = resources.devices.listDevice({ "page": 1, - "fields": ["id", "name"], - "filter": {}, "amount": 20, - "orderBy": ["name", "asc"], - "resolveBucketName": False - } + "fields": ["id", "name", "active"], + "filter": {"name": "Temperature*"}, + "orderBy": ["name", "asc"] + }) + print(devices) # [{'id': 'device-id-123', 'name': 'Temperature Sensor', ...}] + ========= -paramSet +paramList ========= -Create or edit param for the Device +Retrieves a list of configuration parameters for a device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID - | **configObj**: :ref:`ConfigurationParams` or list[:ref:`ConfigurationParams`] - | Configuration Data + | **sentStatus**: Optional[bool] + | [Optional] True return only sent=true, False return only sent=false - | **paramID**: Optional[GenericID: str] - | Parameter ID + **Returns:** + List[:ref:`ConfigurationParams`] -========== -paramList -========== +.. code-block:: + :caption: **Example:** -List Params for the Device + resources = Resources() + # Get all parameters + params = resources.devices.paramList("device-id-123") + # Get only sent parameters + sent_params = resources.devices.paramList("device-id-123", sentStatus=True) + print(params) # [{'id': '...', 'key': 'threshold', 'value': '25.5', 'sent': False}] - **Parameters:** - | **deviceID**: GenericID: str - | Device ID +=========== +paramRemove +=========== - | *Optional* **sentStatus**: bool - | True return only sent=true, False return only sent=false +Removes a specific configuration parameter from a device. -============ -paramRemove -============ + **See:** -Remove param for the Device + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID - | **paramID**: GenericID: str + | **paramID**: :ref:`GenericID` | Parameter ID + **Returns:** -============ -tokenCreate -============ + str + +.. code-block:: + :caption: **Example:** -Generates and retrieves a new token + resources = Resources() + result = resources.devices.paramRemove("device-id-123", "param-id-456") + print(result) # Successfully Removed - **Parameters:** - | **deviceID**: GenericID: str - | Device ID +======== +paramSet +======== - | **tokenParams**: :ref:`DevicesTokenData` - | Params for new token +Creates new configuration parameters or updates existing ones for a device. -============ -tokenDelete -============ + **See:** -Delete a token + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters **Parameters:** - | **token**: GenericToken: str + | **deviceID**: :ref:`GenericID` | Device ID -========== -tokenList -========== + | **configObj**: Union[:ref:`ConfigurationParams`, List[:ref:`ConfigurationParams`]] + | Configuration Data -Retrieves a list of all tokens + | **paramID**: Optional[:ref:`GenericID`] + | [Optional] Parameter ID - **Parameters:** + **Returns:** - | **token**: GenericToken: str - | Device ID - - | *Optional* **queryObj**: :ref:`ListDeviceTokenQuery` - | Search query params + str .. code-block:: - :caption: **Default queryObj:** + :caption: **Example:** + + resources = Resources() + # Create new parameter + result = resources.devices.paramSet("device-id-123", { + "key": "threshold", + "value": "25.5", + "sent": False + }) + # Update existing parameter + result = resources.devices.paramSet("device-id-123", { + "key": "threshold", + "value": "30.0", + "sent": False + }, "param-id-456") + print(result) # Successfully Updated - queryObj: { - "page": 1, - "fields": ["name", "token", "permission"], - "filter": {}, - "amount": 20, - "orderBy": ["created_at", "desc"], - } ============== sendDeviceData ============== -Send data to a device. +Sends data to a device. Accepts a single data object or an array of data objects. + + **See:** + + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID - | **data**: Union[:ref:`CommonData`, list[:ref:`CommonData`]] - | An array or one object with data to be send to TagoIO. + | **data**: Union[DataCreate, List[DataCreate]] + | An array or one object with data to be sent to TagoIO + + **Returns:** + + str .. code-block:: :caption: **Example:** - from tagoio_sdk import Resources - resources = Resources() - resource.devices.sendDeviceData("myDeviceID", { + result = resources.devices.sendDeviceData("device-id-123", { "variable": "temperature", - "unit": "F", - "value": 55, - "time": "2015-11-03 13:44:33", - "location": { "lat": 42.2974279, "lng": -85.628292 }, + "value": 25.5, + "unit": "°C", + "time": "2025-01-09 13:44:33", + "location": {"lat": 42.2974279, "lng": -85.628292} }) + # Send multiple data points + result = resources.devices.sendDeviceData("device-id-123", [ + {"variable": "temperature", "value": 25.5}, + {"variable": "humidity", "value": 60} + ]) + print(result) # Successfully Inserted -============== -editDeviceData -============== -Edit data in a device. +=========== +tokenCreate +=========== + +Generates and retrieves a new authentication token for a device. + + **See:** + + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens **Parameters:** - | **deviceID**: GenericID: str + | **deviceID**: :ref:`GenericID` | Device ID - | **updatedData**: Union[:ref:`CommonData`, list[:ref:`CommonData`]] - | An array or one object with data to be send to TagoIO. + | **tokenParams**: :ref:`CommonTokenData` + | Params for new token + + **Returns:** + + :ref:`TokenCreateResponse` .. code-block:: :caption: **Example:** - resources = Resource() - resource.devices.editDeviceData("myDeviceID", { - "id": "idOfTheRecord", - "value": "new value", - "unit": "new unit" - }) + resources = Resources() + result = resources.devices.tokenCreate("device-id-123", { + "name": "Production Token", + "permission": "write", + "expire_time": "never" + }) + print(result) # {'token': 'new-token-value', 'expire_date': None} -================ -deleteDeviceData -================ -Delete data from a device. +=========== +tokenDelete +=========== + +Permanently deletes an authentication token. + + **See:** + + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens **Parameters:** - | **deviceID**: GenericID: str + | **token**: :ref:`GenericToken` + | Token to delete + + **Returns:** + + str + +.. code-block:: + :caption: **Example:** + + resources = Resources() + result = resources.devices.tokenDelete("token-to-delete") + print(result) # Successfully Removed + + +========= +tokenList +========= + +Retrieves a list of all authentication tokens for a device with optional filtering. + + **See:** + + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens + + **Parameters:** + + | **deviceID**: :ref:`GenericID` | Device ID - | *Optional* **queryParams**: :ref:`DataQuery` - | Query parameters to filter the results. + | **queryObj**: Optional[:ref:`ListDeviceTokenQuery`] + | [Optional] Search query params + + **Returns:** + + List[:ref:`DeviceTokenDataList`] .. code-block:: :caption: **Example:** - resources = Resource() - resource.devices.deleteDeviceData("myDeviceID", { - "ids": ["recordIdToDelete", "anotherRecordIdToDelete" ] - }) + resources = Resources() + tokens = resources.devices.tokenList("device-id-123", { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"], + "orderBy": ["created_at", "desc"] + }) + print(tokens) # [{'name': 'Default Token', 'token': '...', 'permission': 'full', ...}] + .. toctree:: diff --git a/docs/source/Resources/IntegrationNetwork/IntegrationNetwork_Type.rst b/docs/source/Resources/IntegrationNetwork/IntegrationNetwork_Type.rst index 986d6a4..ff2ab4f 100644 --- a/docs/source/Resources/IntegrationNetwork/IntegrationNetwork_Type.rst +++ b/docs/source/Resources/IntegrationNetwork/IntegrationNetwork_Type.rst @@ -96,3 +96,43 @@ TokenData | **serie_number**: Optional[str] | **verification_code**: Optional[str] | **middleware**: Optional[str] + + +.. _NetworkTokenInfo: + +NetworkTokenInfo +---------------- + **Attributes:** + + | **token**: str + | **network**: :ref:`GenericID` + | **name**: str + | **permission**: str + | **created_at**: datetime + | **updated_at**: Optional[datetime] + + +.. _NetworkQuery: + +NetworkQuery +------------ + **Attributes:** + + | **page**: int + | **amount**: int + | **fields**: list[str] + | **filter**: dict + | **orderBy**: list[str] + + +.. _ListTokenQuery: + +ListTokenQuery +-------------- + **Attributes:** + + | **page**: int + | **amount**: int + | **fields**: list[str] + | **filter**: dict + | **orderBy**: list[str] diff --git a/docs/source/Resources/IntegrationNetwork/index.rst b/docs/source/Resources/IntegrationNetwork/index.rst index 703d5b5..8f79334 100644 --- a/docs/source/Resources/IntegrationNetwork/index.rst +++ b/docs/source/Resources/IntegrationNetwork/index.rst @@ -1,29 +1,277 @@ **Integration Network** ======================= -Manage integration network in the account +Manage integration networks in the account. ============ listNetwork ============ -Get list of networks + +Retrieves a list of all networks from the account with pagination support. +Use this to retrieve and manage networks in your application. + +See: `Network Integration `_ **Parameters:** - | **queryObj**: :ref:`Query` - | Network Query + | *Optional* **queryObj**: :ref:`NetworkQuery` + | Query parameters to filter the results. + + .. code-block:: + :caption: **Default queryObj:** + + queryObj = { + "page": 1, + "fields": ["id", "name"], + "filter": {}, + "amount": 20, + "orderBy": ["name", "asc"] + } + + **Returns:** + + | List[:ref:`NetworkInfo`] + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Access" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + networks = resources.integration.networks.listNetwork({ + "page": 1, + "fields": ["id", "name"], + "amount": 20, + "orderBy": ["name", "asc"] + }) + print(networks) # [{'id': 'network-id-123', 'name': 'My Network', ...}] ====== info ====== -Gets information about the network +Retrieves detailed information about a specific network. + +See: `Network Integration `_ + + **Parameters:** + + | **networkID**: :ref:`GenericID` + | Network ID + + | *Optional* **fields**: List[str] + | Fields to retrieve (default: ["id", "name"]) + + **Returns:** + + | :ref:`NetworkInfo` + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Access" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + network_info = resources.integration.networks.info("network-id-123") + print(network_info) # {'id': '...', 'name': 'My Network', ...} + + +====== +create +====== + +Creates a new integration network in the account. + +See: `Creating a Network Integration `_ + + **Parameters:** + + | **networkObj**: :ref:`NetworkCreateInfo` + | Network information + + **Returns:** + + | Dict[str, str] + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Create" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + new_network = resources.integration.networks.create({ + "name": "My Custom Network", + "description": "Custom integration network", + "middleware_endpoint": "https://my-middleware.com/endpoint", + "public": False + }) + print(new_network) # {'network': 'network-id-123'} + + +====== +edit +====== + +Modifies any property of an existing network. + + **Parameters:** + + | **networkID**: :ref:`GenericID` + | Network ID + + | **networkObj**: :ref:`NetworkCreateInfo` + | Network information to update + + **Returns:** + + | str + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Edit" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.integration.networks.edit("network-id-123", { + "name": "Updated Network Name", + "description": "Updated description", + "public": True + }) + print(result) # Successfully Updated + + +====== +delete +====== + +Permanently deletes a network from the account. **Parameters:** - | **networkID**: GenericID: str - | Network identification + | **networkID**: :ref:`GenericID` + | Network ID + + **Returns:** + + | str + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Delete" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.integration.networks.delete("network-id-123") + print(result) # Successfully Removed + + +========== +tokenList +========== + +Retrieves a list of all authentication tokens for a network with optional filtering. + +See: `Tokens and Getting Devices `_ + + **Parameters:** + + | **networkID**: :ref:`GenericID` + | Network ID + + | *Optional* **queryObj**: :ref:`ListTokenQuery` + | Query parameters to filter the results. + + .. code-block:: + :caption: **Default queryObj:** + + queryObj = { + "page": 1, + "fields": ["name", "token", "permission"], + "filter": {}, + "amount": 20, + "orderBy": ["created_at", "desc"] + } + + **Returns:** + + | List[:ref:`NetworkTokenInfo`] + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Access" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + tokens = resources.integration.networks.tokenList("network-id-123", { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"], + "orderBy": ["created_at", "desc"] + }) + print(tokens) # [{'name': 'Default Token', 'token': '...', 'permission': 'full', ...}] + + +=========== +tokenCreate +=========== + +Generates and retrieves a new authentication token for a network. + +See: `Tokens and Getting Devices `_ + + **Parameters:** + + | **networkID**: :ref:`GenericID` + | Network ID + + | **tokenParams**: :ref:`IntegrationTokenData` + | Parameters for the new token + + **Returns:** + + | :ref:`TokenCreateResponse` + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Create Token" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.integration.networks.tokenCreate("network-id-123", { + "name": "Production Token", + "permission": "write", + "expire_time": "never" + }) + print(result) # {'token': 'new-token-value', 'expire_date': None} + + +=========== +tokenDelete +=========== + +Permanently deletes an authentication token. + +See: `Tokens and Getting Devices `_ + + **Parameters:** + + | **token**: :ref:`GenericToken` + | Token to delete + + **Returns:** + + | str + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy "Integration Network" / "Delete Token" in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.integration.networks.tokenDelete("token-to-delete") + print(result) # Successfully Removed + .. toctree:: diff --git a/docs/source/Resources/Profile/Profile_Types.rst b/docs/source/Resources/Profile/Profile_Types.rst index 98e34cd..39453c8 100644 --- a/docs/source/Resources/Profile/Profile_Types.rst +++ b/docs/source/Resources/Profile/Profile_Types.rst @@ -226,3 +226,230 @@ TypedDict representing the summary of a profile. | **addons**: :ref:`ProfileAddOns` | The add-ons of the profile. + + +.. _UsageStatistic: + +UsageStatistic +----------------- +TypedDict representing a single usage statistic with timestamp. + +Not all of the services will be present for every statistic, only if for the usage period the service was used. + + **Properties:** + + | **time**: datetime + | Timestamp for the usage statistic. + + | **input**: Union[int, float] + | Input data usage. + + | **output**: Union[int, float] + | Output data usage. + + | **analysis**: Union[int, float] + | Analysis execution time used. + + | **sms**: Union[int, float] + | SMS messages sent. + + | **email**: Union[int, float] + | Email messages sent. + + | **data_records**: Union[int, float] + | Data records stored. + + | **run_users**: Union[int, float] + | Run users used. + + | **push_notification**: Union[int, float] + | Push notifications sent. + + | **file_storage**: Union[int, float] + | File storage used. + + | **tcore**: Union[int, float] + | TCore resources used. + + +.. _AuditLogEvent: + +AuditLogEvent +----------------- +TypedDict representing a single audit log event. + + **Properties:** + + | **resourceName**: str + | Name of the resource that triggered the event. + + | **message**: str + | Descriptive message about the event. + + | **resourceID**: :ref:`GenericID` + | ID of the resource that triggered the event. + + | **who**: :ref:`GenericID` + | ID of the account that performed the action. + + | **date**: datetime + | Timestamp when the event occurred. + + +.. _AuditLogStatistics: + +AuditLogStatistics +------------------ +TypedDict representing statistics for an audit log query. + + **Properties:** + + | **recordsMatched**: int + | Number of records that matched the query. + + | **recordsScanned**: int + | Number of records scanned during the query. + + | **bytesScanned**: int + | Number of bytes scanned during the query. + + +.. _AuditLog: + +AuditLog +----------------- +TypedDict representing an audit log query result. + + **Properties:** + + | **events**: list[:ref:`AuditLogEvent`] + | List of audit log events. + + | **statistics**: :ref:`AuditLogStatistics` + | Statistics about the query execution. + + | **status**: Literal["Running", "Complete", "Failed", "Timeout", "Unknown"] + | Current status of the audit log query. + + | **queryId**: str + | Unique identifier for the audit log query. + + +.. _AuditLogFilter: + +AuditLogFilter +----------------- +TypedDict representing filters for audit log queries. + + **Properties:** + + | **resourceID**: :ref:`GenericID` + | Filter by specific resource ID. + + | **resourceName**: Literal["action", "am", "analysis", "connector", "dashboard", "device", "dictionary", "network", "profile", "run", "runuser"] + | Filter by resource type. + + | **find**: str + | Search string for filtering events. + + | **start_date**: Union[str, datetime] + | Start date for the query range. + + | **end_date**: Union[str, datetime] + | End date for the query range. + + | **limit**: int + | Maximum number of results to return. + + +.. _AddonInfo: + +AddonInfo +----------------- +TypedDict representing profile addon information. + + **Properties:** + + | **id**: :ref:`GenericID` + | The addon ID. + + | **name**: str + | The addon name. + + | **logo_url**: Optional[str] + | URL of the addon's logo. + + +.. _StatisticsDate: + +StatisticsDate +----------------- +TypedDict representing parameters for fetching usage statistics. + + **Properties:** + + | **timezone**: str + | Timezone to be used in the statistics entries (default: "UTC"). + + | **date**: Union[str, datetime] + | Timestamp for fetching hourly statistics in a day. + + | **start_date**: Union[str, datetime] + | Starting date for fetching statistics in an interval. + + | **end_date**: Union[str, datetime] + | End date for fetching statistics in an interval. + + | **periodicity**: Literal["hour", "day", "month"] + | Periodicity of the statistics to fetch (default: "hour"). + + +.. _ProfileTeam: + +ProfileTeam +----------------- +TypedDict representing a team member with access to a profile. + + **Properties:** + + | **active**: bool + | Whether the team member's access is active. + + | **created_at**: datetime + | When the team member was added. + + | **email**: str + | Email address of the team member. + + | **id**: str + | Account ID of the team member. + + | **name**: str + | Name of the team member. + + +.. _ProfileCreateInfo: + +ProfileCreateInfo +----------------- +TypedDict representing the information needed to create a new profile. + + **Properties:** + + | **name**: str + | Name of the profile to be created. + + +.. _ProfileCredentials: + +ProfileCredentials +------------------ +TypedDict representing credentials required for sensitive profile operations. + + **Properties:** + + | **password**: str + | Account password. + + | **pin_code**: str + | Two-factor authentication PIN code (required when 2FA is enabled). diff --git a/docs/source/Resources/Profile/index.rst b/docs/source/Resources/Profile/index.rst index 8f884fe..4f66a67 100644 --- a/docs/source/Resources/Profile/index.rst +++ b/docs/source/Resources/Profile/index.rst @@ -1,98 +1,518 @@ **Profile** -============ +=========== -Manage profiles in account. +Manage profiles in your TagoIO account. ==== info ==== -Gets information about the bucket +Retrieves detailed information about a specific profile using its ID or 'current' for the active profile. + +See: `Profiles `_ **Parameters:** - | **profileID**: GenericID: str - | Profile identification + | **profileID**: :ref:`GenericID` + | Profile identification (use "current" for the active profile) **Returns:** - | **result**: :ref:`ProfileInfo` + | :ref:`ProfileInfo` -.. code-block:: - :caption: **Example:** + .. code-block:: python + # If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. from tagoio_sdk import Resources resources = Resources() - result = resources.profile.info("Profile ID") + profile_info = resources.profile.info("profile-id-123") + # Or get current profile + current_profile = resources.profile.info("current") + print(profile_info) # {'info': {'id': 'profile-id-123', 'account': 'account-id-123', ...}, ...} + ==== list ==== -Lists all the profiles in your account +Retrieves a list of all profiles associated with the current account. + +See: `Profiles `_ **Returns:** - | **result**: list[:ref:`ProfileListInfo`] + | list[:ref:`ProfileListInfo`] -.. code-block:: - :caption: **Example:** + .. code-block:: python + # If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. from tagoio_sdk import Resources resources = Resources() result = resources.profile.list() + print(result) # [{'id': 'profile-id-123', 'name': 'Profile Test', ...}] -======== +======= summary -======== +======= + +Retrieves a summary of the profile's usage and statistics. -Gets profile summary +See: `Profiles `_ **Parameters:** - | **profileID**: GenericID: str + | **profileID**: :ref:`GenericID` | Profile identification **Returns:** - | **result**: :ref:`ProfileSummary` + | :ref:`ProfileSummary` -.. code-block:: - :caption: **Example:** + .. code-block:: python + # If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. from tagoio_sdk import Resources resources = Resources() - result = resources.profile.summary("Profile ID") + result = resources.profile.summary("profile-id-123") + print(result) # {'amount': {'device': 10, 'bucket': 10, 'dashboard': 5, ...}, ...} ========= tokenList ========= -Gets profile tokenList +Retrieves a list of all tokens associated with a specific profile. + +See: `Account Token `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | *Optional* **queryObj**: :ref:`Query` + | Query parameters to filter the results + + .. code-block:: + :caption: **Default queryObj:** + + queryObj = { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"], + "filter": {}, + "orderBy": ["created_at", "asc"] + } + + **Returns:** + + | list[:ref:`TokenDataList`] + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.tokenList("profile-id-123", { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"] + }) + print(result) # [{'name': 'Token #1', 'token': 'token-value', 'permission': 'full', ...}, ...] + + +====== +create +====== + +Creates a new profile with the specified name and optional resource allocation settings. + +See: `Profiles `_ + + **Parameters:** + + | **profileObj**: :ref:`ProfileCreateInfo` + | Profile information to create + + | *Optional* **allocate_free_resources**: bool + | Whether to allocate free resources to the new profile (default: False) + + **Returns:** + + | dict with key "id" containing the new profile ID + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.create({"name": "New Profile"}, allocate_free_resources=True) + print(result) # {'id': 'profile-id-123'} + + +==== +edit +==== + +Updates profile information with the provided data. + +See: `Profiles `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **profileObj**: dict + | Profile information to update + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.edit("profile-id-123", {"name": "Updated Profile Name"}) + print(result) # Successfully Updated + + +====== +delete +====== + +Permanently removes a profile from the account. + +See: `Two-Factor Authentication (2FA) `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **credentials**: :ref:`ProfileCredentials` + | Account credentials (password and pin_code if 2FA is enabled) + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + # The "pin_code" field is required when 2FA is activated + result = resources.profile.delete("profile-id-123", {"password": "your-password", "pin_code": "123456"}) + print(result) # Successfully Removed + + +================== +usageStatisticList +================== + +Retrieves usage statistics for a profile within a specified time period. + +Usage statistics are cumulative: if a service was not used in a time period, the statistics for that time period will not be in the object. + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | *Optional* **dateObj**: :ref:`StatisticsDate` + | Date range and periodicity parameters + + **Returns:** + + | list[:ref:`UsageStatistic`] + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy **Account** / **Access profile statistics** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.profile.usageStatisticList("profile-id-123", { + "start_date": "2024-09-01", + "end_date": "2024-12-31", + "periodicity": "day" + }) + print(result) # [{'time': '2024-09-02T00:01:29.749Z', 'analysis': 0.07, 'data_records': 67254, ...}, ...] + + +======== +auditLog +======== + +Creates a new audit log query for tracking profile activities. + +See: `Audit Log `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | *Optional* **filterObj**: :ref:`AuditLogFilter` + | Filters to apply to the audit log query + + **Returns:** + + | :ref:`AuditLog` + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.auditLog("profile-id-123", { + "start_date": "2024-12-01", + "end_date": "2024-12-07" + }) + print(result) + + +============== +auditLogQuery +============== + +Retrieves audit log entries using a previously created query. + +See: `Audit Log `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **queryId**: str + | Query ID from a previous auditLog call + + **Returns:** + + | :ref:`AuditLog` + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.auditLogQuery("profile-id-123", "query-id-456") + print(result) + + +=========== +serviceEdit +=========== + +Updates service configuration and resource limits for a profile. + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **serviceObj**: dict + | Service configuration and resource limits to update + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.serviceEdit("profile-id-123", { + "input": 350000, + "output": 342153, + "analysis": 5 + }) + print(result) # Profile resource allocation Successfully Updated + + +================================= +transferTokenToAnotherProfile +================================= + +Transfers the current authentication token to another profile. + + **Parameters:** + + | **targetProfileID**: :ref:`GenericID` + | Target profile identification + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.transferTokenToAnotherProfile("target-profile-123") + print(result) + + +=========== +tokenCreate +=========== + +Creates a new authentication token for the specified profile. + +See: `Account Token `_ + +See: `Two-Factor Authentication (2FA) `_ **Parameters:** - | **profileID**: GenericID: str + | **profileID**: :ref:`GenericID` | Profile identification - | **queryObj**: Optional[:ref:`Query`] - | Token Query + + | **tokenParams**: :ref:`CommonTokenData` + | Token parameters including name, permission, email, and password **Returns:** - | **result**: list[:ref:`TokenDataList`] + | :ref:`TokenCreateResponse` -.. code-block:: - :caption: **Example:** + .. code-block:: python from tagoio_sdk import Resources + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + # The "pin_code" / "otp_type" field is required when 2FA is activated + result = resources.profile.tokenCreate("profile-id-123", { + "name": "API Access", + "permission": "full", + "email": "example@email.com", + "password": "your-password" + }) + print(result) # {'token': 'token-value', 'name': 'API Access', ...} + + +=========== +tokenDelete +=========== + +Revokes and removes an authentication token from the profile. + +See: `Account Token `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **token**: :ref:`GenericToken` + | Token to be deleted + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.tokenDelete("profile-id-123", "token-xyz") + print(result) # Token Successfully Removed + + +============= +addTeamMember +============= + +Adds a new team member to the profile using their email address. + +See: `Team Management - Sharing your Profile `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **email**: str + | Email address of the team member to invite + + **Returns:** + + | str - Success message + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.addTeamMember("profile-id-123", "user@example.com") + print(result) # User invited + + +======== +teamList +======== + +Retrieves a list of all team members that have access to the specified profile. + +See: `Team Management - Sharing your Profile `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + **Returns:** + + | list[:ref:`ProfileTeam`] + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.teamList("profile-id-123") + print(result) # [{'id': 'account-id-123', 'active': False, 'name': 'John Doe', ...}, ...] + + +================ +deleteTeamMember +================ + +Removes a team member from the profile. + +See: `Team Management - Sharing your Profile `_ + + **Parameters:** + + | **profileID**: :ref:`GenericID` + | Profile identification + + | **accountId**: str + | Account ID of the team member to remove + + **Returns:** + + | str - Success message + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy in Access Management. + from tagoio_sdk import Resources + resources = Resources() - result = resources.profile.tokenList("Profile ID") + result = resources.profile.deleteTeamMember("profile-id-123", "account-id-456") + print(result) # Account Successfully Removed + .. toctree:: diff --git a/docs/source/Resources/Run/Run_Types.rst b/docs/source/Resources/Run/Run_Types.rst index 588da9b..0620063 100644 --- a/docs/source/Resources/Run/Run_Types.rst +++ b/docs/source/Resources/Run/Run_Types.rst @@ -319,6 +319,21 @@ LoginAsUserOptions | :default: "8 hours" +.. _EmailTestData: + +EmailTestData +------------- + +Type for the email test data. + + **Attributes:** + + | **subject**: str + | Subject of the test email. + + | **body**: str + | Body content of the test email. + .. _SAMLAttributeMappings: diff --git a/docs/source/Resources/Run/index.rst b/docs/source/Resources/Run/index.rst index 3a27538..ce60e23 100644 --- a/docs/source/Resources/Run/index.rst +++ b/docs/source/Resources/Run/index.rst @@ -1,320 +1,543 @@ - **Run** -======== +======= -Manage services in account. +Manage TagoRUN environment configuration and users. -======= +==== info -======= +==== -Get information about the TagoRUN service. +Retrieves information about the current Run environment configuration. - **Returns** +See: `TagoRun `_ | `Run Themes `_ - | **result**: :ref:`RunInfo` - | Information about the TagoRUN service. + **Returns:** + | :ref:`RunInfo` -======= + .. code-block:: python + + # If receive an error "Authorization Denied", check policy **Profile** / **Access TagoRun settings** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.info() + print(result) # {'name': 'My Run Environment', 'logo': 'https://example.com/logo.png', ...} + + +==== edit -======= +==== + +Updates the Run environment configuration settings. -Edit the TagoRUN service information. +See: `TagoRun `_ | `Run Themes `_ **Parameters:** - | **data**: :ref:`RunInfo` - | Updated information for the TagoRUN service. + | **data**: dict + | Run configuration data to update **Returns:** - | **result**: str - | Success message. + | str + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy **Profile** / **Edit TagoRun settings** in Access Management. + from tagoio_sdk import Resources + resources = Resources() + result = resources.run.edit({"name": "My Run Environment", "logo": "https://example.com/logo.png"}) + print(result) # TagoIO Run Successfully Updated -============ + +========= listUsers -============ +========= + +Retrieves a paginated list of Run users with customizable fields and filtering options. -List users in the TagoRUN service. +See: `TagoRun `_ **Parameters:** | **query**: :ref:`Query` - | Query parameters for filtering and sorting the user list. + | Query parameters for filtering and sorting - **Returns:** + .. code-block:: + :caption: **Default query:** - | **result**: list[:ref:`UserInfo`] - | List of user information. + query = { + "page": 1, + "fields": ["id", "name"], + "filter": {}, + "amount": 20, + "orderBy": ["name", "asc"] + } + **Returns:** -============ -userInfo -============ - -Get information about a specific user in the TagoRUN service. + | list[:ref:`UserInfo`] - **Parameters:** + .. code-block:: python - | **userID**: :ref:`GenericID` - | ID of the user. + # If receive an error "Authorization Denied", or return empty list check policy **Run User** / **Access** in Access Management. + from tagoio_sdk import Resources - **Returns:** + resources = Resources() + result = resources.run.listUsers({ + "page": 1, + "fields": ["id", "name", "email"], + "amount": 20 + }) + print(result) # [{'id': 'user-id-123', 'name': 'John Doe', 'email': 'example@email.com'}] - | **result**: :ref:`UserInfo` - | Information about the user. +======== +userInfo +======== -================ -userCreate -================ +Retrieves detailed information about a specific Run user. -Create a new user in the TagoRUN service. +See: `TagoRun `_ **Parameters:** - | **data**: :ref:`UserCreateInfo` - | Information for creating the user. + | **userID**: :ref:`GenericID` + | User identification **Returns:** - | **result**: str - | Success message. + | :ref:`UserInfo` + .. code-block:: python -================ + # If receive an error "Authorization Denied", check policy **Run User** / **Access** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.userInfo("user-id-123") + print(result) # {'id': 'user-id-123', 'name': 'John Doe', 'email': 'example@email.com', ...} + + +========== userCreate -================ +========== + +Creates a new user in the Run environment. -Create a new user in the TagoRUN service. +See: `TagoRun `_ **Parameters:** | **data**: :ref:`UserCreateInfo` - | Information for creating the user. + | User creation data **Returns:** - | **result**: str - | Success message. + | dict + .. code-block:: python -================ + # If receive an error "Authorization Denied", check policy **Run User** / **Create** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.userCreate({ + "name": "John Doe", + "email": "john@example.com", + "password": "secure123", + "timezone": "America/New_York" + }) + print(result) # {'user': 'user-id-123'} + + +======== userEdit -================ +======== -Edit information about a specific user in the TagoRUN service. +Updates information for an existing Run user. + +See: `TagoRun `_ **Parameters:** | **userID**: :ref:`GenericID` - | ID of the user. + | User identification - | **data**: :ref:`UserInfo` - | Updated information for the user. + | **data**: dict + | User data to update **Returns:** - | **result**: str - | Success message. + | str + .. code-block:: python -================== + # If receive an error "Authorization Denied", check policy **Run User** / **Edit** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.userEdit("user-id-123", {"name": "Updated Name"}) + print(result) # TagoIO Run User Successfully Updated + + +========== userDelete -================== +========== + +Permanently deletes a user from the Run environment. -Delete a specific user from the TagoRUN service. +See: `TagoRun `_ **Parameters:** | **userID**: :ref:`GenericID` - | ID of the user. + | User identification **Returns:** - | **result**: str - | Success message. + | str + .. code-block:: python -================== + # If receive an error "Authorization Denied", check policy **Run User** / **Delete** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.userDelete("user-id-123") + print(result) # Successfully Removed + + +=========== loginAsUser -================== +=========== -Log in as a specific user in the TagoRUN service. +Generates a login token to authenticate as a specific Run user. **Parameters:** | **userID**: :ref:`GenericID` - | ID of the user. + | User identification - | **options**: Optional[:ref:`LoginAsUserOptions`] - | Additional options for the login. + | *Optional* **options**: :ref:`LoginAsUserOptions` + | Login options (e.g., expire_time) **Returns:** - | **result**: :ref:`LoginResponseRunUser` - | Login response. + | :ref:`LoginResponse` + .. code-block:: python -================ + # If receive an error "Authorization Denied", check policy **Run User** / **Login as user** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.loginAsUser("user-id-123") + print(result["token"]) # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ... + + +========= emailTest -================ +========= -Send a test email from the TagoRUN service. +Tests the email configuration by sending a test message. **Parameters:** - | **data**: :ref:`EmailBase` - | Email data including subject and body. + | **data**: :ref:`EmailTestData` + | Email test data with subject and body **Returns:** - | **result**: str - | Success message. + | str + .. code-block:: python -====================== + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.emailTest({"subject": "Test Email", "body": "This is a test message"}) + print(result) # E-mail sent to example@email.com + + +================ notificationList -====================== +================ + +Retrieves a list of notifications for a specific Run user. -List notifications for a specific user in the TagoRUN service. +See: `Notifications for Users `_ **Parameters:** | **userID**: :ref:`GenericID` - | ID of the user. + | User identification **Returns:** - | **result**: list[:ref:`NotificationInfo`] - | List of notification information. + | list[:ref:`NotificationInfo`] + .. code-block:: python -====================== + # If receive an error "Authorization Denied", check policy **Run User** / **Access notification** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.notificationList("user-id-123") + print(result) # [{'id': 'notification-id-123', 'title': 'System Update', 'message': 'Features', ...}] + + +================== notificationCreate -====================== +================== -Create a new notification for a specific user in the TagoRUN service. +Creates a new notification for a Run user. + +See: `Notifications for Users `_ **Parameters:** | **userID**: :ref:`GenericID` - | ID of the user. + | User identification | **data**: :ref:`NotificationCreate` - | Information for creating the notification. + | Notification data **Returns:** - | **result**: :ref:`NotificationCreateReturn` - | Information about the created notification. + | :ref:`NotificationCreateReturn` + .. code-block:: python -====================== + # If receive an error "Authorization Denied", check policy **Run User** / **Create notification** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.notificationCreate("user-id-123", { + "title": "Update", + "message": "New feature available" + }) + print(result) # {'id': 'notification-id-123'} + + +================ notificationEdit -====================== +================ + +Updates an existing notification in the Run environment. -Edit information about a specific notification in the TagoRUN service. +See: `Notifications for Users `_ **Parameters:** | **notificationID**: :ref:`GenericID` - | ID of the notification. + | Notification identification - | **data**: :ref:`NotificationCreate` - | Updated information for the notification. + | **data**: dict + | Notification data to update **Returns:** - | **result**: str - | Success message. + | str + .. code-block:: python -====================== + # If receive an error "Authorization Denied", check policy **Run User** / **Edit notification** in Access Management. + from tagoio_sdk import Resources + + resources = Resources() + result = resources.run.notificationEdit("notification-id-123", {"title": "Updated Title"}) + print(result) # TagoIO Notification User Successfully Updated + + +================== notificationDelete -====================== +================== -Delete a specific notification from the TagoRUN service. +Deletes a notification from the Run environment. + +See: `Notifications for Users `_ **Parameters:** | **notificationID**: :ref:`GenericID` - | ID of the notification. + | Notification identification **Returns:** - | **result**: str - | Success message. + | str + + .. code-block:: python + + # If receive an error "Authorization Denied", check policy **Run User** / **Delete notification** in Access Management. + from tagoio_sdk import Resources + resources = Resources() + result = resources.run.notificationDelete("notification-id-123") + print(result) # Successfully Removed -============ + +=========== ssoSAMLInfo -============ +=========== -Get the SAML Single Sign-On information for the account's RUN. +Retrieves the SAML Single Sign-On configuration information for the Run environment. +See: `Single Sign-On (SSO) `_ -============ + **Returns:** + + | :ref:`RunSAMLInfo` + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.ssoSAMLInfo() + print(result) # {'sp': {'entity_id': 'https://example.com', ...}, ...} + + +=========== ssoSAMLEdit -============ +=========== + +Updates the SAML SSO configuration for the Run environment. -Edit the SAML Single Sign-On metadata and mappings for the account's RUN. +See: `Single Sign-On (SSO) `_ **Parameters:** | **data**: :ref:`RunSAMLEditInfo` - | Updated data for a RUN's SAML Single Sign-On configuration. + | SAML SSO configuration data + + **Returns:** + + | str + .. code-block:: python -=================== + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.ssoSAMLEdit({ + "active": True, + "idp_metadata": "..." + }) + print(result) # TagoIO Run SAML SSO Successfully Updated + + +================== createCustomDomain -=================== +================== -Create a TagoRUN custom domain for the profile. +Creates a custom domain configuration for the Run environment. + +See: `Custom Domain Configuration `_ **Parameters:** - | **profile_id**: str - | ID of the profile + | **profile_id**: str + | Profile identification -.. toctree:: + | **customDomainData**: :ref:`CustomDomainCreate` + | Custom domain configuration data - Run_Types + **Returns:** + | str -================ + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.createCustomDomain("profile-id-123", { + "domain": "app.mycompany.com" + }) + print(result) # Custom domain created successfully + + +=============== getCustomDomain -================ +=============== + +Retrieves the custom domain configuration for a Run profile. + +See: `Custom Domain Configuration `_ + + **Parameters:** -Set details of TagoRun custom domain for the profile. + | **profile_id**: str + | Profile identification - **Parameters** + **Returns:** + + | :ref:`CustomDomainInfo` + + .. code-block:: python - | **profile_id**: str - | ID of the profile + from tagoio_sdk import Resources + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.getCustomDomain("profile-id-123") + print(result) # {'domain': 'app.mycompany.com', 'verified': True, ...} -=================== + +================== deleteCustomDomain -=================== +================== + +Removes the custom domain configuration from a Run profile. -Delete a TagoRUN custom domain for the profile. +See: `Custom Domain Configuration `_ - **Parameters** + **Parameters:** - | **profile_id**: str - | ID of the profile + | **profile_id**: str + | Profile identification + + **Returns:** + | str -======================= + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.deleteCustomDomain("profile-id-123") + print(result) # Custom domain deleted successfully + + +====================== regenerateCustomDomain -======================= +====================== + +Regenerates the custom domain configuration for a Run profile. -Regenerate a TagoRUN custom domain for the profile. +See: `Custom Domain Configuration `_ - **Parameters** + **Parameters:** - | **profile_id**: str - | ID of the profile + | **profile_id**: str + | Profile identification + + **Returns:** + + | str + + .. code-block:: python + + from tagoio_sdk import Resources + + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.regenerateCustomDomain("profile-id-123") + print(result) # Custom domain regenerated successfully + + +.. toctree:: + + Run_Types diff --git a/docs/source/index.rst b/docs/source/index.rst index ab56705..50471cf 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -340,7 +340,7 @@ Support & Resources 🐛 **Issues & Bugs:** `GitHub Issues `_ -💬 **Help Center:** `TagoIO Support `_ +💬 **Help Center:** `TagoIO Support `_ 🌐 **Platform:** `TagoIO Console `_ diff --git a/src/tagoio_sdk/modules/Resources/Device_Type.py b/src/tagoio_sdk/modules/Resources/Device_Type.py index ee56efc..6b87af0 100644 --- a/src/tagoio_sdk/modules/Resources/Device_Type.py +++ b/src/tagoio_sdk/modules/Resources/Device_Type.py @@ -488,3 +488,60 @@ class ListDeviceTokenQuery(TypedDict): """ Tuple with a field and an order """ + + +class DeviceChunkData(TypedDict): + id: str + """ + Chunk ID in format 'from_to' (Unix timestamps). + """ + from_date: str + """ + Start date of the chunk (ISO 8601). + """ + to_date: str + """ + End date of the chunk (ISO 8601). + """ + amount: Union[int, float] + """ + Amount of data records in the chunk. + """ + + +class DeviceDataBackup(TypedDict): + deviceID: GenericID + """ + Device ID. + """ + file_address: str + """ + File path in TagoIO Files where backup will be saved. + Can use template variables: $DEVICE$, $CHUNK$, $FROM$, $TO$, $TIMESTAMP$. + """ + headers: Optional[bool] + """ + Include CSV headers in the exported file. + """ + + +class DeviceDataBackupResponse(TypedDict): + file_address: str + """ + Final file path where the backup was saved. + """ + chunk_id: Optional[str] + """ + Chunk ID if backup was for a specific chunk. + """ + + +class DeviceDataRestore(TypedDict): + deviceID: GenericID + """ + Device ID. + """ + file_address: str + """ + File path in TagoIO Files to restore data from (CSV format). + """ diff --git a/src/tagoio_sdk/modules/Resources/Devices.py b/src/tagoio_sdk/modules/Resources/Devices.py index 60782eb..7292ca3 100644 --- a/src/tagoio_sdk/modules/Resources/Devices.py +++ b/src/tagoio_sdk/modules/Resources/Devices.py @@ -1,3 +1,4 @@ +from typing import List from typing import Optional from typing import Union @@ -11,8 +12,12 @@ from tagoio_sdk.modules.Device.Device_Type import DataQuery from tagoio_sdk.modules.Device.Device_Type import DeviceInfo from tagoio_sdk.modules.Resources.Device_Type import ConfigurationParams +from tagoio_sdk.modules.Resources.Device_Type import DeviceChunkData from tagoio_sdk.modules.Resources.Device_Type import DeviceCreateInfo from tagoio_sdk.modules.Resources.Device_Type import DeviceCreateResponse +from tagoio_sdk.modules.Resources.Device_Type import DeviceDataBackup +from tagoio_sdk.modules.Resources.Device_Type import DeviceDataBackupResponse +from tagoio_sdk.modules.Resources.Device_Type import DeviceDataRestore from tagoio_sdk.modules.Resources.Device_Type import DeviceEditInfo from tagoio_sdk.modules.Resources.Device_Type import DeviceListItem from tagoio_sdk.modules.Resources.Device_Type import DeviceQuery @@ -26,20 +31,25 @@ class Devices(TagoIOModule): def listDevice(self, queryObj: DeviceQuery = None) -> list[DeviceListItem]: """ - Retrieves a list with all devices from the account + @description: + Retrieves a list of all devices from the account with optional filtering and pagination. - :default: + @see: + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview - queryObj: { + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + devices = resources.devices.listDevice({ "page": 1, - "fields": ["id", "name"], - "filter": {}, "amount": 20, - "orderBy": ["name", "asc"], - "resolveBucketName": False - } - - :param DeviceQuery queryObj: Search query params + "fields": ["id", "name", "active"], + "filter": {"name": "Temperature*"}, + "orderBy": ["name", "asc"] + }) + print(devices) # [{'id': 'device-id-123', 'name': 'Temperature Sensor', ...}] + ``` """ if queryObj is None: queryObj = {} @@ -80,9 +90,28 @@ def listDevice(self, queryObj: DeviceQuery = None) -> list[DeviceListItem]: def create(self, deviceObj: DeviceCreateInfo) -> DeviceCreateResponse: """ - Generates and retrieves a new action from the Device - - :param DeviceCreateInfo deviceObj: Object data to create new device + @description: + Creates a new device in the account with specified configuration and returns device credentials. + + @see: + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview + https://help.tago.io/portal/en/kb/articles/481-device-types Device Types + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Create** in Access Management. + ```python + resources = Resources() + result = resources.devices.create({ + "name": "Temperature Sensor", + "network": "network-id-123", + "connector": "connector-id-456", + "type": "immutable", + "chunk_period": "month", + "chunk_retention": 6, + "active": True + }) + print(result) # {'device_id': '...', 'bucket_id': '...', 'token': '...'} + ``` """ result = self.doRequest( { @@ -95,11 +124,23 @@ def create(self, deviceObj: DeviceCreateInfo) -> DeviceCreateResponse: def edit(self, deviceID: GenericID, deviceObj: DeviceEditInfo) -> str: """ - Modify any property of the device + @description: + Modifies any property of an existing device. - :param GenericID deviceID: Device ID + @see: + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview - :param DeviceEditInfo deviceObj: Device object with fields to replace + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.devices.edit("device-id-123", { + "name": "Updated Sensor Name", + "active": False, + "tags": [{"key": "location", "value": "warehouse"}] + }) + print(result) # Successfully Updated + ``` """ result = self.doRequest( { @@ -112,9 +153,19 @@ def edit(self, deviceID: GenericID, deviceObj: DeviceEditInfo) -> str: def delete(self, deviceID: GenericID) -> str: """ - Deletes an device from the account + @description: + Permanently deletes a device from the account along with all its data. - :param GenericID deviceID: Device ID + @see: + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Delete** in Access Management. + ```python + resources = Resources() + result = resources.devices.delete("device-id-123") + print(result) # Successfully Removed + ``` """ result = self.doRequest( { @@ -126,9 +177,19 @@ def delete(self, deviceID: GenericID) -> str: def info(self, deviceID: GenericID) -> DeviceInfo: """ - Get Info of the Device + @description: + Retrieves detailed information about a specific device. + + @see: + https://help.tago.io/portal/en/kb/articles/478-devices Devices Overview - :param GenericID deviceID: Device ID + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + device_info = resources.devices.info("device-id-123") + print(device_info) # {'id': '...', 'name': '...', 'type': 'mutable', ...} + ``` """ result = self.doRequest( { @@ -157,13 +218,30 @@ def paramSet( paramID: Optional[GenericID] = None, ) -> str: """ - Create or edit param for the Device - - :param deviceID Device ID - - :param configObj Configuration Data - - :param paramID Parameter ID + @description: + Creates new configuration parameters or updates existing ones for a device. + + @see: + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + # Create new parameter + result = resources.devices.paramSet("device-id-123", { + "key": "threshold", + "value": "25.5", + "sent": False + }) + # Update existing parameter + result = resources.devices.paramSet("device-id-123", { + "key": "threshold", + "value": "30.0", + "sent": False + }, "param-id-456") + print(result) # Successfully Updated + ``` """ body = configObj if paramID and not isinstance(configObj, list): @@ -182,32 +260,49 @@ def paramSet( return result - def paramList( - self, deviceID: GenericID, sentStatus: bool = None - ) -> list[ConfigurationParams]: + def paramList(self, deviceID: GenericID, sentStatus: bool = None) -> list[ConfigurationParams]: """ - List Params for the Device + @description: + Retrieves a list of configuration parameters for a device. - :param GenericID deviceID: Device ID + @see: + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters - :param bool sentStatus: True return only sent=true, False return only sent=false + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + # Get all parameters + params = resources.devices.paramList("device-id-123") + # Get only sent parameters + sent_params = resources.devices.paramList("device-id-123", sentStatus=True) + print(params) # [{'id': '...', 'key': 'threshold', 'value': '25.5', 'sent': False}] + ``` """ result = self.doRequest( { "path": f"/device/{deviceID}/params", "method": "GET", - "params": {"sent_status": sentStatus}, + "params": {"sent_status": str(sentStatus).lower() if sentStatus is not None else None}, } ) return result def paramRemove(self, deviceID: GenericID, paramID: GenericID) -> str: """ - Remove param for the Device + @description: + Removes a specific configuration parameter from a device. - :param GenericID deviceID: Device ID + @see: + https://help.tago.io/portal/en/kb/articles/configuration-parameters Configuration Parameters - :param GenericID paramID: Parameter ID + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.devices.paramRemove("device-id-123", "param-id-456") + print(result) # Successfully Removed + ``` """ result = self.doRequest( { @@ -223,21 +318,24 @@ def tokenList( queryObj: ListDeviceTokenQuery = None, ) -> list[DeviceTokenDataList]: """ - Retrieves a list of all tokens + @description: + Retrieves a list of all authentication tokens for a device with optional filtering. - :default: + @see: + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens - queryObj: { + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + tokens = resources.devices.tokenList("device-id-123", { "page": 1, - "fields": ["name", "token", "permission"], - "filter": {}, "amount": 20, - "orderBy": ["created_at", "desc"], - } - - :param GenericID deviceID: Device ID - - :param ListDeviceTokenQuery queryObj: Search query params + "fields": ["name", "token", "permission"], + "orderBy": ["created_at", "desc"] + }) + print(tokens) # [{'name': 'Default Token', 'token': '...', 'permission': 'full', ...}] + ``` """ if queryObj is None: @@ -262,21 +360,29 @@ def tokenList( }, } ) - result = dateParserList( - result, ["created_at", "expire_time"] - ) + result = dateParserList(result, ["created_at", "expire_time"]) return result - def tokenCreate( - self, deviceID: GenericID, tokenParams: TokenData - ) -> TokenCreateResponse: + def tokenCreate(self, deviceID: GenericID, tokenParams: TokenData) -> TokenCreateResponse: """ - Generates and retrieves a new token + @description: + Generates and retrieves a new authentication token for a device. - :param deviceID: Device ID + @see: + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens - :param tokenParams: Params for new token + @example: + If receive an error "Authorization Denied", check policy **Device** / **Create Token** in Access Management. + ```python + resources = Resources() + result = resources.devices.tokenCreate("device-id-123", { + "name": "Production Token", + "permission": "write", + "expire_time": "never" + }) + print(result) # {'token': 'new-token-value', 'expire_date': None} + ``` """ result = self.doRequest( { @@ -290,9 +396,19 @@ def tokenCreate( def tokenDelete(self, token: GenericToken) -> str: """ - Delete a token + @description: + Permanently deletes an authentication token. + + @see: + https://help.tago.io/portal/en/kb/articles/495-access-tokens Device Tokens - :param GenericToken token: Token + @example: + If receive an error "Authorization Denied", check policy **Device** / **Delete Token** in Access Management. + ```python + resources = Resources() + result = resources.devices.tokenDelete("token-to-delete") + print(result) # Successfully Removed + ``` """ result = self.doRequest( { @@ -302,25 +418,27 @@ def tokenDelete(self, token: GenericToken) -> str: ) return result - def getDeviceData( - self, deviceID: GenericID, queryParams: DataQuery = None - ) -> list[Data]: + def getDeviceData(self, deviceID: GenericID, queryParams: DataQuery = None) -> list[Data]: """ - Get data from all variables in the device. - - :param deviceID: Device ID. + @description: + Retrieves data from all variables in the device with optional query filters. - :param queryParams: Query parameters to filter the results. + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management - :rtype: Array with the data values stored in the device. - - :example: - - myDevice = Account({ "token": "my_device_token" }) - - result = myAccount.devices.getDeviceData( - "Device Id", {"variables": "location"} - ) + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + # Get all data + data = resources.devices.getDeviceData("device-id-123") + # Get specific variable data + temp_data = resources.devices.getDeviceData("device-id-123", { + "variables": ["temperature"], + "qty": 10 + }) + print(temp_data) # [{'variable': 'temperature', 'value': 25.5, 'time': '...', ...}] + ``` """ if queryParams is None: queryParams = {} @@ -335,11 +453,19 @@ def getDeviceData( def emptyDeviceData(self, deviceID: GenericID) -> str: """ - Empty all data in a device. + @description: + Permanently removes all data from a device. This operation cannot be undone. - :param GenericID deviceID: Device ID. + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management - :rtype: Success message. + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.devices.emptyDeviceData("device-id-123") + print(result) # All data has been removed + ``` """ result = self.doRequest( { @@ -349,110 +475,260 @@ def emptyDeviceData(self, deviceID: GenericID) -> str: ) return result - def sendDeviceData( - self, deviceID: GenericID, data: Union[DataCreate, list[DataCreate]] - ) -> str: + def sendDeviceData(self, deviceID: GenericID, data: Union[DataCreate, list[DataCreate]]) -> str: + """ + @description: + Sends data to a device. Accepts a single data object or an array of data objects. + + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.devices.sendDeviceData("device-id-123", { + "variable": "temperature", + "value": 25.5, + "unit": "°C", + "time": "2025-01-09 13:44:33", + "location": {"lat": 42.2974279, "lng": -85.628292} + }) + # Send multiple data points + result = resources.devices.sendDeviceData("device-id-123", [ + {"variable": "temperature", "value": 25.5}, + {"variable": "humidity", "value": 60} + ]) + print(result) # Successfully Inserted + ``` """ - Send data to a device. + result = self.doRequest( + { + "path": f"/device/{deviceID}/data", + "method": "POST", + "body": data, + } + ) - :param GenericID deviceID: Device ID. + return result - :param DataCreate data: An array or one object with data to be send to TagoIO. + def editDeviceData(self, deviceID: GenericID, updatedData: Union[DataEdit, list[DataEdit]]) -> str: + """ + @description: + Modifies existing data records in a device. Requires the data record ID. + + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.devices.editDeviceData("device-id-123", { + "id": "data-record-id", + "value": 30.0, + "unit": "°F" + }) + # Edit multiple records + result = resources.devices.editDeviceData("device-id-123", [ + {"id": "record-1-id", "value": 25.5}, + {"id": "record-2-id", "value": 65} + ]) + print(result) # Device Data Updated + ``` + """ + result = self.doRequest( + { + "path": f"/device/{deviceID}/data", + "method": "PUT", + "body": updatedData, + } + ) - :rtype: Success message. + return result - Example:: - ```python - # Example of using the function - resources = Resource() - resource.devices.sendDeviceData("myDeviceID", { - "variable": "temperature", - "unit": "F", - "value": 55, - "time": "2015-11-03 13:44:33", - "location": { "lat": 42.2974279, "lng": -85.628292 }, - }) - ``` + def deleteDeviceData(self, deviceID: GenericID, queryParam: DataQuery = None) -> str: + """ + @description: + Deletes data from a device based on specified query parameters. + + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Edit** in Access Management. + ```python + resources = Resources() + # Delete specific records by ID + result = resources.devices.deleteDeviceData("device-id-123", { + "ids": ["record-id-1", "record-id-2"] + }) + # Delete by variable + result = resources.devices.deleteDeviceData("device-id-123", { + "variables": ["old_sensor"], + "qty": 100 + }) + print(result) # Successfully Removed + ``` """ + if queryParam is None: + queryParam = {} result = self.doRequest( { "path": f"/device/{deviceID}/data", - "method": "POST", - "body": data, + "method": "DELETE", + "params": queryParam, } ) return result - def editDeviceData( - self, deviceID: GenericID, updatedData: Union[DataEdit, list[DataEdit]] - ) -> str: + def amount(self, deviceID: GenericID) -> Union[int, float]: """ - Edit data in a device. + @description: + Gets the amount of data stored in a device. - :param GenericID deviceID: Device ID. + @see: + https://help.tago.io/portal/en/kb/articles/device-data Device Data Management + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. + ```python + resources = Resources() + amount = resources.devices.amount("device-id-123") + print(amount) # 15234 + ``` + """ + result = self.doRequest( + { + "path": f"/device/{deviceID}/data_amount", + "method": "GET", + } + ) + return result - :param DataEdit data: A single or an array of updated data records. + def getChunk(self, deviceID: GenericID) -> List[DeviceChunkData]: + """ + @description: + Retrieves chunk information from an immutable device. - :rtype: Success message. + @see: + https://help.tago.io/portal/en/kb/articles/chunk-management Chunk Management - Example:: - ```python - # Example of using the function - resources = Resource() - resource.devices.editDeviceData("myDeviceID", {"id": "idOfTheRecord", "value": "new value", "unit": "new unit"}) - ``` + @example: + If receive an error "Authorization Denied", check policy **Device** / **Manage chunks** in Access Management. + ```python + resources = Resources() + chunks = resources.devices.getChunk("device-id-123") + print(chunks) # [{'amount': 0, 'id': 'chunk-id-123', 'from_date': '2025-01-09T00:00:00.000+00:00', ...}] + ``` """ result = self.doRequest( { - "path": f"/device/{deviceID}/data", - "method": "PUT", - "body": updatedData, + "path": f"/device/{deviceID}/chunk", + "method": "GET", } ) return result - def deleteDeviceData( - self, deviceID: GenericID, queryParam: DataQuery = None - ) -> str: + def deleteChunk(self, deviceID: GenericID, chunkID: GenericID) -> str: """ - Delete data from a device. - - :param GenericID deviceID: Device ID. - - :param DataQuery queryParam: Parameters to specify what should be deleted on the device's data. + @description: + Deletes a chunk from an immutable device. - :rtype: Success message. + @see: + https://help.tago.io/portal/en/kb/articles/chunk-management#Delete_chunks Delete Chunks - Example:: - ```python - # Example of using the function - resources = Resource() - resource.devices.deleteDeviceData("myDeviceID", {"ids": ["recordIdToDelete", "anotherRecordIdToDelete" ]}) - ``` + @example: + If receive an error "Authorization Denied", check policy **Device** / **Manage chunks** in Access Management. + ```python + resources = Resources() + result = resources.devices.deleteChunk("device-id-123", "chunk-id-123") + print(result) # Chunk chunk-id-123 deleted + ``` """ - if queryParam is None: - queryParam = {} result = self.doRequest( { - "path": f"/device/{deviceID}/data", + "path": f"/device/{deviceID}/chunk/{chunkID}", "method": "DELETE", - "params": queryParam, } ) return result - def amount(self, deviceID: GenericID) -> Union[int, float]: + def dataBackup(self, params: DeviceDataBackup, chunkID: Optional[GenericID] = None) -> DeviceDataBackupResponse: + """ + @description: + Schedule to export the device's data to TagoIO Files. + For mutable devices, exports all data. For immutable devices with chunkID, exports specific chunk. + + @see: + https://help.tago.io/portal/en/kb/articles/55-data-export Data Export + https://help.tago.io/portal/en/kb/articles/device-data#Backing_Up_Data Backing Up Data + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Export Data** in Access Management. + ```python + resources = Resources() + import time + timestamp = int(time.time()) + result = resources.devices.dataBackup({ + "deviceID": "device-id-123", + "file_address": f"/backups/device-id-123/{timestamp}", + "headers": True + }) + print(result) # {'file_address': 'backups/device-id-123/1736433519380.csv'} + ``` + """ + body = { + "chunk_id": chunkID, + "headers": params.get("headers"), + "file_address": params.get("file_address"), + } + + path = f"/device/{params['deviceID']}/chunk/backup" if chunkID else f"/device/{params['deviceID']}/data/backup" + + result = self.doRequest( + { + "path": path, + "method": "POST", + "body": body, + } + ) + + return result + + def dataRestore(self, params: DeviceDataRestore) -> str: """ - Get Amount of data stored in the Device - :param deviceID: Device ID + @description: + Restores data to a device from a CSV file in TagoIO Files. + + @see: + https://help.tago.io/portal/en/kb/articles/device-data#Importing Importing Device Data + https://api.docs.tago.io/#7ebca255-6c38-43d3-97d0-9b62155f202e Import Data API + + @example: + If receive an error "Authorization Denied", check policy **Device** / **Import Data** in Access Management. + ```python + resources = Resources() + result = resources.devices.dataRestore({ + "deviceID": "device-id-123", + "file_address": "/backups/backup.csv" + }) + print(result) # Data import added to the queue successfully! + ``` """ + body = { + "file_address": params.get("file_address"), + } + result = self.doRequest( { - "path": f"/device/{deviceID}/data_amount", - "method": "GET", + "path": f"/device/{params['deviceID']}/data/restore", + "method": "POST", + "body": body, } ) + return result diff --git a/src/tagoio_sdk/modules/Resources/IntegrationNetwork.py b/src/tagoio_sdk/modules/Resources/IntegrationNetwork.py index b5e6188..8dc9fcc 100644 --- a/src/tagoio_sdk/modules/Resources/IntegrationNetwork.py +++ b/src/tagoio_sdk/modules/Resources/IntegrationNetwork.py @@ -1,26 +1,48 @@ +from typing import Dict +from typing import List +from typing import Optional + from tagoio_sdk.common.Common_Type import GenericID -from tagoio_sdk.common.Common_Type import Query +from tagoio_sdk.common.Common_Type import GenericToken +from tagoio_sdk.common.Common_Type import TokenCreateResponse +from tagoio_sdk.common.Common_Type import TokenData from tagoio_sdk.common.tagoio_module import TagoIOModule +from tagoio_sdk.modules.Resources.IntegrationNetworkType import ListTokenQuery +from tagoio_sdk.modules.Resources.IntegrationNetworkType import NetworkCreateInfo from tagoio_sdk.modules.Resources.IntegrationNetworkType import NetworkInfo +from tagoio_sdk.modules.Resources.IntegrationNetworkType import NetworkQuery +from tagoio_sdk.modules.Resources.IntegrationNetworkType import NetworkTokenInfo +from tagoio_sdk.modules.Utils.dateParser import dateParser +from tagoio_sdk.modules.Utils.dateParser import dateParserList class Networks(TagoIOModule): - def listNetwork(self, queryObj: Query = None) -> list[NetworkInfo]: + def listNetwork(self, queryObj: Optional[NetworkQuery] = None) -> List[NetworkInfo]: """ - Retrieves a list with all networks from the account + @description: + Retrieves a list of all networks from the account with pagination support. + Use this to retrieve and manage networks in your application. - :default: - fields: ["id", "name"] + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration Network Integration - :param fields Fields to be returned + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Access** in Access Management. + ```python + resources = Resources() + networks = resources.integration.networks.listNetwork({ + "page": 1, + "fields": ["id", "name"], + "amount": 20, + "orderBy": ["name", "asc"] + }) + print(networks) # [{'id': 'network-id-123', 'name': 'My Network', ...}] + ``` """ + queryObj = queryObj or {} - if queryObj is None: - queryObj = {} if "orderBy" in queryObj: - firstArgument = queryObj["orderBy"][0] - secondArgument = queryObj["orderBy"][1] - orderBy = f"{firstArgument},{secondArgument}" + orderBy = f"{queryObj['orderBy'][0]},{queryObj['orderBy'][1]}" else: orderBy = "name,asc" @@ -29,10 +51,10 @@ def listNetwork(self, queryObj: Query = None) -> list[NetworkInfo]: "path": "/integration/network", "method": "GET", "params": { - "page": queryObj.get("page") or 1, - "fields": queryObj.get("fields") or ["id", "name"], - "filter": queryObj.get("filter") or {}, - "amount": queryObj.get("amount") or 20, + "page": queryObj.get("page", 1), + "fields": queryObj.get("fields", ["id", "name"]), + "filter": queryObj.get("filter", {}), + "amount": queryObj.get("amount", 20), "orderBy": orderBy, }, } @@ -40,19 +62,25 @@ def listNetwork(self, queryObj: Query = None) -> list[NetworkInfo]: return result - def info(self, networkID: GenericID, fields: NetworkInfo = None) -> NetworkInfo: + def info(self, networkID: GenericID, fields: Optional[List[str]] = None) -> NetworkInfo: """ - Retrieves the information of the network. + @description: + Retrieves detailed information about a specific network. - :default: - fields: ["id", "name"] + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration Network Integration - :param networkID Network ID - :param fields Fields to be returned + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Access** in Access Management. + ```python + resources = Resources() + network_info = resources.integration.networks.info("network-id-123") + print(network_info) # {'id': '...', 'name': 'My Network', ...} + ``` """ - if fields is None: fields = ["id", "name"] + result = self.doRequest( { "path": f"/integration/network/{networkID}", @@ -64,3 +92,186 @@ def info(self, networkID: GenericID, fields: NetworkInfo = None) -> NetworkInfo: ) return result + + def create(self, networkObj: NetworkCreateInfo) -> Dict[str, str]: + """ + @description: + Creates a new integration network in the account. + + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration#create-a-new-integration Creating a Network Integration + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Create** in Access Management. + ```python + resources = Resources() + new_network = resources.integration.networks.create({ + "name": "My Custom Network", + "description": "Custom integration network", + "middleware_endpoint": "https://my-middleware.com/endpoint", + "public": False + }) + print(new_network) # {'network': 'network-id-123'} + ``` + """ + result = self.doRequest( + { + "path": "/integration/network", + "method": "POST", + "body": networkObj, + } + ) + + return result + + def edit(self, networkID: GenericID, networkObj: NetworkCreateInfo) -> str: + """ + @description: + Modifies any property of an existing network. + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.integration.networks.edit("network-id-123", { + "name": "Updated Network Name", + "description": "Updated description", + "public": True + }) + print(result) # Successfully Updated + ``` + """ + result = self.doRequest( + { + "path": f"/integration/network/{networkID}", + "method": "PUT", + "body": networkObj, + } + ) + + return result + + def delete(self, networkID: GenericID) -> str: + """ + @description: + Permanently deletes a network from the account. + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Delete** in Access Management. + ```python + resources = Resources() + result = resources.integration.networks.delete("network-id-123") + print(result) # Successfully Removed + ``` + """ + result = self.doRequest( + { + "path": f"/integration/network/{networkID}", + "method": "DELETE", + } + ) + + return result + + def tokenList(self, networkID: GenericID, queryObj: Optional[ListTokenQuery] = None) -> List[NetworkTokenInfo]: + """ + @description: + Retrieves a list of all authentication tokens for a network with optional filtering. + + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration#tokens-and-getting-devices Tokens and Getting Devices + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Access** in Access Management. + ```python + resources = Resources() + tokens = resources.integration.networks.tokenList("network-id-123", { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"], + "orderBy": ["created_at", "desc"] + }) + print(tokens) # [{'name': 'Default Token', 'token': '...', 'permission': 'full', ...}] + ``` + """ + queryObj = queryObj or {} + + if "orderBy" in queryObj: + orderBy = f"{queryObj['orderBy'][0]},{queryObj['orderBy'][1]}" + else: + orderBy = "created_at,desc" + + result = self.doRequest( + { + "path": f"/integration/network/token/{networkID}", + "method": "GET", + "params": { + "page": queryObj.get("page", 1), + "fields": queryObj.get("fields", ["name", "token", "permission"]), + "filter": queryObj.get("filter", {}), + "amount": queryObj.get("amount", 20), + "orderBy": orderBy, + }, + } + ) + + result = dateParserList(result, ["created_at", "updated_at"]) + + return result + + def tokenCreate(self, networkID: GenericID, tokenParams: TokenData) -> TokenCreateResponse: + """ + @description: + Generates and retrieves a new authentication token for a network. + + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration#tokens-and-getting-devices Tokens and Getting Devices + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Create Token** in Access Management. + ```python + resources = Resources() + result = resources.integration.networks.tokenCreate("network-id-123", { + "name": "Production Token", + "permission": "write", + "expire_time": "never" + }) + print(result) # {'token': 'new-token-value', 'expire_date': None} + ``` + """ + result = self.doRequest( + { + "path": "/integration/network/token", + "method": "POST", + "body": {"network": networkID, **tokenParams}, + } + ) + + result = dateParser(result, ["expire_date"]) + + return result + + def tokenDelete(self, token: GenericToken) -> str: + """ + @description: + Permanently deletes an authentication token. + + @see: + https://docs.tago.io/docs/tagoio/integrations/general/creating-a-network-integration#tokens-and-getting-devices Tokens and Getting Devices + + @example: + If receive an error "Authorization Denied", check policy **Integration Network** / **Delete Token** in Access Management. + ```python + resources = Resources() + result = resources.integration.networks.tokenDelete("token-to-delete") + print(result) # Successfully Removed + ``` + """ + result = self.doRequest( + { + "path": f"/integration/network/token/{token}", + "method": "DELETE", + } + ) + + return result diff --git a/src/tagoio_sdk/modules/Resources/IntegrationNetworkType.py b/src/tagoio_sdk/modules/Resources/IntegrationNetworkType.py index 23f58fc..df4de38 100644 --- a/src/tagoio_sdk/modules/Resources/IntegrationNetworkType.py +++ b/src/tagoio_sdk/modules/Resources/IntegrationNetworkType.py @@ -58,8 +58,33 @@ class NetworkInfo(NetworkCreateInfo): serial_number: Optional[serial_number] +class NetworkTokenInfo(TypedDict): + token: str + network: GenericID + name: str + permission: str + created_at: datetime + updated_at: Optional[datetime] + + class DeviceNetworkToken(TypedDict): token: uuid.UUID network: GenericID name: str - crated_at: datetime + created_at: datetime + + +class NetworkQuery(TypedDict, total=False): + page: int + amount: int + fields: list[str] + filter: dict + orderBy: list[str] + + +class ListTokenQuery(TypedDict, total=False): + page: int + amount: int + fields: list[str] + filter: dict + orderBy: list[str] diff --git a/src/tagoio_sdk/modules/Resources/Profile.py b/src/tagoio_sdk/modules/Resources/Profile.py index ca9fdf2..9f8f803 100644 --- a/src/tagoio_sdk/modules/Resources/Profile.py +++ b/src/tagoio_sdk/modules/Resources/Profile.py @@ -1,28 +1,46 @@ +from typing import Dict from typing import List from typing import Optional from tagoio_sdk.common.Common_Type import GenericID +from tagoio_sdk.common.Common_Type import GenericToken from tagoio_sdk.common.Common_Type import Query +from tagoio_sdk.common.Common_Type import TokenCreateResponse +from tagoio_sdk.common.Common_Type import TokenData from tagoio_sdk.common.Common_Type import TokenDataList from tagoio_sdk.common.tagoio_module import TagoIOModule +from tagoio_sdk.modules.Resources.Profile_Type import AuditLog +from tagoio_sdk.modules.Resources.Profile_Type import AuditLogFilter +from tagoio_sdk.modules.Resources.Profile_Type import ProfileCreateInfo +from tagoio_sdk.modules.Resources.Profile_Type import ProfileCredentials from tagoio_sdk.modules.Resources.Profile_Type import ProfileInfo from tagoio_sdk.modules.Resources.Profile_Type import ProfileListInfo from tagoio_sdk.modules.Resources.Profile_Type import ProfileSummary +from tagoio_sdk.modules.Resources.Profile_Type import ProfileTeam +from tagoio_sdk.modules.Resources.Profile_Type import StatisticsDate +from tagoio_sdk.modules.Resources.Profile_Type import UsageStatistic from tagoio_sdk.modules.Utils.dateParser import dateParser from tagoio_sdk.modules.Utils.dateParser import dateParserList class Profile(TagoIOModule): - """ - Manage profiles in account be sure to use an - account token with “write” permissions when - using functions like create, edit and delete. - """ - def info(self, profileID: GenericID) -> ProfileInfo: """ - Get Profile info - :param: profileID Profile identification + @description: + Retrieves detailed information about a specific profile using its ID or 'current' for the active profile. + + @see: + https://help.tago.io/portal/en/kb/articles/198-profiles Profiles + + @example: + If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. + ```python + resources = Resources() + profile_info = resources.profile.info("profile-id-123") + # Or get current profile + current_profile = resources.profile.info("current") + print(profile_info) # {'info': {'id': 'profile-id-123', 'account': 'account-id-123', ...}, ...} + ``` """ result = self.doRequest( { @@ -37,7 +55,19 @@ def info(self, profileID: GenericID) -> ProfileInfo: def list(self) -> list[ProfileListInfo]: """ - Lists all the profiles in your account + @description: + Retrieves a list of all profiles associated with the current account. + + @see: + https://help.tago.io/portal/en/kb/articles/198-profiles Profiles + + @example: + If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. + ```python + resources = Resources() + result = resources.profile.list() + print(result) # [{'id': 'profile-id-123', 'name': 'Profile Test', ...}] + ``` """ result = self.doRequest( { @@ -49,8 +79,19 @@ def list(self) -> list[ProfileListInfo]: def summary(self, profileID: GenericID) -> ProfileSummary: """ - Gets profile summary - :param: profileID Profile identification + @description: + Retrieves a summary of the profile's usage and statistics. + + @see: + https://help.tago.io/portal/en/kb/articles/198-profiles Profiles + + @example: + If receive an error "Authorization Denied", check policy **Account** / **Access profile** in Access Management. + ```python + resources = Resources() + result = resources.profile.summary("profile-id-123") + print(result) # {'amount': {'device': 10, 'bucket': 10, 'dashboard': 5, ...}, ...} + ``` """ result = self.doRequest( { @@ -60,12 +101,24 @@ def summary(self, profileID: GenericID) -> ProfileSummary: ) return result - def tokenList( - self, profileID: GenericID, queryObj: Optional[Query] = None - ) -> List[TokenDataList]: + def tokenList(self, profileID: GenericID, queryObj: Optional[Query] = None) -> List[TokenDataList]: """ - Lists all the tokens in your account - :param: profileID Profile identification + @description: + Retrieves a list of all tokens associated with a specific profile. + + @see: + https://help.tago.io/portal/en/kb/articles/495-account-token Account Token + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.tokenList("profile-id-123", { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"] + }) + print(result) # [{'name': 'Token #1', 'token': 'token-value', 'permission': 'full', ...}, ...] + ``` """ if queryObj is None: @@ -91,8 +144,315 @@ def tokenList( } ) - result = dateParserList( - result, ["last_authorization", "expire_time", "created_at"] + result = dateParserList(result, ["last_authorization", "expire_time", "created_at"]) + + return result + + def create(self, profileObj: ProfileCreateInfo, allocate_free_resources: bool = False) -> Dict[str, GenericID]: + """ + @description: + Creates a new profile with the specified name and optional resource allocation settings. + + @see: + https://help.tago.io/portal/en/kb/articles/198-profiles Profiles + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.create({"name": "New Profile"}, allocate_free_resources=True) + print(result) # {'id': 'profile-id-123'} + ``` + """ + params = {} + if allocate_free_resources: + params["allocate_free_resources"] = allocate_free_resources + + result = self.doRequest({"path": "/profile/", "method": "POST", "body": profileObj, "params": params}) + + return result + + def edit(self, profileID: GenericID, profileObj: Dict) -> str: + """ + @description: + Updates profile information with the provided data. + + @see: + https://help.tago.io/portal/en/kb/articles/198-profiles Profiles + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.edit("profile-id-123", {"name": "Updated Profile Name"}) + print(result) # Successfully Updated + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}", "method": "PUT", "body": profileObj}) + + return result + + def delete(self, profileID: GenericID, credentials: ProfileCredentials) -> str: + """ + @description: + Permanently removes a profile from the account. + + @see: + https://help.tago.io/portal/en/kb/articles/526-two-factor-authentication Two-Factor Authentication (2FA) + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + # The "pin_code" field is required when 2FA is activated + result = resources.profile.delete("profile-id-123", {"password": "your-password", "pin_code": "123456"}) + print(result) # Successfully Removed + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}", "method": "DELETE", "body": credentials}) + + return result + + def usageStatisticList( + self, profileID: GenericID, dateObj: Optional[StatisticsDate] = None + ) -> List[UsageStatistic]: + """ + @description: + Retrieves usage statistics for a profile within a specified time period. + Usage statistics are cumulative: if a service was not used in a time period, + the statistics for that time period will not be in the object. + + @example: + If receive an error "Authorization Denied", check policy **Account** / **Access profile statistics** in Access Management. + ```python + resources = Resources() + result = resources.profile.usageStatisticList("profile-id-123", { + "start_date": "2024-09-01", + "end_date": "2024-12-31", + "periodicity": "day" + }) + print(result) # [{'time': '2024-09-02T00:01:29.749Z', 'analysis': 0.07, 'data_records': 67254, ...}, ...] + ``` + """ + result = self.doRequest( + { + "path": f"/profile/{profileID}/statistics", + "method": "GET", + "params": dateObj or {}, + } ) + result = dateParserList(result, ["time"]) + + return result + + def auditLog(self, profileID: GenericID, filterObj: Optional[AuditLogFilter] = None) -> AuditLog: + """ + @description: + Creates a new audit log query for tracking profile activities. + + @see: + https://help.tago.io/portal/en/kb/articles/audit-log Audit Log + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.auditLog("profile-id-123", { + "start_date": "2024-12-01", + "end_date": "2024-12-07" + }) + print(result) + ``` + """ + result = self.doRequest( + { + "path": f"/profile/{profileID}/auditlog", + "method": "GET", + "params": filterObj or {}, + } + ) + + if result.get("events"): + result["events"] = dateParserList(result["events"], ["date"]) + + return result + + def auditLogQuery(self, profileID: GenericID, queryId: str) -> AuditLog: + """ + @description: + Retrieves audit log entries using a previously created query. + + @see: + https://help.tago.io/portal/en/kb/articles/audit-log Audit Log + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.auditLogQuery("profile-id-123", "query-id-456") + print(result) + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}/auditlog/{queryId}", "method": "GET"}) + + if result.get("events"): + result["events"] = dateParserList(result["events"], ["date"]) + + return result + + def serviceEdit(self, profileID: GenericID, serviceObj: Dict) -> str: + """ + @description: + Updates service configuration and resource limits for a profile. + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.serviceEdit("profile-id-123", { + "input": 350000, + "output": 342153, + "analysis": 5 + }) + print(result) # Profile resource allocation Successfully Updated + ``` + """ + result = self.doRequest( + { + "path": f"/profile/{profileID}/services", + "method": "POST", + "body": serviceObj, + } + ) + + return result + + def transferTokenToAnotherProfile(self, targetProfileID: GenericID) -> str: + """ + @description: + Transfers the current authentication token to another profile. + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.transferTokenToAnotherProfile("target-profile-123") + print(result) + ``` + """ + result = self.doRequest({"path": f"/profile/switch/{targetProfileID}", "method": "PUT"}) + + return result + + def tokenCreate(self, profileID: GenericID, tokenParams: TokenData) -> TokenCreateResponse: + """ + @description: + Creates a new authentication token for the specified profile. + + @see: + https://help.tago.io/portal/en/kb/articles/495-account-token Account Token + https://help.tago.io/portal/en/kb/articles/526-two-factor-authentication Two-Factor Authentication (2FA) + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + # The "pin_code" / "otp_type" field is required when 2FA is activated + result = resources.profile.tokenCreate("profile-id-123", { + "name": "API Access", + "permission": "full", + "email": "example@email.com", + "password": "your-password" + }) + print(result) # {'token': 'token-value', 'name': 'API Access', ...} + ``` + """ + result = self.doRequest( + { + "path": f"/profile/{profileID}/token", + "method": "POST", + "body": tokenParams, + } + ) + + result = dateParser(result, ["expire_date"]) + + return result + + def tokenDelete(self, profileID: GenericID, token: GenericToken) -> str: + """ + @description: + Revokes and removes an authentication token from the profile. + + @see: + https://help.tago.io/portal/en/kb/articles/495-account-token Account Token + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.tokenDelete("profile-id-123", "token-xyz") + print(result) # Token Successfully Removed + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}/token/{token}", "method": "DELETE"}) + + return result + + def addTeamMember(self, profileID: GenericID, email: str) -> str: + """ + @description: + Adds a new team member to the profile using their email address. + + @see: + https://help.tago.io/portal/en/kb/articles/106-sharing-your-profile Team Management - Sharing your Profile + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.addTeamMember("profile-id-123", "user@example.com") + print(result) # User invited + ``` + """ + result = self.doRequest( + { + "path": f"/profile/{profileID}/team", + "method": "POST", + "body": {"email": email}, + } + ) + + return result + + def teamList(self, profileID: GenericID) -> List[ProfileTeam]: + """ + @description: + Retrieves a list of all team members that have access to the specified profile. + + @see: + https://help.tago.io/portal/en/kb/articles/106-sharing-your-profile Team Management - Sharing your Profile + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.profile.teamList("profile-id-123") + print(result) # [{'id': 'account-id-123', 'active': False, 'name': 'John Doe', ...}, ...] + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}/team", "method": "GET"}) + + result = dateParserList(result, ["created_at"]) + + return result + + def deleteTeamMember(self, profileID: GenericID, accountId: str) -> str: + """ + @description: + Removes a team member from the profile. + + @see: + https://help.tago.io/portal/en/kb/articles/106-sharing-your-profile Team Management - Sharing your Profile + + @example: + If receive an error "Authorization Denied", check policy in Access Management. + ```python + resources = Resources() + result = resources.profile.deleteTeamMember("profile-id-123", "account-id-456") + print(result) # Account Successfully Removed + ``` + """ + result = self.doRequest({"path": f"/profile/{profileID}/team/{accountId}", "method": "DELETE"}) + return result diff --git a/src/tagoio_sdk/modules/Resources/Profile_Type.py b/src/tagoio_sdk/modules/Resources/Profile_Type.py index 2367372..fcf0acf 100644 --- a/src/tagoio_sdk/modules/Resources/Profile_Type.py +++ b/src/tagoio_sdk/modules/Resources/Profile_Type.py @@ -1,4 +1,6 @@ from datetime import datetime +from typing import Literal +from typing import Optional from typing import TypedDict from typing import Union @@ -84,3 +86,101 @@ class ProfileSummary(TypedDict): amount: amount limit_used: limit_used addons: ProfileAddOns + + +class UsageStatistic(TypedDict, total=False): + """ + Type for a single usage statistic with timestamp. + + Not all of the services will be present for every statistic, + only if for the usage period the service was used. + """ + + time: datetime + input: Union[int, float] + output: Union[int, float] + analysis: Union[int, float] + sms: Union[int, float] + email: Union[int, float] + data_records: Union[int, float] + run_users: Union[int, float] + push_notification: Union[int, float] + file_storage: Union[int, float] + tcore: Union[int, float] + + +class AuditLogEvent(TypedDict): + resourceName: str + message: str + resourceID: GenericID + who: GenericID + date: datetime + + +class AuditLogStatistics(TypedDict): + recordsMatched: int + recordsScanned: int + bytesScanned: int + + +class AuditLog(TypedDict, total=False): + events: list[AuditLogEvent] + statistics: AuditLogStatistics + status: Literal["Running", "Complete", "Failed", "Timeout", "Unknown"] + queryId: str + + +class AuditLogFilter(TypedDict, total=False): + resourceID: GenericID + resourceName: Literal[ + "action", + "am", + "analysis", + "connector", + "dashboard", + "device", + "dictionary", + "network", + "profile", + "run", + "runuser", + ] + find: str + start_date: Union[str, datetime] + end_date: Union[str, datetime] + limit: int + + +class AddonInfo(TypedDict): + id: GenericID + name: str + logo_url: Optional[str] + + +class StatisticsDate(TypedDict, total=False): + """ + Parameters for fetching usage statistics. + """ + + timezone: str + date: Union[str, datetime] + start_date: Union[str, datetime] + end_date: Union[str, datetime] + periodicity: Literal["hour", "day", "month"] + + +class ProfileTeam(TypedDict): + active: bool + created_at: datetime + email: str + id: str + name: str + + +class ProfileCreateInfo(TypedDict): + name: str + + +class ProfileCredentials(TypedDict, total=False): + password: str + pin_code: str diff --git a/src/tagoio_sdk/modules/Resources/Run.py b/src/tagoio_sdk/modules/Resources/Run.py index 63d7f8e..e7c4bf2 100644 --- a/src/tagoio_sdk/modules/Resources/Run.py +++ b/src/tagoio_sdk/modules/Resources/Run.py @@ -1,5 +1,5 @@ +from typing import Dict from typing import Optional -from typing import TypedDict from tagoio_sdk.common.Common_Type import GenericID from tagoio_sdk.common.Common_Type import Query @@ -9,6 +9,7 @@ from tagoio_sdk.modules.Resources.Notification_Type import NotificationInfo from tagoio_sdk.modules.Resources.Run_Type import CustomDomainCreate from tagoio_sdk.modules.Resources.Run_Type import CustomDomainInfo +from tagoio_sdk.modules.Resources.Run_Type import EmailTestData from tagoio_sdk.modules.Resources.Run_Type import LoginAsUserOptions from tagoio_sdk.modules.Resources.Run_Type import LoginResponse from tagoio_sdk.modules.Resources.Run_Type import RunInfo @@ -21,13 +22,23 @@ class Run(TagoIOModule): - """ - Manage services in account - Be sure to use an account token with “write” permissions when using - functions like create, edit and delete. - """ - def info(self) -> RunInfo: + """ + @description: + Retrieves information about the current Run environment configuration. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + https://help.tago.io/portal/en/kb/articles/run-themes Run Themes + + @example: + If receive an error "Authorization Denied", check policy **Profile** / **Access TagoRun settings** in Access Management. + ```python + resources = Resources() + result = resources.run.info() + print(result) # {'name': 'My Run Environment', 'logo': 'https://example.com/logo.png', ...} + ``` + """ result = self.doRequest( { "path": "/run", @@ -37,7 +48,23 @@ def info(self) -> RunInfo: return result - def edit(self, data: RunInfo) -> str: + def edit(self, data: Dict) -> str: + """ + @description: + Updates the Run environment configuration settings. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + https://help.tago.io/portal/en/kb/articles/run-themes Run Themes + + @example: + If receive an error "Authorization Denied", check policy **Profile** / **Edit TagoRun settings** in Access Management. + ```python + resources = Resources() + result = resources.run.edit({"name": "My Run Environment", "logo": "https://example.com/logo.png"}) + print(result) # TagoIO Run Successfully Updated + ``` + """ result = self.doRequest( { "path": "/run", @@ -48,7 +75,28 @@ def edit(self, data: RunInfo) -> str: return result - def listUsers(self, query: Query) -> list[UserInfo]: + def listUsers(self, query: Optional[Query] = None) -> list[UserInfo]: + """ + @description: + Retrieves a paginated list of Run users with customizable fields and filtering options. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + + @example: + If receive an error "Authorization Denied", or return empty list check policy **Run User** / **Access** in Access Management. + ```python + resources = Resources() + result = resources.run.listUsers({ + "page": 1, + "fields": ["id", "name", "email"], + "amount": 20 + }) + print(result) # [{'id': 'user-id-123', 'name': 'John Doe', 'email': 'example@email.com'}] + ``` + """ + if query is None: + query = {} if "orderBy" in query: firstArgument = query["orderBy"][0] secondArgument = query["orderBy"][1] @@ -74,6 +122,21 @@ def listUsers(self, query: Query) -> list[UserInfo]: return result def userInfo(self, userID: GenericID) -> UserInfo: + """ + @description: + Retrieves detailed information about a specific Run user. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Access** in Access Management. + ```python + resources = Resources() + result = resources.run.userInfo("user-id-123") + print(result) # {'id': 'user-id-123', 'name': 'John Doe', 'email': 'example@email.com', ...} + ``` + """ result = self.doRequest( { "path": f"/run/users/{userID}", @@ -85,7 +148,27 @@ def userInfo(self, userID: GenericID) -> UserInfo: return result - def userCreate(self, data: UserCreateInfo) -> str: + def userCreate(self, data: UserCreateInfo) -> Dict[str, str]: + """ + @description: + Creates a new user in the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Create** in Access Management. + ```python + resources = Resources() + result = resources.run.userCreate({ + "name": "John Doe", + "email": "john@example.com", + "password": "secure123", + "timezone": "America/New_York" + }) + print(result) # {'user': 'user-id-123'} + ``` + """ result = self.doRequest( { "path": "/run/users", @@ -96,7 +179,22 @@ def userCreate(self, data: UserCreateInfo) -> str: return result - def userEdit(self, userID: GenericID, data: UserInfo) -> str: + def userEdit(self, userID: GenericID, data: Dict) -> str: + """ + @description: + Updates information for an existing Run user. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Edit** in Access Management. + ```python + resources = Resources() + result = resources.run.userEdit("user-id-123", {"name": "Updated Name"}) + print(result) # TagoIO Run User Successfully Updated + ``` + """ result = self.doRequest( { "path": f"/run/users/{userID}", @@ -108,6 +206,21 @@ def userEdit(self, userID: GenericID, data: UserInfo) -> str: return result def userDelete(self, userID: GenericID) -> str: + """ + @description: + Permanently deletes a user from the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/191-tagorun TagoRun + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Delete** in Access Management. + ```python + resources = Resources() + result = resources.run.userDelete("user-id-123") + print(result) # Successfully Removed + ``` + """ result = self.doRequest( { "path": f"/run/users/{userID}", @@ -117,9 +230,19 @@ def userDelete(self, userID: GenericID) -> str: return result - def loginAsUser( - self, userID: GenericID, options: Optional[LoginAsUserOptions] - ) -> LoginResponse: + def loginAsUser(self, userID: GenericID, options: Optional[LoginAsUserOptions] = None) -> LoginResponse: + """ + @description: + Generates a login token to authenticate as a specific Run user. + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Login as user** in Access Management. + ```python + resources = Resources() + result = resources.run.loginAsUser("user-id-123") + print(result["token"]) # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ... + ``` + """ result = self.doRequest( { "path": f"/run/users/{userID}/login", @@ -132,11 +255,18 @@ def loginAsUser( return result - class emailData(TypedDict): - subject: str - body: str - - def emailTest(self, data: emailData) -> str: + def emailTest(self, data: EmailTestData) -> str: + """ + @description: + Tests the email configuration by sending a test message. + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.emailTest({"subject": "Test Email", "body": "This is a test message"}) + print(result) # E-mail sent to example@email.com + ``` + """ result = self.doRequest( { "path": "/run/email_test", @@ -148,6 +278,21 @@ def emailTest(self, data: emailData) -> str: return result def notificationList(self, userID: GenericID) -> list[NotificationInfo]: + """ + @description: + Retrieves a list of notifications for a specific Run user. + + @see: + https://help.tago.io/portal/en/kb/articles/223-notifications-for-users Notifications for Users + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Access notification** in Access Management. + ```python + resources = Resources() + result = resources.run.notificationList("user-id-123") + print(result) # [{'id': 'notification-id-123', 'title': 'System Update', 'message': 'Features', ...}] + ``` + """ result = self.doRequest( { "path": f"/run/notification/{userID}", @@ -157,9 +302,25 @@ def notificationList(self, userID: GenericID) -> list[NotificationInfo]: return result - def notificationCreate( - self, userID: GenericID, data: NotificationCreate - ) -> NotificationCreateReturn: + def notificationCreate(self, userID: GenericID, data: NotificationCreate) -> NotificationCreateReturn: + """ + @description: + Creates a new notification for a Run user. + + @see: + https://help.tago.io/portal/en/kb/articles/223-notifications-for-users Notifications for Users + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Create notification** in Access Management. + ```python + resources = Resources() + result = resources.run.notificationCreate("user-id-123", { + "title": "Update", + "message": "New feature available" + }) + print(result) # {'id': 'notification-id-123'} + ``` + """ result = self.doRequest( { "path": "/run/notification/", @@ -173,9 +334,22 @@ def notificationCreate( return result - def notificationEdit( - self, notificationID: GenericID, data: NotificationCreate - ) -> str: + def notificationEdit(self, notificationID: GenericID, data: Dict) -> str: + """ + @description: + Updates an existing notification in the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/223-notifications-for-users Notifications for Users + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Edit notification** in Access Management. + ```python + resources = Resources() + result = resources.run.notificationEdit("notification-id-123", {"title": "Updated Title"}) + print(result) # TagoIO Notification User Successfully Updated + ``` + """ result = self.doRequest( { "path": f"/run/notification/{notificationID}", @@ -187,6 +361,21 @@ def notificationEdit( return result def notificationDelete(self, notificationID: GenericID) -> str: + """ + @description: + Deletes a notification from the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/223-notifications-for-users Notifications for Users + + @example: + If receive an error "Authorization Denied", check policy **Run User** / **Delete notification** in Access Management. + ```python + resources = Resources() + result = resources.run.notificationDelete("notification-id-123") + print(result) # Successfully Removed + ``` + """ result = self.doRequest( { "path": f"/run/notification/{notificationID}", @@ -198,9 +387,19 @@ def notificationDelete(self, notificationID: GenericID) -> str: def ssoSAMLInfo(self) -> RunSAMLInfo: """ - Get the SAML Single Sign-On information for the account's RUN. + @description: + Retrieves the SAML Single Sign-On configuration information for the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/491-single-sign-on-sso Single Sign-On (SSO) + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.ssoSAMLInfo() + print(result) # {'sp': {'entity_id': 'https://example.com', ...}, ...} + ``` """ - result = self.doRequest( { "path": "/run/sso/saml", @@ -212,10 +411,22 @@ def ssoSAMLInfo(self) -> RunSAMLInfo: def ssoSAMLEdit(self, data: RunSAMLEditInfo) -> str: """ - Edit the SAML Single Sign-On metadata and mappings for the account's RUN. - :param: data Updated data for a RUN's SAML Single Sign-On configuration. + @description: + Updates the SAML SSO configuration for the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/491-single-sign-on-sso Single Sign-On (SSO) + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.ssoSAMLEdit({ + "active": True, + "idp_metadata": "..." + }) + print(result) # TagoIO Run SAML SSO Successfully Updated + ``` """ - result = self.doRequest( { "path": "/run/sso/saml", @@ -226,16 +437,23 @@ def ssoSAMLEdit(self, data: RunSAMLEditInfo) -> str: return result - def createCustomDomain( - self, profile_id: str, customDomainData: CustomDomainCreate - ) -> str: + def createCustomDomain(self, profile_id: str, customDomainData: CustomDomainCreate) -> str: """ - Create a TagoRUN custom domain for the profile. - :param: profile_id ID of the profile - :param: customDomainData query params - :returns: Success message. + @description: + Creates a custom domain configuration for the Run environment. + + @see: + https://help.tago.io/portal/en/kb/articles/custom-domain-configuration Custom Domain Configuration + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.createCustomDomain("profile-id-123", { + "domain": "app.mycompany.com" + }) + print(result) # Custom domain created successfully + ``` """ - result = self.doRequest( { "path": f"/run/customdomain/{profile_id}", @@ -248,11 +466,19 @@ def createCustomDomain( def getCustomDomain(self, profile_id: str) -> CustomDomainInfo: """ - set details of TagoRun custom domain for the profile. - :param: profile_id ID of the profile - :returns: Data for the profile's custom DNS configuration. + @description: + Retrieves the custom domain configuration for a Run profile. + + @see: + https://help.tago.io/portal/en/kb/articles/custom-domain-configuration Custom Domain Configuration + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.getCustomDomain("profile-id-123") + print(result) # {'domain': 'app.mycompany.com', 'verified': True, ...} + ``` """ - result = self.doRequest( { "path": f"/run/customdomain/{profile_id}", @@ -266,11 +492,19 @@ def getCustomDomain(self, profile_id: str) -> CustomDomainInfo: def deleteCustomDomain(self, profile_id: str) -> str: """ - delete a TagoRUN custom domain for the profile. - :param: profile_id ID of the profile - :returns: Success message. + @description: + Removes the custom domain configuration from a Run profile. + + @see: + https://help.tago.io/portal/en/kb/articles/custom-domain-configuration Custom Domain Configuration + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.deleteCustomDomain("profile-id-123") + print(result) # Custom domain deleted successfully + ``` """ - result = self.doRequest( { "path": f"/run/customdomain/{profile_id}", @@ -281,11 +515,19 @@ def deleteCustomDomain(self, profile_id: str) -> str: def regenerateCustomDomain(self, profile_id: str) -> str: """ - Regenerate a TagoRUN custom domain for the profile. - :param: profile_id ID of the profile - :returns: Success message. + @description: + Regenerates the custom domain configuration for a Run profile. + + @see: + https://help.tago.io/portal/en/kb/articles/custom-domain-configuration Custom Domain Configuration + + @example: + ```python + resources = Resources({"token": "YOUR-PROFILE-TOKEN"}) + result = resources.run.regenerateCustomDomain("profile-id-123") + print(result) # Custom domain regenerated successfully + ``` """ - result = self.doRequest( { "path": f"/run/customdomain/regenerate/{profile_id}", diff --git a/src/tagoio_sdk/modules/Resources/Run_Type.py b/src/tagoio_sdk/modules/Resources/Run_Type.py index e18dcb8..6e22b66 100644 --- a/src/tagoio_sdk/modules/Resources/Run_Type.py +++ b/src/tagoio_sdk/modules/Resources/Run_Type.py @@ -255,6 +255,17 @@ class LoginAsUserOptions(TypedDict): """ +class EmailTestData(TypedDict): + subject: str + """ + Subject of the test email. + """ + body: str + """ + Body content of the test email. + """ + + class SAMLAttributeMappings(TypedDict): email: str firstName: str diff --git a/tests/Resources/test_devices.py b/tests/Resources/test_devices.py index 5969776..e700c74 100644 --- a/tests/Resources/test_devices.py +++ b/tests/Resources/test_devices.py @@ -1,4 +1,8 @@ import os + +from typing import Any +from typing import Dict + from requests_mock.mocker import Mocker from tagoio_sdk.modules.Resources.Resources import Resources @@ -7,17 +11,139 @@ os.environ["T_ANALYSIS_TOKEN"] = "your_token_value" +# Mock data generators +def mockDeviceList() -> Dict[str, Any]: + """Generate mock device list response.""" + return { + "status": True, + "result": [ + { + "id": "device-id-123", + "name": "Temperature Sensor", + "active": True, + "type": "mutable", + "last_input": "2025-01-09T10:00:00.000Z", + "created_at": "2025-01-01T00:00:00.000Z", + } + ], + } + + +def mockDeviceCreate() -> Dict[str, Any]: + """Generate mock device create response.""" + return { + "status": True, + "result": { + "device_id": "device-id-new", + "bucket_id": "bucket-id-new", + "token": "new-token-123", + }, + } + + +def mockDeviceInfo() -> Dict[str, Any]: + """Generate mock device info response.""" + return { + "status": True, + "result": { + "id": "device-id-123", + "name": "Temperature Sensor", + "active": True, + "type": "mutable", + "bucket": {"id": "bucket-id-123", "name": "Device Data"}, + "network": "network-id-123", + "connector": "connector-id-123", + "last_input": "2025-01-09T10:00:00.000Z", + "created_at": "2025-01-01T00:00:00.000Z", + }, + } + + +def mockConfigParams() -> Dict[str, Any]: + """Generate mock configuration parameters.""" + return { + "status": True, + "result": [ + {"id": "param-id-123", "key": "threshold", "value": "25.5", "sent": False} + ], + } + + +def mockTokenList() -> Dict[str, Any]: + """Generate mock token list response.""" + return { + "status": True, + "result": [ + { + "token": "token-123", + "name": "Default Token", + "permission": "full", + "device_id": "device-id-123", + "created_at": "2025-01-01T00:00:00.000Z", + } + ], + } + + +def mockTokenCreate() -> Dict[str, Any]: + """Generate mock token create response.""" + return {"status": True, "result": {"token": "new-token-456", "expire_date": None}} + + +def mockDeviceData() -> Dict[str, Any]: + """Generate mock device data response.""" + return { + "status": True, + "result": [ + { + "id": "data-id-123", + "variable": "temperature", + "value": 25.5, + "unit": "°C", + "time": "2025-01-09T10:00:00.000Z", + } + ], + } + + +def mockChunkList() -> Dict[str, Any]: + """Generate mock chunk list response.""" + return { + "status": True, + "result": [ + { + "id": "1704067200_1706745600", + "from_date": "2025-01-01T00:00:00.000Z", + "to_date": "2025-02-01T00:00:00.000Z", + "amount": 1000, + } + ], + } + + +def mockBackupResponse() -> Dict[str, Any]: + """Generate mock backup response.""" + return { + "status": True, + "result": {"file_address": "/backups/device-123/backup.csv"}, + } + + def testSendDeviceDataMethod(requests_mock: Mocker) -> None: """ :param requests_mock are a plugin of pytest to mock the requests. """ deviceID = "device1" - requests_mock.post(f"https://api.tago.io/device/{deviceID}/data", - json={"status": True, "result": "1 Data Added"}) + requests_mock.post( + f"https://api.tago.io/device/{deviceID}/data", + json={"status": True, "result": "1 Data Added"}, + ) resources = Resources() - response = resources.devices.sendDeviceData(deviceID, {"variable": "test", "value": 1}) + response = resources.devices.sendDeviceData( + deviceID, {"variable": "test", "value": 1} + ) assert response == "1 Data Added" assert isinstance(response, str) @@ -29,11 +155,15 @@ def testEditDeviceDataMethod(requests_mock: Mocker) -> None: """ deviceID = "device1" - requests_mock.put(f"https://api.tago.io/device/{deviceID}/data", - json={"status": True, "result": "1 item(s) updated"}) + requests_mock.put( + f"https://api.tago.io/device/{deviceID}/data", + json={"status": True, "result": "1 item(s) updated"}, + ) resources = Resources() - response = resources.devices.editDeviceData(deviceID, {"id": "idOfTheRecord", "value": "new value", "unit": "new unit"}) + response = resources.devices.editDeviceData( + deviceID, {"id": "idOfTheRecord", "value": "new value", "unit": "new unit"} + ) assert response == "1 item(s) updated" assert isinstance(response, str) @@ -45,11 +175,281 @@ def testDeleteDeviceDataMethod(requests_mock: Mocker) -> None: """ deviceID = "device1" - requests_mock.delete(f"https://api.tago.io/device/{deviceID}/data", - json={"status": True, "result": "2 Data Removed"}) + requests_mock.delete( + f"https://api.tago.io/device/{deviceID}/data", + json={"status": True, "result": "2 Data Removed"}, + ) resources = Resources() - response = resources.devices.deleteDeviceData(deviceID, {"ids": ["recordIdToDelete", "anotherRecordIdToDelete"]}) + response = resources.devices.deleteDeviceData( + deviceID, {"ids": ["recordIdToDelete", "anotherRecordIdToDelete"]} + ) assert response == "2 Data Removed" assert isinstance(response, str) + + +def testListDevice(requests_mock: Mocker) -> None: + """Test listDevice method of Devices class.""" + mock_response = mockDeviceList() + requests_mock.get("https://api.tago.io/device", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.listDevice({"page": 1, "amount": 20}) + + assert isinstance(result, list) + assert len(result) > 0 + assert result[0]["id"] == "device-id-123" + + +def testCreateDevice(requests_mock: Mocker) -> None: + """Test create method of Devices class.""" + mock_response = mockDeviceCreate() + requests_mock.post("https://api.tago.io/device", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.create( + { + "name": "New Device", + "network": "network-id-123", + "connector": "connector-id-123", + "type": "mutable", + } + ) + + assert result["device_id"] == "device-id-new" + assert result["token"] == "new-token-123" + + +def testEditDevice(requests_mock: Mocker) -> None: + """Test edit method of Devices class.""" + device_id = "device-id-123" + requests_mock.put( + f"https://api.tago.io/device/{device_id}", + json={"status": True, "result": "Successfully Updated"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.edit(device_id, {"name": "Updated Name"}) + + assert result == "Successfully Updated" + + +def testDeleteDevice(requests_mock: Mocker) -> None: + """Test delete method of Devices class.""" + device_id = "device-id-123" + requests_mock.delete( + f"https://api.tago.io/device/{device_id}", + json={"status": True, "result": "Successfully Removed"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.delete(device_id) + + assert result == "Successfully Removed" + + +def testDeviceInfo(requests_mock: Mocker) -> None: + """Test info method of Devices class.""" + device_id = "device-id-123" + mock_response = mockDeviceInfo() + requests_mock.get(f"https://api.tago.io/device/{device_id}", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.info(device_id) + + assert result["id"] == "device-id-123" + assert result["name"] == "Temperature Sensor" + + +def testParamSet(requests_mock: Mocker) -> None: + """Test paramSet method of Devices class.""" + device_id = "device-id-123" + requests_mock.post( + f"https://api.tago.io/device/{device_id}/params", + json={"status": True, "result": "Successfully Updated"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.paramSet( + device_id, {"key": "threshold", "value": "30", "sent": False} + ) + + assert result == "Successfully Updated" + + +def testParamList(requests_mock: Mocker) -> None: + """Test paramList method of Devices class.""" + device_id = "device-id-123" + mock_response = mockConfigParams() + requests_mock.get( + f"https://api.tago.io/device/{device_id}/params", json=mock_response + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.paramList(device_id) + + assert isinstance(result, list) + assert result[0]["key"] == "threshold" + + +def testParamRemove(requests_mock: Mocker) -> None: + """Test paramRemove method of Devices class.""" + device_id = "device-id-123" + param_id = "param-id-123" + requests_mock.delete( + f"https://api.tago.io/device/{device_id}/params/{param_id}", + json={"status": True, "result": "Successfully Removed"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.paramRemove(device_id, param_id) + + assert result == "Successfully Removed" + + +def testTokenList(requests_mock: Mocker) -> None: + """Test tokenList method of Devices class.""" + device_id = "device-id-123" + mock_response = mockTokenList() + requests_mock.get( + f"https://api.tago.io/device/token/{device_id}", json=mock_response + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.tokenList(device_id) + + assert isinstance(result, list) + assert result[0]["token"] == "token-123" + + +def testTokenCreate(requests_mock: Mocker) -> None: + """Test tokenCreate method of Devices class.""" + mock_response = mockTokenCreate() + requests_mock.post("https://api.tago.io/device/token", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.tokenCreate( + "device-id-123", {"name": "New Token", "permission": "write"} + ) + + assert result["token"] == "new-token-456" + + +def testTokenDelete(requests_mock: Mocker) -> None: + """Test tokenDelete method of Devices class.""" + token = "token-to-delete" + requests_mock.delete( + f"https://api.tago.io/device/token/{token}", + json={"status": True, "result": "Successfully Removed"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.tokenDelete(token) + + assert result == "Successfully Removed" + + +def testGetDeviceData(requests_mock: Mocker) -> None: + """Test getDeviceData method of Devices class.""" + device_id = "device-id-123" + mock_response = mockDeviceData() + requests_mock.get( + f"https://api.tago.io/device/{device_id}/data", json=mock_response + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.getDeviceData(device_id, {"variables": ["temperature"]}) + + assert isinstance(result, list) + assert result[0]["variable"] == "temperature" + + +def testEmptyDeviceData(requests_mock: Mocker) -> None: + """Test emptyDeviceData method of Devices class.""" + device_id = "device-id-123" + requests_mock.post( + f"https://api.tago.io/device/{device_id}/empty", + json={"status": True, "result": "All data removed"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.emptyDeviceData(device_id) + + assert result == "All data removed" + + +def testAmount(requests_mock: Mocker) -> None: + """Test amount method of Devices class.""" + device_id = "device-id-123" + requests_mock.get( + f"https://api.tago.io/device/{device_id}/data_amount", + json={"status": True, "result": 15234}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.amount(device_id) + + assert result == 15234 + + +def testGetChunk(requests_mock: Mocker) -> None: + """Test getChunk method of Devices class.""" + device_id = "device-id-123" + mock_response = mockChunkList() + requests_mock.get( + f"https://api.tago.io/device/{device_id}/chunk", json=mock_response + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.getChunk(device_id) + + assert isinstance(result, list) + assert result[0]["amount"] == 1000 + + +def testDeleteChunk(requests_mock: Mocker) -> None: + """Test deleteChunk method of Devices class.""" + device_id = "device-id-123" + chunk_id = "chunk-id-456" + requests_mock.delete( + f"https://api.tago.io/device/{device_id}/chunk/{chunk_id}", + json={"status": True, "result": f"Chunk {chunk_id} deleted"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.deleteChunk(device_id, chunk_id) + + assert result == f"Chunk {chunk_id} deleted" + + +def testDataBackup(requests_mock: Mocker) -> None: + """Test dataBackup method of Devices class.""" + device_id = "device-id-123" + mock_response = mockBackupResponse() + requests_mock.post( + f"https://api.tago.io/device/{device_id}/data/backup", json=mock_response + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.dataBackup( + {"deviceID": device_id, "file_address": "/backups/backup.csv", "headers": True} + ) + + assert result["file_address"] == "/backups/device-123/backup.csv" + + +def testDataRestore(requests_mock: Mocker) -> None: + """Test dataRestore method of Devices class.""" + device_id = "device-id-123" + requests_mock.post( + f"https://api.tago.io/device/{device_id}/data/restore", + json={"status": True, "result": "Data import added to the queue successfully!"}, + ) + + resources = Resources({"token": "your_token_value"}) + result = resources.devices.dataRestore( + {"deviceID": device_id, "file_address": "/backups/restore.csv"} + ) + + assert result == "Data import added to the queue successfully!" diff --git a/tests/Resources/test_integration_network.py b/tests/Resources/test_integration_network.py index 35e4516..4c1aea7 100644 --- a/tests/Resources/test_integration_network.py +++ b/tests/Resources/test_integration_network.py @@ -1,39 +1,236 @@ -from requests_mock.mocker import Mocker +from typing import Any +from typing import Dict +from requests_mock.mocker import Mocker from src.tagoio_sdk.modules.Resources.IntegrationNetwork import Networks -from tests.conftest import mockListNetwork, mockNetworkInfo -def testNetworksMethodListNetworks( - requests_mock: Mocker, mockListNetwork: mockListNetwork -): - """ - :param requests_mock are a plugin of pytest to mock the requests. - :param mockListNetwork is a fixture to return list of NetworkInfo. - """ +def mockListNetwork() -> Dict[str, Any]: + """Mock to return the list of NetworkInfo.""" + return { + "status": True, + "result": [ + {"id": "60af66df0ae39d0012b0bbe9", "name": "AWS IoT"}, + {"id": "5d48632019b67f001c874a6b", "name": "BeWhere"}, + ], + } + + +def mockNetworkInfo() -> Dict[str, Any]: + """Mock to return the object NetworkInfo.""" + return { + "status": True, + "result": { + "id": "5ede22a7427104001c248b08", + "name": "LoRaWAN Activity", + "profile": "5bbcb03b667d7a002e56664b", + "middleware_endpoint": "https://lorawan.tago.io", + }, + } + + +def mockNetworkCreate() -> Dict[str, Any]: + """Mock to return network create response.""" + return { + "status": True, + "result": {"network": "network-id-123"}, + } + + +def mockNetworkEdit() -> Dict[str, Any]: + """Mock to return network edit response.""" + return { + "status": True, + "result": "Successfully Updated", + } + + +def mockNetworkDelete() -> Dict[str, Any]: + """Mock to return network delete response.""" + return { + "status": True, + "result": "Successfully Removed", + } + + +def mockNetworkTokenList() -> Dict[str, Any]: + """Mock to return list of network tokens.""" + return { + "status": True, + "result": [ + { + "token": "token-123", + "name": "Default Token", + "permission": "full", + "created_at": "2025-01-09T10:00:00.000Z", + }, + { + "token": "token-456", + "name": "Read-Only Token", + "permission": "read", + "created_at": "2025-01-08T15:30:00.000Z", + }, + ], + } + + +def mockNetworkTokenCreate() -> Dict[str, Any]: + """Mock to return token create response.""" + return { + "status": True, + "result": { + "token": "new-token-value", + "expire_date": None, + }, + } + - requests_mock.get("https://api.tago.io/integration/network", json=mockListNetwork) +def mockNetworkTokenDelete() -> Dict[str, Any]: + """Mock to return token delete response.""" + return { + "status": True, + "result": "Successfully Removed", + } + + +def testNetworksMethodListNetworks(requests_mock: Mocker): + """Test listNetwork method with pagination.""" + requests_mock.get("https://api.tago.io/integration/network", json=mockListNetwork()) tokenFake = {"token": "fake_token"} response = Networks(params=tokenFake).listNetwork(queryObj={"amount": 100}) - assert response == mockListNetwork["result"] + assert response == mockListNetwork()["result"] assert isinstance(response, list) assert isinstance(response[0], dict) -def testNetworksMethodInfo(requests_mock: Mocker, mockNetworkInfo: mockNetworkInfo): - """ - :param requests_mock are a plugin of pytest to mock the requests. - :param mockNetworkInfo is a fixture to return the object NetworkInfo. - """ +def testNetworksMethodInfo(requests_mock: Mocker): + """Test info method to retrieve network details.""" networkID = "fake_network_id" requests_mock.get( - f"https://api.tago.io/integration/network/{networkID}", json=mockNetworkInfo + f"https://api.tago.io/integration/network/{networkID}", json=mockNetworkInfo() ) tokenFake = {"token": "fake_token"} response = Networks(params=tokenFake).info(networkID=networkID) - assert response == mockNetworkInfo["result"] + assert response == mockNetworkInfo()["result"] + assert isinstance(response, dict) + + +def testNetworksMethodCreate(requests_mock: Mocker): + """Test create method to create a new network.""" + requests_mock.post("https://api.tago.io/integration/network", json=mockNetworkCreate()) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).create( + { + "name": "My Custom Network", + "description": "Custom integration network", + "middleware_endpoint": "https://my-middleware.com/endpoint", + "public": False, + } + ) + + assert response == mockNetworkCreate()["result"] + assert isinstance(response, dict) + assert "network" in response + + +def testNetworksMethodEdit(requests_mock: Mocker): + """Test edit method to update network properties.""" + networkID = "network-id-123" + requests_mock.put( + f"https://api.tago.io/integration/network/{networkID}", json=mockNetworkEdit() + ) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).edit( + networkID, + { + "name": "Updated Network Name", + "description": "Updated description", + "public": True, + }, + ) + + assert response == mockNetworkEdit()["result"] + assert response == "Successfully Updated" + + +def testNetworksMethodDelete(requests_mock: Mocker): + """Test delete method to remove a network.""" + networkID = "network-id-123" + requests_mock.delete( + f"https://api.tago.io/integration/network/{networkID}", json=mockNetworkDelete() + ) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).delete(networkID) + + assert response == mockNetworkDelete()["result"] + assert response == "Successfully Removed" + + +def testNetworksMethodTokenList(requests_mock: Mocker): + """Test tokenList method to retrieve network tokens.""" + networkID = "network-id-123" + requests_mock.get( + f"https://api.tago.io/integration/network/token/{networkID}", + json=mockNetworkTokenList(), + ) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).tokenList( + networkID, + { + "page": 1, + "amount": 20, + "fields": ["name", "token", "permission"], + "orderBy": ["created_at", "desc"], + }, + ) + + # dateParserList converts created_at strings to datetime objects + assert isinstance(response, list) + assert len(response) == 2 + assert response[0]["token"] == "token-123" + assert response[0]["name"] == "Default Token" + assert response[0]["permission"] == "full" + + +def testNetworksMethodTokenCreate(requests_mock: Mocker): + """Test tokenCreate method to generate a new token.""" + requests_mock.post( + "https://api.tago.io/integration/network/token", json=mockNetworkTokenCreate() + ) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).tokenCreate( + "network-id-123", + { + "name": "Production Token", + "permission": "write", + "expire_time": "never", + }, + ) + + assert response == mockNetworkTokenCreate()["result"] assert isinstance(response, dict) + assert "token" in response + + +def testNetworksMethodTokenDelete(requests_mock: Mocker): + """Test tokenDelete method to remove a token.""" + token = "token-to-delete" + requests_mock.delete( + f"https://api.tago.io/integration/network/token/{token}", + json=mockNetworkTokenDelete(), + ) + + tokenFake = {"token": "fake_token"} + response = Networks(params=tokenFake).tokenDelete(token) + + assert response == mockNetworkTokenDelete()["result"] + assert response == "Successfully Removed" diff --git a/tests/Resources/test_profile.py b/tests/Resources/test_profile.py index 784238f..463312f 100644 --- a/tests/Resources/test_profile.py +++ b/tests/Resources/test_profile.py @@ -1,8 +1,10 @@ import os + from requests_mock.mocker import Mocker from tagoio_sdk.common.Common_Type import TokenDataList -from tagoio_sdk.modules.Resources.Profile_Type import ProfileInfo, ProfileSummary +from tagoio_sdk.modules.Resources.Profile_Type import ProfileInfo +from tagoio_sdk.modules.Resources.Profile_Type import ProfileSummary from tagoio_sdk.modules.Resources.Resources import Resources @@ -187,3 +189,306 @@ def testProfileMethodTokenList(requests_mock: Mocker) -> None: assert response[1]["token"] == mockTokenDataList()["result"][1]["token"] assert isinstance(response, list) assert isinstance(response[1], dict) + + +def mockProfileCreate() -> dict: + return {"status": True, "result": {"id": "new-profile-id-123"}} + + +def mockProfileEdit() -> dict: + return {"status": True, "result": "Successfully Updated"} + + +def mockProfileDelete() -> dict: + return {"status": True, "result": "Successfully Removed"} + + +def mockUsageStatisticList() -> dict: + return { + "status": True, + "result": [ + { + "time": "2024-09-02T00:01:29.749Z", + "analysis": 0.07, + "data_records": 67254, + "input": 1500, + "output": 250, + }, + { + "time": "2024-09-03T00:01:29.749Z", + "analysis": 0.12, + "data_records": 85123, + "input": 2100, + "output": 340, + }, + ], + } + + +def mockAuditLog() -> dict: + return { + "status": True, + "result": { + "events": [ + { + "resourceName": "device", + "message": "Device created", + "resourceID": "device-id-123", + "who": "account-id-456", + "date": "2024-12-01T10:00:00.000Z", + } + ], + "statistics": { + "recordsMatched": 1, + "recordsScanned": 100, + "bytesScanned": 5000, + }, + "status": "Complete", + "queryId": "query-id-789", + }, + } + + +def mockAddonInfo() -> dict: + return { + "status": True, + "result": {"id": "addon-id-123", "name": "Custom DNS", "logo_url": None}, + } + + +def mockServiceEdit() -> dict: + return {"status": True, "result": "Profile resource allocation Successfully Updated"} + + +def mockTransferToken() -> dict: + return {"status": True, "result": "Token Successfully Transferred"} + + +def mockTokenCreate() -> dict: + return { + "status": True, + "result": { + "token": "new-token-value", + "name": "API Access", + "permission": "full", + "expire_date": "2025-12-31T23:59:59.000Z", + }, + } + + +def mockTokenDelete() -> dict: + return {"status": True, "result": "Token Successfully Removed"} + + +def mockTeamList() -> dict: + return { + "status": True, + "result": [ + { + "id": "account-id-123", + "active": False, + "name": "John Doe", + "email": "john@example.com", + "created_at": "2024-01-01T10:00:00.000Z", + }, + { + "id": "account-id-456", + "active": True, + "name": "Jane Smith", + "email": "jane@example.com", + "created_at": "2024-02-01T10:00:00.000Z", + }, + ], + } + + +def mockAddTeamMember() -> dict: + return {"status": True, "result": "User invited"} + + +def mockDeleteTeamMember() -> dict: + return {"status": True, "result": "Account Successfully Removed"} + + +def testProfileMethodCreate(requests_mock: Mocker) -> None: + """Test profile creation""" + requests_mock.post("https://api.tago.io/profile/", json=mockProfileCreate()) + + resources = Resources() + response = resources.profile.create( + {"name": "New Profile"}, allocate_free_resources=True + ) + + assert response["id"] == mockProfileCreate()["result"]["id"] + assert isinstance(response, dict) + + +def testProfileMethodEdit(requests_mock: Mocker) -> None: + """Test profile editing""" + requests_mock.put( + "https://api.tago.io/profile/profile-id-123", json=mockProfileEdit() + ) + + resources = Resources() + response = resources.profile.edit("profile-id-123", {"name": "Updated Name"}) + + assert response == mockProfileEdit()["result"] + assert isinstance(response, str) + + +def testProfileMethodDelete(requests_mock: Mocker) -> None: + """Test profile deletion""" + requests_mock.delete( + "https://api.tago.io/profile/profile-id-123", json=mockProfileDelete() + ) + + resources = Resources() + response = resources.profile.delete( + "profile-id-123", {"password": "test-password"} + ) + + assert response == mockProfileDelete()["result"] + assert isinstance(response, str) + + +def testProfileMethodUsageStatisticList(requests_mock: Mocker) -> None: + """Test usage statistics listing""" + requests_mock.get( + "https://api.tago.io/profile/profile-id-123/statistics", + json=mockUsageStatisticList(), + ) + + resources = Resources() + response = resources.profile.usageStatisticList("profile-id-123") + + assert len(response) == 2 + assert response[0]["analysis"] == 0.07 + assert isinstance(response, list) + + +def testProfileMethodAuditLog(requests_mock: Mocker) -> None: + """Test audit log creation""" + requests_mock.get( + "https://api.tago.io/profile/profile-id-123/auditlog", json=mockAuditLog() + ) + + resources = Resources() + response = resources.profile.auditLog("profile-id-123") + + assert response["queryId"] == mockAuditLog()["result"]["queryId"] + assert response["status"] == "Complete" + assert isinstance(response, dict) + + +def testProfileMethodAuditLogQuery(requests_mock: Mocker) -> None: + """Test audit log query""" + requests_mock.get( + "https://api.tago.io/profile/profile-id-123/auditlog/query-id-789", + json=mockAuditLog(), + ) + + resources = Resources() + response = resources.profile.auditLogQuery("profile-id-123", "query-id-789") + + assert response["queryId"] == mockAuditLog()["result"]["queryId"] + assert isinstance(response, dict) + + +def testProfileMethodServiceEdit(requests_mock: Mocker) -> None: + """Test service editing""" + requests_mock.post( + "https://api.tago.io/profile/profile-id-123/services", json=mockServiceEdit() + ) + + resources = Resources() + response = resources.profile.serviceEdit( + "profile-id-123", {"input": 350000, "output": 342153} + ) + + assert response == mockServiceEdit()["result"] + assert isinstance(response, str) + + +def testProfileMethodTransferToken(requests_mock: Mocker) -> None: + """Test token transfer""" + requests_mock.put( + "https://api.tago.io/profile/switch/target-profile-123", + json=mockTransferToken(), + ) + + resources = Resources() + response = resources.profile.transferTokenToAnotherProfile("target-profile-123") + + assert response == mockTransferToken()["result"] + assert isinstance(response, str) + + +def testProfileMethodTokenCreate(requests_mock: Mocker) -> None: + """Test token creation""" + requests_mock.post( + "https://api.tago.io/profile/profile-id-123/token", json=mockTokenCreate() + ) + + resources = Resources() + response = resources.profile.tokenCreate( + "profile-id-123", + {"name": "API Access", "permission": "full", "email": "test@example.com"}, + ) + + assert response["token"] == mockTokenCreate()["result"]["token"] + assert isinstance(response, dict) + + +def testProfileMethodTokenDelete(requests_mock: Mocker) -> None: + """Test token deletion""" + requests_mock.delete( + "https://api.tago.io/profile/profile-id-123/token/token-xyz", + json=mockTokenDelete(), + ) + + resources = Resources() + response = resources.profile.tokenDelete("profile-id-123", "token-xyz") + + assert response == mockTokenDelete()["result"] + assert isinstance(response, str) + + +def testProfileMethodAddTeamMember(requests_mock: Mocker) -> None: + """Test adding team member""" + requests_mock.post( + "https://api.tago.io/profile/profile-id-123/team", json=mockAddTeamMember() + ) + + resources = Resources() + response = resources.profile.addTeamMember("profile-id-123", "user@example.com") + + assert response == mockAddTeamMember()["result"] + assert isinstance(response, str) + + +def testProfileMethodTeamList(requests_mock: Mocker) -> None: + """Test team member listing""" + requests_mock.get( + "https://api.tago.io/profile/profile-id-123/team", json=mockTeamList() + ) + + resources = Resources() + response = resources.profile.teamList("profile-id-123") + + assert len(response) == 2 + assert response[0]["name"] == "John Doe" + assert isinstance(response, list) + + +def testProfileMethodDeleteTeamMember(requests_mock: Mocker) -> None: + """Test team member deletion""" + requests_mock.delete( + "https://api.tago.io/profile/profile-id-123/team/account-id-456", + json=mockDeleteTeamMember(), + ) + + resources = Resources() + response = resources.profile.deleteTeamMember("profile-id-123", "account-id-456") + + assert response == mockDeleteTeamMember()["result"] + assert isinstance(response, str) diff --git a/tests/Resources/test_run.py b/tests/Resources/test_run.py new file mode 100644 index 0000000..67216d8 --- /dev/null +++ b/tests/Resources/test_run.py @@ -0,0 +1,421 @@ +import os + +from requests_mock.mocker import Mocker + +from tagoio_sdk.modules.Resources.Resources import Resources + + +os.environ["T_ANALYSIS_TOKEN"] = "your_token_value" + + +def mockRunInfo() -> dict: + return { + "status": True, + "result": { + "profile": "profile_id_123", + "active": True, + "name": "My Run Environment", + "sub_title": "IoT Application", + "url": "myapp.run.tago.io", + "email_domain": None, + "signup_method": "default", + "favicon": None, + "logo": "https://example.com/logo.png", + "signup_logo": None, + "signup_logo_options": {}, + "sidebar_buttons": [], + "signup_fields": [], + "email_templates": {}, + "feature_devicewifisetup": {}, + "feature_geolocation": {}, + "theme": {}, + "integration": {}, + "sso_saml_active": False, + "security": {}, + }, + } + + +def mockRunEdit() -> dict: + return {"status": True, "result": "TagoIO Run Successfully Updated"} + + +def mockUserList() -> dict: + return { + "status": True, + "result": [ + { + "id": "user_id_1", + "name": "John Doe", + "email": "john@example.com", + "timezone": "America/New_York", + "created_at": "2023-02-21T15:17:35.759Z", + "updated_at": "2023-02-21T15:17:35.759Z", + "last_login": "2023-02-21T15:17:35.759Z", + }, + { + "id": "user_id_2", + "name": "Jane Smith", + "email": "jane@example.com", + "timezone": "America/Los_Angeles", + "created_at": "2023-02-21T16:17:35.759Z", + "updated_at": "2023-02-21T16:17:35.759Z", + "last_login": "2023-02-21T16:17:35.759Z", + }, + ], + } + + +def mockUserInfo() -> dict: + return { + "status": True, + "result": { + "id": "user_id_1", + "name": "John Doe", + "email": "john@example.com", + "timezone": "America/New_York", + "company": "ACME Corp", + "phone": "+1234567890", + "language": "en-US", + "profile": "profile_id_123", + "active": True, + "newsletter": False, + "last_login": "2023-02-21T15:17:35.759Z", + "created_at": "2023-02-21T15:17:35.759Z", + "updated_at": "2023-02-21T15:17:35.759Z", + "options": {}, + "tags": [{"key": "role", "value": "admin"}], + }, + } + + +def mockUserCreate() -> dict: + return {"status": True, "result": {"user": "user_id_new"}} + + +def mockUserEdit() -> dict: + return {"status": True, "result": "TagoIO Run User Successfully Updated"} + + +def mockUserDelete() -> dict: + return {"status": True, "result": "Successfully Removed"} + + +def mockLoginAsUser() -> dict: + return { + "status": True, + "result": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...", + "expire_date": "2024-02-21T15:17:35.759Z", + }, + } + + +def mockEmailTest() -> dict: + return {"status": True, "result": "E-mail sent to example@email.com"} + + +def mockNotificationList() -> dict: + return { + "status": True, + "result": [ + { + "id": "notification_id_1", + "title": "System Update", + "message": "New features available", + }, + { + "id": "notification_id_2", + "title": "Maintenance", + "message": "Scheduled maintenance tonight", + }, + ], + } + + +def mockNotificationCreate() -> dict: + return {"status": True, "result": {"id": "notification_id_new"}} + + +def mockNotificationEdit() -> dict: + return {"status": True, "result": "TagoIO Notification User Successfully Updated"} + + +def mockNotificationDelete() -> dict: + return {"status": True, "result": "Successfully Removed"} + + +def mockSSOSAMLInfo() -> dict: + return { + "status": True, + "result": { + "sp": { + "entity_id": "https://example.com", + "acs_url": "https://example.com/acs", + "metadata": "...", + }, + "idp": {"issuer": "https://idp.example.com"}, + "mapping": {"email": "email", "firstName": "firstName"}, + }, + } + + +def mockSSOSAMLEdit() -> dict: + return {"status": True, "result": "TagoIO Run SAML SSO Successfully Updated"} + + +def mockCustomDomainCreate() -> dict: + return {"status": True, "result": "Custom domain created successfully"} + + +def mockCustomDomainInfo() -> dict: + return { + "status": True, + "result": { + "active": True, + "domain": "mycompany.com", + "subdomain": "app", + "email": "app.mycompany.com", + "dns_ssl": {"status": True, "type": "TXT", "key": "key1", "value": "value1"}, + "dns_page": {"status": True, "type": "CNAME", "key": "key2", "value": "value2"}, + "dns_email_1": {"status": True, "type": "MX", "key": "key3", "value": "value3"}, + "dns_email_2": {"status": True, "type": "TXT", "key": "key4", "value": "value4"}, + "dns_email_3": {"status": True, "type": "TXT", "key": "key5", "value": "value5"}, + "created_at": "2023-02-21T15:17:35.759Z", + }, + } + + +def mockCustomDomainDelete() -> dict: + return {"status": True, "result": "Custom domain deleted successfully"} + + +def mockCustomDomainRegenerate() -> dict: + return {"status": True, "result": "Custom domain regenerated successfully"} + + +def testRunMethodInfo(requests_mock: Mocker) -> None: + """Test info method of Run class.""" + mock_response = mockRunInfo() + requests_mock.get("https://api.tago.io/run", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.info() + + assert result["name"] == "My Run Environment" + assert result["active"] is True + + +def testRunMethodEdit(requests_mock: Mocker) -> None: + """Test edit method of Run class.""" + mock_response = mockRunEdit() + requests_mock.put("https://api.tago.io/run", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.edit({"name": "Updated Name"}) + + assert result == "TagoIO Run Successfully Updated" + + +def testRunMethodListUsers(requests_mock: Mocker) -> None: + """Test listUsers method of Run class.""" + mock_response = mockUserList() + requests_mock.get("https://api.tago.io/run/users", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.listUsers({"page": 1, "amount": 20}) + + assert isinstance(result, list) + assert len(result) == 2 + assert result[0]["id"] == "user_id_1" + assert result[1]["id"] == "user_id_2" + + +def testRunMethodUserInfo(requests_mock: Mocker) -> None: + """Test userInfo method of Run class.""" + mock_response = mockUserInfo() + requests_mock.get("https://api.tago.io/run/users/user_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.userInfo("user_id_1") + + assert result["id"] == "user_id_1" + assert result["name"] == "John Doe" + assert result["email"] == "john@example.com" + + +def testRunMethodUserCreate(requests_mock: Mocker) -> None: + """Test userCreate method of Run class.""" + mock_response = mockUserCreate() + requests_mock.post("https://api.tago.io/run/users", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.userCreate( + { + "name": "New User", + "email": "newuser@example.com", + "password": "secure123", + "timezone": "America/New_York", + } + ) + + assert result["user"] == "user_id_new" + + +def testRunMethodUserEdit(requests_mock: Mocker) -> None: + """Test userEdit method of Run class.""" + mock_response = mockUserEdit() + requests_mock.put("https://api.tago.io/run/users/user_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.userEdit("user_id_1", {"name": "Updated Name"}) + + assert result == "TagoIO Run User Successfully Updated" + + +def testRunMethodUserDelete(requests_mock: Mocker) -> None: + """Test userDelete method of Run class.""" + mock_response = mockUserDelete() + requests_mock.delete("https://api.tago.io/run/users/user_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.userDelete("user_id_1") + + assert result == "Successfully Removed" + + +def testRunMethodLoginAsUser(requests_mock: Mocker) -> None: + """Test loginAsUser method of Run class.""" + mock_response = mockLoginAsUser() + requests_mock.get("https://api.tago.io/run/users/user_id_1/login", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.loginAsUser("user_id_1") + + assert "token" in result + assert result["token"].startswith("eyJ") + + +def testRunMethodEmailTest(requests_mock: Mocker) -> None: + """Test emailTest method of Run class.""" + mock_response = mockEmailTest() + requests_mock.post("https://api.tago.io/run/email_test", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.emailTest({"subject": "Test", "body": "Test message"}) + + assert result == "E-mail sent to example@email.com" + + +def testRunMethodNotificationList(requests_mock: Mocker) -> None: + """Test notificationList method of Run class.""" + mock_response = mockNotificationList() + requests_mock.get("https://api.tago.io/run/notification/user_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.notificationList("user_id_1") + + assert isinstance(result, list) + assert len(result) == 2 + assert result[0]["id"] == "notification_id_1" + + +def testRunMethodNotificationCreate(requests_mock: Mocker) -> None: + """Test notificationCreate method of Run class.""" + mock_response = mockNotificationCreate() + requests_mock.post("https://api.tago.io/run/notification/", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.notificationCreate("user_id_1", {"title": "Alert", "message": "Important message"}) + + assert result["id"] == "notification_id_new" + + +def testRunMethodNotificationEdit(requests_mock: Mocker) -> None: + """Test notificationEdit method of Run class.""" + mock_response = mockNotificationEdit() + requests_mock.put("https://api.tago.io/run/notification/notification_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.notificationEdit("notification_id_1", {"title": "Updated Title"}) + + assert result == "TagoIO Notification User Successfully Updated" + + +def testRunMethodNotificationDelete(requests_mock: Mocker) -> None: + """Test notificationDelete method of Run class.""" + mock_response = mockNotificationDelete() + requests_mock.delete("https://api.tago.io/run/notification/notification_id_1", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.notificationDelete("notification_id_1") + + assert result == "Successfully Removed" + + +def testRunMethodSSOSAMLInfo(requests_mock: Mocker) -> None: + """Test ssoSAMLInfo method of Run class.""" + mock_response = mockSSOSAMLInfo() + requests_mock.get("https://api.tago.io/run/sso/saml", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.ssoSAMLInfo() + + assert "sp" in result + assert "idp" in result + + +def testRunMethodSSOSAMLEdit(requests_mock: Mocker) -> None: + """Test ssoSAMLEdit method of Run class.""" + mock_response = mockSSOSAMLEdit() + requests_mock.put("https://api.tago.io/run/sso/saml", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.ssoSAMLEdit({"active": True, "idp_metadata": "..."}) + + assert result == "TagoIO Run SAML SSO Successfully Updated" + + +def testRunMethodCreateCustomDomain(requests_mock: Mocker) -> None: + """Test createCustomDomain method of Run class.""" + mock_response = mockCustomDomainCreate() + requests_mock.post("https://api.tago.io/run/customdomain/profile_id_123", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.createCustomDomain("profile_id_123", {"domain": "mycompany.com", "subdomain": "app"}) + + assert result == "Custom domain created successfully" + + +def testRunMethodGetCustomDomain(requests_mock: Mocker) -> None: + """Test getCustomDomain method of Run class.""" + mock_response = mockCustomDomainInfo() + requests_mock.get("https://api.tago.io/run/customdomain/profile_id_123", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.getCustomDomain("profile_id_123") + + assert result["domain"] == "mycompany.com" + assert result["active"] is True + + +def testRunMethodDeleteCustomDomain(requests_mock: Mocker) -> None: + """Test deleteCustomDomain method of Run class.""" + mock_response = mockCustomDomainDelete() + requests_mock.delete("https://api.tago.io/run/customdomain/profile_id_123", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.deleteCustomDomain("profile_id_123") + + assert result == "Custom domain deleted successfully" + + +def testRunMethodRegenerateCustomDomain(requests_mock: Mocker) -> None: + """Test regenerateCustomDomain method of Run class.""" + mock_response = mockCustomDomainRegenerate() + requests_mock.put("https://api.tago.io/run/customdomain/regenerate/profile_id_123", json=mock_response) + + resources = Resources({"token": "your_token_value"}) + result = resources.run.regenerateCustomDomain("profile_id_123") + + assert result == "Custom domain regenerated successfully"