truto-python-sdk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truto_python_sdk/__init__.py +16 -0
- truto_python_sdk/base_resource.py +25 -0
- truto_python_sdk/dict_to_query_string.py +44 -0
- truto_python_sdk/environment.py +13 -0
- truto_python_sdk/environment_integration.py +17 -0
- truto_python_sdk/environment_unified_model.py +17 -0
- truto_python_sdk/integrated_account.py +37 -0
- truto_python_sdk/integration.py +33 -0
- truto_python_sdk/link_token.py +17 -0
- truto_python_sdk/pagination_result.py +60 -0
- truto_python_sdk/proxy_api.py +29 -0
- truto_python_sdk/sync_job.py +25 -0
- truto_python_sdk/sync_job_cron_trigger.py +29 -0
- truto_python_sdk/sync_job_run.py +25 -0
- truto_python_sdk/team.py +13 -0
- truto_python_sdk/truto_api.py +101 -0
- truto_python_sdk/unified_api.py +29 -0
- truto_python_sdk/unified_model.py +33 -0
- truto_python_sdk/user.py +13 -0
- truto_python_sdk/webhook.py +29 -0
- truto_python_sdk-0.1.0.dist-info/LICENSE +21 -0
- truto_python_sdk-0.1.0.dist-info/METADATA +16 -0
- truto_python_sdk-0.1.0.dist-info/RECORD +24 -0
- truto_python_sdk-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# truto_python_sdk/__init__.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
truto_python_sdk
|
|
5
|
+
~~~~~~~~~~~~~~~~
|
|
6
|
+
|
|
7
|
+
A Python3 SDK for the Truto API.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__title__ = 'truto_python_sdk'
|
|
11
|
+
__version__ = '0.1.0'
|
|
12
|
+
__author__ = 'Roopendra Talekar <roopendra@truto.dev>'
|
|
13
|
+
__license__ = 'MIT'
|
|
14
|
+
__copyright__ = 'Copyright 2023 Yin Yang Inc.'
|
|
15
|
+
|
|
16
|
+
from .truto_api import TrutoApi
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class BaseResource:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all base_resources with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "base_resource", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, base_resource_id: str):
|
|
12
|
+
"""Get a specific base_resource by ID."""
|
|
13
|
+
return self.api_client._get(f"base_resource/{base_resource_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, base_resource_data: dict):
|
|
16
|
+
"""Create a new base_resource."""
|
|
17
|
+
return self.api_client._post("base_resource", data=base_resource_data)
|
|
18
|
+
|
|
19
|
+
def update(self, base_resource_id: str, base_resource_data: dict):
|
|
20
|
+
"""Update an existing base_resource."""
|
|
21
|
+
return self.api_client._patch(f"base_resource/{base_resource_id}", data=base_resource_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, base_resource_id: str):
|
|
24
|
+
"""Delete a base_resource by ID."""
|
|
25
|
+
return self.api_client._delete(f"base_resource/{base_resource_id}")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from urllib.parse import urlencode, quote_plus
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _convert_to_query_params(prefix, data):
|
|
6
|
+
"""
|
|
7
|
+
Recursively convert a Python dictionary, list, or primitive into a valid
|
|
8
|
+
HTTP query parameter string.
|
|
9
|
+
"""
|
|
10
|
+
params = []
|
|
11
|
+
|
|
12
|
+
if isinstance(data, dict):
|
|
13
|
+
# Recursively handle dictionaries (objects)
|
|
14
|
+
for key, value in data.items():
|
|
15
|
+
new_prefix = f"{prefix}[{key}]" if prefix else key
|
|
16
|
+
params.extend(_convert_to_query_params(new_prefix, value))
|
|
17
|
+
elif isinstance(data, list):
|
|
18
|
+
# Handle lists (arrays)
|
|
19
|
+
for item in data:
|
|
20
|
+
new_prefix = f"{prefix}[]"
|
|
21
|
+
params.extend(_convert_to_query_params(new_prefix, item))
|
|
22
|
+
else:
|
|
23
|
+
# Base case for strings, numbers, booleans, and date/time
|
|
24
|
+
if isinstance(data, (datetime.date, datetime.datetime)):
|
|
25
|
+
# Convert date/time to ISO 8601 format
|
|
26
|
+
data = data.isoformat()
|
|
27
|
+
elif isinstance(data, bool):
|
|
28
|
+
# Convert booleans to lowercase string "true" or "false"
|
|
29
|
+
data = str(data).lower()
|
|
30
|
+
|
|
31
|
+
params.append((prefix, data))
|
|
32
|
+
|
|
33
|
+
return params
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def dict_to_query_string(data):
|
|
37
|
+
"""
|
|
38
|
+
Convert a Python dictionary into a valid HTTP query string with proper encoding.
|
|
39
|
+
"""
|
|
40
|
+
query_params = []
|
|
41
|
+
for key, value in data.items():
|
|
42
|
+
query_params.extend(_convert_to_query_params(key, value))
|
|
43
|
+
|
|
44
|
+
return urlencode(query_params, doseq=True, quote_via=quote_plus)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class Environment:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all environments with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "environment", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, environment_id: str):
|
|
12
|
+
"""Get a specific environment by ID."""
|
|
13
|
+
return self.api_client._get(f"environment/{environment_id}")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class EnvironmentIntegration:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all environment integrations with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "environment-integration", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, environment_integration_id: str):
|
|
12
|
+
"""Get a specific environment_integration by ID."""
|
|
13
|
+
return self.api_client._get(f"environment-integration/{environment_integration_id}")
|
|
14
|
+
|
|
15
|
+
def update(self, environment_integration_id: str, environment_integration_data: dict):
|
|
16
|
+
"""Update an existing environment_integration."""
|
|
17
|
+
return self.api_client._patch(f"environment-integration/{environment_integration_id}", data=environment_integration_data)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class EnvironmentUnifiedModel:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all environment unified models with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "environment-unified-model", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, environment_unified_model_id: str):
|
|
12
|
+
"""Get a specific environment_unified_model by ID."""
|
|
13
|
+
return self.api_client._get(f"environment-unified-model/{environment_unified_model_id}")
|
|
14
|
+
|
|
15
|
+
def update(self, environment_unified_model_id: str, environment_unified_model_data: dict):
|
|
16
|
+
"""Update an existing environment_unified_model."""
|
|
17
|
+
return self.api_client._patch(f"environment-unified-model/{environment_unified_model_id}", data=environment_unified_model_data)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class IntegratedAccount:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all integrated_accounts with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "integrated-account", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, integrated_account_id: str):
|
|
12
|
+
"""Get a specific integrated_account by ID."""
|
|
13
|
+
return self.api_client._get(f"integrated-account/{integrated_account_id}")
|
|
14
|
+
|
|
15
|
+
def _import(self, integrated_account_data: dict):
|
|
16
|
+
"""Create a new integrated_account."""
|
|
17
|
+
return self.api_client._post("integrated-account", data=integrated_account_data)
|
|
18
|
+
|
|
19
|
+
def update(self, integrated_account_id: str, integrated_account_data: dict):
|
|
20
|
+
"""Update an existing integrated_account."""
|
|
21
|
+
return self.api_client._patch(f"integrated-account/{integrated_account_id}", data=integrated_account_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, integrated_account_id: str):
|
|
24
|
+
"""Delete an integrated_account by ID."""
|
|
25
|
+
return self.api_client._delete(f"integrated-account/{integrated_account_id}")
|
|
26
|
+
|
|
27
|
+
def create_integrated_account_token(self, integrated_account_id: str):
|
|
28
|
+
"""Create a new token for the integrated account."""
|
|
29
|
+
return self.api_client._post(f"integrated-account/token", data={"integrated_account_id": integrated_account_id})
|
|
30
|
+
|
|
31
|
+
def refresh_credentials(self, integrated_account_id: str):
|
|
32
|
+
"""Refresh the credentials for the integrated account."""
|
|
33
|
+
return self.api_client._post(f"integrated-account/refresh-credentials", data={"id": integrated_account_id})
|
|
34
|
+
|
|
35
|
+
def run_post_install_actions(self, integrated_account_id: str):
|
|
36
|
+
"""Run post install actions for the integrated account."""
|
|
37
|
+
return self.api_client._post(f"integrated-account/run-post-install-actions", data={"id": integrated_account_id})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class Integration:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all integrations with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "integration", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, integration_id: str):
|
|
12
|
+
"""Get a specific integration by ID."""
|
|
13
|
+
return self.api_client._get(f"integration/{integration_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, integration_data: dict):
|
|
16
|
+
"""Create a new integration."""
|
|
17
|
+
return self.api_client._post("integration", data=integration_data)
|
|
18
|
+
|
|
19
|
+
def update(self, integration_id: str, integration_data: dict):
|
|
20
|
+
"""Update an existing integration."""
|
|
21
|
+
return self.api_client._patch(f"integration/{integration_id}", data=integration_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, integration_id: str):
|
|
24
|
+
"""Delete an integration by ID."""
|
|
25
|
+
return self.api_client._delete(f"integration/{integration_id}")
|
|
26
|
+
|
|
27
|
+
def install(self, integration_id: str, is_enabled: bool = True):
|
|
28
|
+
"""Install an integration by ID."""
|
|
29
|
+
return self.api_client._post(f"environment-integration", data={"integration_id": integration_id, "is_enabled": is_enabled})
|
|
30
|
+
|
|
31
|
+
def uninstall(self, integration_id: str):
|
|
32
|
+
"""Uninstall an integration by ID."""
|
|
33
|
+
return self.api_client._delete(f"environment-integration/{integration_id}")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import TypedDict
|
|
2
|
+
|
|
3
|
+
class LinkTokenCreateBody(TypedDict, total=False):
|
|
4
|
+
tenant_id: str # When connecting a new integrated account, this is required.
|
|
5
|
+
integrated_account_id: str # When reconnecting an existing integrated account, this is required.
|
|
6
|
+
redirect_uri: str # The URL to redirect to after the user has completed the connect flow. Useful if you are authenticating via a Desktop app
|
|
7
|
+
context: dict # A dictionary of key-value pairs that will be stored in the integrated account's context attribute.
|
|
8
|
+
persist_previous_context: bool # If true, the context attribute of the integrated account will be retained when reconnecting the account. If false, the context attribute will be reset.
|
|
9
|
+
environment_unified_model_id: str # Only works if the integration you want to connect follows OAuth 2.0. This option sets the scopes to ask for depending on the Unified API you want to use.
|
|
10
|
+
|
|
11
|
+
class LinkToken:
|
|
12
|
+
def __init__(self, api_client):
|
|
13
|
+
self.api_client = api_client
|
|
14
|
+
|
|
15
|
+
def create(self, link_token_data: LinkTokenCreateBody):
|
|
16
|
+
"""Create a new link_token."""
|
|
17
|
+
return self.api_client._post("link-token", data=link_token_data)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# truto_sdk/pagination_result.py
|
|
2
|
+
|
|
3
|
+
class PaginationResult:
|
|
4
|
+
def __init__(self, api_client, endpoint, query_params):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
self.endpoint = endpoint
|
|
7
|
+
self.query_params = query_params
|
|
8
|
+
self.next_cursor = None
|
|
9
|
+
self.items = []
|
|
10
|
+
self.initial_fetch_done = False # To check if we fetched the first page
|
|
11
|
+
|
|
12
|
+
def __aiter__(self):
|
|
13
|
+
return self
|
|
14
|
+
|
|
15
|
+
async def __anext__(self):
|
|
16
|
+
# If the first fetch is not done, do it now
|
|
17
|
+
if not self.initial_fetch_done:
|
|
18
|
+
await self._fetch_next_page()
|
|
19
|
+
self.initial_fetch_done = True
|
|
20
|
+
|
|
21
|
+
# If no items are left in the current page and there's a next_cursor, fetch the next page
|
|
22
|
+
if not self.items and self.next_cursor:
|
|
23
|
+
await self._fetch_next_page()
|
|
24
|
+
|
|
25
|
+
# If there are no items and no next_cursor, stop iteration
|
|
26
|
+
if not self.items:
|
|
27
|
+
raise StopAsyncIteration
|
|
28
|
+
|
|
29
|
+
# Pop and return the first item from the items list
|
|
30
|
+
return self.items.pop(0)
|
|
31
|
+
|
|
32
|
+
async def _fetch_next_page(self):
|
|
33
|
+
# If there's a next cursor, include it in the query parameters
|
|
34
|
+
if self.next_cursor:
|
|
35
|
+
self.query_params['next_cursor'] = self.next_cursor
|
|
36
|
+
|
|
37
|
+
# Fetch the next page asynchronously
|
|
38
|
+
response = await self.api_client._get(self.endpoint, params=self.query_params)
|
|
39
|
+
|
|
40
|
+
# Get the result (list of items) from the response
|
|
41
|
+
self.items = response.get('result', [])
|
|
42
|
+
|
|
43
|
+
# Update the next cursor for pagination
|
|
44
|
+
self.next_cursor = response.get('next_cursor')
|
|
45
|
+
|
|
46
|
+
# If the first API call returns an empty result and no next_cursor, break early
|
|
47
|
+
if not self.items and not self.next_cursor:
|
|
48
|
+
self.items = [] # Ensure no more items will be processed
|
|
49
|
+
|
|
50
|
+
async def to_array(self):
|
|
51
|
+
"""Fetch all results and return them as a list."""
|
|
52
|
+
all_items = []
|
|
53
|
+
async for item in self:
|
|
54
|
+
all_items.append(item)
|
|
55
|
+
return all_items
|
|
56
|
+
|
|
57
|
+
async def next_page(self):
|
|
58
|
+
"""Fetch the next page and return the items."""
|
|
59
|
+
await self._fetch_next_page()
|
|
60
|
+
return self.items
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class ProxyApi:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, resource: str, query_params: dict):
|
|
8
|
+
"""List all proxy api resources with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, f"proxy/{resource}", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, resource: str, id: str, query_params: dict):
|
|
12
|
+
"""Get a specific proxy api resource by ID."""
|
|
13
|
+
return self.api_client._get(f"proxy/{resource}/{id}", params=query_params)
|
|
14
|
+
|
|
15
|
+
def create(self, resource: str, data: dict, query_params: dict):
|
|
16
|
+
"""Create a new proxy api resource."""
|
|
17
|
+
return self.api_client._post(f"proxy/{resource}", data=data, params=query_params)
|
|
18
|
+
|
|
19
|
+
def update(self, resource: str, id: str, data: dict, query_params: dict):
|
|
20
|
+
"""Update an existing proxy api resource."""
|
|
21
|
+
return self.api_client._patch(f"proxy/{resource}/{id}", data=data, params=query_params)
|
|
22
|
+
|
|
23
|
+
def delete(self, resource: str, id: str, query_params: dict):
|
|
24
|
+
"""Delete a proxy api resource by ID."""
|
|
25
|
+
return self.api_client._delete(f"proxy/{resource}/{id}", query_params)
|
|
26
|
+
|
|
27
|
+
def custom_method(self, resource: str, method: str, data: dict, query_params: dict):
|
|
28
|
+
"""Call a custom method on a proxy api resource."""
|
|
29
|
+
return self.api_client._post(method, f"proxy/{resource}/{method}", data=data, params=query_params)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class SyncJob:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all sync jobs with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "sync-job", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, sync_job_id: str):
|
|
12
|
+
"""Get a specific sync_job by ID."""
|
|
13
|
+
return self.api_client._get(f"sync-job/{sync_job_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, sync_job_data: dict):
|
|
16
|
+
"""Create a new sync_job."""
|
|
17
|
+
return self.api_client._post("sync-job", data=sync_job_data)
|
|
18
|
+
|
|
19
|
+
def update(self, sync_job_id: str, sync_job_data: dict):
|
|
20
|
+
"""Update an existing sync_job."""
|
|
21
|
+
return self.api_client._patch(f"sync-job/{sync_job_id}", data=sync_job_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, sync_job_id: str):
|
|
24
|
+
"""Delete an sync_job by ID."""
|
|
25
|
+
return self.api_client._delete(f"sync-job/{sync_job_id}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class SyncJobCronTrigger:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all sync job cron triggers with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "sync-job-cron-trigger", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, sync_job_cron_trigger_id: str):
|
|
12
|
+
"""Get a specific sync_job_cron_trigger by ID."""
|
|
13
|
+
return self.api_client._get(f"sync-job-cron-trigger/{sync_job_cron_trigger_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, sync_job_cron_trigger_data: dict):
|
|
16
|
+
"""Create a new sync_job_cron_trigger."""
|
|
17
|
+
return self.api_client._post("sync-job-cron-trigger", data=sync_job_cron_trigger_data)
|
|
18
|
+
|
|
19
|
+
def update(self, sync_job_cron_trigger_id: str, sync_job_cron_trigger_data: dict):
|
|
20
|
+
"""Update an existing sync_job_cron_trigger."""
|
|
21
|
+
return self.api_client._patch(f"sync-job-cron-trigger/{sync_job_cron_trigger_id}", data=sync_job_cron_trigger_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, sync_job_cron_trigger_id: str):
|
|
24
|
+
"""Delete an sync_job_cron_trigger by ID."""
|
|
25
|
+
return self.api_client._delete(f"sync-job-cron-trigger/{sync_job_cron_trigger_id}")
|
|
26
|
+
|
|
27
|
+
def schedule(self, sync_job_cron_trigger_id: str):
|
|
28
|
+
"""Schedule a sync job cron trigger by ID."""
|
|
29
|
+
return self.api_client._post(f"sync-job-cron-trigger/{sync_job_cron_trigger_id}/schedule")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class SyncJobRun:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all sync job runs with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "sync-job-run", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, sync_job_run_id: str):
|
|
12
|
+
"""Get a specific sync_job_run by ID."""
|
|
13
|
+
return self.api_client._get(f"sync-job-run/{sync_job_run_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, sync_job_run_data: dict):
|
|
16
|
+
"""Create a new sync_job_run."""
|
|
17
|
+
return self.api_client._post("sync-job-run", data=sync_job_run_data)
|
|
18
|
+
|
|
19
|
+
def update(self, sync_job_run_id: str, sync_job_run_data: dict):
|
|
20
|
+
"""Update an existing sync_job_run."""
|
|
21
|
+
return self.api_client._patch(f"sync-job-run/{sync_job_run_id}", data=sync_job_run_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, sync_job_run_id: str):
|
|
24
|
+
"""Delete an sync_job_run by ID."""
|
|
25
|
+
return self.api_client._delete(f"sync-job-run/{sync_job_run_id}")
|
truto_python_sdk/team.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class Team:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all teams with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "team", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, team_id: str):
|
|
12
|
+
"""Get a specific team by ID."""
|
|
13
|
+
return self.api_client._get(f"team/{team_id}")
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import aiohttp
|
|
2
|
+
|
|
3
|
+
from .dict_to_query_string import dict_to_query_string
|
|
4
|
+
from .environment import Environment
|
|
5
|
+
from .environment_integration import EnvironmentIntegration
|
|
6
|
+
from .integrated_account import IntegratedAccount
|
|
7
|
+
from .integration import Integration
|
|
8
|
+
from .link_token import LinkToken
|
|
9
|
+
from .proxy_api import ProxyApi
|
|
10
|
+
from .sync_job import SyncJob
|
|
11
|
+
from .sync_job_cron_trigger import SyncJobCronTrigger
|
|
12
|
+
from .sync_job_run import SyncJobRun
|
|
13
|
+
from .team import Team
|
|
14
|
+
from .unified_api import UnifiedApi
|
|
15
|
+
from .user import User
|
|
16
|
+
from .unified_model import UnifiedModel
|
|
17
|
+
from .environment_unified_model import EnvironmentUnifiedModel
|
|
18
|
+
from .webhook import Webhook
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TrutoApi:
|
|
22
|
+
def __init__(self, token: str, base_url: str = 'https://api.truto.one'):
|
|
23
|
+
self.token = token
|
|
24
|
+
self.base_url = base_url
|
|
25
|
+
self.headers = {
|
|
26
|
+
"Authorization": f"Bearer {self.token}",
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
"Accept": "application/json"
|
|
29
|
+
}
|
|
30
|
+
self.users = User(self)
|
|
31
|
+
self.teams = Team(self)
|
|
32
|
+
self.environments = Environment(self)
|
|
33
|
+
self.link_tokens = LinkToken(self)
|
|
34
|
+
self.integrated_accounts = IntegratedAccount(self)
|
|
35
|
+
self.integrations = Integration(self)
|
|
36
|
+
self.environment_integrations = EnvironmentIntegration(self)
|
|
37
|
+
self.unified_models = UnifiedModel(self)
|
|
38
|
+
self.environment_unified_models = EnvironmentUnifiedModel(self)
|
|
39
|
+
self.sync_jobs = SyncJob(self)
|
|
40
|
+
self.sync_job_runs = SyncJobRun(self)
|
|
41
|
+
self.sync_job_cron_triggers = SyncJobCronTrigger(self)
|
|
42
|
+
self.webhooks = Webhook(self)
|
|
43
|
+
self.unified_api = UnifiedApi(self)
|
|
44
|
+
self.proxy_api = ProxyApi(self)
|
|
45
|
+
|
|
46
|
+
async def _process_response(self, response):
|
|
47
|
+
# Get the Content-Type header
|
|
48
|
+
content_type = response.headers.get('Content-Type', '').lower()
|
|
49
|
+
|
|
50
|
+
if 'json' in content_type:
|
|
51
|
+
# If it's a JSON-like content type (application/json, text/json, etc.)
|
|
52
|
+
return await response.json()
|
|
53
|
+
elif content_type.startswith('text/'):
|
|
54
|
+
# If it's any kind of text (e.g., text/plain, text/html)
|
|
55
|
+
return await response.text()
|
|
56
|
+
else:
|
|
57
|
+
# For everything else (binary data, unknown types)
|
|
58
|
+
return await response.read()
|
|
59
|
+
|
|
60
|
+
async def _get(self, endpoint, params=None):
|
|
61
|
+
url = f"{self.base_url}/{endpoint}"
|
|
62
|
+
query_string = dict_to_query_string(params) if params else ""
|
|
63
|
+
async with aiohttp.ClientSession() as session:
|
|
64
|
+
async with session.get(f"{url}?{query_string}", headers=self.headers) as response:
|
|
65
|
+
response.raise_for_status()
|
|
66
|
+
return await self._process_response(response)
|
|
67
|
+
|
|
68
|
+
async def _post(self, endpoint, data=None, params=None):
|
|
69
|
+
url = f"{self.base_url}/{endpoint}"
|
|
70
|
+
query_string = dict_to_query_string(params) if params else ""
|
|
71
|
+
async with aiohttp.ClientSession() as session:
|
|
72
|
+
async with session.post(f"{url}?{query_string}", headers=self.headers, json=data) as response:
|
|
73
|
+
response.raise_for_status()
|
|
74
|
+
return await self._process_response(response)
|
|
75
|
+
|
|
76
|
+
async def _patch(self, endpoint, data=None, params=None):
|
|
77
|
+
url = f"{self.base_url}/{endpoint}"
|
|
78
|
+
query_string = dict_to_query_string(params) if params else ""
|
|
79
|
+
async with aiohttp.ClientSession() as session:
|
|
80
|
+
async with session.patch(f"{url}?{query_string}", headers=self.headers, json=data) as response:
|
|
81
|
+
response.raise_for_status()
|
|
82
|
+
return await self._process_response(response)
|
|
83
|
+
|
|
84
|
+
async def _put(self, endpoint, data=None, params=None):
|
|
85
|
+
url = f"{self.base_url}/{endpoint}"
|
|
86
|
+
query_string = dict_to_query_string(params) if params else ""
|
|
87
|
+
async with aiohttp.ClientSession() as session:
|
|
88
|
+
async with session.put(f"{url}?{query_string}", headers=self.headers, json=data) as response:
|
|
89
|
+
response.raise_for_status()
|
|
90
|
+
return await self._process_response(response)
|
|
91
|
+
|
|
92
|
+
async def _delete(self, endpoint, params=None):
|
|
93
|
+
url = f"{self.base_url}/{endpoint}"
|
|
94
|
+
query_string = dict_to_query_string(params) if params else ""
|
|
95
|
+
async with aiohttp.ClientSession() as session:
|
|
96
|
+
async with session.delete(f"{url}?{query_string}", headers=self.headers) as response:
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
return await self._process_response(response)
|
|
99
|
+
|
|
100
|
+
async def ping(self):
|
|
101
|
+
return await self._get("ping")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class UnifiedApi:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, unified_model_name: str, unified_model_resource: str, query_params: dict):
|
|
8
|
+
"""List all unified api resources with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, f"unified/{unified_model_name}/{unified_model_resource}", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, unified_model_name: str, unified_model_resource: str, id: str, query_params: dict):
|
|
12
|
+
"""Get a specific unified api resource by ID."""
|
|
13
|
+
return self.api_client._get(f"unified/{unified_model_name}/{unified_model_resource}/{id}", query_params)
|
|
14
|
+
|
|
15
|
+
def create(self, unified_model_name: str, unified_model_resource: str, data: dict, query_params: dict):
|
|
16
|
+
"""Create a new unified api resource."""
|
|
17
|
+
return self.api_client._post(f"unified/{unified_model_name}/{unified_model_resource}", data=data, params=query_params)
|
|
18
|
+
|
|
19
|
+
def update(self, unified_model_name: str, unified_model_resource: str, id: str, data: dict, query_params: dict):
|
|
20
|
+
"""Update an existing unified api resource."""
|
|
21
|
+
return self.api_client._patch(f"unified/{unified_model_name}/{unified_model_resource}/{id}", data=data, params=query_params)
|
|
22
|
+
|
|
23
|
+
def delete(self, unified_model_name: str, unified_model_resource: str, id: str, query_params: dict):
|
|
24
|
+
"""Delete an unified api resource by ID."""
|
|
25
|
+
return self.api_client._delete(f"unified/{unified_model_name}/{unified_model_resource}/{id}", query_params)
|
|
26
|
+
|
|
27
|
+
def custom_method(self, unified_model_name: str, unified_model_resource: str, method: str, data: dict, query_params: dict):
|
|
28
|
+
"""Call a custom method on a unified api resource."""
|
|
29
|
+
return self.api_client._post(method, f"unified/{unified_model_name}/{unified_model_resource}/{method}", data=data, params=query_params)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class UnifiedModel:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all unified models with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "unified-model", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, unified_model_id: str):
|
|
12
|
+
"""Get a specific unified_model by ID."""
|
|
13
|
+
return self.api_client._get(f"unified-model/{unified_model_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, unified_model_data: dict):
|
|
16
|
+
"""Create a new unified_model."""
|
|
17
|
+
return self.api_client._post("unified-model", data=unified_model_data)
|
|
18
|
+
|
|
19
|
+
def update(self, unified_model_id: str, unified_model_data: dict):
|
|
20
|
+
"""Update an existing unified_model."""
|
|
21
|
+
return self.api_client._patch(f"unified-model/{unified_model_id}", data=unified_model_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, unified_model_id: str):
|
|
24
|
+
"""Delete an unified_model by ID."""
|
|
25
|
+
return self.api_client._delete(f"unified-model/{unified_model_id}")
|
|
26
|
+
|
|
27
|
+
def install(self, unified_model_id: str):
|
|
28
|
+
"""Install an unified_model by ID."""
|
|
29
|
+
return self.api_client._post(f"environment-unified-model", data={"unified_model_id": unified_model_id})
|
|
30
|
+
|
|
31
|
+
def uninstall(self, unified_model_id: str):
|
|
32
|
+
"""Uninstall an unified_model by ID."""
|
|
33
|
+
return self.api_client._delete(f"environment-unified-model/{unified_model_id}")
|
truto_python_sdk/user.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class User:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all users with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "user", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, user_id: str):
|
|
12
|
+
"""Get a specific user by ID."""
|
|
13
|
+
return self.api_client._get(f"user/{user_id}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .pagination_result import PaginationResult
|
|
2
|
+
|
|
3
|
+
class Webhook:
|
|
4
|
+
def __init__(self, api_client):
|
|
5
|
+
self.api_client = api_client
|
|
6
|
+
|
|
7
|
+
def list(self, **query_params):
|
|
8
|
+
"""List all webhooks with pagination support."""
|
|
9
|
+
return PaginationResult(self.api_client, "webhook", query_params)
|
|
10
|
+
|
|
11
|
+
def get(self, webhook_id: str):
|
|
12
|
+
"""Get a specific webhook by ID."""
|
|
13
|
+
return self.api_client._get(f"webhook/{webhook_id}")
|
|
14
|
+
|
|
15
|
+
def create(self, webhook_data: dict):
|
|
16
|
+
"""Create a new webhook."""
|
|
17
|
+
return self.api_client._post("webhook", data=webhook_data)
|
|
18
|
+
|
|
19
|
+
def update(self, webhook_id: str, webhook_data: dict):
|
|
20
|
+
"""Update an existing webhook."""
|
|
21
|
+
return self.api_client._patch(f"webhook/{webhook_id}", data=webhook_data)
|
|
22
|
+
|
|
23
|
+
def delete(self, webhook_id: str):
|
|
24
|
+
"""Delete a webhook by ID."""
|
|
25
|
+
return self.api_client._delete(f"webhook/{webhook_id}")
|
|
26
|
+
|
|
27
|
+
def test(self, webhook_id: str):
|
|
28
|
+
"""Test a webhook by ID."""
|
|
29
|
+
return self.api_client._post(f"webhook/test", data={"id": webhook_id})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Roopendra Talekar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: truto-python-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python3 SDK for the Truto API
|
|
5
|
+
Author: Roopendra Talekar
|
|
6
|
+
Author-email: roopendratalekar@gmail.com
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: aiohttp (==3.10.5)
|
|
11
|
+
Requires-Dist: asyncio (==3.4.3)
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# truto-python-sdk
|
|
15
|
+
Python3 SDK for the Truto API
|
|
16
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
truto_python_sdk/__init__.py,sha256=CPaEKamw1S6U0qJH96oNrrSmExVgQZ40pLnjc41E3Gg,316
|
|
2
|
+
truto_python_sdk/base_resource.py,sha256=9pkj3gWmCU4tCWufTVJF27a4KTJ4QddEXKvibufhstc,1043
|
|
3
|
+
truto_python_sdk/dict_to_query_string.py,sha256=jSbAZcmgKQ2wFmtlisQ9UqbGTPn_EUYpNyO0vHvKODM,1466
|
|
4
|
+
truto_python_sdk/environment.py,sha256=TVVfVwslcOWVImwjsyCBp0JemGDXxvHCnUFNmysTWiw,474
|
|
5
|
+
truto_python_sdk/environment_integration.py,sha256=CfpUMA-zB08uczypIsGTf99Ivh-Sm6dCXD1opiDTImU,837
|
|
6
|
+
truto_python_sdk/environment_unified_model.py,sha256=EtGRV20GTWVe9XJ3neSxskhy-2SXOA27H3o-izi1CWY,862
|
|
7
|
+
truto_python_sdk/integrated_account.py,sha256=fC11ehQLPP7n8jXnOfjWSqC-zElBVEehx6eKsrcePJI,1911
|
|
8
|
+
truto_python_sdk/integration.py,sha256=YDC0dBwbEa57oJnPmCysZDPxwJa3SgJy1KpuLP6thcQ,1426
|
|
9
|
+
truto_python_sdk/link_token.py,sha256=L58Fgjy26JukCNzLh7IpyW6qkbj7Q3bnFLy-CzdsWNE,1173
|
|
10
|
+
truto_python_sdk/pagination_result.py,sha256=KwRT1s0F6RpNkW1D5WjDlGJxOewrreTmRVfuL73QCUI,2152
|
|
11
|
+
truto_python_sdk/proxy_api.py,sha256=kLIM5qB0jncyS48XGxeKEsn1sYhweJaGmz3UEWhew74,1434
|
|
12
|
+
truto_python_sdk/sync_job.py,sha256=7H1YxoZ3oW-hJnnmKVe1BCpolbXWxrgcpHUreTdFqa0,939
|
|
13
|
+
truto_python_sdk/sync_job_cron_trigger.py,sha256=6ur3WKy17zP7_6Ox1_PwTdFFD6y8jaitpB4MCMPSRKg,1419
|
|
14
|
+
truto_python_sdk/sync_job_run.py,sha256=Q7XTyyz6XOQRFYnSC0hcI1WjtrED7iIacEKtOfQ1zOU,1022
|
|
15
|
+
truto_python_sdk/team.py,sha256=JnV6HO4nnYKY2iOipatBfzVlDKgo8VNj13-ESU9FKuE,425
|
|
16
|
+
truto_python_sdk/truto_api.py,sha256=qKGVnm5vKa1Y_KyNeMIQS0cV8PN62aqL16gZu33kAWU,4544
|
|
17
|
+
truto_python_sdk/unified_api.py,sha256=NquYiVCEIUIjB_a-8Ntv1lIFI-cCt4g2sAsVVAYa9EA,1898
|
|
18
|
+
truto_python_sdk/unified_model.py,sha256=5bvnrn7JppXwwnsGd7F1klbNWhbF261_yRCGD0Gph8k,1434
|
|
19
|
+
truto_python_sdk/user.py,sha256=756FsS96nql5D0ig7a_nUPnJgStP2Za_OHUuqz-T1OQ,425
|
|
20
|
+
truto_python_sdk/webhook.py,sha256=eyTw98Whdj9kOaYV0LNgNpfol9_4uYa_QrbJP-iRSdo,1071
|
|
21
|
+
truto_python_sdk-0.1.0.dist-info/LICENSE,sha256=l6HsZ8Od004T_53MFVmUGPU9WtTg3Lclh-deEnGp5pY,1074
|
|
22
|
+
truto_python_sdk-0.1.0.dist-info/METADATA,sha256=uNuUTm_YJTMuzL4lWv0Sq2Mg7IyeZHzuJHFmldmDZ3I,452
|
|
23
|
+
truto_python_sdk-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
24
|
+
truto_python_sdk-0.1.0.dist-info/RECORD,,
|