scheme-sdk 0.3.1__py3-none-any.whl → 0.3.3__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.
@@ -3,24 +3,6 @@ SeedVault Connector SDK - Base Classes and Utilities
3
3
 
4
4
  This module provides the core interfaces for building platform connectors.
5
5
  Third-party developers should inherit from BaseConnector to create custom integrations.
6
-
7
- Example:
8
- from seedvault.connector_sdk.base import BaseConnector, ConnectorAuthError
9
-
10
- class MyConnector(BaseConnector):
11
- platform = "my_platform"
12
-
13
- def authenticate(self):
14
- # Verify credentials
15
- pass
16
-
17
- def fetch_conversations(self):
18
- # Return conversation/channel list
19
- yield {...}
20
-
21
- def fetch_messages(self, conversation_id, since=None):
22
- # Return messages from a conversation
23
- yield {...}
24
6
  """
25
7
 
26
8
  import logging
@@ -29,8 +11,12 @@ from abc import ABC, abstractmethod
29
11
  from functools import wraps
30
12
  from typing import Any, Dict, Optional
31
13
 
32
- from .errors import (ConnectorAPIError, ConnectorAuthError,
33
- ConnectorConfigError, ConnectorRateLimitError)
14
+ from .errors import (
15
+ ConnectorAPIError,
16
+ ConnectorAuthError,
17
+ ConnectorConfigError,
18
+ ConnectorRateLimitError,
19
+ )
34
20
 
35
21
  # ============================================================================
36
22
  # CONTEXT
@@ -111,7 +97,6 @@ class BaseConnector(ABC):
111
97
  This is the primary interface that third-party developers should implement
112
98
  to create custom platform integrations. The connector handles:
113
99
 
114
- - Authentication with the platform
115
100
  - Fetching conversations/channels/threads
116
101
  - Fetching messages with incremental sync support
117
102
  - Pagination and rate limiting (via helper methods)
@@ -120,11 +105,6 @@ class BaseConnector(ABC):
120
105
  Required Attributes:
121
106
  platform (str): Unique identifier for the platform (e.g., "slack", "gmail")
122
107
 
123
- Required Methods:
124
- authenticate(): Verify credentials are valid
125
- fetch_conversations(): Return list of conversations/channels
126
- fetch_messages(): Return messages from a specific conversation
127
-
128
108
  Optional Methods:
129
109
  normalize_message(): Transform platform-specific format to canonical format
130
110
  get_user_info(): Fetch information about a specific user
@@ -139,12 +119,6 @@ class BaseConnector(ABC):
139
119
  ... self.api_key = context.require_config("api_key")
140
120
  ... self.base_url = "https://api.myplatform.com/v1"
141
121
  ...
142
- ... def authenticate(self):
143
- ... response = requests.get(f"{self.base_url}/auth/verify",
144
- ... headers=self._get_headers())
145
- ... if response.status_code != 200:
146
- ... raise ConnectorAuthError("Invalid API key")
147
- ...
148
122
  ... def fetch_conversations(self):
149
123
  ... for page in self._paginate("/conversations"):
150
124
  ... yield from page.get("conversations", [])
@@ -176,38 +150,8 @@ class BaseConnector(ABC):
176
150
  )
177
151
 
178
152
  self.context = context
179
- self._authenticated = False
180
153
  self._logger = context.logger
181
154
 
182
- # ========================================================================
183
- # REQUIRED METHODS - Must be implemented by subclasses
184
- # ========================================================================
185
-
186
- @abstractmethod
187
- def authenticate(self) -> None:
188
- """
189
- Verify that credentials are valid and the connector can access the platform.
190
-
191
- This method should perform a lightweight check (e.g., GET /auth/verify)
192
- rather than expensive operations. It will be called before any data
193
- fetching operations.
194
-
195
- Raises:
196
- ConnectorAuthError: If authentication fails
197
- ConnectorAPIError: If the API request fails
198
-
199
- Example:
200
- >>> def authenticate(self):
201
- ... response = requests.get(f"{self.base_url}/auth/verify",
202
- ... headers=self._get_headers())
203
- ... if response.status_code == 401:
204
- ... raise ConnectorAuthError("Invalid credentials")
205
- ... elif response.status_code != 200:
206
- ... raise ConnectorAPIError("Auth check failed", response.status_code)
207
- ... self._authenticated = True
208
- """
209
- ...
210
-
211
155
  # ========================================================================
212
156
  # OPTIONAL METHODS - Can be overridden for enhanced functionality
213
157
  # ========================================================================
@@ -299,28 +243,6 @@ class BaseConnector(ABC):
299
243
  # ============================================================================
300
244
 
301
245
 
302
- def require_auth(func):
303
- """
304
- Decorator to ensure connector is authenticated before executing method.
305
-
306
- Example:
307
- >>> @require_auth
308
- ... def fetch_conversations(self):
309
- ... # This will only run if authenticate() has been called
310
- ... pass
311
- """
312
-
313
- @wraps(func)
314
- def wrapper(self, *args, **kwargs):
315
- if not self._authenticated:
316
- raise ConnectorAuthError(
317
- f"Connector not authenticated. Call authenticate() first."
318
- )
319
- return func(self, *args, **kwargs)
320
-
321
- return wrapper
322
-
323
-
324
246
  def retry_on_error(max_retries: int = 3, backoff: float = 1.0):
325
247
  """
326
248
  Decorator to retry operations on transient failures.
@@ -18,33 +18,54 @@ class OutlookConnector(MessageConnector):
18
18
  self.base = "https://graph.microsoft.com/v1.0"
19
19
 
20
20
  def fetch_conversations(
21
- self, top: int = 50, since_iso: Optional[str] = None
21
+ self,
22
+ top: int = 50,
23
+ query: Optional[str] = None,
24
+ since_iso: Optional[str] = None,
22
25
  ) -> List[Dict]:
23
26
  url = f"{self.base}/me/messages"
27
+ page_size = min(max(top, 50), 200)
24
28
  params = {
25
29
  "$select": "id,conversationId,subject,from,receivedDateTime,hasAttachments",
26
30
  "$orderby": "receivedDateTime desc",
27
- "$top": str(top),
31
+ "$top": str(page_size),
28
32
  }
29
33
  if since_iso:
30
34
  params["$filter"] = f"receivedDateTime ge {since_iso}"
35
+ if query:
36
+ params["$search"] = query
31
37
 
32
- messages = self._get_paged(url, headers=self._headers(), params=params)
33
-
34
- # Dedupe by conversationId
38
+ # Dedupe by conversationId, fetching more pages until we have enough.
35
39
  threads_by_conv: Dict[str, Dict] = {}
36
- for m in messages:
37
- cid = m.get("conversationId")
38
- if not cid:
39
- continue
40
- if cid not in threads_by_conv:
40
+ next_url = url
41
+ next_params: Optional[Dict[str, str]] = params
42
+
43
+ while next_url and len(threads_by_conv) < top:
44
+ resp = self._request_with_throttling_retry(
45
+ next_url, headers=self._headers(), params=next_params
46
+ )
47
+ resp.raise_for_status()
48
+ data = resp.json()
49
+
50
+ for m in data.get("value", []):
51
+ cid = m.get("conversationId")
52
+ if not cid or cid in threads_by_conv:
53
+ continue
41
54
  threads_by_conv[cid] = m
55
+ if len(threads_by_conv) >= top:
56
+ break
57
+
58
+ # Per Graph guidance: use @odata.nextLink as-is
59
+ next_url = data.get("@odata.nextLink")
60
+ next_params = None # nextLink already includes the query string
42
61
 
43
62
  conv_list = list(threads_by_conv.values())
44
63
  return [self.normalize_conversation(c) for c in conv_list]
45
64
 
46
65
  def fetch_messages(
47
- self, conversation_id: str, since_iso: Optional[str] = None
66
+ self,
67
+ conversation_id: str,
68
+ since_iso: Optional[str] = None,
48
69
  ) -> List[Dict]:
49
70
  url = f"{self.base}/me/messages"
50
71
  filter_parts = [f"conversationId eq '{conversation_id}'"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: scheme_sdk
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: The Scheme SDK provides connectors for ingesting conversations, messages, and files across communication platforms.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,13 +1,13 @@
1
1
  scheme_sdk/__init__.py,sha256=bQ_i10iSwabzAhS5CapMVplDhMXptJB0qhcgxW_-lnU,26
2
2
  scheme_sdk/connectors/__init__.py,sha256=d7XyCKbx0QklcAFyhTbNxTg0PakQfTs3eQSayYUonqs,144
3
3
  scheme_sdk/connectors/base/__init__.py,sha256=-cfbvvzduJG-hBnojPEkwJEj_dHoVi-EqsDf2l3Oie8,692
4
- scheme_sdk/connectors/base/base.py,sha256=pgZkS5x-GYB2Cv4UeQs94TmcROeGzH9ZgqXubwXW6EI,11771
4
+ scheme_sdk/connectors/base/base.py,sha256=JtsD-HBdjC_Jl22M9j-ihe0tyXJw6JpgWsZp_ozCwDA,8855
5
5
  scheme_sdk/connectors/base/errors.py,sha256=53Dhz5qImXJe0q0cO7IqG4yja0OC0hnxMNMMmgdCtW0,1152
6
6
  scheme_sdk/connectors/base/message.py,sha256=pW-snYsHlAQ5-qMFzAQr3CDaNG1Z-HRYB1fdmZww1TU,5423
7
7
  scheme_sdk/connectors/discord.py,sha256=CdtzVlwT0aHcUayhLGno_vmfhdt_WRtGOeV2zj7Ye7I,844
8
8
  scheme_sdk/connectors/gmail.py,sha256=wZCj917qujXSE9jexqvkDgdmz02y6QST8q9KNRwEvg0,866
9
- scheme_sdk/connectors/outlook.py,sha256=Pm96yZjmCe9YcqbUNNtoegAbEGDciEEK5rrzSDlx5E0,5477
9
+ scheme_sdk/connectors/outlook.py,sha256=ddkA9crJrFgUKT58GC9Brubc4X2tUPDDKDN4RTILYlo,6232
10
10
  scheme_sdk/connectors/slack.py,sha256=_g--XxS4_ImT0fs0HX9vNquTkUlaikiylBhsgJzCC-4,1434
11
- scheme_sdk-0.3.1.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
12
- scheme_sdk-0.3.1.dist-info/METADATA,sha256=D9GkB8KnO1hHdxiH9Mv5iFiJfkxx8LgCmGId2LRpBxw,15084
13
- scheme_sdk-0.3.1.dist-info/RECORD,,
11
+ scheme_sdk-0.3.3.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
12
+ scheme_sdk-0.3.3.dist-info/METADATA,sha256=QyUCmLuOHyxTeby0OEZlGl2s0_HpK4hDZQqHiNcfqsg,15084
13
+ scheme_sdk-0.3.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.26
2
+ Generator: uv 0.9.27
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any