definite-sdk 0.1.15__tar.gz → 0.1.17__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.4
2
2
  Name: definite-sdk
3
- Version: 0.1.15
3
+ Version: 0.1.17
4
4
  Summary: Definite SDK for Python
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -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
- try:
89
- response = requests.get(
90
- f"{self.api_url}/v1/api/integration/Ducklake",
91
- headers={"Authorization": f"Bearer {self.api_key}"},
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
- response.raise_for_status()
94
- dl_integration = response.json()
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
- TYPE gcs,
107
- KEY_ID '{dl_integration['gcs_access_key_id']}',
108
- SECRET '{dl_integration['gcs_secret_access_key']}'
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://{dl_integration['pg_user']}:"
114
- f"{dl_integration['pg_password']}@"
115
- f"{dl_integration['pg_host']}:"
116
- f"{dl_integration['pg_port']}/"
117
- f"{dl_integration['pg_database']}"
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://{dl_integration['gcs_bucket_path']}', "
123
- f"METADATA_SCHEMA '{dl_integration['pg_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,190 @@
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", {})
131
+
132
+ def get_syncs(
133
+ self,
134
+ integration_id: str,
135
+ *,
136
+ limit: int = 50,
137
+ offset: int = 0,
138
+ desc: bool = True,
139
+ status: Optional[str] = None,
140
+ ) -> List[Dict]:
141
+ """
142
+ Retrieves sync runs (DAG runs) for an integration.
143
+
144
+ Args:
145
+ integration_id (str): The ID of the integration.
146
+ limit (int): Maximum number of results to return (default: 50, max: 100).
147
+ offset (int): Number of results to skip for pagination (default: 0).
148
+ desc (bool): Sort by created_at descending if True (default: True).
149
+ status (str): Optional filter by status ("STARTED", "SUCCESS", "FAILED").
150
+
151
+ Returns:
152
+ List[Dict]: List of DAG run records with keys:
153
+ - dag_name: Name of the DAG
154
+ - run_id: Unique ID for this run
155
+ - created_at: ISO datetime when created
156
+ - updated_at: ISO datetime when updated
157
+ - src_integration_id: Source integration UUID
158
+ - dst_integration_id: Destination integration UUID
159
+ - status: Run status (STARTED, SUCCESS, FAILED)
160
+ - details: Additional run details
161
+ """
162
+ params: Dict = {
163
+ "limit": limit,
164
+ "offset": offset,
165
+ "desc": str(desc).lower(),
166
+ }
167
+ if status:
168
+ params["status"] = status
169
+
170
+ response = requests.get(
171
+ f"{self._integrations_url}/{integration_id}/syncs",
172
+ params=params,
173
+ headers={"Authorization": "Bearer " + self._api_key},
174
+ )
175
+ response.raise_for_status()
176
+ cursor_page = response.json()
177
+ return cast(List[Dict], cursor_page.get("data", []))
178
+
179
+ def get_latest_sync(self, integration_id: str) -> Optional[Dict]:
180
+ """
181
+ Retrieves the most recent sync run for an integration.
182
+
183
+ Args:
184
+ integration_id (str): The ID of the integration.
185
+
186
+ Returns:
187
+ Optional[Dict]: The most recent DAG run, or None if no syncs exist.
188
+ """
189
+ syncs = self.get_syncs(integration_id, limit=1, desc=True)
190
+ return syncs[0] if syncs else None
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "definite-sdk"
3
- version = "0.1.15"
3
+ version = "0.1.17"
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,117 +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
- >>> 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._integration_store_url = api_url + INTEGRATION_ENDPOINT
31
-
32
- def list_integrations(self) -> Iterator[dict]:
33
- """
34
- Lists all integrations in the store.
35
-
36
- Returns:
37
- Iterator[str]: An iterator of integrations.
38
- """
39
- response = requests.get(
40
- self._integration_store_url,
41
- headers={"Authorization": "Bearer " + self._api_key},
42
- )
43
- response.raise_for_status()
44
- return iter(response.json())
45
-
46
- def get_integration(self, name: str) -> dict:
47
- """
48
- Retrieves an integration by name.
49
-
50
- Args:
51
- name (str): The name of the integration.
52
-
53
- Returns:
54
- str: The value of the integration.
55
- """
56
- response = requests.get(
57
- self._integration_store_url + f"/{name}",
58
- headers={"Authorization": "Bearer " + self._api_key},
59
- )
60
- response.raise_for_status()
61
- return dict(response.json())
62
-
63
- def get_integration_by_id(self, integration_id: str) -> dict:
64
- """
65
- Retrieves an integration by ID.
66
-
67
- Args:
68
- integration_id (str): The ID of the integration.
69
-
70
- Returns:
71
- dict: The integration details.
72
- """
73
- response = requests.get(
74
- self._integration_store_url + f"/id/{integration_id}",
75
- headers={"Authorization": "Bearer " + self._api_key},
76
- )
77
- response.raise_for_status()
78
- return dict(response.json())
79
-
80
- def lookup_duckdb_integration(self) -> Optional[Tuple[str, str]]:
81
- """
82
- Look up the team's DuckDB integration.
83
-
84
- Note: Currently, the API only returns extractor (source) integrations.
85
- Destination integrations like DuckDB are not yet exposed through this endpoint.
86
- This method is provided for future compatibility when the API is updated.
87
-
88
- Returns:
89
- Optional[Tuple[str, str]]: Tuple of (integration_id, connection_uri)
90
- if found, None if no DuckDB integration exists.
91
- """
92
- try:
93
- response = requests.get(
94
- self._integration_store_url,
95
- headers={"Authorization": "Bearer " + self._api_key},
96
- )
97
- response.raise_for_status()
98
- integrations = response.json()
99
- except:
100
- return None
101
-
102
- # Check for DuckDB in the integrations
103
- # Note: Currently this will not find DuckDB as it's a destination integration
104
- for integration in integrations:
105
- integration_type = integration.get("integration_type", "").lower()
106
- if integration_type == "duckdb" and integration.get("active", True):
107
- integration_id = integration.get("id")
108
- # Connection URI might be in config or connection_string field
109
- connection_uri = (
110
- integration.get("connection_string")
111
- or integration.get("config", {}).get("database_path")
112
- or integration.get("config", {}).get("connection_string")
113
- )
114
- if integration_id and connection_uri:
115
- return (integration_id, connection_uri)
116
-
117
- return None
File without changes
File without changes