langchain-trigger-server 0.2.6rc8__py3-none-any.whl → 0.2.7__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.
Potentially problematic release.
This version of langchain-trigger-server might be problematic. Click here for more details.
- {langchain_trigger_server-0.2.6rc8.dist-info → langchain_trigger_server-0.2.7.dist-info}/METADATA +4 -5
- langchain_trigger_server-0.2.7.dist-info/RECORD +15 -0
- langchain_triggers/__init__.py +8 -3
- langchain_triggers/app.py +345 -253
- langchain_triggers/auth/__init__.py +3 -4
- langchain_triggers/auth/slack_hmac.py +21 -26
- langchain_triggers/core.py +51 -27
- langchain_triggers/cron_manager.py +79 -56
- langchain_triggers/database/__init__.py +2 -2
- langchain_triggers/database/interface.py +55 -68
- langchain_triggers/database/supabase.py +217 -159
- langchain_triggers/decorators.py +52 -25
- langchain_triggers/triggers/__init__.py +1 -1
- langchain_triggers/triggers/cron_trigger.py +11 -11
- langchain_trigger_server-0.2.6rc8.dist-info/RECORD +0 -15
- {langchain_trigger_server-0.2.6rc8.dist-info → langchain_trigger_server-0.2.7.dist-info}/WHEEL +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"""Supabase implementation of trigger database interface."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
import logging
|
|
5
|
-
from typing import List, Optional, Dict, Any
|
|
6
|
-
from supabase import create_client, Client
|
|
7
3
|
import base64
|
|
8
4
|
import hashlib
|
|
9
|
-
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
10
9
|
from cryptography.hazmat.backends import default_backend
|
|
10
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
11
|
+
from supabase import create_client
|
|
11
12
|
|
|
12
13
|
from .interface import TriggerDatabaseInterface
|
|
13
14
|
|
|
@@ -16,67 +17,73 @@ logger = logging.getLogger(__name__)
|
|
|
16
17
|
|
|
17
18
|
class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
18
19
|
"""Supabase implementation of trigger database operations."""
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def __init__(self, supabase_url: str = None, supabase_key: str = None):
|
|
21
22
|
self.supabase_url = supabase_url or os.getenv("SUPABASE_URL")
|
|
22
23
|
self.supabase_key = supabase_key or os.getenv("SUPABASE_KEY")
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
if not self.supabase_url or not self.supabase_key:
|
|
25
|
-
raise ValueError(
|
|
26
|
-
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"SUPABASE_URL and SUPABASE_KEY environment variables are required"
|
|
28
|
+
)
|
|
29
|
+
|
|
27
30
|
self.client = create_client(self.supabase_url, self.supabase_key)
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
# Get encryption key for API key decryption - required
|
|
30
33
|
self.encryption_key = os.getenv("SECRETS_ENCRYPTION_KEY")
|
|
31
34
|
if not self.encryption_key:
|
|
32
35
|
raise ValueError("SECRETS_ENCRYPTION_KEY environment variable is required")
|
|
33
|
-
|
|
36
|
+
|
|
34
37
|
logger.info("Initialized SupabaseTriggerDatabase")
|
|
35
|
-
|
|
38
|
+
|
|
36
39
|
def _decrypt_secret(self, encrypted_secret: str) -> str:
|
|
37
40
|
"""Decrypt an encrypted secret using AES-256-GCM to match OAP Node.js implementation."""
|
|
38
41
|
try:
|
|
39
42
|
# Decode the base64 encoded encrypted data
|
|
40
43
|
combined = base64.b64decode(encrypted_secret)
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
# Constants from Node.js implementation
|
|
43
46
|
IV_LENGTH = 12 # 96 bits
|
|
44
47
|
TAG_LENGTH = 16 # 128 bits
|
|
45
|
-
|
|
48
|
+
|
|
46
49
|
# Minimum length check
|
|
47
50
|
if len(combined) < IV_LENGTH + TAG_LENGTH + 1:
|
|
48
|
-
raise ValueError(
|
|
49
|
-
|
|
51
|
+
raise ValueError(
|
|
52
|
+
"Invalid encrypted secret format: too short or malformed"
|
|
53
|
+
)
|
|
54
|
+
|
|
50
55
|
# Extract IV, encrypted data, and auth tag
|
|
51
56
|
iv = combined[:IV_LENGTH]
|
|
52
57
|
tag = combined[-TAG_LENGTH:]
|
|
53
58
|
encrypted_data = combined[IV_LENGTH:-TAG_LENGTH]
|
|
54
|
-
|
|
59
|
+
|
|
55
60
|
# Derive key using SHA-256 hash (same as Node.js deriveKey function)
|
|
56
61
|
key = hashlib.sha256(self.encryption_key.encode()).digest()
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
# Create AES-GCM cipher
|
|
59
|
-
cipher = Cipher(
|
|
64
|
+
cipher = Cipher(
|
|
65
|
+
algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend()
|
|
66
|
+
)
|
|
60
67
|
decryptor = cipher.decryptor()
|
|
61
|
-
|
|
68
|
+
|
|
62
69
|
# Decrypt the data
|
|
63
70
|
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
|
64
|
-
|
|
65
|
-
return decrypted_data.decode(
|
|
71
|
+
|
|
72
|
+
return decrypted_data.decode("utf-8")
|
|
66
73
|
except Exception as e:
|
|
67
74
|
logger.error(f"Error decrypting secret: {e}")
|
|
68
75
|
raise ValueError("Failed to decrypt API key")
|
|
69
|
-
|
|
76
|
+
|
|
70
77
|
# ========== Trigger Templates ==========
|
|
71
|
-
|
|
78
|
+
|
|
72
79
|
async def create_trigger_template(
|
|
73
|
-
self,
|
|
80
|
+
self,
|
|
74
81
|
id: str,
|
|
75
82
|
provider: str,
|
|
76
|
-
name: str,
|
|
83
|
+
name: str,
|
|
77
84
|
description: str = None,
|
|
78
|
-
registration_schema:
|
|
79
|
-
) ->
|
|
85
|
+
registration_schema: dict = None,
|
|
86
|
+
) -> dict[str, Any] | None:
|
|
80
87
|
"""Create a new trigger template."""
|
|
81
88
|
try:
|
|
82
89
|
data = {
|
|
@@ -84,17 +91,17 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
84
91
|
"provider": provider,
|
|
85
92
|
"name": name,
|
|
86
93
|
"description": description,
|
|
87
|
-
"registration_schema": registration_schema or {}
|
|
94
|
+
"registration_schema": registration_schema or {},
|
|
88
95
|
}
|
|
89
|
-
|
|
96
|
+
|
|
90
97
|
response = self.client.table("trigger_templates").insert(data).execute()
|
|
91
98
|
return response.data[0] if response.data else None
|
|
92
|
-
|
|
99
|
+
|
|
93
100
|
except Exception as e:
|
|
94
101
|
logger.error(f"Error creating trigger template: {e}")
|
|
95
102
|
return None
|
|
96
|
-
|
|
97
|
-
async def get_trigger_templates(self) ->
|
|
103
|
+
|
|
104
|
+
async def get_trigger_templates(self) -> list[dict[str, Any]]:
|
|
98
105
|
"""Get all available trigger templates."""
|
|
99
106
|
try:
|
|
100
107
|
response = self.client.table("trigger_templates").select("*").execute()
|
|
@@ -102,29 +109,34 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
102
109
|
except Exception as e:
|
|
103
110
|
logger.error(f"Error getting trigger templates: {e}")
|
|
104
111
|
return []
|
|
105
|
-
|
|
106
|
-
async def get_trigger_template(self, id: str) ->
|
|
112
|
+
|
|
113
|
+
async def get_trigger_template(self, id: str) -> dict[str, Any] | None:
|
|
107
114
|
"""Get a specific trigger template by ID."""
|
|
108
115
|
try:
|
|
109
|
-
response =
|
|
116
|
+
response = (
|
|
117
|
+
self.client.table("trigger_templates")
|
|
118
|
+
.select("*")
|
|
119
|
+
.eq("id", id)
|
|
120
|
+
.single()
|
|
121
|
+
.execute()
|
|
122
|
+
)
|
|
110
123
|
return response.data if response.data else None
|
|
111
124
|
except Exception as e:
|
|
112
125
|
# Don't log as error if template just doesn't exist (expected on first startup)
|
|
113
|
-
if
|
|
126
|
+
if (
|
|
127
|
+
"no rows returned" in str(e).lower()
|
|
128
|
+
or "multiple (or no) rows returned" in str(e).lower()
|
|
129
|
+
):
|
|
114
130
|
logger.debug(f"Trigger template {id} not found in database")
|
|
115
131
|
else:
|
|
116
132
|
logger.error(f"Error getting trigger template {id}: {e}")
|
|
117
133
|
return None
|
|
118
|
-
|
|
134
|
+
|
|
119
135
|
# ========== Trigger Registrations ==========
|
|
120
|
-
|
|
136
|
+
|
|
121
137
|
async def create_trigger_registration(
|
|
122
|
-
self,
|
|
123
|
-
|
|
124
|
-
template_id: str,
|
|
125
|
-
resource: Dict,
|
|
126
|
-
metadata: Dict = None
|
|
127
|
-
) -> Optional[Dict[str, Any]]:
|
|
138
|
+
self, user_id: str, template_id: str, resource: dict, metadata: dict = None
|
|
139
|
+
) -> dict[str, Any] | None:
|
|
128
140
|
"""Create a new trigger registration for a user."""
|
|
129
141
|
try:
|
|
130
142
|
# Verify template exists
|
|
@@ -132,78 +144,101 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
132
144
|
if not template:
|
|
133
145
|
logger.error(f"Template not found for ID: {template_id}")
|
|
134
146
|
return None
|
|
135
|
-
|
|
147
|
+
|
|
136
148
|
data = {
|
|
137
149
|
"user_id": user_id,
|
|
138
150
|
"template_id": template_id,
|
|
139
151
|
"resource": resource,
|
|
140
152
|
"metadata": metadata or {},
|
|
141
|
-
"status": "active"
|
|
153
|
+
"status": "active",
|
|
142
154
|
}
|
|
143
|
-
|
|
155
|
+
|
|
144
156
|
response = self.client.table("trigger_registrations").insert(data).execute()
|
|
145
157
|
return response.data[0] if response.data else None
|
|
146
|
-
|
|
158
|
+
|
|
147
159
|
except Exception as e:
|
|
148
160
|
logger.exception(f"Error creating trigger registration: {e}")
|
|
149
161
|
return None
|
|
150
|
-
|
|
151
|
-
async def get_user_trigger_registrations(
|
|
162
|
+
|
|
163
|
+
async def get_user_trigger_registrations(
|
|
164
|
+
self, user_id: str
|
|
165
|
+
) -> list[dict[str, Any]]:
|
|
152
166
|
"""Get all trigger registrations for a user."""
|
|
153
167
|
try:
|
|
154
|
-
response =
|
|
168
|
+
response = (
|
|
169
|
+
self.client.table("trigger_registrations")
|
|
170
|
+
.select("""
|
|
155
171
|
*,
|
|
156
172
|
trigger_templates(id, name, description)
|
|
157
|
-
""")
|
|
158
|
-
|
|
173
|
+
""")
|
|
174
|
+
.eq("user_id", user_id)
|
|
175
|
+
.order("created_at", desc=True)
|
|
176
|
+
.execute()
|
|
177
|
+
)
|
|
178
|
+
|
|
159
179
|
return response.data or []
|
|
160
180
|
except Exception as e:
|
|
161
181
|
logger.error(f"Error getting user trigger registrations: {e}")
|
|
162
182
|
return []
|
|
163
183
|
|
|
164
|
-
async def get_user_trigger_registrations_with_agents(
|
|
184
|
+
async def get_user_trigger_registrations_with_agents(
|
|
185
|
+
self, user_id: str
|
|
186
|
+
) -> list[dict[str, Any]]:
|
|
165
187
|
"""Get all trigger registrations for a user with linked agents in a single query."""
|
|
166
188
|
try:
|
|
167
|
-
response =
|
|
189
|
+
response = (
|
|
190
|
+
self.client.table("trigger_registrations")
|
|
191
|
+
.select("""
|
|
168
192
|
*,
|
|
169
193
|
trigger_templates(id, name, description),
|
|
170
194
|
agent_trigger_links(agent_id)
|
|
171
|
-
""")
|
|
172
|
-
|
|
195
|
+
""")
|
|
196
|
+
.eq("user_id", user_id)
|
|
197
|
+
.order("created_at", desc=True)
|
|
198
|
+
.execute()
|
|
199
|
+
)
|
|
200
|
+
|
|
173
201
|
# Process the results to extract linked agent IDs
|
|
174
202
|
if response.data:
|
|
175
203
|
for registration in response.data:
|
|
176
204
|
# Extract agent IDs from the agent_trigger_links
|
|
177
205
|
agent_links = registration.get("agent_trigger_links", [])
|
|
178
|
-
linked_agent_ids = [
|
|
206
|
+
linked_agent_ids = [
|
|
207
|
+
link.get("agent_id")
|
|
208
|
+
for link in agent_links
|
|
209
|
+
if link.get("agent_id")
|
|
210
|
+
]
|
|
179
211
|
registration["linked_agent_ids"] = linked_agent_ids
|
|
180
|
-
|
|
212
|
+
|
|
181
213
|
# Clean up the raw agent_trigger_links data as it's no longer needed
|
|
182
214
|
registration.pop("agent_trigger_links", None)
|
|
183
|
-
|
|
215
|
+
|
|
184
216
|
return response.data or []
|
|
185
217
|
except Exception as e:
|
|
186
218
|
logger.error(f"Error getting user trigger registrations with agents: {e}")
|
|
187
219
|
return []
|
|
188
|
-
|
|
189
|
-
async def get_trigger_registration(
|
|
220
|
+
|
|
221
|
+
async def get_trigger_registration(
|
|
222
|
+
self, registration_id: str, user_id: str = None
|
|
223
|
+
) -> dict[str, Any] | None:
|
|
190
224
|
"""Get a specific trigger registration."""
|
|
191
225
|
try:
|
|
192
|
-
query =
|
|
226
|
+
query = (
|
|
227
|
+
self.client.table("trigger_registrations")
|
|
228
|
+
.select("*")
|
|
229
|
+
.eq("id", registration_id)
|
|
230
|
+
)
|
|
193
231
|
if user_id:
|
|
194
232
|
query = query.eq("user_id", user_id)
|
|
195
|
-
|
|
233
|
+
|
|
196
234
|
response = query.single().execute()
|
|
197
235
|
return response.data if response.data else None
|
|
198
236
|
except Exception as e:
|
|
199
237
|
logger.error(f"Error getting trigger registration {registration_id}: {e}")
|
|
200
238
|
return None
|
|
201
|
-
|
|
239
|
+
|
|
202
240
|
async def update_trigger_metadata(
|
|
203
|
-
self,
|
|
204
|
-
registration_id: str,
|
|
205
|
-
metadata_updates: Dict,
|
|
206
|
-
user_id: str = None
|
|
241
|
+
self, registration_id: str, metadata_updates: dict, user_id: str = None
|
|
207
242
|
) -> bool:
|
|
208
243
|
"""Update metadata for a trigger registration."""
|
|
209
244
|
try:
|
|
@@ -211,117 +246,125 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
211
246
|
current = await self.get_trigger_registration(registration_id, user_id)
|
|
212
247
|
if not current:
|
|
213
248
|
return False
|
|
214
|
-
|
|
249
|
+
|
|
215
250
|
# Merge existing metadata with updates
|
|
216
251
|
current_metadata = current.get("metadata", {})
|
|
217
252
|
updated_metadata = {**current_metadata, **metadata_updates}
|
|
218
|
-
|
|
219
|
-
query =
|
|
220
|
-
"
|
|
221
|
-
"updated_at": "NOW()"
|
|
222
|
-
|
|
223
|
-
|
|
253
|
+
|
|
254
|
+
query = (
|
|
255
|
+
self.client.table("trigger_registrations")
|
|
256
|
+
.update({"metadata": updated_metadata, "updated_at": "NOW()"})
|
|
257
|
+
.eq("id", registration_id)
|
|
258
|
+
)
|
|
259
|
+
|
|
224
260
|
if user_id:
|
|
225
261
|
query = query.eq("user_id", user_id)
|
|
226
|
-
|
|
262
|
+
|
|
227
263
|
response = query.execute()
|
|
228
264
|
return bool(response.data)
|
|
229
|
-
|
|
265
|
+
|
|
230
266
|
except Exception as e:
|
|
231
267
|
logger.error(f"Error updating trigger metadata: {e}")
|
|
232
268
|
return False
|
|
233
|
-
|
|
269
|
+
|
|
234
270
|
async def delete_trigger_registration(
|
|
235
|
-
self,
|
|
236
|
-
registration_id: str,
|
|
237
|
-
user_id: str = None
|
|
271
|
+
self, registration_id: str, user_id: str = None
|
|
238
272
|
) -> bool:
|
|
239
273
|
"""Delete a trigger registration."""
|
|
240
274
|
try:
|
|
241
|
-
query =
|
|
275
|
+
query = (
|
|
276
|
+
self.client.table("trigger_registrations")
|
|
277
|
+
.delete()
|
|
278
|
+
.eq("id", registration_id)
|
|
279
|
+
)
|
|
242
280
|
if user_id:
|
|
243
281
|
query = query.eq("user_id", user_id)
|
|
244
|
-
|
|
245
|
-
|
|
282
|
+
|
|
283
|
+
query.execute()
|
|
246
284
|
return True # Delete operations don't return data
|
|
247
|
-
|
|
285
|
+
|
|
248
286
|
except Exception as e:
|
|
249
287
|
logger.error(f"Error deleting trigger registration: {e}")
|
|
250
288
|
return False
|
|
251
|
-
|
|
289
|
+
|
|
252
290
|
async def find_registration_by_resource(
|
|
253
|
-
self,
|
|
254
|
-
|
|
255
|
-
resource_data: Dict[str, Any]
|
|
256
|
-
) -> Optional[Dict[str, Any]]:
|
|
291
|
+
self, template_id: str, resource_data: dict[str, Any]
|
|
292
|
+
) -> dict[str, Any] | None:
|
|
257
293
|
"""Find trigger registration by matching resource data."""
|
|
258
294
|
try:
|
|
259
295
|
# Build query to match against trigger_registrations with template_id filter
|
|
260
|
-
query =
|
|
261
|
-
"
|
|
262
|
-
|
|
263
|
-
|
|
296
|
+
query = (
|
|
297
|
+
self.client.table("trigger_registrations")
|
|
298
|
+
.select("*, trigger_templates(id, name, description)")
|
|
299
|
+
.eq("template_id", template_id)
|
|
300
|
+
)
|
|
301
|
+
|
|
264
302
|
# Add resource field matches
|
|
265
303
|
for field, value in resource_data.items():
|
|
266
304
|
query = query.eq(f"resource->>{field}", value)
|
|
267
|
-
|
|
305
|
+
|
|
268
306
|
response = query.execute()
|
|
269
|
-
|
|
307
|
+
|
|
270
308
|
if response.data:
|
|
271
309
|
return response.data[0] # Return first match
|
|
272
310
|
return None
|
|
273
|
-
|
|
311
|
+
|
|
274
312
|
except Exception as e:
|
|
275
313
|
logger.error(f"Error finding registration by resource: {e}")
|
|
276
314
|
return None
|
|
277
|
-
|
|
315
|
+
|
|
278
316
|
async def find_user_registration_by_resource(
|
|
279
|
-
self,
|
|
280
|
-
|
|
281
|
-
template_id: str,
|
|
282
|
-
resource_data: Dict[str, Any]
|
|
283
|
-
) -> Optional[Dict[str, Any]]:
|
|
317
|
+
self, user_id: str, template_id: str, resource_data: dict[str, Any]
|
|
318
|
+
) -> dict[str, Any] | None:
|
|
284
319
|
"""Find trigger registration by matching resource data for a specific user."""
|
|
285
320
|
try:
|
|
286
321
|
# Build query to match against trigger_registrations with template_id and user_id filter
|
|
287
|
-
query =
|
|
288
|
-
"
|
|
289
|
-
|
|
290
|
-
|
|
322
|
+
query = (
|
|
323
|
+
self.client.table("trigger_registrations")
|
|
324
|
+
.select("*, trigger_templates(id, name, description)")
|
|
325
|
+
.eq("template_id", template_id)
|
|
326
|
+
.eq("user_id", user_id)
|
|
327
|
+
)
|
|
328
|
+
|
|
291
329
|
# Add resource field matches
|
|
292
330
|
for field, value in resource_data.items():
|
|
293
331
|
query = query.eq(f"resource->>{field}", value)
|
|
294
|
-
|
|
332
|
+
|
|
295
333
|
response = query.execute()
|
|
296
|
-
|
|
334
|
+
|
|
297
335
|
if response.data:
|
|
298
336
|
return response.data[0] # Return first match
|
|
299
337
|
return None
|
|
300
|
-
|
|
338
|
+
|
|
301
339
|
except Exception as e:
|
|
302
340
|
logger.error(f"Error finding user registration by resource: {e}")
|
|
303
341
|
return None
|
|
304
|
-
|
|
305
|
-
async def get_all_registrations(self, template_id: str) ->
|
|
342
|
+
|
|
343
|
+
async def get_all_registrations(self, template_id: str) -> list[dict[str, Any]]:
|
|
306
344
|
"""Get all registrations for a specific trigger template."""
|
|
307
345
|
try:
|
|
308
|
-
response =
|
|
309
|
-
"
|
|
310
|
-
|
|
311
|
-
|
|
346
|
+
response = (
|
|
347
|
+
self.client.table("trigger_registrations")
|
|
348
|
+
.select("*, trigger_templates(id, name, description)")
|
|
349
|
+
.eq("template_id", template_id)
|
|
350
|
+
.execute()
|
|
351
|
+
)
|
|
352
|
+
|
|
312
353
|
return response.data or []
|
|
313
354
|
except Exception as e:
|
|
314
|
-
logger.error(
|
|
355
|
+
logger.error(
|
|
356
|
+
f"Error getting all registrations for template {template_id}: {e}"
|
|
357
|
+
)
|
|
315
358
|
return []
|
|
316
|
-
|
|
359
|
+
|
|
317
360
|
# ========== Agent-Trigger Links ==========
|
|
318
|
-
|
|
361
|
+
|
|
319
362
|
async def link_agent_to_trigger(
|
|
320
363
|
self,
|
|
321
364
|
agent_id: str,
|
|
322
|
-
registration_id: str,
|
|
365
|
+
registration_id: str,
|
|
323
366
|
created_by: str,
|
|
324
|
-
field_selection:
|
|
367
|
+
field_selection: dict[str, bool] | None = None,
|
|
325
368
|
) -> bool:
|
|
326
369
|
"""Link an agent to a trigger registration with optional field selection."""
|
|
327
370
|
try:
|
|
@@ -329,66 +372,78 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
329
372
|
"agent_id": agent_id,
|
|
330
373
|
"registration_id": registration_id,
|
|
331
374
|
"created_by": created_by,
|
|
332
|
-
"field_selection": field_selection
|
|
375
|
+
"field_selection": field_selection,
|
|
333
376
|
}
|
|
334
|
-
|
|
377
|
+
|
|
335
378
|
response = self.client.table("agent_trigger_links").insert(data).execute()
|
|
336
379
|
return bool(response.data)
|
|
337
|
-
|
|
380
|
+
|
|
338
381
|
except Exception as e:
|
|
339
382
|
logger.error(f"Error linking agent to trigger: {e}")
|
|
340
383
|
return False
|
|
341
|
-
|
|
384
|
+
|
|
342
385
|
async def unlink_agent_from_trigger(
|
|
343
|
-
self,
|
|
344
|
-
agent_id: str,
|
|
345
|
-
registration_id: str
|
|
386
|
+
self, agent_id: str, registration_id: str
|
|
346
387
|
) -> bool:
|
|
347
388
|
"""Unlink an agent from a trigger registration."""
|
|
348
389
|
try:
|
|
349
|
-
|
|
350
|
-
"
|
|
351
|
-
|
|
352
|
-
|
|
390
|
+
(
|
|
391
|
+
self.client.table("agent_trigger_links")
|
|
392
|
+
.delete()
|
|
393
|
+
.eq("agent_id", agent_id)
|
|
394
|
+
.eq("registration_id", registration_id)
|
|
395
|
+
.execute()
|
|
396
|
+
)
|
|
397
|
+
|
|
353
398
|
return True # Delete operations don't return data
|
|
354
|
-
|
|
399
|
+
|
|
355
400
|
except Exception as e:
|
|
356
401
|
logger.error(f"Error unlinking agent from trigger: {e}")
|
|
357
402
|
return False
|
|
358
|
-
|
|
359
|
-
async def get_agents_for_trigger(
|
|
403
|
+
|
|
404
|
+
async def get_agents_for_trigger(
|
|
405
|
+
self, registration_id: str
|
|
406
|
+
) -> list[dict[str, Any]]:
|
|
360
407
|
"""Get all agent links for a trigger registration with field_selection."""
|
|
361
408
|
try:
|
|
362
|
-
response =
|
|
363
|
-
"
|
|
364
|
-
|
|
365
|
-
|
|
409
|
+
response = (
|
|
410
|
+
self.client.table("agent_trigger_links")
|
|
411
|
+
.select("agent_id, field_selection")
|
|
412
|
+
.eq("registration_id", registration_id)
|
|
413
|
+
.execute()
|
|
414
|
+
)
|
|
415
|
+
|
|
366
416
|
return response.data or []
|
|
367
|
-
|
|
417
|
+
|
|
368
418
|
except Exception as e:
|
|
369
419
|
logger.error(f"Error getting agents for trigger: {e}")
|
|
370
420
|
return []
|
|
371
|
-
|
|
372
|
-
async def get_triggers_for_agent(self, agent_id: str) ->
|
|
421
|
+
|
|
422
|
+
async def get_triggers_for_agent(self, agent_id: str) -> list[dict[str, Any]]:
|
|
373
423
|
"""Get all trigger registrations linked to an agent."""
|
|
374
424
|
try:
|
|
375
|
-
response =
|
|
425
|
+
response = (
|
|
426
|
+
self.client.table("agent_trigger_links")
|
|
427
|
+
.select("""
|
|
376
428
|
registration_id,
|
|
377
429
|
trigger_registrations(
|
|
378
430
|
*,
|
|
379
431
|
trigger_templates(id, name, description)
|
|
380
432
|
)
|
|
381
|
-
""")
|
|
382
|
-
|
|
433
|
+
""")
|
|
434
|
+
.eq("agent_id", agent_id)
|
|
435
|
+
.execute()
|
|
436
|
+
)
|
|
437
|
+
|
|
383
438
|
return [row["trigger_registrations"] for row in response.data or []]
|
|
384
|
-
|
|
439
|
+
|
|
385
440
|
except Exception as e:
|
|
386
441
|
logger.error(f"Error getting triggers for agent: {e}")
|
|
387
442
|
return []
|
|
388
|
-
|
|
443
|
+
|
|
389
444
|
# ========== Helper Methods ==========
|
|
390
|
-
|
|
391
|
-
async def get_user_from_token(self, token: str) ->
|
|
445
|
+
|
|
446
|
+
async def get_user_from_token(self, token: str) -> str | None:
|
|
392
447
|
"""Extract user ID from JWT token via Supabase auth."""
|
|
393
448
|
try:
|
|
394
449
|
client = self._create_user_client(token)
|
|
@@ -397,17 +452,20 @@ class SupabaseTriggerDatabase(TriggerDatabaseInterface):
|
|
|
397
452
|
except Exception as e:
|
|
398
453
|
logger.error(f"Error getting user from token: {e}")
|
|
399
454
|
return None
|
|
400
|
-
|
|
401
|
-
async def get_user_by_email(self, email: str) ->
|
|
455
|
+
|
|
456
|
+
async def get_user_by_email(self, email: str) -> str | None:
|
|
402
457
|
"""Get user ID by email from trigger registrations."""
|
|
403
458
|
try:
|
|
404
|
-
response =
|
|
405
|
-
"
|
|
406
|
-
|
|
407
|
-
|
|
459
|
+
response = (
|
|
460
|
+
self.client.table("trigger_registrations")
|
|
461
|
+
.select("user_id")
|
|
462
|
+
.eq("resource->>email", email)
|
|
463
|
+
.limit(1)
|
|
464
|
+
.execute()
|
|
465
|
+
)
|
|
466
|
+
|
|
408
467
|
return response.data[0]["user_id"] if response.data else None
|
|
409
|
-
|
|
468
|
+
|
|
410
469
|
except Exception as e:
|
|
411
470
|
logger.error(f"Error getting user by email: {e}")
|
|
412
471
|
return None
|
|
413
|
-
|