definite-sdk 0.1.14__tar.gz → 0.1.16__tar.gz
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.
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/PKG-INFO +4 -2
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/client.py +22 -26
- definite_sdk-0.1.16/definite_sdk/integration.py +130 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/pyproject.toml +3 -4
- definite_sdk-0.1.14/definite_sdk/integration.py +0 -99
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/LICENSE +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/README.md +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/__init__.py +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/dlt.py +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/message.py +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/py.typed +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/secret.py +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/sql.py +0 -0
- {definite_sdk-0.1.14 → definite_sdk-0.1.16}/definite_sdk/store.py +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: definite-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Definite SDK for Python
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Author: Definite
|
|
7
8
|
Author-email: hello@definite.app
|
|
8
9
|
Requires-Python: >=3.9,<4.0
|
|
@@ -13,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
18
|
Provides-Extra: dlt
|
|
17
19
|
Requires-Dist: dlt (>=1.0,<2.0) ; extra == "dlt"
|
|
18
20
|
Requires-Dist: duckdb (>=1.0,<2.0) ; extra == "dlt"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
import requests
|
|
5
4
|
from definite_sdk.integration import DefiniteIntegrationStore
|
|
6
5
|
from definite_sdk.message import DefiniteMessageClient
|
|
7
6
|
from definite_sdk.secret import DefiniteSecretStore
|
|
@@ -73,6 +72,7 @@ class DefiniteClient:
|
|
|
73
72
|
This method fetches the team's DuckLake integration credentials and generates
|
|
74
73
|
the necessary SQL statements to create a GCS secret and attach DuckLake.
|
|
75
74
|
|
|
75
|
+
|
|
76
76
|
Args:
|
|
77
77
|
alias: The alias name for the attached DuckLake database (default: "lake")
|
|
78
78
|
|
|
@@ -85,42 +85,38 @@ class DefiniteClient:
|
|
|
85
85
|
>>> conn.execute(sql)
|
|
86
86
|
"""
|
|
87
87
|
# Fetch DuckLake integration details
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
integrations_client = self.get_integration_store()
|
|
89
|
+
integrations = integrations_client.list_integrations(
|
|
90
|
+
integration_type="ducklake"
|
|
91
|
+
)
|
|
92
|
+
if len(integrations) == 0:
|
|
93
|
+
raise Exception(
|
|
94
|
+
"DuckLake integration not found. Please make sure one is"
|
|
95
|
+
"created for your team at https://ui.definite.app/settings/integrations"
|
|
92
96
|
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
except requests.exceptions.HTTPError as e:
|
|
96
|
-
if response.status_code == 404:
|
|
97
|
-
raise ValueError(
|
|
98
|
-
"DuckLake integration not found. Please ensure you have a DuckLake integration "
|
|
99
|
-
"configured in your Definite account at https://ui.definite.app"
|
|
100
|
-
) from e
|
|
101
|
-
else:
|
|
102
|
-
raise
|
|
97
|
+
|
|
98
|
+
integration = integrations.pop()
|
|
103
99
|
|
|
104
100
|
# Generate SQL statements
|
|
105
101
|
create_secret_sql = f"""CREATE SECRET (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
);"""
|
|
102
|
+
TYPE gcs,
|
|
103
|
+
KEY_ID '{integration["gcs_access_key_id"]}',
|
|
104
|
+
SECRET '{integration["gcs_secret_access_key"]}'
|
|
105
|
+
);"""
|
|
110
106
|
|
|
111
107
|
# Build PostgreSQL connection string
|
|
112
108
|
pg_conn_str = (
|
|
113
|
-
f"postgresql://{
|
|
114
|
-
f"{
|
|
115
|
-
f"{
|
|
116
|
-
f"{
|
|
117
|
-
f"{
|
|
109
|
+
f"postgresql://{integration['pg_user']}:"
|
|
110
|
+
f"{integration['pg_password']}@"
|
|
111
|
+
f"{integration['pg_host']}:"
|
|
112
|
+
f"{integration['pg_port']}/"
|
|
113
|
+
f"{integration['pg_database']}"
|
|
118
114
|
)
|
|
119
115
|
|
|
120
116
|
attach_sql = (
|
|
121
117
|
f"ATTACH 'ducklake:postgres:{pg_conn_str}' AS {alias} "
|
|
122
|
-
f"(DATA_PATH 'gs://{
|
|
123
|
-
f"METADATA_SCHEMA '{
|
|
118
|
+
f"(DATA_PATH 'gs://{integration['gcs_bucket_path']}', "
|
|
119
|
+
f"METADATA_SCHEMA '{integration['pg_schema']}');"
|
|
124
120
|
)
|
|
125
121
|
|
|
126
122
|
return f"{create_secret_sql}\n\n{attach_sql}"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from typing import Dict, List, Optional, cast
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
INTEGRATION_ENDPOINT = "/v1/api/integrations"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DefiniteIntegrationStore:
|
|
9
|
+
"""
|
|
10
|
+
Read only access to the integration store on Definite.
|
|
11
|
+
|
|
12
|
+
Initialization:
|
|
13
|
+
>>> client = DefiniteSdkClient("MY_API_KEY")
|
|
14
|
+
>>> integration_store = client.get_integration_store()
|
|
15
|
+
|
|
16
|
+
Accessing values:
|
|
17
|
+
>>> integration_store.list_integrations()
|
|
18
|
+
>>> integration_store.get_integration("name")
|
|
19
|
+
>>> integration_store.get_integration_by_id("integration_id")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, api_key: str, api_url: str):
|
|
23
|
+
"""
|
|
24
|
+
Initializes the DefiniteSecretStore
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
api_key (str): The API key for authorization.
|
|
28
|
+
"""
|
|
29
|
+
self._api_key = api_key
|
|
30
|
+
self._integrations_url = api_url + INTEGRATION_ENDPOINT
|
|
31
|
+
|
|
32
|
+
def list_integrations(
|
|
33
|
+
self,
|
|
34
|
+
*,
|
|
35
|
+
integration_type: Optional[str] = None,
|
|
36
|
+
category: Optional[str] = None,
|
|
37
|
+
) -> List[Dict]:
|
|
38
|
+
"""
|
|
39
|
+
Lists all integrations in the store.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
integration_type (str): Optional type filter
|
|
43
|
+
category (str): Optional category filter
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Iterator[str]: An iterator of integrations.
|
|
47
|
+
"""
|
|
48
|
+
params = {}
|
|
49
|
+
if integration_type:
|
|
50
|
+
params |= {"type": integration_type}
|
|
51
|
+
if category:
|
|
52
|
+
params |= {"category": category}
|
|
53
|
+
|
|
54
|
+
response = requests.get(
|
|
55
|
+
self._integrations_url,
|
|
56
|
+
params=params,
|
|
57
|
+
headers={"Authorization": "Bearer " + self._api_key},
|
|
58
|
+
)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
cursor_page = response.json()
|
|
61
|
+
integrations = cursor_page.get("data", [])
|
|
62
|
+
details = [i.get("details", {}) for i in integrations]
|
|
63
|
+
return cast(List[Dict], details)
|
|
64
|
+
|
|
65
|
+
def get_integration(self, name: str) -> Dict:
|
|
66
|
+
"""
|
|
67
|
+
Retrieves an integration by name.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
name (str): The name of the integration.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: The value of the integration.
|
|
74
|
+
"""
|
|
75
|
+
response = requests.get(
|
|
76
|
+
self._integrations_url,
|
|
77
|
+
params={"name": name, "limit": 1},
|
|
78
|
+
headers={"Authorization": "Bearer " + self._api_key},
|
|
79
|
+
)
|
|
80
|
+
response.raise_for_status()
|
|
81
|
+
cursor_page = response.json()
|
|
82
|
+
integrations = cursor_page.get("data", [])
|
|
83
|
+
if len(integrations) == 0:
|
|
84
|
+
raise Exception(f"Integration with name {name} not found")
|
|
85
|
+
integration = integrations[0]
|
|
86
|
+
return integration.get("details", {})
|
|
87
|
+
|
|
88
|
+
def get_integration_by_id(self, integration_id: str) -> Dict:
|
|
89
|
+
"""
|
|
90
|
+
Retrieves an integration by ID.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
integration_id (str): The ID of the integration.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
dict: The integration details.
|
|
97
|
+
"""
|
|
98
|
+
response = requests.get(
|
|
99
|
+
self._integrations_url,
|
|
100
|
+
params={"id": integration_id, "limit": 1},
|
|
101
|
+
headers={"Authorization": "Bearer " + self._api_key},
|
|
102
|
+
)
|
|
103
|
+
response.raise_for_status()
|
|
104
|
+
cursor_page = response.json()
|
|
105
|
+
integrations = cursor_page.get("data", [])
|
|
106
|
+
if len(integrations) == 0:
|
|
107
|
+
raise Exception(f"Integration with ID {integration_id} not found")
|
|
108
|
+
integration = integrations[0]
|
|
109
|
+
return integration.get("details", {})
|
|
110
|
+
|
|
111
|
+
def lookup_duckdb_integration(self) -> Dict:
|
|
112
|
+
"""
|
|
113
|
+
Look up the team's DuckDB integration.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Optional[Tuple[str, str]]: Tuple of (integration_id, connection_uri)
|
|
117
|
+
if found, None if no DuckDB integration exists.
|
|
118
|
+
"""
|
|
119
|
+
response = requests.get(
|
|
120
|
+
self._integrations_url,
|
|
121
|
+
params={"type": "duckdb", "limit": 1},
|
|
122
|
+
headers={"Authorization": "Bearer " + self._api_key},
|
|
123
|
+
)
|
|
124
|
+
response.raise_for_status()
|
|
125
|
+
cursor_page = response.json()
|
|
126
|
+
integrations = cursor_page.get("data", [])
|
|
127
|
+
if len(integrations) == 0:
|
|
128
|
+
raise Exception("Integration with type `duckdb` not found")
|
|
129
|
+
integration = integrations[0]
|
|
130
|
+
return integration.get("details", {})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "definite-sdk"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.16"
|
|
4
4
|
description = "Definite SDK for Python"
|
|
5
5
|
authors = ["Definite <hello@definite.app>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -9,13 +9,12 @@ readme = "README.md"
|
|
|
9
9
|
[tool.poetry.dependencies]
|
|
10
10
|
python = "^3.9"
|
|
11
11
|
requests = "^2.31.0"
|
|
12
|
-
dlt = {version = "^1.0", optional = true}
|
|
13
|
-
duckdb = {version = "^1.0", optional = true}
|
|
12
|
+
dlt = { version = "^1.0", optional = true }
|
|
13
|
+
duckdb = { version = "^1.0", optional = true }
|
|
14
14
|
|
|
15
15
|
[tool.poetry.extras]
|
|
16
16
|
dlt = ["dlt", "duckdb"]
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
[tool.poetry.group.dev.dependencies]
|
|
20
19
|
black = "^24.4.2"
|
|
21
20
|
flake8 = "^7.0.0"
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
from typing import Iterator, Optional, Tuple
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
|
|
5
|
-
INTEGRATION_ENDPOINT = "/v1/api/integration"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DefiniteIntegrationStore:
|
|
9
|
-
"""
|
|
10
|
-
Read only access to the integration store on Definite.
|
|
11
|
-
|
|
12
|
-
Initialization:
|
|
13
|
-
>>> client = DefiniteSdkClient("MY_API_KEY")
|
|
14
|
-
>>> integration_store = client.get_integration_store()
|
|
15
|
-
|
|
16
|
-
Accessing values:
|
|
17
|
-
>>> integration_store.list_integrations()
|
|
18
|
-
>>> integration_store.get_integration("name")
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
def __init__(self, api_key: str, api_url: str):
|
|
22
|
-
"""
|
|
23
|
-
Initializes the DefiniteSecretStore
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
api_key (str): The API key for authorization.
|
|
27
|
-
"""
|
|
28
|
-
self._api_key = api_key
|
|
29
|
-
self._integration_store_url = api_url + INTEGRATION_ENDPOINT
|
|
30
|
-
|
|
31
|
-
def list_integrations(self) -> Iterator[dict]:
|
|
32
|
-
"""
|
|
33
|
-
Lists all integrations in the store.
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
Iterator[str]: An iterator of integrations.
|
|
37
|
-
"""
|
|
38
|
-
response = requests.get(
|
|
39
|
-
self._integration_store_url,
|
|
40
|
-
headers={"Authorization": "Bearer " + self._api_key},
|
|
41
|
-
)
|
|
42
|
-
response.raise_for_status()
|
|
43
|
-
return iter(response.json())
|
|
44
|
-
|
|
45
|
-
def get_integration(self, name: str) -> dict:
|
|
46
|
-
"""
|
|
47
|
-
Retrieves an integration by name.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
name (str): The name of the integration.
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
str: The value of the integration.
|
|
54
|
-
"""
|
|
55
|
-
response = requests.get(
|
|
56
|
-
self._integration_store_url + f"/{name}",
|
|
57
|
-
headers={"Authorization": "Bearer " + self._api_key},
|
|
58
|
-
)
|
|
59
|
-
response.raise_for_status()
|
|
60
|
-
return dict(response.json())
|
|
61
|
-
|
|
62
|
-
def lookup_duckdb_integration(self) -> Optional[Tuple[str, str]]:
|
|
63
|
-
"""
|
|
64
|
-
Look up the team's DuckDB integration.
|
|
65
|
-
|
|
66
|
-
Note: Currently, the API only returns extractor (source) integrations.
|
|
67
|
-
Destination integrations like DuckDB are not yet exposed through this endpoint.
|
|
68
|
-
This method is provided for future compatibility when the API is updated.
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
Optional[Tuple[str, str]]: Tuple of (integration_id, connection_uri)
|
|
72
|
-
if found, None if no DuckDB integration exists.
|
|
73
|
-
"""
|
|
74
|
-
try:
|
|
75
|
-
response = requests.get(
|
|
76
|
-
self._integration_store_url,
|
|
77
|
-
headers={"Authorization": "Bearer " + self._api_key},
|
|
78
|
-
)
|
|
79
|
-
response.raise_for_status()
|
|
80
|
-
integrations = response.json()
|
|
81
|
-
except:
|
|
82
|
-
return None
|
|
83
|
-
|
|
84
|
-
# Check for DuckDB in the integrations
|
|
85
|
-
# Note: Currently this will not find DuckDB as it's a destination integration
|
|
86
|
-
for integration in integrations:
|
|
87
|
-
integration_type = integration.get("integration_type", "").lower()
|
|
88
|
-
if integration_type == "duckdb" and integration.get("active", True):
|
|
89
|
-
integration_id = integration.get("id")
|
|
90
|
-
# Connection URI might be in config or connection_string field
|
|
91
|
-
connection_uri = (
|
|
92
|
-
integration.get("connection_string")
|
|
93
|
-
or integration.get("config", {}).get("database_path")
|
|
94
|
-
or integration.get("config", {}).get("connection_string")
|
|
95
|
-
)
|
|
96
|
-
if integration_id and connection_uri:
|
|
97
|
-
return (integration_id, connection_uri)
|
|
98
|
-
|
|
99
|
-
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|