definite-sdk 0.1.8__tar.gz → 0.1.11__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: definite-sdk
3
- Version: 0.1.8
3
+ Version: 0.1.11
4
4
  Summary: Definite SDK for Python
5
5
  License: MIT
6
6
  Author: Definite
@@ -21,7 +21,7 @@ Description-Content-Type: text/markdown
21
21
 
22
22
  # Definite SDK
23
23
 
24
- A Python client for interacting with the Definite API, providing a convenient interface for key-value store operations, SQL query execution, secrets management, and DLT (Data Load Tool) integration with state persistence.
24
+ A Python client for interacting with the Definite API, providing a convenient interface for key-value store operations, SQL query execution, secrets management, messaging capabilities, and DLT (Data Load Tool) integration with state persistence.
25
25
 
26
26
  ## Installation
27
27
 
@@ -57,7 +57,9 @@ client = DefiniteClient("YOUR_API_KEY")
57
57
  - **Cube Query Execution**: Execute Cube queries for advanced analytics and data modeling
58
58
  - **Secret Management**: Secure storage and retrieval of application secrets
59
59
  - **Integration Store**: Read-only access to integration configurations
60
+ - **Messaging**: Send messages through various channels (Slack, and more coming soon)
60
61
  - **dlt Integration**: Run dlt pipelines with automatic state persistence to Definite
62
+ - **DuckLake Integration**: Easy attachment of your team's DuckLake to DuckDB connections
61
63
  - **DuckDB Support**: Automatic discovery and connection to team's DuckDB integrations
62
64
 
63
65
  ## Basic Usage
@@ -167,6 +169,49 @@ integrations = list(integration_store.list_integrations())
167
169
  integration = integration_store.get_integration("my_integration")
168
170
  ```
169
171
 
172
+ ### 💬 Messaging
173
+
174
+ Send messages through various channels using the messaging client.
175
+
176
+ ```python
177
+ # Initialize the message client
178
+ message_client = client.get_message_client()
179
+ # Or use the alias method
180
+ message_client = client.message_client()
181
+
182
+ # Send a Slack message using the unified interface
183
+ result = message_client.send_message(
184
+ channel="slack",
185
+ integration_id="your_slack_integration_id",
186
+ to="C0920MVPWFN", # Slack channel ID
187
+ content="Hello from Definite SDK! 👋"
188
+ )
189
+
190
+ # Send a Slack message with blocks and threading
191
+ result = message_client.send_message(
192
+ channel="slack",
193
+ integration_id="your_slack_integration_id",
194
+ to="C0920MVPWFN",
195
+ content="Fallback text",
196
+ blocks=[{
197
+ "type": "section",
198
+ "text": {"type": "mrkdwn", "text": "*Important Update*"}
199
+ }],
200
+ thread_ts="1234567890.123456" # Reply in thread
201
+ )
202
+
203
+ # Or use the convenience method for Slack
204
+ result = message_client.send_slack_message(
205
+ integration_id="your_slack_integration_id",
206
+ channel_id="C0920MVPWFN",
207
+ text="Quick message using the convenience method!",
208
+ blocks=[{
209
+ "type": "section",
210
+ "text": {"type": "mrkdwn", "text": "Message with *rich* _formatting_"}
211
+ }]
212
+ )
213
+ ```
214
+
170
215
  ### dlt Integration
171
216
 
172
217
  ```python
@@ -187,6 +232,39 @@ pipeline.run(orders())
187
232
  last_cursor = pipeline.get_state("orders")
188
233
  ```
189
234
 
235
+ ### DuckLake Integration
236
+
237
+ Attach your team's DuckLake to a DuckDB connection for seamless data access:
238
+
239
+ ```python
240
+ import duckdb
241
+ from definite_sdk import DefiniteClient
242
+
243
+ # Initialize the client
244
+ client = DefiniteClient("YOUR_API_KEY")
245
+
246
+ # Connect to DuckDB and attach DuckLake
247
+ conn = duckdb.connect()
248
+ conn.execute(client.attach_ducklake())
249
+
250
+ # Now you can use DuckLake tables
251
+ conn.execute("CREATE SCHEMA IF NOT EXISTS lake.my_schema;")
252
+ conn.execute("CREATE OR REPLACE TABLE lake.my_schema.users AS SELECT * FROM df")
253
+
254
+ # Query your DuckLake data
255
+ result = conn.sql("SELECT * FROM lake.my_schema.users").df()
256
+ ```
257
+
258
+ You can also specify a custom alias for the attached DuckLake:
259
+
260
+ ```python
261
+ # Attach with custom alias
262
+ conn.execute(client.attach_ducklake(alias="warehouse"))
263
+
264
+ # Use the custom alias
265
+ conn.execute("SELECT * FROM warehouse.my_schema.users")
266
+ ```
267
+
190
268
  ### DuckDB Integration Discovery
191
269
 
192
270
  ```python
@@ -1,6 +1,6 @@
1
1
  # Definite SDK
2
2
 
3
- A Python client for interacting with the Definite API, providing a convenient interface for key-value store operations, SQL query execution, secrets management, and DLT (Data Load Tool) integration with state persistence.
3
+ A Python client for interacting with the Definite API, providing a convenient interface for key-value store operations, SQL query execution, secrets management, messaging capabilities, and DLT (Data Load Tool) integration with state persistence.
4
4
 
5
5
  ## Installation
6
6
 
@@ -36,7 +36,9 @@ client = DefiniteClient("YOUR_API_KEY")
36
36
  - **Cube Query Execution**: Execute Cube queries for advanced analytics and data modeling
37
37
  - **Secret Management**: Secure storage and retrieval of application secrets
38
38
  - **Integration Store**: Read-only access to integration configurations
39
+ - **Messaging**: Send messages through various channels (Slack, and more coming soon)
39
40
  - **dlt Integration**: Run dlt pipelines with automatic state persistence to Definite
41
+ - **DuckLake Integration**: Easy attachment of your team's DuckLake to DuckDB connections
40
42
  - **DuckDB Support**: Automatic discovery and connection to team's DuckDB integrations
41
43
 
42
44
  ## Basic Usage
@@ -146,6 +148,49 @@ integrations = list(integration_store.list_integrations())
146
148
  integration = integration_store.get_integration("my_integration")
147
149
  ```
148
150
 
151
+ ### 💬 Messaging
152
+
153
+ Send messages through various channels using the messaging client.
154
+
155
+ ```python
156
+ # Initialize the message client
157
+ message_client = client.get_message_client()
158
+ # Or use the alias method
159
+ message_client = client.message_client()
160
+
161
+ # Send a Slack message using the unified interface
162
+ result = message_client.send_message(
163
+ channel="slack",
164
+ integration_id="your_slack_integration_id",
165
+ to="C0920MVPWFN", # Slack channel ID
166
+ content="Hello from Definite SDK! 👋"
167
+ )
168
+
169
+ # Send a Slack message with blocks and threading
170
+ result = message_client.send_message(
171
+ channel="slack",
172
+ integration_id="your_slack_integration_id",
173
+ to="C0920MVPWFN",
174
+ content="Fallback text",
175
+ blocks=[{
176
+ "type": "section",
177
+ "text": {"type": "mrkdwn", "text": "*Important Update*"}
178
+ }],
179
+ thread_ts="1234567890.123456" # Reply in thread
180
+ )
181
+
182
+ # Or use the convenience method for Slack
183
+ result = message_client.send_slack_message(
184
+ integration_id="your_slack_integration_id",
185
+ channel_id="C0920MVPWFN",
186
+ text="Quick message using the convenience method!",
187
+ blocks=[{
188
+ "type": "section",
189
+ "text": {"type": "mrkdwn", "text": "Message with *rich* _formatting_"}
190
+ }]
191
+ )
192
+ ```
193
+
149
194
  ### dlt Integration
150
195
 
151
196
  ```python
@@ -166,6 +211,39 @@ pipeline.run(orders())
166
211
  last_cursor = pipeline.get_state("orders")
167
212
  ```
168
213
 
214
+ ### DuckLake Integration
215
+
216
+ Attach your team's DuckLake to a DuckDB connection for seamless data access:
217
+
218
+ ```python
219
+ import duckdb
220
+ from definite_sdk import DefiniteClient
221
+
222
+ # Initialize the client
223
+ client = DefiniteClient("YOUR_API_KEY")
224
+
225
+ # Connect to DuckDB and attach DuckLake
226
+ conn = duckdb.connect()
227
+ conn.execute(client.attach_ducklake())
228
+
229
+ # Now you can use DuckLake tables
230
+ conn.execute("CREATE SCHEMA IF NOT EXISTS lake.my_schema;")
231
+ conn.execute("CREATE OR REPLACE TABLE lake.my_schema.users AS SELECT * FROM df")
232
+
233
+ # Query your DuckLake data
234
+ result = conn.sql("SELECT * FROM lake.my_schema.users").df()
235
+ ```
236
+
237
+ You can also specify a custom alias for the attached DuckLake:
238
+
239
+ ```python
240
+ # Attach with custom alias
241
+ conn.execute(client.attach_ducklake(alias="warehouse"))
242
+
243
+ # Use the custom alias
244
+ conn.execute("SELECT * FROM warehouse.my_schema.users")
245
+ ```
246
+
169
247
  ### DuckDB Integration Discovery
170
248
 
171
249
  ```python
@@ -6,14 +6,16 @@ A Python library for interacting with the Definite API and tools.
6
6
 
7
7
  from definite_sdk.client import DefiniteClient
8
8
  from definite_sdk.integration import DefiniteIntegrationStore
9
+ from definite_sdk.message import DefiniteMessageClient
9
10
  from definite_sdk.secret import DefiniteSecretStore
10
11
  from definite_sdk.sql import DefiniteSqlClient
11
12
  from definite_sdk.store import DefiniteKVStore
12
13
 
13
- __version__ = "0.1.8"
14
+ __version__ = "0.1.9"
14
15
  __all__ = [
15
16
  "DefiniteClient",
16
17
  "DefiniteIntegrationStore",
18
+ "DefiniteMessageClient",
17
19
  "DefiniteSecretStore",
18
20
  "DefiniteSqlClient",
19
21
  "DefiniteKVStore",
@@ -1,7 +1,9 @@
1
1
  import os
2
2
  from typing import Optional
3
3
 
4
+ import requests
4
5
  from definite_sdk.integration import DefiniteIntegrationStore
6
+ from definite_sdk.message import DefiniteMessageClient
5
7
  from definite_sdk.secret import DefiniteSecretStore
6
8
  from definite_sdk.sql import DefiniteSqlClient
7
9
  from definite_sdk.store import DefiniteKVStore
@@ -65,6 +67,55 @@ class DefiniteClient:
65
67
 
66
68
  return DefiniteSqlClient(self.api_key, self.api_url)
67
69
 
70
+ def attach_ducklake(self, alias: str = "lake") -> str:
71
+ """Generates SQL statements to attach DuckLake to a DuckDB connection.
72
+
73
+ This method fetches the team's DuckLake integration credentials and generates
74
+ the necessary SQL statements to create a GCS secret and attach DuckLake.
75
+
76
+ Args:
77
+ alias: The alias name for the attached DuckLake database (default: "lake")
78
+
79
+ Returns:
80
+ str: SQL statements to execute for attaching DuckLake
81
+
82
+ Example:
83
+ >>> client = DefiniteClient(os.environ["DEFINITE_API_KEY"])
84
+ >>> sql = client.attach_ducklake()
85
+ >>> conn.execute(sql)
86
+ """
87
+ # Fetch DuckLake integration details
88
+ response = requests.get(
89
+ f"{self.api_url}/v1/api/integration/DuckLake",
90
+ headers={"Authorization": f"Bearer {self.api_key}"},
91
+ )
92
+ response.raise_for_status()
93
+ dl_integration = response.json()
94
+
95
+ # Generate SQL statements
96
+ create_secret_sql = f"""CREATE SECRET (
97
+ TYPE gcs,
98
+ KEY_ID '{dl_integration['gcs_access_key_id']}',
99
+ SECRET '{dl_integration['gcs_secret_access_key']}'
100
+ );"""
101
+
102
+ # Build PostgreSQL connection string
103
+ pg_conn_str = (
104
+ f"postgresql://{dl_integration['pg_user']}:"
105
+ f"{dl_integration['pg_password']}@"
106
+ f"{dl_integration['pg_host']}:"
107
+ f"{dl_integration['pg_port']}/"
108
+ f"{dl_integration['pg_database']}"
109
+ )
110
+
111
+ attach_sql = (
112
+ f"ATTACH 'ducklake:postgres:{pg_conn_str}' AS {alias} "
113
+ f"(DATA_PATH 'gs://{dl_integration['gcs_bucket_path']}', "
114
+ f"METADATA_SCHEMA '{dl_integration['pg_schema']}');"
115
+ )
116
+
117
+ return f"{create_secret_sql}\n\n{attach_sql}"
118
+
68
119
  # Alias methods for consistency
69
120
  def kv_store(self, name: str) -> DefiniteKVStore:
70
121
  """Alias for get_kv_store."""
@@ -77,3 +128,15 @@ class DefiniteClient:
77
128
  def integration_store(self) -> DefiniteIntegrationStore:
78
129
  """Alias for get_integration_store."""
79
130
  return self.get_integration_store()
131
+
132
+ def get_message_client(self) -> DefiniteMessageClient:
133
+ """Initializes the message client for sending messages via various channels.
134
+
135
+ See DefiniteMessageClient for more how to send messages.
136
+ """
137
+
138
+ return DefiniteMessageClient(self.api_key, self.api_url)
139
+
140
+ def message_client(self) -> DefiniteMessageClient:
141
+ """Alias for get_message_client."""
142
+ return self.get_message_client()
@@ -0,0 +1,199 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ import requests
4
+
5
+ MESSAGE_ENDPOINT = "/v3"
6
+
7
+
8
+ class DefiniteMessageClient:
9
+ """
10
+ A message client for sending messages via various channels through the Definite API.
11
+
12
+ Initialization:
13
+ >>> client = DefiniteClient("MY_API_KEY")
14
+ >>> message_client = client.get_message_client()
15
+
16
+ Sending messages:
17
+ >>> # Send a Slack message
18
+ >>> result = message_client.send_message(
19
+ ... channel="slack",
20
+ ... integration_id="slack_integration_id",
21
+ ... to="C0920MVPWFN", # channel_id
22
+ ... content="Hello from Definite SDK!"
23
+ ... )
24
+
25
+ >>> # Send a Slack message with blocks and thread
26
+ >>> result = message_client.send_message(
27
+ ... channel="slack",
28
+ ... integration_id="slack_integration_id",
29
+ ... to="C0920MVPWFN",
30
+ ... content="Hello!",
31
+ ... blocks=[{"type": "section", "text": {"type": "mrkdwn", "text": "Hello!"}}],
32
+ ... thread_ts="1234567890.123456"
33
+ ... )
34
+ """
35
+
36
+ def __init__(self, api_key: str, api_url: str):
37
+ """
38
+ Initializes the DefiniteMessageClient.
39
+
40
+ Args:
41
+ api_key (str): The API key for authorization.
42
+ api_url (str): The base URL for the Definite API.
43
+ """
44
+ self._api_key = api_key
45
+ self._message_url = api_url + MESSAGE_ENDPOINT
46
+
47
+ def send_message(
48
+ self,
49
+ channel: str,
50
+ integration_id: str,
51
+ to: str,
52
+ content: str,
53
+ subject: Optional[str] = None,
54
+ blocks: Optional[List[Dict[str, Any]]] = None,
55
+ thread_ts: Optional[str] = None,
56
+ **kwargs: Any
57
+ ) -> Dict[str, Any]:
58
+ """
59
+ Sends a message through the specified channel.
60
+
61
+ Args:
62
+ channel (str): The messaging channel to use (e.g., "slack", "email").
63
+ integration_id (str): The ID of the integration to use.
64
+ to (str): The recipient identifier (channel_id for Slack, email address for email).
65
+ content (str): The message content (text for Slack, body for email).
66
+ subject (Optional[str]): Subject line (used for email).
67
+ blocks (Optional[List[Dict[str, Any]]]): Slack Block Kit blocks for formatting.
68
+ thread_ts (Optional[str]): Slack thread timestamp to reply to.
69
+ **kwargs: Additional channel-specific parameters.
70
+
71
+ Returns:
72
+ Dict[str, Any]: The response from the API.
73
+
74
+ Raises:
75
+ requests.HTTPError: If the API request fails.
76
+ ValueError: If the channel is not supported.
77
+
78
+ Example:
79
+ >>> # Slack message
80
+ >>> result = message_client.send_message(
81
+ ... channel="slack",
82
+ ... integration_id="slack_123",
83
+ ... to="C0920MVPWFN",
84
+ ... content="Hello team!"
85
+ ... )
86
+
87
+ >>> # Slack message with blocks
88
+ >>> result = message_client.send_message(
89
+ ... channel="slack",
90
+ ... integration_id="slack_123",
91
+ ... to="C0920MVPWFN",
92
+ ... content="Fallback text",
93
+ ... blocks=[{
94
+ ... "type": "section",
95
+ ... "text": {"type": "mrkdwn", "text": "*Important Update*"}
96
+ ... }]
97
+ ... )
98
+ """
99
+ channel_lower = channel.lower()
100
+
101
+ if channel_lower == "slack":
102
+ return self._send_slack_message(
103
+ integration_id=integration_id,
104
+ channel_id=to,
105
+ text=content,
106
+ blocks=blocks,
107
+ thread_ts=thread_ts,
108
+ **kwargs
109
+ )
110
+ else:
111
+ raise ValueError(f"Unsupported channel: {channel}")
112
+
113
+ def _send_slack_message(
114
+ self,
115
+ integration_id: str,
116
+ channel_id: str,
117
+ text: str,
118
+ blocks: Optional[List[Dict[str, Any]]] = None,
119
+ thread_ts: Optional[str] = None,
120
+ **kwargs: Any
121
+ ) -> Dict[str, Any]:
122
+ """
123
+ Internal method to send a Slack message.
124
+
125
+ Args:
126
+ integration_id (str): The Slack integration ID.
127
+ channel_id (str): The Slack channel ID.
128
+ text (str): The message text.
129
+ blocks (Optional[List[Dict[str, Any]]]): Slack blocks for formatting.
130
+ thread_ts (Optional[str]): Thread timestamp for replies.
131
+ **kwargs: Additional Slack-specific parameters.
132
+
133
+ Returns:
134
+ Dict[str, Any]: The API response.
135
+ """
136
+ url = f"{self._message_url}/slack/message"
137
+
138
+ payload = {
139
+ "integration_id": integration_id,
140
+ "channel_id": channel_id,
141
+ "text": text
142
+ }
143
+
144
+ if blocks:
145
+ payload["blocks"] = blocks
146
+ if thread_ts:
147
+ payload["thread_ts"] = thread_ts
148
+
149
+ # Add any additional kwargs to the payload
150
+ payload.update(kwargs)
151
+
152
+ response = requests.post(
153
+ url,
154
+ json=payload,
155
+ headers={"Authorization": "Bearer " + self._api_key},
156
+ )
157
+ response.raise_for_status()
158
+ return response.json()
159
+
160
+ def send_slack_message(
161
+ self,
162
+ integration_id: str,
163
+ channel_id: str,
164
+ text: str,
165
+ blocks: Optional[List[Dict[str, Any]]] = None,
166
+ thread_ts: Optional[str] = None,
167
+ **kwargs: Any
168
+ ) -> Dict[str, Any]:
169
+ """
170
+ Convenience method to send a Slack message directly.
171
+
172
+ Args:
173
+ integration_id (str): The Slack integration ID.
174
+ channel_id (str): The Slack channel ID.
175
+ text (str): The message text.
176
+ blocks (Optional[List[Dict[str, Any]]]): Slack blocks for formatting.
177
+ thread_ts (Optional[str]): Thread timestamp for replies.
178
+ **kwargs: Additional Slack-specific parameters.
179
+
180
+ Returns:
181
+ Dict[str, Any]: The API response.
182
+
183
+ Example:
184
+ >>> result = message_client.send_slack_message(
185
+ ... integration_id="slack_123",
186
+ ... channel_id="C0920MVPWFN",
187
+ ... text="Hello from Definite SDK! 👋"
188
+ ... )
189
+ >>> print(f"Message sent! Timestamp: {result['ts']}")
190
+ """
191
+ return self.send_message(
192
+ channel="slack",
193
+ integration_id=integration_id,
194
+ to=channel_id,
195
+ content=text,
196
+ blocks=blocks,
197
+ thread_ts=thread_ts,
198
+ **kwargs
199
+ )
@@ -84,7 +84,9 @@ class DefiniteSqlClient:
84
84
  def execute_cube_query(
85
85
  self,
86
86
  cube_query: Dict[str, Any],
87
- integration_id: Optional[str] = None
87
+ integration_id: Optional[str] = None,
88
+ persist: bool = True,
89
+ invalidate: bool = False,
88
90
  ) -> Dict[str, Any]:
89
91
  """
90
92
  Executes a Cube query against a Cube integration.
@@ -93,6 +95,8 @@ class DefiniteSqlClient:
93
95
  cube_query (Dict[str, Any]): The Cube query in JSON format.
94
96
  integration_id (Optional[str]): The Cube integration ID to query against.
95
97
  If not provided, the default integration will be used.
98
+ persist (bool): Whether to persist the query result to the cache.
99
+ invalidate (bool): Whether to invalidate the cached result.
96
100
 
97
101
  Returns:
98
102
  Dict[str, Any]: The query result as returned by the API.
@@ -116,7 +120,10 @@ class DefiniteSqlClient:
116
120
  payload = {"cube_query": cube_query}
117
121
  if integration_id:
118
122
  payload["integration_id"] = integration_id
119
-
123
+ if persist:
124
+ payload["persist"] = persist
125
+ if invalidate:
126
+ payload["invalidate"] = invalidate
120
127
  response = requests.post(
121
128
  self._sql_url,
122
129
  json=payload,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "definite-sdk"
3
- version = "0.1.8"
3
+ version = "0.1.11"
4
4
  description = "Definite SDK for Python"
5
5
  authors = ["Definite <hello@definite.app>"]
6
6
  license = "MIT"
File without changes