airbyte-agent-salesforce 0.1.17__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.
Files changed (55) hide show
  1. airbyte_agent_salesforce/__init__.py +87 -0
  2. airbyte_agent_salesforce/_vendored/__init__.py +1 -0
  3. airbyte_agent_salesforce/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_salesforce/_vendored/connector_sdk/auth_strategies.py +1123 -0
  5. airbyte_agent_salesforce/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_salesforce/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_salesforce/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_salesforce/_vendored/connector_sdk/connector_model_loader.py +957 -0
  9. airbyte_agent_salesforce/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_salesforce/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_salesforce/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_salesforce/_vendored/connector_sdk/executor/hosted_executor.py +197 -0
  13. airbyte_agent_salesforce/_vendored/connector_sdk/executor/local_executor.py +1504 -0
  14. airbyte_agent_salesforce/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_salesforce/_vendored/connector_sdk/extensions.py +655 -0
  16. airbyte_agent_salesforce/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_salesforce/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_salesforce/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_salesforce/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_salesforce/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_salesforce/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_salesforce/_vendored/connector_sdk/http/response.py +102 -0
  23. airbyte_agent_salesforce/_vendored/connector_sdk/http_client.py +686 -0
  24. airbyte_agent_salesforce/_vendored/connector_sdk/logging/__init__.py +11 -0
  25. airbyte_agent_salesforce/_vendored/connector_sdk/logging/logger.py +264 -0
  26. airbyte_agent_salesforce/_vendored/connector_sdk/logging/types.py +92 -0
  27. airbyte_agent_salesforce/_vendored/connector_sdk/observability/__init__.py +11 -0
  28. airbyte_agent_salesforce/_vendored/connector_sdk/observability/models.py +19 -0
  29. airbyte_agent_salesforce/_vendored/connector_sdk/observability/redactor.py +81 -0
  30. airbyte_agent_salesforce/_vendored/connector_sdk/observability/session.py +94 -0
  31. airbyte_agent_salesforce/_vendored/connector_sdk/performance/__init__.py +6 -0
  32. airbyte_agent_salesforce/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  33. airbyte_agent_salesforce/_vendored/connector_sdk/performance/metrics.py +93 -0
  34. airbyte_agent_salesforce/_vendored/connector_sdk/schema/__init__.py +75 -0
  35. airbyte_agent_salesforce/_vendored/connector_sdk/schema/base.py +161 -0
  36. airbyte_agent_salesforce/_vendored/connector_sdk/schema/components.py +238 -0
  37. airbyte_agent_salesforce/_vendored/connector_sdk/schema/connector.py +131 -0
  38. airbyte_agent_salesforce/_vendored/connector_sdk/schema/extensions.py +109 -0
  39. airbyte_agent_salesforce/_vendored/connector_sdk/schema/operations.py +146 -0
  40. airbyte_agent_salesforce/_vendored/connector_sdk/schema/security.py +213 -0
  41. airbyte_agent_salesforce/_vendored/connector_sdk/secrets.py +182 -0
  42. airbyte_agent_salesforce/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  43. airbyte_agent_salesforce/_vendored/connector_sdk/telemetry/config.py +32 -0
  44. airbyte_agent_salesforce/_vendored/connector_sdk/telemetry/events.py +58 -0
  45. airbyte_agent_salesforce/_vendored/connector_sdk/telemetry/tracker.py +151 -0
  46. airbyte_agent_salesforce/_vendored/connector_sdk/types.py +241 -0
  47. airbyte_agent_salesforce/_vendored/connector_sdk/utils.py +60 -0
  48. airbyte_agent_salesforce/_vendored/connector_sdk/validation.py +822 -0
  49. airbyte_agent_salesforce/connector.py +1862 -0
  50. airbyte_agent_salesforce/connector_model.py +1597 -0
  51. airbyte_agent_salesforce/models.py +365 -0
  52. airbyte_agent_salesforce/types.py +166 -0
  53. airbyte_agent_salesforce-0.1.17.dist-info/METADATA +113 -0
  54. airbyte_agent_salesforce-0.1.17.dist-info/RECORD +55 -0
  55. airbyte_agent_salesforce-0.1.17.dist-info/WHEEL +4 -0
@@ -0,0 +1,1862 @@
1
+ """
2
+ salesforce connector.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Any, AsyncIterator, overload
8
+ try:
9
+ from typing import Literal
10
+ except ImportError:
11
+ from typing_extensions import Literal
12
+
13
+ from .connector_model import SalesforceConnectorModel
14
+
15
+ from .types import (
16
+ AccountsGetParams,
17
+ AccountsListParams,
18
+ AccountsSearchParams,
19
+ AttachmentsDownloadParams,
20
+ AttachmentsGetParams,
21
+ AttachmentsListParams,
22
+ CampaignsGetParams,
23
+ CampaignsListParams,
24
+ CampaignsSearchParams,
25
+ CasesGetParams,
26
+ CasesListParams,
27
+ CasesSearchParams,
28
+ ContactsGetParams,
29
+ ContactsListParams,
30
+ ContactsSearchParams,
31
+ ContentVersionsDownloadParams,
32
+ ContentVersionsGetParams,
33
+ ContentVersionsListParams,
34
+ EventsGetParams,
35
+ EventsListParams,
36
+ EventsSearchParams,
37
+ LeadsGetParams,
38
+ LeadsListParams,
39
+ LeadsSearchParams,
40
+ NotesGetParams,
41
+ NotesListParams,
42
+ NotesSearchParams,
43
+ OpportunitiesGetParams,
44
+ OpportunitiesListParams,
45
+ OpportunitiesSearchParams,
46
+ QueryListParams,
47
+ TasksGetParams,
48
+ TasksListParams,
49
+ TasksSearchParams,
50
+ )
51
+
52
+ if TYPE_CHECKING:
53
+ from .models import SalesforceAuthConfig
54
+ # Import response models and envelope models at runtime
55
+ from .models import (
56
+ SalesforceExecuteResult,
57
+ SalesforceExecuteResultWithMeta,
58
+ Account,
59
+ AccountQueryResult,
60
+ Attachment,
61
+ AttachmentQueryResult,
62
+ Campaign,
63
+ CampaignQueryResult,
64
+ Case,
65
+ CaseQueryResult,
66
+ Contact,
67
+ ContactQueryResult,
68
+ ContentVersion,
69
+ ContentVersionQueryResult,
70
+ Event,
71
+ EventQueryResult,
72
+ Lead,
73
+ LeadQueryResult,
74
+ Note,
75
+ NoteQueryResult,
76
+ Opportunity,
77
+ OpportunityQueryResult,
78
+ QueryResult,
79
+ SearchResult,
80
+ Task,
81
+ TaskQueryResult,
82
+ )
83
+
84
+
85
+ class SalesforceConnector:
86
+ """
87
+ Type-safe Salesforce API connector.
88
+
89
+ Auto-generated from OpenAPI specification with full type safety.
90
+ """
91
+
92
+ connector_name = "salesforce"
93
+ connector_version = "1.0.3"
94
+ vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
95
+
96
+ # Map of (entity, action) -> has_extractors for envelope wrapping decision
97
+ _EXTRACTOR_MAP = {
98
+ ("accounts", "list"): False,
99
+ ("accounts", "get"): False,
100
+ ("accounts", "search"): False,
101
+ ("contacts", "list"): False,
102
+ ("contacts", "get"): False,
103
+ ("contacts", "search"): False,
104
+ ("leads", "list"): False,
105
+ ("leads", "get"): False,
106
+ ("leads", "search"): False,
107
+ ("opportunities", "list"): False,
108
+ ("opportunities", "get"): False,
109
+ ("opportunities", "search"): False,
110
+ ("tasks", "list"): False,
111
+ ("tasks", "get"): False,
112
+ ("tasks", "search"): False,
113
+ ("events", "list"): False,
114
+ ("events", "get"): False,
115
+ ("events", "search"): False,
116
+ ("campaigns", "list"): False,
117
+ ("campaigns", "get"): False,
118
+ ("campaigns", "search"): False,
119
+ ("cases", "list"): False,
120
+ ("cases", "get"): False,
121
+ ("cases", "search"): False,
122
+ ("notes", "list"): False,
123
+ ("notes", "get"): False,
124
+ ("notes", "search"): False,
125
+ ("content_versions", "list"): False,
126
+ ("content_versions", "get"): False,
127
+ ("content_versions", "download"): False,
128
+ ("attachments", "list"): False,
129
+ ("attachments", "get"): False,
130
+ ("attachments", "download"): False,
131
+ ("query", "list"): False,
132
+ }
133
+
134
+ # Map of (entity, action) -> {python_param_name: api_param_name}
135
+ # Used to convert snake_case TypedDict keys to API parameter names in execute()
136
+ _PARAM_MAP = {
137
+ ('accounts', 'list'): {'q': 'q'},
138
+ ('accounts', 'get'): {'id': 'id', 'fields': 'fields'},
139
+ ('accounts', 'search'): {'q': 'q'},
140
+ ('contacts', 'list'): {'q': 'q'},
141
+ ('contacts', 'get'): {'id': 'id', 'fields': 'fields'},
142
+ ('contacts', 'search'): {'q': 'q'},
143
+ ('leads', 'list'): {'q': 'q'},
144
+ ('leads', 'get'): {'id': 'id', 'fields': 'fields'},
145
+ ('leads', 'search'): {'q': 'q'},
146
+ ('opportunities', 'list'): {'q': 'q'},
147
+ ('opportunities', 'get'): {'id': 'id', 'fields': 'fields'},
148
+ ('opportunities', 'search'): {'q': 'q'},
149
+ ('tasks', 'list'): {'q': 'q'},
150
+ ('tasks', 'get'): {'id': 'id', 'fields': 'fields'},
151
+ ('tasks', 'search'): {'q': 'q'},
152
+ ('events', 'list'): {'q': 'q'},
153
+ ('events', 'get'): {'id': 'id', 'fields': 'fields'},
154
+ ('events', 'search'): {'q': 'q'},
155
+ ('campaigns', 'list'): {'q': 'q'},
156
+ ('campaigns', 'get'): {'id': 'id', 'fields': 'fields'},
157
+ ('campaigns', 'search'): {'q': 'q'},
158
+ ('cases', 'list'): {'q': 'q'},
159
+ ('cases', 'get'): {'id': 'id', 'fields': 'fields'},
160
+ ('cases', 'search'): {'q': 'q'},
161
+ ('notes', 'list'): {'q': 'q'},
162
+ ('notes', 'get'): {'id': 'id', 'fields': 'fields'},
163
+ ('notes', 'search'): {'q': 'q'},
164
+ ('content_versions', 'list'): {'q': 'q'},
165
+ ('content_versions', 'get'): {'id': 'id', 'fields': 'fields'},
166
+ ('content_versions', 'download'): {'id': 'id', 'range_header': 'range_header'},
167
+ ('attachments', 'list'): {'q': 'q'},
168
+ ('attachments', 'get'): {'id': 'id', 'fields': 'fields'},
169
+ ('attachments', 'download'): {'id': 'id', 'range_header': 'range_header'},
170
+ ('query', 'list'): {'q': 'q'},
171
+ }
172
+
173
+ def __init__(
174
+ self,
175
+ auth_config: SalesforceAuthConfig | None = None,
176
+ external_user_id: str | None = None,
177
+ airbyte_client_id: str | None = None,
178
+ airbyte_client_secret: str | None = None,
179
+ on_token_refresh: Any | None = None,
180
+ instance_url: str | None = None ):
181
+ """
182
+ Initialize a new salesforce connector instance.
183
+
184
+ Supports both local and hosted execution modes:
185
+ - Local mode: Provide `auth_config` for direct API calls
186
+ - Hosted mode: Provide `external_user_id`, `airbyte_client_id`, and `airbyte_client_secret` for hosted execution
187
+
188
+ Args:
189
+ auth_config: Typed authentication configuration (required for local mode)
190
+ external_user_id: External user ID (required for hosted mode)
191
+ airbyte_client_id: Airbyte OAuth client ID (required for hosted mode)
192
+ airbyte_client_secret: Airbyte OAuth client secret (required for hosted mode)
193
+ on_token_refresh: Optional callback for OAuth2 token refresh persistence.
194
+ Called with new_tokens dict when tokens are refreshed. Can be sync or async.
195
+ Example: lambda tokens: save_to_database(tokens) instance_url: Your Salesforce instance URL (e.g., https://na1.salesforce.com)
196
+ Examples:
197
+ # Local mode (direct API calls)
198
+ connector = SalesforceConnector(auth_config=SalesforceAuthConfig(refresh_token="...", client_id="...", client_secret="..."))
199
+ # Hosted mode (executed on Airbyte cloud)
200
+ connector = SalesforceConnector(
201
+ external_user_id="user-123",
202
+ airbyte_client_id="client_abc123",
203
+ airbyte_client_secret="secret_xyz789"
204
+ )
205
+
206
+ # Local mode with OAuth2 token refresh callback
207
+ def save_tokens(new_tokens: dict) -> None:
208
+ # Persist updated tokens to your storage (file, database, etc.)
209
+ with open("tokens.json", "w") as f:
210
+ json.dump(new_tokens, f)
211
+
212
+ connector = SalesforceConnector(
213
+ auth_config=SalesforceAuthConfig(access_token="...", refresh_token="..."),
214
+ on_token_refresh=save_tokens
215
+ )
216
+ """
217
+ # Hosted mode: external_user_id, airbyte_client_id, and airbyte_client_secret provided
218
+ if external_user_id and airbyte_client_id and airbyte_client_secret:
219
+ from ._vendored.connector_sdk.executor import HostedExecutor
220
+ self._executor = HostedExecutor(
221
+ external_user_id=external_user_id,
222
+ airbyte_client_id=airbyte_client_id,
223
+ airbyte_client_secret=airbyte_client_secret,
224
+ connector_definition_id=str(SalesforceConnectorModel.id),
225
+ )
226
+ else:
227
+ # Local mode: auth_config required
228
+ if not auth_config:
229
+ raise ValueError(
230
+ "Either provide (external_user_id, airbyte_client_id, airbyte_client_secret) for hosted mode "
231
+ "or auth_config for local mode"
232
+ )
233
+
234
+ from ._vendored.connector_sdk.executor import LocalExecutor
235
+
236
+ # Build config_values dict from server variables
237
+ config_values: dict[str, str] = {}
238
+ if instance_url:
239
+ config_values["instance_url"] = instance_url
240
+
241
+ self._executor = LocalExecutor(
242
+ model=SalesforceConnectorModel,
243
+ auth_config=auth_config.model_dump() if auth_config else None,
244
+ config_values=config_values,
245
+ on_token_refresh=on_token_refresh
246
+ )
247
+
248
+ # Update base_url with server variables if provided
249
+ base_url = self._executor.http_client.base_url
250
+ if instance_url:
251
+ base_url = base_url.replace("{instance_url}", instance_url)
252
+ self._executor.http_client.base_url = base_url
253
+
254
+ # Initialize entity query objects
255
+ self.accounts = AccountsQuery(self)
256
+ self.contacts = ContactsQuery(self)
257
+ self.leads = LeadsQuery(self)
258
+ self.opportunities = OpportunitiesQuery(self)
259
+ self.tasks = TasksQuery(self)
260
+ self.events = EventsQuery(self)
261
+ self.campaigns = CampaignsQuery(self)
262
+ self.cases = CasesQuery(self)
263
+ self.notes = NotesQuery(self)
264
+ self.content_versions = ContentVersionsQuery(self)
265
+ self.attachments = AttachmentsQuery(self)
266
+ self.query = QueryQuery(self)
267
+
268
+ # ===== TYPED EXECUTE METHOD (Recommended Interface) =====
269
+
270
+ @overload
271
+ async def execute(
272
+ self,
273
+ entity: Literal["accounts"],
274
+ action: Literal["list"],
275
+ params: "AccountsListParams"
276
+ ) -> "AccountQueryResult": ...
277
+
278
+ @overload
279
+ async def execute(
280
+ self,
281
+ entity: Literal["accounts"],
282
+ action: Literal["get"],
283
+ params: "AccountsGetParams"
284
+ ) -> "Account": ...
285
+
286
+ @overload
287
+ async def execute(
288
+ self,
289
+ entity: Literal["accounts"],
290
+ action: Literal["search"],
291
+ params: "AccountsSearchParams"
292
+ ) -> "SearchResult": ...
293
+
294
+ @overload
295
+ async def execute(
296
+ self,
297
+ entity: Literal["contacts"],
298
+ action: Literal["list"],
299
+ params: "ContactsListParams"
300
+ ) -> "ContactQueryResult": ...
301
+
302
+ @overload
303
+ async def execute(
304
+ self,
305
+ entity: Literal["contacts"],
306
+ action: Literal["get"],
307
+ params: "ContactsGetParams"
308
+ ) -> "Contact": ...
309
+
310
+ @overload
311
+ async def execute(
312
+ self,
313
+ entity: Literal["contacts"],
314
+ action: Literal["search"],
315
+ params: "ContactsSearchParams"
316
+ ) -> "SearchResult": ...
317
+
318
+ @overload
319
+ async def execute(
320
+ self,
321
+ entity: Literal["leads"],
322
+ action: Literal["list"],
323
+ params: "LeadsListParams"
324
+ ) -> "LeadQueryResult": ...
325
+
326
+ @overload
327
+ async def execute(
328
+ self,
329
+ entity: Literal["leads"],
330
+ action: Literal["get"],
331
+ params: "LeadsGetParams"
332
+ ) -> "Lead": ...
333
+
334
+ @overload
335
+ async def execute(
336
+ self,
337
+ entity: Literal["leads"],
338
+ action: Literal["search"],
339
+ params: "LeadsSearchParams"
340
+ ) -> "SearchResult": ...
341
+
342
+ @overload
343
+ async def execute(
344
+ self,
345
+ entity: Literal["opportunities"],
346
+ action: Literal["list"],
347
+ params: "OpportunitiesListParams"
348
+ ) -> "OpportunityQueryResult": ...
349
+
350
+ @overload
351
+ async def execute(
352
+ self,
353
+ entity: Literal["opportunities"],
354
+ action: Literal["get"],
355
+ params: "OpportunitiesGetParams"
356
+ ) -> "Opportunity": ...
357
+
358
+ @overload
359
+ async def execute(
360
+ self,
361
+ entity: Literal["opportunities"],
362
+ action: Literal["search"],
363
+ params: "OpportunitiesSearchParams"
364
+ ) -> "SearchResult": ...
365
+
366
+ @overload
367
+ async def execute(
368
+ self,
369
+ entity: Literal["tasks"],
370
+ action: Literal["list"],
371
+ params: "TasksListParams"
372
+ ) -> "TaskQueryResult": ...
373
+
374
+ @overload
375
+ async def execute(
376
+ self,
377
+ entity: Literal["tasks"],
378
+ action: Literal["get"],
379
+ params: "TasksGetParams"
380
+ ) -> "Task": ...
381
+
382
+ @overload
383
+ async def execute(
384
+ self,
385
+ entity: Literal["tasks"],
386
+ action: Literal["search"],
387
+ params: "TasksSearchParams"
388
+ ) -> "SearchResult": ...
389
+
390
+ @overload
391
+ async def execute(
392
+ self,
393
+ entity: Literal["events"],
394
+ action: Literal["list"],
395
+ params: "EventsListParams"
396
+ ) -> "EventQueryResult": ...
397
+
398
+ @overload
399
+ async def execute(
400
+ self,
401
+ entity: Literal["events"],
402
+ action: Literal["get"],
403
+ params: "EventsGetParams"
404
+ ) -> "Event": ...
405
+
406
+ @overload
407
+ async def execute(
408
+ self,
409
+ entity: Literal["events"],
410
+ action: Literal["search"],
411
+ params: "EventsSearchParams"
412
+ ) -> "SearchResult": ...
413
+
414
+ @overload
415
+ async def execute(
416
+ self,
417
+ entity: Literal["campaigns"],
418
+ action: Literal["list"],
419
+ params: "CampaignsListParams"
420
+ ) -> "CampaignQueryResult": ...
421
+
422
+ @overload
423
+ async def execute(
424
+ self,
425
+ entity: Literal["campaigns"],
426
+ action: Literal["get"],
427
+ params: "CampaignsGetParams"
428
+ ) -> "Campaign": ...
429
+
430
+ @overload
431
+ async def execute(
432
+ self,
433
+ entity: Literal["campaigns"],
434
+ action: Literal["search"],
435
+ params: "CampaignsSearchParams"
436
+ ) -> "SearchResult": ...
437
+
438
+ @overload
439
+ async def execute(
440
+ self,
441
+ entity: Literal["cases"],
442
+ action: Literal["list"],
443
+ params: "CasesListParams"
444
+ ) -> "CaseQueryResult": ...
445
+
446
+ @overload
447
+ async def execute(
448
+ self,
449
+ entity: Literal["cases"],
450
+ action: Literal["get"],
451
+ params: "CasesGetParams"
452
+ ) -> "Case": ...
453
+
454
+ @overload
455
+ async def execute(
456
+ self,
457
+ entity: Literal["cases"],
458
+ action: Literal["search"],
459
+ params: "CasesSearchParams"
460
+ ) -> "SearchResult": ...
461
+
462
+ @overload
463
+ async def execute(
464
+ self,
465
+ entity: Literal["notes"],
466
+ action: Literal["list"],
467
+ params: "NotesListParams"
468
+ ) -> "NoteQueryResult": ...
469
+
470
+ @overload
471
+ async def execute(
472
+ self,
473
+ entity: Literal["notes"],
474
+ action: Literal["get"],
475
+ params: "NotesGetParams"
476
+ ) -> "Note": ...
477
+
478
+ @overload
479
+ async def execute(
480
+ self,
481
+ entity: Literal["notes"],
482
+ action: Literal["search"],
483
+ params: "NotesSearchParams"
484
+ ) -> "SearchResult": ...
485
+
486
+ @overload
487
+ async def execute(
488
+ self,
489
+ entity: Literal["content_versions"],
490
+ action: Literal["list"],
491
+ params: "ContentVersionsListParams"
492
+ ) -> "ContentVersionQueryResult": ...
493
+
494
+ @overload
495
+ async def execute(
496
+ self,
497
+ entity: Literal["content_versions"],
498
+ action: Literal["get"],
499
+ params: "ContentVersionsGetParams"
500
+ ) -> "ContentVersion": ...
501
+
502
+ @overload
503
+ async def execute(
504
+ self,
505
+ entity: Literal["content_versions"],
506
+ action: Literal["download"],
507
+ params: "ContentVersionsDownloadParams"
508
+ ) -> "AsyncIterator[bytes]": ...
509
+
510
+ @overload
511
+ async def execute(
512
+ self,
513
+ entity: Literal["attachments"],
514
+ action: Literal["list"],
515
+ params: "AttachmentsListParams"
516
+ ) -> "AttachmentQueryResult": ...
517
+
518
+ @overload
519
+ async def execute(
520
+ self,
521
+ entity: Literal["attachments"],
522
+ action: Literal["get"],
523
+ params: "AttachmentsGetParams"
524
+ ) -> "Attachment": ...
525
+
526
+ @overload
527
+ async def execute(
528
+ self,
529
+ entity: Literal["attachments"],
530
+ action: Literal["download"],
531
+ params: "AttachmentsDownloadParams"
532
+ ) -> "AsyncIterator[bytes]": ...
533
+
534
+ @overload
535
+ async def execute(
536
+ self,
537
+ entity: Literal["query"],
538
+ action: Literal["list"],
539
+ params: "QueryListParams"
540
+ ) -> "QueryResult": ...
541
+
542
+
543
+ @overload
544
+ async def execute(
545
+ self,
546
+ entity: str,
547
+ action: str,
548
+ params: dict[str, Any]
549
+ ) -> SalesforceExecuteResult[Any] | SalesforceExecuteResultWithMeta[Any, Any] | Any: ...
550
+
551
+ async def execute(
552
+ self,
553
+ entity: str,
554
+ action: str,
555
+ params: dict[str, Any] | None = None
556
+ ) -> Any:
557
+ """
558
+ Execute an entity operation with full type safety.
559
+
560
+ This is the recommended interface for blessed connectors as it:
561
+ - Uses the same signature as non-blessed connectors
562
+ - Provides full IDE autocomplete for entity/action/params
563
+ - Makes migration from generic to blessed connectors seamless
564
+
565
+ Args:
566
+ entity: Entity name (e.g., "customers")
567
+ action: Operation action (e.g., "create", "get", "list")
568
+ params: Operation parameters (typed based on entity+action)
569
+
570
+ Returns:
571
+ Typed response based on the operation
572
+
573
+ Example:
574
+ customer = await connector.execute(
575
+ entity="customers",
576
+ action="get",
577
+ params={"id": "cus_123"}
578
+ )
579
+ """
580
+ from ._vendored.connector_sdk.executor import ExecutionConfig
581
+
582
+ # Remap parameter names from snake_case (TypedDict keys) to API parameter names
583
+ if params:
584
+ param_map = self._PARAM_MAP.get((entity, action), {})
585
+ if param_map:
586
+ params = {param_map.get(k, k): v for k, v in params.items()}
587
+
588
+ # Use ExecutionConfig for both local and hosted executors
589
+ config = ExecutionConfig(
590
+ entity=entity,
591
+ action=action,
592
+ params=params
593
+ )
594
+
595
+ result = await self._executor.execute(config)
596
+
597
+ if not result.success:
598
+ raise RuntimeError(f"Execution failed: {result.error}")
599
+
600
+ # Check if this operation has extractors configured
601
+ has_extractors = self._EXTRACTOR_MAP.get((entity, action), False)
602
+
603
+ if has_extractors:
604
+ # With extractors - return Pydantic envelope with data and meta
605
+ if result.meta is not None:
606
+ return SalesforceExecuteResultWithMeta[Any, Any](
607
+ data=result.data,
608
+ meta=result.meta
609
+ )
610
+ else:
611
+ return SalesforceExecuteResult[Any](data=result.data)
612
+ else:
613
+ # No extractors - return raw response data
614
+ return result.data
615
+
616
+
617
+
618
+ class AccountsQuery:
619
+ """
620
+ Query class for Accounts entity operations.
621
+ """
622
+
623
+ def __init__(self, connector: SalesforceConnector):
624
+ """Initialize query with connector reference."""
625
+ self._connector = connector
626
+
627
+ async def list(
628
+ self,
629
+ q: str,
630
+ **kwargs
631
+ ) -> AccountQueryResult:
632
+ """
633
+ Returns a list of accounts via SOQL query. Default returns up to 200 records.
634
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
635
+
636
+
637
+ Args:
638
+ q: SOQL query for accounts. Default returns up to 200 records.
639
+ To change the limit, provide your own query with a LIMIT clause.
640
+ Example: "SELECT FIELDS(STANDARD) FROM Account ORDER BY LastModifiedDate DESC LIMIT 50"
641
+
642
+ **kwargs: Additional parameters
643
+
644
+ Returns:
645
+ AccountQueryResult
646
+ """
647
+ params = {k: v for k, v in {
648
+ "q": q,
649
+ **kwargs
650
+ }.items() if v is not None}
651
+
652
+ result = await self._connector.execute("accounts", "list", params)
653
+ return result
654
+
655
+
656
+
657
+ async def get(
658
+ self,
659
+ id: str | None = None,
660
+ fields: str | None = None,
661
+ **kwargs
662
+ ) -> Account:
663
+ """
664
+ Get a single account by ID. Returns all accessible fields by default.
665
+ Use the `fields` parameter to retrieve only specific fields for better performance.
666
+
667
+
668
+ Args:
669
+ id: Salesforce Account ID (18-character ID starting with '001')
670
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
671
+ Example: "Id,Name,Industry,AnnualRevenue,Website"
672
+
673
+ **kwargs: Additional parameters
674
+
675
+ Returns:
676
+ Account
677
+ """
678
+ params = {k: v for k, v in {
679
+ "id": id,
680
+ "fields": fields,
681
+ **kwargs
682
+ }.items() if v is not None}
683
+
684
+ result = await self._connector.execute("accounts", "get", params)
685
+ return result
686
+
687
+
688
+
689
+ async def search(
690
+ self,
691
+ q: str,
692
+ **kwargs
693
+ ) -> SearchResult:
694
+ """
695
+ Search for accounts using SOSL (Salesforce Object Search Language).
696
+ SOSL is optimized for text-based searches across multiple fields and objects.
697
+ Use SOQL (list action) for structured queries with specific field conditions.
698
+
699
+
700
+ Args:
701
+ q: SOSL search query. Format: FIND {searchTerm} IN scope RETURNING Object(fields) [LIMIT n]
702
+ Examples:
703
+ - "FIND {Acme} IN ALL FIELDS RETURNING Account(Id,Name)"
704
+ - "FIND {tech*} IN NAME FIELDS RETURNING Account(Id,Name,Industry) LIMIT 50"
705
+ - "FIND {\"exact phrase\"} RETURNING Account(Id,Name,Website)"
706
+
707
+ **kwargs: Additional parameters
708
+
709
+ Returns:
710
+ SearchResult
711
+ """
712
+ params = {k: v for k, v in {
713
+ "q": q,
714
+ **kwargs
715
+ }.items() if v is not None}
716
+
717
+ result = await self._connector.execute("accounts", "search", params)
718
+ return result
719
+
720
+
721
+
722
+ class ContactsQuery:
723
+ """
724
+ Query class for Contacts entity operations.
725
+ """
726
+
727
+ def __init__(self, connector: SalesforceConnector):
728
+ """Initialize query with connector reference."""
729
+ self._connector = connector
730
+
731
+ async def list(
732
+ self,
733
+ q: str,
734
+ **kwargs
735
+ ) -> ContactQueryResult:
736
+ """
737
+ Returns a list of contacts via SOQL query. Default returns up to 200 records.
738
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
739
+
740
+
741
+ Args:
742
+ q: SOQL query for contacts. Default returns up to 200 records.
743
+ To change the limit, provide your own query with a LIMIT clause.
744
+ Example: "SELECT FIELDS(STANDARD) FROM Contact WHERE AccountId = '001xx...' LIMIT 50"
745
+
746
+ **kwargs: Additional parameters
747
+
748
+ Returns:
749
+ ContactQueryResult
750
+ """
751
+ params = {k: v for k, v in {
752
+ "q": q,
753
+ **kwargs
754
+ }.items() if v is not None}
755
+
756
+ result = await self._connector.execute("contacts", "list", params)
757
+ return result
758
+
759
+
760
+
761
+ async def get(
762
+ self,
763
+ id: str | None = None,
764
+ fields: str | None = None,
765
+ **kwargs
766
+ ) -> Contact:
767
+ """
768
+ Get a single contact by ID. Returns all accessible fields by default.
769
+ Use the `fields` parameter to retrieve only specific fields for better performance.
770
+
771
+
772
+ Args:
773
+ id: Salesforce Contact ID (18-character ID starting with '003')
774
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
775
+ Example: "Id,FirstName,LastName,Email,Phone,AccountId"
776
+
777
+ **kwargs: Additional parameters
778
+
779
+ Returns:
780
+ Contact
781
+ """
782
+ params = {k: v for k, v in {
783
+ "id": id,
784
+ "fields": fields,
785
+ **kwargs
786
+ }.items() if v is not None}
787
+
788
+ result = await self._connector.execute("contacts", "get", params)
789
+ return result
790
+
791
+
792
+
793
+ async def search(
794
+ self,
795
+ q: str,
796
+ **kwargs
797
+ ) -> SearchResult:
798
+ """
799
+ Search for contacts using SOSL (Salesforce Object Search Language).
800
+ SOSL is optimized for text-based searches across multiple fields.
801
+
802
+
803
+ Args:
804
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Contact(fields) [LIMIT n]
805
+ Examples:
806
+ - "FIND {John} IN NAME FIELDS RETURNING Contact(Id,FirstName,LastName,Email)"
807
+ - "FIND {*@example.com} IN EMAIL FIELDS RETURNING Contact(Id,Name,Email) LIMIT 25"
808
+
809
+ **kwargs: Additional parameters
810
+
811
+ Returns:
812
+ SearchResult
813
+ """
814
+ params = {k: v for k, v in {
815
+ "q": q,
816
+ **kwargs
817
+ }.items() if v is not None}
818
+
819
+ result = await self._connector.execute("contacts", "search", params)
820
+ return result
821
+
822
+
823
+
824
+ class LeadsQuery:
825
+ """
826
+ Query class for Leads entity operations.
827
+ """
828
+
829
+ def __init__(self, connector: SalesforceConnector):
830
+ """Initialize query with connector reference."""
831
+ self._connector = connector
832
+
833
+ async def list(
834
+ self,
835
+ q: str,
836
+ **kwargs
837
+ ) -> LeadQueryResult:
838
+ """
839
+ Returns a list of leads via SOQL query. Default returns up to 200 records.
840
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
841
+
842
+
843
+ Args:
844
+ q: SOQL query for leads. Default returns up to 200 records.
845
+ To change the limit, provide your own query with a LIMIT clause.
846
+ Example: "SELECT FIELDS(STANDARD) FROM Lead WHERE Status = 'Open' LIMIT 100"
847
+
848
+ **kwargs: Additional parameters
849
+
850
+ Returns:
851
+ LeadQueryResult
852
+ """
853
+ params = {k: v for k, v in {
854
+ "q": q,
855
+ **kwargs
856
+ }.items() if v is not None}
857
+
858
+ result = await self._connector.execute("leads", "list", params)
859
+ return result
860
+
861
+
862
+
863
+ async def get(
864
+ self,
865
+ id: str | None = None,
866
+ fields: str | None = None,
867
+ **kwargs
868
+ ) -> Lead:
869
+ """
870
+ Get a single lead by ID. Returns all accessible fields by default.
871
+ Use the `fields` parameter to retrieve only specific fields for better performance.
872
+
873
+
874
+ Args:
875
+ id: Salesforce Lead ID (18-character ID starting with '00Q')
876
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
877
+ Example: "Id,FirstName,LastName,Email,Company,Status,LeadSource"
878
+
879
+ **kwargs: Additional parameters
880
+
881
+ Returns:
882
+ Lead
883
+ """
884
+ params = {k: v for k, v in {
885
+ "id": id,
886
+ "fields": fields,
887
+ **kwargs
888
+ }.items() if v is not None}
889
+
890
+ result = await self._connector.execute("leads", "get", params)
891
+ return result
892
+
893
+
894
+
895
+ async def search(
896
+ self,
897
+ q: str,
898
+ **kwargs
899
+ ) -> SearchResult:
900
+ """
901
+ Search for leads using SOSL (Salesforce Object Search Language).
902
+ SOSL is optimized for text-based searches across multiple fields.
903
+
904
+
905
+ Args:
906
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Lead(fields) [LIMIT n]
907
+ Examples:
908
+ - "FIND {Smith} IN NAME FIELDS RETURNING Lead(Id,FirstName,LastName,Company,Status)"
909
+ - "FIND {marketing} IN ALL FIELDS RETURNING Lead(Id,Name,LeadSource) LIMIT 50"
910
+
911
+ **kwargs: Additional parameters
912
+
913
+ Returns:
914
+ SearchResult
915
+ """
916
+ params = {k: v for k, v in {
917
+ "q": q,
918
+ **kwargs
919
+ }.items() if v is not None}
920
+
921
+ result = await self._connector.execute("leads", "search", params)
922
+ return result
923
+
924
+
925
+
926
+ class OpportunitiesQuery:
927
+ """
928
+ Query class for Opportunities entity operations.
929
+ """
930
+
931
+ def __init__(self, connector: SalesforceConnector):
932
+ """Initialize query with connector reference."""
933
+ self._connector = connector
934
+
935
+ async def list(
936
+ self,
937
+ q: str,
938
+ **kwargs
939
+ ) -> OpportunityQueryResult:
940
+ """
941
+ Returns a list of opportunities via SOQL query. Default returns up to 200 records.
942
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
943
+
944
+
945
+ Args:
946
+ q: SOQL query for opportunities. Default returns up to 200 records.
947
+ To change the limit, provide your own query with a LIMIT clause.
948
+ Example: "SELECT FIELDS(STANDARD) FROM Opportunity WHERE StageName = 'Closed Won' LIMIT 50"
949
+
950
+ **kwargs: Additional parameters
951
+
952
+ Returns:
953
+ OpportunityQueryResult
954
+ """
955
+ params = {k: v for k, v in {
956
+ "q": q,
957
+ **kwargs
958
+ }.items() if v is not None}
959
+
960
+ result = await self._connector.execute("opportunities", "list", params)
961
+ return result
962
+
963
+
964
+
965
+ async def get(
966
+ self,
967
+ id: str | None = None,
968
+ fields: str | None = None,
969
+ **kwargs
970
+ ) -> Opportunity:
971
+ """
972
+ Get a single opportunity by ID. Returns all accessible fields by default.
973
+ Use the `fields` parameter to retrieve only specific fields for better performance.
974
+
975
+
976
+ Args:
977
+ id: Salesforce Opportunity ID (18-character ID starting with '006')
978
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
979
+ Example: "Id,Name,Amount,StageName,CloseDate,AccountId"
980
+
981
+ **kwargs: Additional parameters
982
+
983
+ Returns:
984
+ Opportunity
985
+ """
986
+ params = {k: v for k, v in {
987
+ "id": id,
988
+ "fields": fields,
989
+ **kwargs
990
+ }.items() if v is not None}
991
+
992
+ result = await self._connector.execute("opportunities", "get", params)
993
+ return result
994
+
995
+
996
+
997
+ async def search(
998
+ self,
999
+ q: str,
1000
+ **kwargs
1001
+ ) -> SearchResult:
1002
+ """
1003
+ Search for opportunities using SOSL (Salesforce Object Search Language).
1004
+ SOSL is optimized for text-based searches across multiple fields.
1005
+
1006
+
1007
+ Args:
1008
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Opportunity(fields) [LIMIT n]
1009
+ Examples:
1010
+ - "FIND {Enterprise} IN NAME FIELDS RETURNING Opportunity(Id,Name,Amount,StageName)"
1011
+ - "FIND {renewal} IN ALL FIELDS RETURNING Opportunity(Id,Name,CloseDate) LIMIT 25"
1012
+
1013
+ **kwargs: Additional parameters
1014
+
1015
+ Returns:
1016
+ SearchResult
1017
+ """
1018
+ params = {k: v for k, v in {
1019
+ "q": q,
1020
+ **kwargs
1021
+ }.items() if v is not None}
1022
+
1023
+ result = await self._connector.execute("opportunities", "search", params)
1024
+ return result
1025
+
1026
+
1027
+
1028
+ class TasksQuery:
1029
+ """
1030
+ Query class for Tasks entity operations.
1031
+ """
1032
+
1033
+ def __init__(self, connector: SalesforceConnector):
1034
+ """Initialize query with connector reference."""
1035
+ self._connector = connector
1036
+
1037
+ async def list(
1038
+ self,
1039
+ q: str,
1040
+ **kwargs
1041
+ ) -> TaskQueryResult:
1042
+ """
1043
+ Returns a list of tasks via SOQL query. Default returns up to 200 records.
1044
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1045
+
1046
+
1047
+ Args:
1048
+ q: SOQL query for tasks. Default returns up to 200 records.
1049
+ To change the limit, provide your own query with a LIMIT clause.
1050
+ Example: "SELECT FIELDS(STANDARD) FROM Task WHERE Status = 'Not Started' LIMIT 100"
1051
+
1052
+ **kwargs: Additional parameters
1053
+
1054
+ Returns:
1055
+ TaskQueryResult
1056
+ """
1057
+ params = {k: v for k, v in {
1058
+ "q": q,
1059
+ **kwargs
1060
+ }.items() if v is not None}
1061
+
1062
+ result = await self._connector.execute("tasks", "list", params)
1063
+ return result
1064
+
1065
+
1066
+
1067
+ async def get(
1068
+ self,
1069
+ id: str | None = None,
1070
+ fields: str | None = None,
1071
+ **kwargs
1072
+ ) -> Task:
1073
+ """
1074
+ Get a single task by ID. Returns all accessible fields by default.
1075
+ Use the `fields` parameter to retrieve only specific fields for better performance.
1076
+
1077
+
1078
+ Args:
1079
+ id: Salesforce Task ID (18-character ID starting with '00T')
1080
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1081
+ Example: "Id,Subject,Status,Priority,ActivityDate,WhoId,WhatId"
1082
+
1083
+ **kwargs: Additional parameters
1084
+
1085
+ Returns:
1086
+ Task
1087
+ """
1088
+ params = {k: v for k, v in {
1089
+ "id": id,
1090
+ "fields": fields,
1091
+ **kwargs
1092
+ }.items() if v is not None}
1093
+
1094
+ result = await self._connector.execute("tasks", "get", params)
1095
+ return result
1096
+
1097
+
1098
+
1099
+ async def search(
1100
+ self,
1101
+ q: str,
1102
+ **kwargs
1103
+ ) -> SearchResult:
1104
+ """
1105
+ Search for tasks using SOSL (Salesforce Object Search Language).
1106
+ SOSL is optimized for text-based searches across multiple fields.
1107
+
1108
+
1109
+ Args:
1110
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Task(fields) [LIMIT n]
1111
+ Examples:
1112
+ - "FIND {follow up} IN ALL FIELDS RETURNING Task(Id,Subject,Status,Priority)"
1113
+ - "FIND {call} IN NAME FIELDS RETURNING Task(Id,Subject,ActivityDate) LIMIT 50"
1114
+
1115
+ **kwargs: Additional parameters
1116
+
1117
+ Returns:
1118
+ SearchResult
1119
+ """
1120
+ params = {k: v for k, v in {
1121
+ "q": q,
1122
+ **kwargs
1123
+ }.items() if v is not None}
1124
+
1125
+ result = await self._connector.execute("tasks", "search", params)
1126
+ return result
1127
+
1128
+
1129
+
1130
+ class EventsQuery:
1131
+ """
1132
+ Query class for Events entity operations.
1133
+ """
1134
+
1135
+ def __init__(self, connector: SalesforceConnector):
1136
+ """Initialize query with connector reference."""
1137
+ self._connector = connector
1138
+
1139
+ async def list(
1140
+ self,
1141
+ q: str,
1142
+ **kwargs
1143
+ ) -> EventQueryResult:
1144
+ """
1145
+ Returns a list of events via SOQL query. Default returns up to 200 records.
1146
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1147
+
1148
+
1149
+ Args:
1150
+ q: SOQL query for events. Default returns up to 200 records.
1151
+ To change the limit, provide your own query with a LIMIT clause.
1152
+ Example: "SELECT FIELDS(STANDARD) FROM Event WHERE StartDateTime > TODAY LIMIT 50"
1153
+
1154
+ **kwargs: Additional parameters
1155
+
1156
+ Returns:
1157
+ EventQueryResult
1158
+ """
1159
+ params = {k: v for k, v in {
1160
+ "q": q,
1161
+ **kwargs
1162
+ }.items() if v is not None}
1163
+
1164
+ result = await self._connector.execute("events", "list", params)
1165
+ return result
1166
+
1167
+
1168
+
1169
+ async def get(
1170
+ self,
1171
+ id: str | None = None,
1172
+ fields: str | None = None,
1173
+ **kwargs
1174
+ ) -> Event:
1175
+ """
1176
+ Get a single event by ID. Returns all accessible fields by default.
1177
+ Use the `fields` parameter to retrieve only specific fields for better performance.
1178
+
1179
+
1180
+ Args:
1181
+ id: Salesforce Event ID (18-character ID starting with '00U')
1182
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1183
+ Example: "Id,Subject,StartDateTime,EndDateTime,Location,WhoId,WhatId"
1184
+
1185
+ **kwargs: Additional parameters
1186
+
1187
+ Returns:
1188
+ Event
1189
+ """
1190
+ params = {k: v for k, v in {
1191
+ "id": id,
1192
+ "fields": fields,
1193
+ **kwargs
1194
+ }.items() if v is not None}
1195
+
1196
+ result = await self._connector.execute("events", "get", params)
1197
+ return result
1198
+
1199
+
1200
+
1201
+ async def search(
1202
+ self,
1203
+ q: str,
1204
+ **kwargs
1205
+ ) -> SearchResult:
1206
+ """
1207
+ Search for events using SOSL (Salesforce Object Search Language).
1208
+ SOSL is optimized for text-based searches across multiple fields.
1209
+
1210
+
1211
+ Args:
1212
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Event(fields) [LIMIT n]
1213
+ Examples:
1214
+ - "FIND {meeting} IN ALL FIELDS RETURNING Event(Id,Subject,StartDateTime,Location)"
1215
+ - "FIND {demo} IN NAME FIELDS RETURNING Event(Id,Subject,EndDateTime) LIMIT 25"
1216
+
1217
+ **kwargs: Additional parameters
1218
+
1219
+ Returns:
1220
+ SearchResult
1221
+ """
1222
+ params = {k: v for k, v in {
1223
+ "q": q,
1224
+ **kwargs
1225
+ }.items() if v is not None}
1226
+
1227
+ result = await self._connector.execute("events", "search", params)
1228
+ return result
1229
+
1230
+
1231
+
1232
+ class CampaignsQuery:
1233
+ """
1234
+ Query class for Campaigns entity operations.
1235
+ """
1236
+
1237
+ def __init__(self, connector: SalesforceConnector):
1238
+ """Initialize query with connector reference."""
1239
+ self._connector = connector
1240
+
1241
+ async def list(
1242
+ self,
1243
+ q: str,
1244
+ **kwargs
1245
+ ) -> CampaignQueryResult:
1246
+ """
1247
+ Returns a list of campaigns via SOQL query. Default returns up to 200 records.
1248
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1249
+
1250
+
1251
+ Args:
1252
+ q: SOQL query for campaigns. Default returns up to 200 records.
1253
+ To change the limit, provide your own query with a LIMIT clause.
1254
+ Example: "SELECT FIELDS(STANDARD) FROM Campaign WHERE IsActive = true LIMIT 50"
1255
+
1256
+ **kwargs: Additional parameters
1257
+
1258
+ Returns:
1259
+ CampaignQueryResult
1260
+ """
1261
+ params = {k: v for k, v in {
1262
+ "q": q,
1263
+ **kwargs
1264
+ }.items() if v is not None}
1265
+
1266
+ result = await self._connector.execute("campaigns", "list", params)
1267
+ return result
1268
+
1269
+
1270
+
1271
+ async def get(
1272
+ self,
1273
+ id: str | None = None,
1274
+ fields: str | None = None,
1275
+ **kwargs
1276
+ ) -> Campaign:
1277
+ """
1278
+ Get a single campaign by ID. Returns all accessible fields by default.
1279
+ Use the `fields` parameter to retrieve only specific fields for better performance.
1280
+
1281
+
1282
+ Args:
1283
+ id: Salesforce Campaign ID (18-character ID starting with '701')
1284
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1285
+ Example: "Id,Name,Type,Status,StartDate,EndDate,IsActive"
1286
+
1287
+ **kwargs: Additional parameters
1288
+
1289
+ Returns:
1290
+ Campaign
1291
+ """
1292
+ params = {k: v for k, v in {
1293
+ "id": id,
1294
+ "fields": fields,
1295
+ **kwargs
1296
+ }.items() if v is not None}
1297
+
1298
+ result = await self._connector.execute("campaigns", "get", params)
1299
+ return result
1300
+
1301
+
1302
+
1303
+ async def search(
1304
+ self,
1305
+ q: str,
1306
+ **kwargs
1307
+ ) -> SearchResult:
1308
+ """
1309
+ Search for campaigns using SOSL (Salesforce Object Search Language).
1310
+ SOSL is optimized for text-based searches across multiple fields.
1311
+
1312
+
1313
+ Args:
1314
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Campaign(fields) [LIMIT n]
1315
+ Examples:
1316
+ - "FIND {webinar} IN ALL FIELDS RETURNING Campaign(Id,Name,Type,Status)"
1317
+ - "FIND {2024} IN NAME FIELDS RETURNING Campaign(Id,Name,StartDate,IsActive) LIMIT 50"
1318
+
1319
+ **kwargs: Additional parameters
1320
+
1321
+ Returns:
1322
+ SearchResult
1323
+ """
1324
+ params = {k: v for k, v in {
1325
+ "q": q,
1326
+ **kwargs
1327
+ }.items() if v is not None}
1328
+
1329
+ result = await self._connector.execute("campaigns", "search", params)
1330
+ return result
1331
+
1332
+
1333
+
1334
+ class CasesQuery:
1335
+ """
1336
+ Query class for Cases entity operations.
1337
+ """
1338
+
1339
+ def __init__(self, connector: SalesforceConnector):
1340
+ """Initialize query with connector reference."""
1341
+ self._connector = connector
1342
+
1343
+ async def list(
1344
+ self,
1345
+ q: str,
1346
+ **kwargs
1347
+ ) -> CaseQueryResult:
1348
+ """
1349
+ Returns a list of cases via SOQL query. Default returns up to 200 records.
1350
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1351
+
1352
+
1353
+ Args:
1354
+ q: SOQL query for cases. Default returns up to 200 records.
1355
+ To change the limit, provide your own query with a LIMIT clause.
1356
+ Example: "SELECT FIELDS(STANDARD) FROM Case WHERE Status = 'New' LIMIT 100"
1357
+
1358
+ **kwargs: Additional parameters
1359
+
1360
+ Returns:
1361
+ CaseQueryResult
1362
+ """
1363
+ params = {k: v for k, v in {
1364
+ "q": q,
1365
+ **kwargs
1366
+ }.items() if v is not None}
1367
+
1368
+ result = await self._connector.execute("cases", "list", params)
1369
+ return result
1370
+
1371
+
1372
+
1373
+ async def get(
1374
+ self,
1375
+ id: str | None = None,
1376
+ fields: str | None = None,
1377
+ **kwargs
1378
+ ) -> Case:
1379
+ """
1380
+ Get a single case by ID. Returns all accessible fields by default.
1381
+ Use the `fields` parameter to retrieve only specific fields for better performance.
1382
+
1383
+
1384
+ Args:
1385
+ id: Salesforce Case ID (18-character ID starting with '500')
1386
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1387
+ Example: "Id,CaseNumber,Subject,Status,Priority,ContactId,AccountId"
1388
+
1389
+ **kwargs: Additional parameters
1390
+
1391
+ Returns:
1392
+ Case
1393
+ """
1394
+ params = {k: v for k, v in {
1395
+ "id": id,
1396
+ "fields": fields,
1397
+ **kwargs
1398
+ }.items() if v is not None}
1399
+
1400
+ result = await self._connector.execute("cases", "get", params)
1401
+ return result
1402
+
1403
+
1404
+
1405
+ async def search(
1406
+ self,
1407
+ q: str,
1408
+ **kwargs
1409
+ ) -> SearchResult:
1410
+ """
1411
+ Search for cases using SOSL (Salesforce Object Search Language).
1412
+ SOSL is optimized for text-based searches across multiple fields.
1413
+
1414
+
1415
+ Args:
1416
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Case(fields) [LIMIT n]
1417
+ Examples:
1418
+ - "FIND {login issue} IN ALL FIELDS RETURNING Case(Id,CaseNumber,Subject,Status)"
1419
+ - "FIND {urgent} IN NAME FIELDS RETURNING Case(Id,Subject,Priority) LIMIT 25"
1420
+
1421
+ **kwargs: Additional parameters
1422
+
1423
+ Returns:
1424
+ SearchResult
1425
+ """
1426
+ params = {k: v for k, v in {
1427
+ "q": q,
1428
+ **kwargs
1429
+ }.items() if v is not None}
1430
+
1431
+ result = await self._connector.execute("cases", "search", params)
1432
+ return result
1433
+
1434
+
1435
+
1436
+ class NotesQuery:
1437
+ """
1438
+ Query class for Notes entity operations.
1439
+ """
1440
+
1441
+ def __init__(self, connector: SalesforceConnector):
1442
+ """Initialize query with connector reference."""
1443
+ self._connector = connector
1444
+
1445
+ async def list(
1446
+ self,
1447
+ q: str,
1448
+ **kwargs
1449
+ ) -> NoteQueryResult:
1450
+ """
1451
+ Returns a list of notes via SOQL query. Default returns up to 200 records.
1452
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1453
+
1454
+
1455
+ Args:
1456
+ q: SOQL query for notes. Default returns up to 200 records.
1457
+ To change the limit, provide your own query with a LIMIT clause.
1458
+ Example: "SELECT FIELDS(STANDARD) FROM Note WHERE ParentId = '001xx...' LIMIT 50"
1459
+
1460
+ **kwargs: Additional parameters
1461
+
1462
+ Returns:
1463
+ NoteQueryResult
1464
+ """
1465
+ params = {k: v for k, v in {
1466
+ "q": q,
1467
+ **kwargs
1468
+ }.items() if v is not None}
1469
+
1470
+ result = await self._connector.execute("notes", "list", params)
1471
+ return result
1472
+
1473
+
1474
+
1475
+ async def get(
1476
+ self,
1477
+ id: str | None = None,
1478
+ fields: str | None = None,
1479
+ **kwargs
1480
+ ) -> Note:
1481
+ """
1482
+ Get a single note by ID. Returns all accessible fields by default.
1483
+ Use the `fields` parameter to retrieve only specific fields for better performance.
1484
+
1485
+
1486
+ Args:
1487
+ id: Salesforce Note ID (18-character ID starting with '002')
1488
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1489
+ Example: "Id,Title,Body,ParentId,OwnerId"
1490
+
1491
+ **kwargs: Additional parameters
1492
+
1493
+ Returns:
1494
+ Note
1495
+ """
1496
+ params = {k: v for k, v in {
1497
+ "id": id,
1498
+ "fields": fields,
1499
+ **kwargs
1500
+ }.items() if v is not None}
1501
+
1502
+ result = await self._connector.execute("notes", "get", params)
1503
+ return result
1504
+
1505
+
1506
+
1507
+ async def search(
1508
+ self,
1509
+ q: str,
1510
+ **kwargs
1511
+ ) -> SearchResult:
1512
+ """
1513
+ Search for notes using SOSL (Salesforce Object Search Language).
1514
+ SOSL is optimized for text-based searches across multiple fields.
1515
+
1516
+
1517
+ Args:
1518
+ q: SOSL search query. Format: FIND {searchTerm} RETURNING Note(fields) [LIMIT n]
1519
+ Examples:
1520
+ - "FIND {important} IN ALL FIELDS RETURNING Note(Id,Title,ParentId)"
1521
+ - "FIND {action items} IN NAME FIELDS RETURNING Note(Id,Title,Body) LIMIT 50"
1522
+
1523
+ **kwargs: Additional parameters
1524
+
1525
+ Returns:
1526
+ SearchResult
1527
+ """
1528
+ params = {k: v for k, v in {
1529
+ "q": q,
1530
+ **kwargs
1531
+ }.items() if v is not None}
1532
+
1533
+ result = await self._connector.execute("notes", "search", params)
1534
+ return result
1535
+
1536
+
1537
+
1538
+ class ContentVersionsQuery:
1539
+ """
1540
+ Query class for ContentVersions entity operations.
1541
+ """
1542
+
1543
+ def __init__(self, connector: SalesforceConnector):
1544
+ """Initialize query with connector reference."""
1545
+ self._connector = connector
1546
+
1547
+ async def list(
1548
+ self,
1549
+ q: str,
1550
+ **kwargs
1551
+ ) -> ContentVersionQueryResult:
1552
+ """
1553
+ Returns a list of content versions (file metadata) via SOQL query. Default returns up to 200 records.
1554
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1555
+ Note: ContentVersion does not support FIELDS(STANDARD), so specific fields must be listed.
1556
+
1557
+
1558
+ Args:
1559
+ q: SOQL query for content versions. Default returns up to 200 records.
1560
+ To change the limit, provide your own query with a LIMIT clause.
1561
+ Example: "SELECT Id, Title, FileExtension, ContentSize FROM ContentVersion WHERE IsLatest = true LIMIT 50"
1562
+
1563
+ **kwargs: Additional parameters
1564
+
1565
+ Returns:
1566
+ ContentVersionQueryResult
1567
+ """
1568
+ params = {k: v for k, v in {
1569
+ "q": q,
1570
+ **kwargs
1571
+ }.items() if v is not None}
1572
+
1573
+ result = await self._connector.execute("content_versions", "list", params)
1574
+ return result
1575
+
1576
+
1577
+
1578
+ async def get(
1579
+ self,
1580
+ id: str | None = None,
1581
+ fields: str | None = None,
1582
+ **kwargs
1583
+ ) -> ContentVersion:
1584
+ """
1585
+ Get a single content version's metadata by ID. Returns file metadata, not the file content.
1586
+ Use the download action to retrieve the actual file binary.
1587
+
1588
+
1589
+ Args:
1590
+ id: Salesforce ContentVersion ID (18-character ID starting with '068')
1591
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1592
+ Example: "Id,Title,FileExtension,ContentSize,ContentDocumentId,IsLatest"
1593
+
1594
+ **kwargs: Additional parameters
1595
+
1596
+ Returns:
1597
+ ContentVersion
1598
+ """
1599
+ params = {k: v for k, v in {
1600
+ "id": id,
1601
+ "fields": fields,
1602
+ **kwargs
1603
+ }.items() if v is not None}
1604
+
1605
+ result = await self._connector.execute("content_versions", "get", params)
1606
+ return result
1607
+
1608
+
1609
+
1610
+ async def download(
1611
+ self,
1612
+ id: str | None = None,
1613
+ range_header: str | None = None,
1614
+ **kwargs
1615
+ ) -> AsyncIterator[bytes]:
1616
+ """
1617
+ Downloads the binary file content of a content version.
1618
+ First use the list or get action to retrieve the ContentVersion ID and file metadata (size, type, etc.),
1619
+ then use this action to download the actual file content.
1620
+ The response is the raw binary file data.
1621
+
1622
+
1623
+ Args:
1624
+ id: Salesforce ContentVersion ID (18-character ID starting with '068').
1625
+ Obtain this ID from the list or get action.
1626
+
1627
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1628
+ **kwargs: Additional parameters
1629
+
1630
+ Returns:
1631
+ AsyncIterator[bytes]
1632
+ """
1633
+ params = {k: v for k, v in {
1634
+ "id": id,
1635
+ "range_header": range_header,
1636
+ **kwargs
1637
+ }.items() if v is not None}
1638
+
1639
+ result = await self._connector.execute("content_versions", "download", params)
1640
+ return result
1641
+
1642
+
1643
+ async def download_local(
1644
+ self,
1645
+ path: str,
1646
+ id: str | None = None,
1647
+ range_header: str | None = None,
1648
+ **kwargs
1649
+ ) -> Path:
1650
+ """
1651
+ Downloads the binary file content of a content version.
1652
+ First use the list or get action to retrieve the ContentVersion ID and file metadata (size, type, etc.),
1653
+ then use this action to download the actual file content.
1654
+ The response is the raw binary file data.
1655
+ and save to file.
1656
+
1657
+ Args:
1658
+ id: Salesforce ContentVersion ID (18-character ID starting with '068').
1659
+ Obtain this ID from the list or get action.
1660
+
1661
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1662
+ path: File path to save downloaded content
1663
+ **kwargs: Additional parameters
1664
+
1665
+ Returns:
1666
+ str: Path to the downloaded file
1667
+ """
1668
+ from ._vendored.connector_sdk import save_download
1669
+
1670
+ # Get the async iterator
1671
+ content_iterator = await self.download(
1672
+ id=id,
1673
+ range_header=range_header,
1674
+ **kwargs
1675
+ )
1676
+
1677
+ return await save_download(content_iterator, path)
1678
+
1679
+
1680
+ class AttachmentsQuery:
1681
+ """
1682
+ Query class for Attachments entity operations.
1683
+ """
1684
+
1685
+ def __init__(self, connector: SalesforceConnector):
1686
+ """Initialize query with connector reference."""
1687
+ self._connector = connector
1688
+
1689
+ async def list(
1690
+ self,
1691
+ q: str,
1692
+ **kwargs
1693
+ ) -> AttachmentQueryResult:
1694
+ """
1695
+ Returns a list of attachments (legacy) via SOQL query. Default returns up to 200 records.
1696
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1697
+ Note: Attachments are a legacy feature; consider using ContentVersion (Salesforce Files) for new implementations.
1698
+
1699
+
1700
+ Args:
1701
+ q: SOQL query for attachments. Default returns up to 200 records.
1702
+ To change the limit, provide your own query with a LIMIT clause.
1703
+ Example: "SELECT Id, Name, ContentType, BodyLength, ParentId FROM Attachment WHERE ParentId = '001xx...' LIMIT 50"
1704
+
1705
+ **kwargs: Additional parameters
1706
+
1707
+ Returns:
1708
+ AttachmentQueryResult
1709
+ """
1710
+ params = {k: v for k, v in {
1711
+ "q": q,
1712
+ **kwargs
1713
+ }.items() if v is not None}
1714
+
1715
+ result = await self._connector.execute("attachments", "list", params)
1716
+ return result
1717
+
1718
+
1719
+
1720
+ async def get(
1721
+ self,
1722
+ id: str | None = None,
1723
+ fields: str | None = None,
1724
+ **kwargs
1725
+ ) -> Attachment:
1726
+ """
1727
+ Get a single attachment's metadata by ID. Returns file metadata, not the file content.
1728
+ Use the download action to retrieve the actual file binary.
1729
+ Note: Attachments are a legacy feature; consider using ContentVersion for new implementations.
1730
+
1731
+
1732
+ Args:
1733
+ id: Salesforce Attachment ID (18-character ID starting with '00P')
1734
+ fields: Comma-separated list of fields to retrieve. If omitted, returns all accessible fields.
1735
+ Example: "Id,Name,ContentType,BodyLength,ParentId"
1736
+
1737
+ **kwargs: Additional parameters
1738
+
1739
+ Returns:
1740
+ Attachment
1741
+ """
1742
+ params = {k: v for k, v in {
1743
+ "id": id,
1744
+ "fields": fields,
1745
+ **kwargs
1746
+ }.items() if v is not None}
1747
+
1748
+ result = await self._connector.execute("attachments", "get", params)
1749
+ return result
1750
+
1751
+
1752
+
1753
+ async def download(
1754
+ self,
1755
+ id: str | None = None,
1756
+ range_header: str | None = None,
1757
+ **kwargs
1758
+ ) -> AsyncIterator[bytes]:
1759
+ """
1760
+ Downloads the binary file content of an attachment (legacy).
1761
+ First use the list or get action to retrieve the Attachment ID and file metadata,
1762
+ then use this action to download the actual file content.
1763
+ Note: Attachments are a legacy feature; consider using ContentVersion for new implementations.
1764
+
1765
+
1766
+ Args:
1767
+ id: Salesforce Attachment ID (18-character ID starting with '00P').
1768
+ Obtain this ID from the list or get action.
1769
+
1770
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1771
+ **kwargs: Additional parameters
1772
+
1773
+ Returns:
1774
+ AsyncIterator[bytes]
1775
+ """
1776
+ params = {k: v for k, v in {
1777
+ "id": id,
1778
+ "range_header": range_header,
1779
+ **kwargs
1780
+ }.items() if v is not None}
1781
+
1782
+ result = await self._connector.execute("attachments", "download", params)
1783
+ return result
1784
+
1785
+
1786
+ async def download_local(
1787
+ self,
1788
+ path: str,
1789
+ id: str | None = None,
1790
+ range_header: str | None = None,
1791
+ **kwargs
1792
+ ) -> Path:
1793
+ """
1794
+ Downloads the binary file content of an attachment (legacy).
1795
+ First use the list or get action to retrieve the Attachment ID and file metadata,
1796
+ then use this action to download the actual file content.
1797
+ Note: Attachments are a legacy feature; consider using ContentVersion for new implementations.
1798
+ and save to file.
1799
+
1800
+ Args:
1801
+ id: Salesforce Attachment ID (18-character ID starting with '00P').
1802
+ Obtain this ID from the list or get action.
1803
+
1804
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1805
+ path: File path to save downloaded content
1806
+ **kwargs: Additional parameters
1807
+
1808
+ Returns:
1809
+ str: Path to the downloaded file
1810
+ """
1811
+ from ._vendored.connector_sdk import save_download
1812
+
1813
+ # Get the async iterator
1814
+ content_iterator = await self.download(
1815
+ id=id,
1816
+ range_header=range_header,
1817
+ **kwargs
1818
+ )
1819
+
1820
+ return await save_download(content_iterator, path)
1821
+
1822
+
1823
+ class QueryQuery:
1824
+ """
1825
+ Query class for Query entity operations.
1826
+ """
1827
+
1828
+ def __init__(self, connector: SalesforceConnector):
1829
+ """Initialize query with connector reference."""
1830
+ self._connector = connector
1831
+
1832
+ async def list(
1833
+ self,
1834
+ q: str,
1835
+ **kwargs
1836
+ ) -> QueryResult:
1837
+ """
1838
+ Execute a custom SOQL query and return results. Use this for querying any Salesforce object.
1839
+ For pagination, check the response: if `done` is false, use `nextRecordsUrl` to fetch the next page.
1840
+
1841
+
1842
+ Args:
1843
+ q: SOQL query string. Include LIMIT clause to control the number of records returned.
1844
+ Examples:
1845
+ - "SELECT Id, Name FROM Account LIMIT 100"
1846
+ - "SELECT FIELDS(STANDARD) FROM Contact WHERE AccountId = '001xx...' LIMIT 50"
1847
+ - "SELECT Id, Subject, Status FROM Case WHERE CreatedDate = TODAY"
1848
+
1849
+ **kwargs: Additional parameters
1850
+
1851
+ Returns:
1852
+ QueryResult
1853
+ """
1854
+ params = {k: v for k, v in {
1855
+ "q": q,
1856
+ **kwargs
1857
+ }.items() if v is not None}
1858
+
1859
+ result = await self._connector.execute("query", "list", params)
1860
+ return result
1861
+
1862
+