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/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_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"