airbyte-agent-asana 0.19.27__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 airbyte-agent-asana might be problematic. Click here for more details.

Files changed (55) hide show
  1. airbyte_agent_asana/__init__.py +145 -0
  2. airbyte_agent_asana/_vendored/__init__.py +1 -0
  3. airbyte_agent_asana/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_asana/_vendored/connector_sdk/auth_strategies.py +1123 -0
  5. airbyte_agent_asana/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_asana/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_asana/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_asana/_vendored/connector_sdk/connector_model_loader.py +957 -0
  9. airbyte_agent_asana/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_asana/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_asana/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_asana/_vendored/connector_sdk/executor/hosted_executor.py +197 -0
  13. airbyte_agent_asana/_vendored/connector_sdk/executor/local_executor.py +1504 -0
  14. airbyte_agent_asana/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_asana/_vendored/connector_sdk/extensions.py +655 -0
  16. airbyte_agent_asana/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_asana/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_asana/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_asana/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_asana/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_asana/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_asana/_vendored/connector_sdk/http/response.py +102 -0
  23. airbyte_agent_asana/_vendored/connector_sdk/http_client.py +686 -0
  24. airbyte_agent_asana/_vendored/connector_sdk/logging/__init__.py +11 -0
  25. airbyte_agent_asana/_vendored/connector_sdk/logging/logger.py +264 -0
  26. airbyte_agent_asana/_vendored/connector_sdk/logging/types.py +92 -0
  27. airbyte_agent_asana/_vendored/connector_sdk/observability/__init__.py +11 -0
  28. airbyte_agent_asana/_vendored/connector_sdk/observability/models.py +19 -0
  29. airbyte_agent_asana/_vendored/connector_sdk/observability/redactor.py +81 -0
  30. airbyte_agent_asana/_vendored/connector_sdk/observability/session.py +94 -0
  31. airbyte_agent_asana/_vendored/connector_sdk/performance/__init__.py +6 -0
  32. airbyte_agent_asana/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  33. airbyte_agent_asana/_vendored/connector_sdk/performance/metrics.py +93 -0
  34. airbyte_agent_asana/_vendored/connector_sdk/schema/__init__.py +75 -0
  35. airbyte_agent_asana/_vendored/connector_sdk/schema/base.py +161 -0
  36. airbyte_agent_asana/_vendored/connector_sdk/schema/components.py +238 -0
  37. airbyte_agent_asana/_vendored/connector_sdk/schema/connector.py +131 -0
  38. airbyte_agent_asana/_vendored/connector_sdk/schema/extensions.py +109 -0
  39. airbyte_agent_asana/_vendored/connector_sdk/schema/operations.py +146 -0
  40. airbyte_agent_asana/_vendored/connector_sdk/schema/security.py +213 -0
  41. airbyte_agent_asana/_vendored/connector_sdk/secrets.py +182 -0
  42. airbyte_agent_asana/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  43. airbyte_agent_asana/_vendored/connector_sdk/telemetry/config.py +32 -0
  44. airbyte_agent_asana/_vendored/connector_sdk/telemetry/events.py +58 -0
  45. airbyte_agent_asana/_vendored/connector_sdk/telemetry/tracker.py +151 -0
  46. airbyte_agent_asana/_vendored/connector_sdk/types.py +241 -0
  47. airbyte_agent_asana/_vendored/connector_sdk/utils.py +60 -0
  48. airbyte_agent_asana/_vendored/connector_sdk/validation.py +822 -0
  49. airbyte_agent_asana/connector.py +1770 -0
  50. airbyte_agent_asana/connector_model.py +1863 -0
  51. airbyte_agent_asana/models.py +744 -0
  52. airbyte_agent_asana/types.py +195 -0
  53. airbyte_agent_asana-0.19.27.dist-info/METADATA +144 -0
  54. airbyte_agent_asana-0.19.27.dist-info/RECORD +55 -0
  55. airbyte_agent_asana-0.19.27.dist-info/WHEEL +4 -0
@@ -0,0 +1,1770 @@
1
+ """
2
+ asana 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 AsanaConnectorModel
14
+
15
+ from .types import (
16
+ AttachmentsDownloadParams,
17
+ AttachmentsGetParams,
18
+ AttachmentsListParams,
19
+ ProjectSectionsListParams,
20
+ ProjectTasksListParams,
21
+ ProjectsGetParams,
22
+ ProjectsListParams,
23
+ SectionsGetParams,
24
+ TagsGetParams,
25
+ TaskDependenciesListParams,
26
+ TaskDependentsListParams,
27
+ TaskProjectsListParams,
28
+ TaskSubtasksListParams,
29
+ TasksGetParams,
30
+ TasksListParams,
31
+ TeamProjectsListParams,
32
+ TeamUsersListParams,
33
+ TeamsGetParams,
34
+ UserTeamsListParams,
35
+ UsersGetParams,
36
+ UsersListParams,
37
+ WorkspaceProjectsListParams,
38
+ WorkspaceTagsListParams,
39
+ WorkspaceTaskSearchListParams,
40
+ WorkspaceTeamsListParams,
41
+ WorkspaceUsersListParams,
42
+ WorkspacesGetParams,
43
+ WorkspacesListParams,
44
+ )
45
+
46
+ if TYPE_CHECKING:
47
+ from .models import AsanaAuthConfig
48
+ # Import specific auth config classes for multi-auth isinstance checks
49
+ from .models import AsanaOauth2AuthConfig, AsanaPersonalAccessTokenAuthConfig
50
+ # Import response models and envelope models at runtime
51
+ from .models import (
52
+ AsanaExecuteResult,
53
+ AsanaExecuteResultWithMeta,
54
+ TasksListResult,
55
+ ProjectTasksListResult,
56
+ TasksGetResult,
57
+ WorkspaceTaskSearchListResult,
58
+ ProjectsListResult,
59
+ ProjectsGetResult,
60
+ TaskProjectsListResult,
61
+ TeamProjectsListResult,
62
+ WorkspaceProjectsListResult,
63
+ WorkspacesListResult,
64
+ WorkspacesGetResult,
65
+ UsersListResult,
66
+ UsersGetResult,
67
+ WorkspaceUsersListResult,
68
+ TeamUsersListResult,
69
+ TeamsGetResult,
70
+ WorkspaceTeamsListResult,
71
+ UserTeamsListResult,
72
+ AttachmentsListResult,
73
+ AttachmentsGetResult,
74
+ WorkspaceTagsListResult,
75
+ TagsGetResult,
76
+ ProjectSectionsListResult,
77
+ SectionsGetResult,
78
+ TaskSubtasksListResult,
79
+ TaskDependenciesListResult,
80
+ TaskDependentsListResult,
81
+ )
82
+
83
+
84
+ class AsanaConnector:
85
+ """
86
+ Type-safe Asana API connector.
87
+
88
+ Auto-generated from OpenAPI specification with full type safety.
89
+ """
90
+
91
+ connector_name = "asana"
92
+ connector_version = "0.1.6"
93
+ vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
94
+
95
+ # Map of (entity, action) -> has_extractors for envelope wrapping decision
96
+ _EXTRACTOR_MAP = {
97
+ ("tasks", "list"): True,
98
+ ("project_tasks", "list"): True,
99
+ ("tasks", "get"): True,
100
+ ("workspace_task_search", "list"): True,
101
+ ("projects", "list"): True,
102
+ ("projects", "get"): True,
103
+ ("task_projects", "list"): True,
104
+ ("team_projects", "list"): True,
105
+ ("workspace_projects", "list"): True,
106
+ ("workspaces", "list"): True,
107
+ ("workspaces", "get"): True,
108
+ ("users", "list"): True,
109
+ ("users", "get"): True,
110
+ ("workspace_users", "list"): True,
111
+ ("team_users", "list"): True,
112
+ ("teams", "get"): True,
113
+ ("workspace_teams", "list"): True,
114
+ ("user_teams", "list"): True,
115
+ ("attachments", "list"): True,
116
+ ("attachments", "get"): True,
117
+ ("attachments", "download"): False,
118
+ ("workspace_tags", "list"): True,
119
+ ("tags", "get"): True,
120
+ ("project_sections", "list"): True,
121
+ ("sections", "get"): True,
122
+ ("task_subtasks", "list"): True,
123
+ ("task_dependencies", "list"): True,
124
+ ("task_dependents", "list"): True,
125
+ }
126
+
127
+ # Map of (entity, action) -> {python_param_name: api_param_name}
128
+ # Used to convert snake_case TypedDict keys to API parameter names in execute()
129
+ _PARAM_MAP = {
130
+ ('tasks', 'list'): {'limit': 'limit', 'offset': 'offset', 'project': 'project', 'workspace': 'workspace', 'section': 'section', 'assignee': 'assignee', 'completed_since': 'completed_since', 'modified_since': 'modified_since'},
131
+ ('project_tasks', 'list'): {'project_gid': 'project_gid', 'limit': 'limit', 'offset': 'offset', 'completed_since': 'completed_since'},
132
+ ('tasks', 'get'): {'task_gid': 'task_gid'},
133
+ ('workspace_task_search', 'list'): {'workspace_gid': 'workspace_gid', 'limit': 'limit', 'offset': 'offset', 'text': 'text', 'completed': 'completed', 'assignee_any': 'assignee.any', 'projects_any': 'projects.any', 'sections_any': 'sections.any', 'teams_any': 'teams.any', 'followers_any': 'followers.any', 'created_at_after': 'created_at.after', 'created_at_before': 'created_at.before', 'modified_at_after': 'modified_at.after', 'modified_at_before': 'modified_at.before', 'due_on_after': 'due_on.after', 'due_on_before': 'due_on.before', 'resource_subtype': 'resource_subtype', 'sort_by': 'sort_by', 'sort_ascending': 'sort_ascending'},
134
+ ('projects', 'list'): {'limit': 'limit', 'offset': 'offset', 'workspace': 'workspace', 'team': 'team', 'archived': 'archived'},
135
+ ('projects', 'get'): {'project_gid': 'project_gid'},
136
+ ('task_projects', 'list'): {'task_gid': 'task_gid', 'limit': 'limit', 'offset': 'offset'},
137
+ ('team_projects', 'list'): {'team_gid': 'team_gid', 'limit': 'limit', 'offset': 'offset', 'archived': 'archived'},
138
+ ('workspace_projects', 'list'): {'workspace_gid': 'workspace_gid', 'limit': 'limit', 'offset': 'offset', 'archived': 'archived'},
139
+ ('workspaces', 'list'): {'limit': 'limit', 'offset': 'offset'},
140
+ ('workspaces', 'get'): {'workspace_gid': 'workspace_gid'},
141
+ ('users', 'list'): {'limit': 'limit', 'offset': 'offset', 'workspace': 'workspace', 'team': 'team'},
142
+ ('users', 'get'): {'user_gid': 'user_gid'},
143
+ ('workspace_users', 'list'): {'workspace_gid': 'workspace_gid', 'limit': 'limit', 'offset': 'offset'},
144
+ ('team_users', 'list'): {'team_gid': 'team_gid', 'limit': 'limit', 'offset': 'offset'},
145
+ ('teams', 'get'): {'team_gid': 'team_gid'},
146
+ ('workspace_teams', 'list'): {'workspace_gid': 'workspace_gid', 'limit': 'limit', 'offset': 'offset'},
147
+ ('user_teams', 'list'): {'user_gid': 'user_gid', 'organization': 'organization', 'limit': 'limit', 'offset': 'offset'},
148
+ ('attachments', 'list'): {'parent': 'parent', 'limit': 'limit', 'offset': 'offset'},
149
+ ('attachments', 'get'): {'attachment_gid': 'attachment_gid'},
150
+ ('attachments', 'download'): {'attachment_gid': 'attachment_gid', 'range_header': 'range_header'},
151
+ ('workspace_tags', 'list'): {'workspace_gid': 'workspace_gid', 'limit': 'limit', 'offset': 'offset'},
152
+ ('tags', 'get'): {'tag_gid': 'tag_gid'},
153
+ ('project_sections', 'list'): {'project_gid': 'project_gid', 'limit': 'limit', 'offset': 'offset'},
154
+ ('sections', 'get'): {'section_gid': 'section_gid'},
155
+ ('task_subtasks', 'list'): {'task_gid': 'task_gid', 'limit': 'limit', 'offset': 'offset'},
156
+ ('task_dependencies', 'list'): {'task_gid': 'task_gid', 'limit': 'limit', 'offset': 'offset'},
157
+ ('task_dependents', 'list'): {'task_gid': 'task_gid', 'limit': 'limit', 'offset': 'offset'},
158
+ }
159
+
160
+ def __init__(
161
+ self,
162
+ auth_config: AsanaAuthConfig | None = None,
163
+ external_user_id: str | None = None,
164
+ airbyte_client_id: str | None = None,
165
+ airbyte_client_secret: str | None = None,
166
+ on_token_refresh: Any | None = None ):
167
+ """
168
+ Initialize a new asana connector instance.
169
+
170
+ Supports both local and hosted execution modes:
171
+ - Local mode: Provide `auth_config` for direct API calls
172
+ - Hosted mode: Provide `external_user_id`, `airbyte_client_id`, and `airbyte_client_secret` for hosted execution
173
+
174
+ Args:
175
+ auth_config: Typed authentication configuration (required for local mode)
176
+ external_user_id: External user ID (required for hosted mode)
177
+ airbyte_client_id: Airbyte OAuth client ID (required for hosted mode)
178
+ airbyte_client_secret: Airbyte OAuth client secret (required for hosted mode)
179
+ on_token_refresh: Optional callback for OAuth2 token refresh persistence.
180
+ Called with new_tokens dict when tokens are refreshed. Can be sync or async.
181
+ Example: lambda tokens: save_to_database(tokens)
182
+ Examples:
183
+ # Local mode (direct API calls)
184
+ connector = AsanaConnector(auth_config=AsanaAuthConfig(access_token="...", refresh_token="...", client_id="...", client_secret="..."))
185
+ # Hosted mode (executed on Airbyte cloud)
186
+ connector = AsanaConnector(
187
+ external_user_id="user-123",
188
+ airbyte_client_id="client_abc123",
189
+ airbyte_client_secret="secret_xyz789"
190
+ )
191
+
192
+ # Local mode with OAuth2 token refresh callback
193
+ def save_tokens(new_tokens: dict) -> None:
194
+ # Persist updated tokens to your storage (file, database, etc.)
195
+ with open("tokens.json", "w") as f:
196
+ json.dump(new_tokens, f)
197
+
198
+ connector = AsanaConnector(
199
+ auth_config=AsanaAuthConfig(access_token="...", refresh_token="..."),
200
+ on_token_refresh=save_tokens
201
+ )
202
+ """
203
+ # Hosted mode: external_user_id, airbyte_client_id, and airbyte_client_secret provided
204
+ if external_user_id and airbyte_client_id and airbyte_client_secret:
205
+ from ._vendored.connector_sdk.executor import HostedExecutor
206
+ self._executor = HostedExecutor(
207
+ external_user_id=external_user_id,
208
+ airbyte_client_id=airbyte_client_id,
209
+ airbyte_client_secret=airbyte_client_secret,
210
+ connector_definition_id=str(AsanaConnectorModel.id),
211
+ )
212
+ else:
213
+ # Local mode: auth_config required
214
+ if not auth_config:
215
+ raise ValueError(
216
+ "Either provide (external_user_id, airbyte_client_id, airbyte_client_secret) for hosted mode "
217
+ "or auth_config for local mode"
218
+ )
219
+
220
+ from ._vendored.connector_sdk.executor import LocalExecutor
221
+
222
+ # Build config_values dict from server variables
223
+ config_values = None
224
+
225
+ # Multi-auth connector: detect auth scheme from auth_config type
226
+ auth_scheme: str | None = None
227
+ if auth_config:
228
+ if isinstance(auth_config, AsanaOauth2AuthConfig):
229
+ auth_scheme = "oauth2"
230
+ if isinstance(auth_config, AsanaPersonalAccessTokenAuthConfig):
231
+ auth_scheme = "personalAccessToken"
232
+
233
+ self._executor = LocalExecutor(
234
+ model=AsanaConnectorModel,
235
+ auth_config=auth_config.model_dump() if auth_config else None,
236
+ auth_scheme=auth_scheme,
237
+ config_values=config_values,
238
+ on_token_refresh=on_token_refresh
239
+ )
240
+
241
+ # Update base_url with server variables if provided
242
+
243
+ # Initialize entity query objects
244
+ self.tasks = TasksQuery(self)
245
+ self.project_tasks = ProjectTasksQuery(self)
246
+ self.workspace_task_search = WorkspaceTaskSearchQuery(self)
247
+ self.projects = ProjectsQuery(self)
248
+ self.task_projects = TaskProjectsQuery(self)
249
+ self.team_projects = TeamProjectsQuery(self)
250
+ self.workspace_projects = WorkspaceProjectsQuery(self)
251
+ self.workspaces = WorkspacesQuery(self)
252
+ self.users = UsersQuery(self)
253
+ self.workspace_users = WorkspaceUsersQuery(self)
254
+ self.team_users = TeamUsersQuery(self)
255
+ self.teams = TeamsQuery(self)
256
+ self.workspace_teams = WorkspaceTeamsQuery(self)
257
+ self.user_teams = UserTeamsQuery(self)
258
+ self.attachments = AttachmentsQuery(self)
259
+ self.workspace_tags = WorkspaceTagsQuery(self)
260
+ self.tags = TagsQuery(self)
261
+ self.project_sections = ProjectSectionsQuery(self)
262
+ self.sections = SectionsQuery(self)
263
+ self.task_subtasks = TaskSubtasksQuery(self)
264
+ self.task_dependencies = TaskDependenciesQuery(self)
265
+ self.task_dependents = TaskDependentsQuery(self)
266
+
267
+ # ===== TYPED EXECUTE METHOD (Recommended Interface) =====
268
+
269
+ @overload
270
+ async def execute(
271
+ self,
272
+ entity: Literal["tasks"],
273
+ action: Literal["list"],
274
+ params: "TasksListParams"
275
+ ) -> "TasksListResult": ...
276
+
277
+ @overload
278
+ async def execute(
279
+ self,
280
+ entity: Literal["project_tasks"],
281
+ action: Literal["list"],
282
+ params: "ProjectTasksListParams"
283
+ ) -> "ProjectTasksListResult": ...
284
+
285
+ @overload
286
+ async def execute(
287
+ self,
288
+ entity: Literal["tasks"],
289
+ action: Literal["get"],
290
+ params: "TasksGetParams"
291
+ ) -> "TasksGetResult": ...
292
+
293
+ @overload
294
+ async def execute(
295
+ self,
296
+ entity: Literal["workspace_task_search"],
297
+ action: Literal["list"],
298
+ params: "WorkspaceTaskSearchListParams"
299
+ ) -> "WorkspaceTaskSearchListResult": ...
300
+
301
+ @overload
302
+ async def execute(
303
+ self,
304
+ entity: Literal["projects"],
305
+ action: Literal["list"],
306
+ params: "ProjectsListParams"
307
+ ) -> "ProjectsListResult": ...
308
+
309
+ @overload
310
+ async def execute(
311
+ self,
312
+ entity: Literal["projects"],
313
+ action: Literal["get"],
314
+ params: "ProjectsGetParams"
315
+ ) -> "ProjectsGetResult": ...
316
+
317
+ @overload
318
+ async def execute(
319
+ self,
320
+ entity: Literal["task_projects"],
321
+ action: Literal["list"],
322
+ params: "TaskProjectsListParams"
323
+ ) -> "TaskProjectsListResult": ...
324
+
325
+ @overload
326
+ async def execute(
327
+ self,
328
+ entity: Literal["team_projects"],
329
+ action: Literal["list"],
330
+ params: "TeamProjectsListParams"
331
+ ) -> "TeamProjectsListResult": ...
332
+
333
+ @overload
334
+ async def execute(
335
+ self,
336
+ entity: Literal["workspace_projects"],
337
+ action: Literal["list"],
338
+ params: "WorkspaceProjectsListParams"
339
+ ) -> "WorkspaceProjectsListResult": ...
340
+
341
+ @overload
342
+ async def execute(
343
+ self,
344
+ entity: Literal["workspaces"],
345
+ action: Literal["list"],
346
+ params: "WorkspacesListParams"
347
+ ) -> "WorkspacesListResult": ...
348
+
349
+ @overload
350
+ async def execute(
351
+ self,
352
+ entity: Literal["workspaces"],
353
+ action: Literal["get"],
354
+ params: "WorkspacesGetParams"
355
+ ) -> "WorkspacesGetResult": ...
356
+
357
+ @overload
358
+ async def execute(
359
+ self,
360
+ entity: Literal["users"],
361
+ action: Literal["list"],
362
+ params: "UsersListParams"
363
+ ) -> "UsersListResult": ...
364
+
365
+ @overload
366
+ async def execute(
367
+ self,
368
+ entity: Literal["users"],
369
+ action: Literal["get"],
370
+ params: "UsersGetParams"
371
+ ) -> "UsersGetResult": ...
372
+
373
+ @overload
374
+ async def execute(
375
+ self,
376
+ entity: Literal["workspace_users"],
377
+ action: Literal["list"],
378
+ params: "WorkspaceUsersListParams"
379
+ ) -> "WorkspaceUsersListResult": ...
380
+
381
+ @overload
382
+ async def execute(
383
+ self,
384
+ entity: Literal["team_users"],
385
+ action: Literal["list"],
386
+ params: "TeamUsersListParams"
387
+ ) -> "TeamUsersListResult": ...
388
+
389
+ @overload
390
+ async def execute(
391
+ self,
392
+ entity: Literal["teams"],
393
+ action: Literal["get"],
394
+ params: "TeamsGetParams"
395
+ ) -> "TeamsGetResult": ...
396
+
397
+ @overload
398
+ async def execute(
399
+ self,
400
+ entity: Literal["workspace_teams"],
401
+ action: Literal["list"],
402
+ params: "WorkspaceTeamsListParams"
403
+ ) -> "WorkspaceTeamsListResult": ...
404
+
405
+ @overload
406
+ async def execute(
407
+ self,
408
+ entity: Literal["user_teams"],
409
+ action: Literal["list"],
410
+ params: "UserTeamsListParams"
411
+ ) -> "UserTeamsListResult": ...
412
+
413
+ @overload
414
+ async def execute(
415
+ self,
416
+ entity: Literal["attachments"],
417
+ action: Literal["list"],
418
+ params: "AttachmentsListParams"
419
+ ) -> "AttachmentsListResult": ...
420
+
421
+ @overload
422
+ async def execute(
423
+ self,
424
+ entity: Literal["attachments"],
425
+ action: Literal["get"],
426
+ params: "AttachmentsGetParams"
427
+ ) -> "AttachmentsGetResult": ...
428
+
429
+ @overload
430
+ async def execute(
431
+ self,
432
+ entity: Literal["attachments"],
433
+ action: Literal["download"],
434
+ params: "AttachmentsDownloadParams"
435
+ ) -> "AsyncIterator[bytes]": ...
436
+
437
+ @overload
438
+ async def execute(
439
+ self,
440
+ entity: Literal["workspace_tags"],
441
+ action: Literal["list"],
442
+ params: "WorkspaceTagsListParams"
443
+ ) -> "WorkspaceTagsListResult": ...
444
+
445
+ @overload
446
+ async def execute(
447
+ self,
448
+ entity: Literal["tags"],
449
+ action: Literal["get"],
450
+ params: "TagsGetParams"
451
+ ) -> "TagsGetResult": ...
452
+
453
+ @overload
454
+ async def execute(
455
+ self,
456
+ entity: Literal["project_sections"],
457
+ action: Literal["list"],
458
+ params: "ProjectSectionsListParams"
459
+ ) -> "ProjectSectionsListResult": ...
460
+
461
+ @overload
462
+ async def execute(
463
+ self,
464
+ entity: Literal["sections"],
465
+ action: Literal["get"],
466
+ params: "SectionsGetParams"
467
+ ) -> "SectionsGetResult": ...
468
+
469
+ @overload
470
+ async def execute(
471
+ self,
472
+ entity: Literal["task_subtasks"],
473
+ action: Literal["list"],
474
+ params: "TaskSubtasksListParams"
475
+ ) -> "TaskSubtasksListResult": ...
476
+
477
+ @overload
478
+ async def execute(
479
+ self,
480
+ entity: Literal["task_dependencies"],
481
+ action: Literal["list"],
482
+ params: "TaskDependenciesListParams"
483
+ ) -> "TaskDependenciesListResult": ...
484
+
485
+ @overload
486
+ async def execute(
487
+ self,
488
+ entity: Literal["task_dependents"],
489
+ action: Literal["list"],
490
+ params: "TaskDependentsListParams"
491
+ ) -> "TaskDependentsListResult": ...
492
+
493
+
494
+ @overload
495
+ async def execute(
496
+ self,
497
+ entity: str,
498
+ action: str,
499
+ params: dict[str, Any]
500
+ ) -> AsanaExecuteResult[Any] | AsanaExecuteResultWithMeta[Any, Any] | Any: ...
501
+
502
+ async def execute(
503
+ self,
504
+ entity: str,
505
+ action: str,
506
+ params: dict[str, Any] | None = None
507
+ ) -> Any:
508
+ """
509
+ Execute an entity operation with full type safety.
510
+
511
+ This is the recommended interface for blessed connectors as it:
512
+ - Uses the same signature as non-blessed connectors
513
+ - Provides full IDE autocomplete for entity/action/params
514
+ - Makes migration from generic to blessed connectors seamless
515
+
516
+ Args:
517
+ entity: Entity name (e.g., "customers")
518
+ action: Operation action (e.g., "create", "get", "list")
519
+ params: Operation parameters (typed based on entity+action)
520
+
521
+ Returns:
522
+ Typed response based on the operation
523
+
524
+ Example:
525
+ customer = await connector.execute(
526
+ entity="customers",
527
+ action="get",
528
+ params={"id": "cus_123"}
529
+ )
530
+ """
531
+ from ._vendored.connector_sdk.executor import ExecutionConfig
532
+
533
+ # Remap parameter names from snake_case (TypedDict keys) to API parameter names
534
+ if params:
535
+ param_map = self._PARAM_MAP.get((entity, action), {})
536
+ if param_map:
537
+ params = {param_map.get(k, k): v for k, v in params.items()}
538
+
539
+ # Use ExecutionConfig for both local and hosted executors
540
+ config = ExecutionConfig(
541
+ entity=entity,
542
+ action=action,
543
+ params=params
544
+ )
545
+
546
+ result = await self._executor.execute(config)
547
+
548
+ if not result.success:
549
+ raise RuntimeError(f"Execution failed: {result.error}")
550
+
551
+ # Check if this operation has extractors configured
552
+ has_extractors = self._EXTRACTOR_MAP.get((entity, action), False)
553
+
554
+ if has_extractors:
555
+ # With extractors - return Pydantic envelope with data and meta
556
+ if result.meta is not None:
557
+ return AsanaExecuteResultWithMeta[Any, Any](
558
+ data=result.data,
559
+ meta=result.meta
560
+ )
561
+ else:
562
+ return AsanaExecuteResult[Any](data=result.data)
563
+ else:
564
+ # No extractors - return raw response data
565
+ return result.data
566
+
567
+
568
+
569
+ class TasksQuery:
570
+ """
571
+ Query class for Tasks entity operations.
572
+ """
573
+
574
+ def __init__(self, connector: AsanaConnector):
575
+ """Initialize query with connector reference."""
576
+ self._connector = connector
577
+
578
+ async def list(
579
+ self,
580
+ limit: int | None = None,
581
+ offset: str | None = None,
582
+ project: str | None = None,
583
+ workspace: str | None = None,
584
+ section: str | None = None,
585
+ assignee: str | None = None,
586
+ completed_since: str | None = None,
587
+ modified_since: str | None = None,
588
+ **kwargs
589
+ ) -> TasksListResult:
590
+ """
591
+ Returns a paginated list of tasks. Must include either a project OR a section OR a workspace AND assignee parameter.
592
+
593
+ Args:
594
+ limit: Number of items to return per page
595
+ offset: Pagination offset token
596
+ project: The project to filter tasks on
597
+ workspace: The workspace to filter tasks on
598
+ section: The workspace to filter tasks on
599
+ assignee: The assignee to filter tasks on
600
+ completed_since: Only return tasks that have been completed since this time
601
+ modified_since: Only return tasks that have been completed since this time
602
+ **kwargs: Additional parameters
603
+
604
+ Returns:
605
+ TasksListResult
606
+ """
607
+ params = {k: v for k, v in {
608
+ "limit": limit,
609
+ "offset": offset,
610
+ "project": project,
611
+ "workspace": workspace,
612
+ "section": section,
613
+ "assignee": assignee,
614
+ "completed_since": completed_since,
615
+ "modified_since": modified_since,
616
+ **kwargs
617
+ }.items() if v is not None}
618
+
619
+ result = await self._connector.execute("tasks", "list", params)
620
+ # Cast generic envelope to concrete typed result
621
+ return TasksListResult(
622
+ data=result.data,
623
+ meta=result.meta )
624
+
625
+
626
+
627
+ async def get(
628
+ self,
629
+ task_gid: str,
630
+ **kwargs
631
+ ) -> TasksGetResult:
632
+ """
633
+ Get a single task by its ID
634
+
635
+ Args:
636
+ task_gid: Task GID
637
+ **kwargs: Additional parameters
638
+
639
+ Returns:
640
+ TasksGetResult
641
+ """
642
+ params = {k: v for k, v in {
643
+ "task_gid": task_gid,
644
+ **kwargs
645
+ }.items() if v is not None}
646
+
647
+ result = await self._connector.execute("tasks", "get", params)
648
+ # Cast generic envelope to concrete typed result
649
+ return TasksGetResult(
650
+ data=result.data )
651
+
652
+
653
+
654
+ class ProjectTasksQuery:
655
+ """
656
+ Query class for ProjectTasks entity operations.
657
+ """
658
+
659
+ def __init__(self, connector: AsanaConnector):
660
+ """Initialize query with connector reference."""
661
+ self._connector = connector
662
+
663
+ async def list(
664
+ self,
665
+ project_gid: str,
666
+ limit: int | None = None,
667
+ offset: str | None = None,
668
+ completed_since: str | None = None,
669
+ **kwargs
670
+ ) -> ProjectTasksListResult:
671
+ """
672
+ Returns all tasks in a project
673
+
674
+ Args:
675
+ project_gid: Project GID to list tasks from
676
+ limit: Number of items to return per page
677
+ offset: Pagination offset token
678
+ completed_since: Only return tasks that have been completed since this time
679
+ **kwargs: Additional parameters
680
+
681
+ Returns:
682
+ ProjectTasksListResult
683
+ """
684
+ params = {k: v for k, v in {
685
+ "project_gid": project_gid,
686
+ "limit": limit,
687
+ "offset": offset,
688
+ "completed_since": completed_since,
689
+ **kwargs
690
+ }.items() if v is not None}
691
+
692
+ result = await self._connector.execute("project_tasks", "list", params)
693
+ # Cast generic envelope to concrete typed result
694
+ return ProjectTasksListResult(
695
+ data=result.data,
696
+ meta=result.meta )
697
+
698
+
699
+
700
+ class WorkspaceTaskSearchQuery:
701
+ """
702
+ Query class for WorkspaceTaskSearch entity operations.
703
+ """
704
+
705
+ def __init__(self, connector: AsanaConnector):
706
+ """Initialize query with connector reference."""
707
+ self._connector = connector
708
+
709
+ async def list(
710
+ self,
711
+ workspace_gid: str,
712
+ limit: int | None = None,
713
+ offset: str | None = None,
714
+ text: str | None = None,
715
+ completed: bool | None = None,
716
+ assignee_any: str | None = None,
717
+ projects_any: str | None = None,
718
+ sections_any: str | None = None,
719
+ teams_any: str | None = None,
720
+ followers_any: str | None = None,
721
+ created_at_after: str | None = None,
722
+ created_at_before: str | None = None,
723
+ modified_at_after: str | None = None,
724
+ modified_at_before: str | None = None,
725
+ due_on_after: str | None = None,
726
+ due_on_before: str | None = None,
727
+ resource_subtype: str | None = None,
728
+ sort_by: str | None = None,
729
+ sort_ascending: bool | None = None,
730
+ **kwargs
731
+ ) -> WorkspaceTaskSearchListResult:
732
+ """
733
+ Returns tasks that match the specified search criteria. Note - This endpoint requires a premium Asana account. At least one search parameter must be provided.
734
+
735
+ Args:
736
+ workspace_gid: Workspace GID to search tasks in
737
+ limit: Number of items to return per page
738
+ offset: Pagination offset token
739
+ text: Search text to filter tasks
740
+ completed: Filter by completion status
741
+ assignee_any: Comma-separated list of assignee GIDs
742
+ projects_any: Comma-separated list of project GIDs
743
+ sections_any: Comma-separated list of section GIDs
744
+ teams_any: Comma-separated list of team GIDs
745
+ followers_any: Comma-separated list of follower GIDs
746
+ created_at_after: Filter tasks created after this date (ISO 8601 format)
747
+ created_at_before: Filter tasks created before this date (ISO 8601 format)
748
+ modified_at_after: Filter tasks modified after this date (ISO 8601 format)
749
+ modified_at_before: Filter tasks modified before this date (ISO 8601 format)
750
+ due_on_after: Filter tasks due after this date (ISO 8601 date format)
751
+ due_on_before: Filter tasks due before this date (ISO 8601 date format)
752
+ resource_subtype: Filter by task resource subtype (e.g., default_task, milestone)
753
+ sort_by: Field to sort by (e.g., created_at, modified_at, due_date)
754
+ sort_ascending: Sort order (true for ascending, false for descending)
755
+ **kwargs: Additional parameters
756
+
757
+ Returns:
758
+ WorkspaceTaskSearchListResult
759
+ """
760
+ params = {k: v for k, v in {
761
+ "workspace_gid": workspace_gid,
762
+ "limit": limit,
763
+ "offset": offset,
764
+ "text": text,
765
+ "completed": completed,
766
+ "assignee.any": assignee_any,
767
+ "projects.any": projects_any,
768
+ "sections.any": sections_any,
769
+ "teams.any": teams_any,
770
+ "followers.any": followers_any,
771
+ "created_at.after": created_at_after,
772
+ "created_at.before": created_at_before,
773
+ "modified_at.after": modified_at_after,
774
+ "modified_at.before": modified_at_before,
775
+ "due_on.after": due_on_after,
776
+ "due_on.before": due_on_before,
777
+ "resource_subtype": resource_subtype,
778
+ "sort_by": sort_by,
779
+ "sort_ascending": sort_ascending,
780
+ **kwargs
781
+ }.items() if v is not None}
782
+
783
+ result = await self._connector.execute("workspace_task_search", "list", params)
784
+ # Cast generic envelope to concrete typed result
785
+ return WorkspaceTaskSearchListResult(
786
+ data=result.data,
787
+ meta=result.meta )
788
+
789
+
790
+
791
+ class ProjectsQuery:
792
+ """
793
+ Query class for Projects entity operations.
794
+ """
795
+
796
+ def __init__(self, connector: AsanaConnector):
797
+ """Initialize query with connector reference."""
798
+ self._connector = connector
799
+
800
+ async def list(
801
+ self,
802
+ limit: int | None = None,
803
+ offset: str | None = None,
804
+ workspace: str | None = None,
805
+ team: str | None = None,
806
+ archived: bool | None = None,
807
+ **kwargs
808
+ ) -> ProjectsListResult:
809
+ """
810
+ Returns a paginated list of projects
811
+
812
+ Args:
813
+ limit: Number of items to return per page
814
+ offset: Pagination offset token
815
+ workspace: The workspace to filter projects on
816
+ team: The team to filter projects on
817
+ archived: Filter by archived status
818
+ **kwargs: Additional parameters
819
+
820
+ Returns:
821
+ ProjectsListResult
822
+ """
823
+ params = {k: v for k, v in {
824
+ "limit": limit,
825
+ "offset": offset,
826
+ "workspace": workspace,
827
+ "team": team,
828
+ "archived": archived,
829
+ **kwargs
830
+ }.items() if v is not None}
831
+
832
+ result = await self._connector.execute("projects", "list", params)
833
+ # Cast generic envelope to concrete typed result
834
+ return ProjectsListResult(
835
+ data=result.data,
836
+ meta=result.meta )
837
+
838
+
839
+
840
+ async def get(
841
+ self,
842
+ project_gid: str,
843
+ **kwargs
844
+ ) -> ProjectsGetResult:
845
+ """
846
+ Get a single project by its ID
847
+
848
+ Args:
849
+ project_gid: Project GID
850
+ **kwargs: Additional parameters
851
+
852
+ Returns:
853
+ ProjectsGetResult
854
+ """
855
+ params = {k: v for k, v in {
856
+ "project_gid": project_gid,
857
+ **kwargs
858
+ }.items() if v is not None}
859
+
860
+ result = await self._connector.execute("projects", "get", params)
861
+ # Cast generic envelope to concrete typed result
862
+ return ProjectsGetResult(
863
+ data=result.data )
864
+
865
+
866
+
867
+ class TaskProjectsQuery:
868
+ """
869
+ Query class for TaskProjects entity operations.
870
+ """
871
+
872
+ def __init__(self, connector: AsanaConnector):
873
+ """Initialize query with connector reference."""
874
+ self._connector = connector
875
+
876
+ async def list(
877
+ self,
878
+ task_gid: str,
879
+ limit: int | None = None,
880
+ offset: str | None = None,
881
+ **kwargs
882
+ ) -> TaskProjectsListResult:
883
+ """
884
+ Returns all projects a task is in
885
+
886
+ Args:
887
+ task_gid: Task GID to list projects from
888
+ limit: Number of items to return per page
889
+ offset: Pagination offset token
890
+ **kwargs: Additional parameters
891
+
892
+ Returns:
893
+ TaskProjectsListResult
894
+ """
895
+ params = {k: v for k, v in {
896
+ "task_gid": task_gid,
897
+ "limit": limit,
898
+ "offset": offset,
899
+ **kwargs
900
+ }.items() if v is not None}
901
+
902
+ result = await self._connector.execute("task_projects", "list", params)
903
+ # Cast generic envelope to concrete typed result
904
+ return TaskProjectsListResult(
905
+ data=result.data,
906
+ meta=result.meta )
907
+
908
+
909
+
910
+ class TeamProjectsQuery:
911
+ """
912
+ Query class for TeamProjects entity operations.
913
+ """
914
+
915
+ def __init__(self, connector: AsanaConnector):
916
+ """Initialize query with connector reference."""
917
+ self._connector = connector
918
+
919
+ async def list(
920
+ self,
921
+ team_gid: str,
922
+ limit: int | None = None,
923
+ offset: str | None = None,
924
+ archived: bool | None = None,
925
+ **kwargs
926
+ ) -> TeamProjectsListResult:
927
+ """
928
+ Returns all projects for a team
929
+
930
+ Args:
931
+ team_gid: Team GID to list projects from
932
+ limit: Number of items to return per page
933
+ offset: Pagination offset token
934
+ archived: Filter by archived status
935
+ **kwargs: Additional parameters
936
+
937
+ Returns:
938
+ TeamProjectsListResult
939
+ """
940
+ params = {k: v for k, v in {
941
+ "team_gid": team_gid,
942
+ "limit": limit,
943
+ "offset": offset,
944
+ "archived": archived,
945
+ **kwargs
946
+ }.items() if v is not None}
947
+
948
+ result = await self._connector.execute("team_projects", "list", params)
949
+ # Cast generic envelope to concrete typed result
950
+ return TeamProjectsListResult(
951
+ data=result.data,
952
+ meta=result.meta )
953
+
954
+
955
+
956
+ class WorkspaceProjectsQuery:
957
+ """
958
+ Query class for WorkspaceProjects entity operations.
959
+ """
960
+
961
+ def __init__(self, connector: AsanaConnector):
962
+ """Initialize query with connector reference."""
963
+ self._connector = connector
964
+
965
+ async def list(
966
+ self,
967
+ workspace_gid: str,
968
+ limit: int | None = None,
969
+ offset: str | None = None,
970
+ archived: bool | None = None,
971
+ **kwargs
972
+ ) -> WorkspaceProjectsListResult:
973
+ """
974
+ Returns all projects in a workspace
975
+
976
+ Args:
977
+ workspace_gid: Workspace GID to list projects from
978
+ limit: Number of items to return per page
979
+ offset: Pagination offset token
980
+ archived: Filter by archived status
981
+ **kwargs: Additional parameters
982
+
983
+ Returns:
984
+ WorkspaceProjectsListResult
985
+ """
986
+ params = {k: v for k, v in {
987
+ "workspace_gid": workspace_gid,
988
+ "limit": limit,
989
+ "offset": offset,
990
+ "archived": archived,
991
+ **kwargs
992
+ }.items() if v is not None}
993
+
994
+ result = await self._connector.execute("workspace_projects", "list", params)
995
+ # Cast generic envelope to concrete typed result
996
+ return WorkspaceProjectsListResult(
997
+ data=result.data,
998
+ meta=result.meta )
999
+
1000
+
1001
+
1002
+ class WorkspacesQuery:
1003
+ """
1004
+ Query class for Workspaces entity operations.
1005
+ """
1006
+
1007
+ def __init__(self, connector: AsanaConnector):
1008
+ """Initialize query with connector reference."""
1009
+ self._connector = connector
1010
+
1011
+ async def list(
1012
+ self,
1013
+ limit: int | None = None,
1014
+ offset: str | None = None,
1015
+ **kwargs
1016
+ ) -> WorkspacesListResult:
1017
+ """
1018
+ Returns a paginated list of workspaces
1019
+
1020
+ Args:
1021
+ limit: Number of items to return per page
1022
+ offset: Pagination offset token
1023
+ **kwargs: Additional parameters
1024
+
1025
+ Returns:
1026
+ WorkspacesListResult
1027
+ """
1028
+ params = {k: v for k, v in {
1029
+ "limit": limit,
1030
+ "offset": offset,
1031
+ **kwargs
1032
+ }.items() if v is not None}
1033
+
1034
+ result = await self._connector.execute("workspaces", "list", params)
1035
+ # Cast generic envelope to concrete typed result
1036
+ return WorkspacesListResult(
1037
+ data=result.data,
1038
+ meta=result.meta )
1039
+
1040
+
1041
+
1042
+ async def get(
1043
+ self,
1044
+ workspace_gid: str,
1045
+ **kwargs
1046
+ ) -> WorkspacesGetResult:
1047
+ """
1048
+ Get a single workspace by its ID
1049
+
1050
+ Args:
1051
+ workspace_gid: Workspace GID
1052
+ **kwargs: Additional parameters
1053
+
1054
+ Returns:
1055
+ WorkspacesGetResult
1056
+ """
1057
+ params = {k: v for k, v in {
1058
+ "workspace_gid": workspace_gid,
1059
+ **kwargs
1060
+ }.items() if v is not None}
1061
+
1062
+ result = await self._connector.execute("workspaces", "get", params)
1063
+ # Cast generic envelope to concrete typed result
1064
+ return WorkspacesGetResult(
1065
+ data=result.data )
1066
+
1067
+
1068
+
1069
+ class UsersQuery:
1070
+ """
1071
+ Query class for Users entity operations.
1072
+ """
1073
+
1074
+ def __init__(self, connector: AsanaConnector):
1075
+ """Initialize query with connector reference."""
1076
+ self._connector = connector
1077
+
1078
+ async def list(
1079
+ self,
1080
+ limit: int | None = None,
1081
+ offset: str | None = None,
1082
+ workspace: str | None = None,
1083
+ team: str | None = None,
1084
+ **kwargs
1085
+ ) -> UsersListResult:
1086
+ """
1087
+ Returns a paginated list of users
1088
+
1089
+ Args:
1090
+ limit: Number of items to return per page
1091
+ offset: Pagination offset token
1092
+ workspace: The workspace to filter users on
1093
+ team: The team to filter users on
1094
+ **kwargs: Additional parameters
1095
+
1096
+ Returns:
1097
+ UsersListResult
1098
+ """
1099
+ params = {k: v for k, v in {
1100
+ "limit": limit,
1101
+ "offset": offset,
1102
+ "workspace": workspace,
1103
+ "team": team,
1104
+ **kwargs
1105
+ }.items() if v is not None}
1106
+
1107
+ result = await self._connector.execute("users", "list", params)
1108
+ # Cast generic envelope to concrete typed result
1109
+ return UsersListResult(
1110
+ data=result.data,
1111
+ meta=result.meta )
1112
+
1113
+
1114
+
1115
+ async def get(
1116
+ self,
1117
+ user_gid: str,
1118
+ **kwargs
1119
+ ) -> UsersGetResult:
1120
+ """
1121
+ Get a single user by their ID
1122
+
1123
+ Args:
1124
+ user_gid: User GID
1125
+ **kwargs: Additional parameters
1126
+
1127
+ Returns:
1128
+ UsersGetResult
1129
+ """
1130
+ params = {k: v for k, v in {
1131
+ "user_gid": user_gid,
1132
+ **kwargs
1133
+ }.items() if v is not None}
1134
+
1135
+ result = await self._connector.execute("users", "get", params)
1136
+ # Cast generic envelope to concrete typed result
1137
+ return UsersGetResult(
1138
+ data=result.data )
1139
+
1140
+
1141
+
1142
+ class WorkspaceUsersQuery:
1143
+ """
1144
+ Query class for WorkspaceUsers entity operations.
1145
+ """
1146
+
1147
+ def __init__(self, connector: AsanaConnector):
1148
+ """Initialize query with connector reference."""
1149
+ self._connector = connector
1150
+
1151
+ async def list(
1152
+ self,
1153
+ workspace_gid: str,
1154
+ limit: int | None = None,
1155
+ offset: str | None = None,
1156
+ **kwargs
1157
+ ) -> WorkspaceUsersListResult:
1158
+ """
1159
+ Returns all users in a workspace
1160
+
1161
+ Args:
1162
+ workspace_gid: Workspace GID to list users from
1163
+ limit: Number of items to return per page
1164
+ offset: Pagination offset token
1165
+ **kwargs: Additional parameters
1166
+
1167
+ Returns:
1168
+ WorkspaceUsersListResult
1169
+ """
1170
+ params = {k: v for k, v in {
1171
+ "workspace_gid": workspace_gid,
1172
+ "limit": limit,
1173
+ "offset": offset,
1174
+ **kwargs
1175
+ }.items() if v is not None}
1176
+
1177
+ result = await self._connector.execute("workspace_users", "list", params)
1178
+ # Cast generic envelope to concrete typed result
1179
+ return WorkspaceUsersListResult(
1180
+ data=result.data,
1181
+ meta=result.meta )
1182
+
1183
+
1184
+
1185
+ class TeamUsersQuery:
1186
+ """
1187
+ Query class for TeamUsers entity operations.
1188
+ """
1189
+
1190
+ def __init__(self, connector: AsanaConnector):
1191
+ """Initialize query with connector reference."""
1192
+ self._connector = connector
1193
+
1194
+ async def list(
1195
+ self,
1196
+ team_gid: str,
1197
+ limit: int | None = None,
1198
+ offset: str | None = None,
1199
+ **kwargs
1200
+ ) -> TeamUsersListResult:
1201
+ """
1202
+ Returns all users in a team
1203
+
1204
+ Args:
1205
+ team_gid: Team GID to list users from
1206
+ limit: Number of items to return per page
1207
+ offset: Pagination offset token
1208
+ **kwargs: Additional parameters
1209
+
1210
+ Returns:
1211
+ TeamUsersListResult
1212
+ """
1213
+ params = {k: v for k, v in {
1214
+ "team_gid": team_gid,
1215
+ "limit": limit,
1216
+ "offset": offset,
1217
+ **kwargs
1218
+ }.items() if v is not None}
1219
+
1220
+ result = await self._connector.execute("team_users", "list", params)
1221
+ # Cast generic envelope to concrete typed result
1222
+ return TeamUsersListResult(
1223
+ data=result.data,
1224
+ meta=result.meta )
1225
+
1226
+
1227
+
1228
+ class TeamsQuery:
1229
+ """
1230
+ Query class for Teams entity operations.
1231
+ """
1232
+
1233
+ def __init__(self, connector: AsanaConnector):
1234
+ """Initialize query with connector reference."""
1235
+ self._connector = connector
1236
+
1237
+ async def get(
1238
+ self,
1239
+ team_gid: str,
1240
+ **kwargs
1241
+ ) -> TeamsGetResult:
1242
+ """
1243
+ Get a single team by its ID
1244
+
1245
+ Args:
1246
+ team_gid: Team GID
1247
+ **kwargs: Additional parameters
1248
+
1249
+ Returns:
1250
+ TeamsGetResult
1251
+ """
1252
+ params = {k: v for k, v in {
1253
+ "team_gid": team_gid,
1254
+ **kwargs
1255
+ }.items() if v is not None}
1256
+
1257
+ result = await self._connector.execute("teams", "get", params)
1258
+ # Cast generic envelope to concrete typed result
1259
+ return TeamsGetResult(
1260
+ data=result.data )
1261
+
1262
+
1263
+
1264
+ class WorkspaceTeamsQuery:
1265
+ """
1266
+ Query class for WorkspaceTeams entity operations.
1267
+ """
1268
+
1269
+ def __init__(self, connector: AsanaConnector):
1270
+ """Initialize query with connector reference."""
1271
+ self._connector = connector
1272
+
1273
+ async def list(
1274
+ self,
1275
+ workspace_gid: str,
1276
+ limit: int | None = None,
1277
+ offset: str | None = None,
1278
+ **kwargs
1279
+ ) -> WorkspaceTeamsListResult:
1280
+ """
1281
+ Returns all teams in a workspace
1282
+
1283
+ Args:
1284
+ workspace_gid: Workspace GID to list teams from
1285
+ limit: Number of items to return per page
1286
+ offset: Pagination offset token
1287
+ **kwargs: Additional parameters
1288
+
1289
+ Returns:
1290
+ WorkspaceTeamsListResult
1291
+ """
1292
+ params = {k: v for k, v in {
1293
+ "workspace_gid": workspace_gid,
1294
+ "limit": limit,
1295
+ "offset": offset,
1296
+ **kwargs
1297
+ }.items() if v is not None}
1298
+
1299
+ result = await self._connector.execute("workspace_teams", "list", params)
1300
+ # Cast generic envelope to concrete typed result
1301
+ return WorkspaceTeamsListResult(
1302
+ data=result.data,
1303
+ meta=result.meta )
1304
+
1305
+
1306
+
1307
+ class UserTeamsQuery:
1308
+ """
1309
+ Query class for UserTeams entity operations.
1310
+ """
1311
+
1312
+ def __init__(self, connector: AsanaConnector):
1313
+ """Initialize query with connector reference."""
1314
+ self._connector = connector
1315
+
1316
+ async def list(
1317
+ self,
1318
+ user_gid: str,
1319
+ organization: str,
1320
+ limit: int | None = None,
1321
+ offset: str | None = None,
1322
+ **kwargs
1323
+ ) -> UserTeamsListResult:
1324
+ """
1325
+ Returns all teams a user is a member of
1326
+
1327
+ Args:
1328
+ user_gid: User GID to list teams from
1329
+ organization: The workspace or organization to filter teams on
1330
+ limit: Number of items to return per page
1331
+ offset: Pagination offset token
1332
+ **kwargs: Additional parameters
1333
+
1334
+ Returns:
1335
+ UserTeamsListResult
1336
+ """
1337
+ params = {k: v for k, v in {
1338
+ "user_gid": user_gid,
1339
+ "organization": organization,
1340
+ "limit": limit,
1341
+ "offset": offset,
1342
+ **kwargs
1343
+ }.items() if v is not None}
1344
+
1345
+ result = await self._connector.execute("user_teams", "list", params)
1346
+ # Cast generic envelope to concrete typed result
1347
+ return UserTeamsListResult(
1348
+ data=result.data,
1349
+ meta=result.meta )
1350
+
1351
+
1352
+
1353
+ class AttachmentsQuery:
1354
+ """
1355
+ Query class for Attachments entity operations.
1356
+ """
1357
+
1358
+ def __init__(self, connector: AsanaConnector):
1359
+ """Initialize query with connector reference."""
1360
+ self._connector = connector
1361
+
1362
+ async def list(
1363
+ self,
1364
+ parent: str,
1365
+ limit: int | None = None,
1366
+ offset: str | None = None,
1367
+ **kwargs
1368
+ ) -> AttachmentsListResult:
1369
+ """
1370
+ Returns a list of attachments for an object (task, project, etc.)
1371
+
1372
+ Args:
1373
+ parent: Globally unique identifier for the object to fetch attachments for (e.g., a task GID)
1374
+ limit: Number of items to return per page
1375
+ offset: Pagination offset token
1376
+ **kwargs: Additional parameters
1377
+
1378
+ Returns:
1379
+ AttachmentsListResult
1380
+ """
1381
+ params = {k: v for k, v in {
1382
+ "parent": parent,
1383
+ "limit": limit,
1384
+ "offset": offset,
1385
+ **kwargs
1386
+ }.items() if v is not None}
1387
+
1388
+ result = await self._connector.execute("attachments", "list", params)
1389
+ # Cast generic envelope to concrete typed result
1390
+ return AttachmentsListResult(
1391
+ data=result.data,
1392
+ meta=result.meta )
1393
+
1394
+
1395
+
1396
+ async def get(
1397
+ self,
1398
+ attachment_gid: str,
1399
+ **kwargs
1400
+ ) -> AttachmentsGetResult:
1401
+ """
1402
+ Get details for a single attachment by its GID
1403
+
1404
+ Args:
1405
+ attachment_gid: Globally unique identifier for the attachment
1406
+ **kwargs: Additional parameters
1407
+
1408
+ Returns:
1409
+ AttachmentsGetResult
1410
+ """
1411
+ params = {k: v for k, v in {
1412
+ "attachment_gid": attachment_gid,
1413
+ **kwargs
1414
+ }.items() if v is not None}
1415
+
1416
+ result = await self._connector.execute("attachments", "get", params)
1417
+ # Cast generic envelope to concrete typed result
1418
+ return AttachmentsGetResult(
1419
+ data=result.data )
1420
+
1421
+
1422
+
1423
+ async def download(
1424
+ self,
1425
+ attachment_gid: str,
1426
+ range_header: str | None = None,
1427
+ **kwargs
1428
+ ) -> AsyncIterator[bytes]:
1429
+ """
1430
+ Downloads the file content of an attachment. This operation first retrieves the attachment
1431
+ metadata to get the download_url, then downloads the file from that URL.
1432
+
1433
+
1434
+ Args:
1435
+ attachment_gid: Globally unique identifier for the attachment
1436
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1437
+ **kwargs: Additional parameters
1438
+
1439
+ Returns:
1440
+ AsyncIterator[bytes]
1441
+ """
1442
+ params = {k: v for k, v in {
1443
+ "attachment_gid": attachment_gid,
1444
+ "range_header": range_header,
1445
+ **kwargs
1446
+ }.items() if v is not None}
1447
+
1448
+ result = await self._connector.execute("attachments", "download", params)
1449
+ return result
1450
+
1451
+
1452
+ async def download_local(
1453
+ self,
1454
+ attachment_gid: str,
1455
+ path: str,
1456
+ range_header: str | None = None,
1457
+ **kwargs
1458
+ ) -> Path:
1459
+ """
1460
+ Downloads the file content of an attachment. This operation first retrieves the attachment
1461
+ metadata to get the download_url, then downloads the file from that URL.
1462
+ and save to file.
1463
+
1464
+ Args:
1465
+ attachment_gid: Globally unique identifier for the attachment
1466
+ range_header: Optional Range header for partial downloads (e.g., 'bytes=0-99')
1467
+ path: File path to save downloaded content
1468
+ **kwargs: Additional parameters
1469
+
1470
+ Returns:
1471
+ str: Path to the downloaded file
1472
+ """
1473
+ from ._vendored.connector_sdk import save_download
1474
+
1475
+ # Get the async iterator
1476
+ content_iterator = await self.download(
1477
+ attachment_gid=attachment_gid,
1478
+ range_header=range_header,
1479
+ **kwargs
1480
+ )
1481
+
1482
+ return await save_download(content_iterator, path)
1483
+
1484
+
1485
+ class WorkspaceTagsQuery:
1486
+ """
1487
+ Query class for WorkspaceTags entity operations.
1488
+ """
1489
+
1490
+ def __init__(self, connector: AsanaConnector):
1491
+ """Initialize query with connector reference."""
1492
+ self._connector = connector
1493
+
1494
+ async def list(
1495
+ self,
1496
+ workspace_gid: str,
1497
+ limit: int | None = None,
1498
+ offset: str | None = None,
1499
+ **kwargs
1500
+ ) -> WorkspaceTagsListResult:
1501
+ """
1502
+ Returns all tags in a workspace
1503
+
1504
+ Args:
1505
+ workspace_gid: Workspace GID to list tags from
1506
+ limit: Number of items to return per page
1507
+ offset: Pagination offset token
1508
+ **kwargs: Additional parameters
1509
+
1510
+ Returns:
1511
+ WorkspaceTagsListResult
1512
+ """
1513
+ params = {k: v for k, v in {
1514
+ "workspace_gid": workspace_gid,
1515
+ "limit": limit,
1516
+ "offset": offset,
1517
+ **kwargs
1518
+ }.items() if v is not None}
1519
+
1520
+ result = await self._connector.execute("workspace_tags", "list", params)
1521
+ # Cast generic envelope to concrete typed result
1522
+ return WorkspaceTagsListResult(
1523
+ data=result.data,
1524
+ meta=result.meta )
1525
+
1526
+
1527
+
1528
+ class TagsQuery:
1529
+ """
1530
+ Query class for Tags entity operations.
1531
+ """
1532
+
1533
+ def __init__(self, connector: AsanaConnector):
1534
+ """Initialize query with connector reference."""
1535
+ self._connector = connector
1536
+
1537
+ async def get(
1538
+ self,
1539
+ tag_gid: str,
1540
+ **kwargs
1541
+ ) -> TagsGetResult:
1542
+ """
1543
+ Get a single tag by its ID
1544
+
1545
+ Args:
1546
+ tag_gid: Tag GID
1547
+ **kwargs: Additional parameters
1548
+
1549
+ Returns:
1550
+ TagsGetResult
1551
+ """
1552
+ params = {k: v for k, v in {
1553
+ "tag_gid": tag_gid,
1554
+ **kwargs
1555
+ }.items() if v is not None}
1556
+
1557
+ result = await self._connector.execute("tags", "get", params)
1558
+ # Cast generic envelope to concrete typed result
1559
+ return TagsGetResult(
1560
+ data=result.data )
1561
+
1562
+
1563
+
1564
+ class ProjectSectionsQuery:
1565
+ """
1566
+ Query class for ProjectSections entity operations.
1567
+ """
1568
+
1569
+ def __init__(self, connector: AsanaConnector):
1570
+ """Initialize query with connector reference."""
1571
+ self._connector = connector
1572
+
1573
+ async def list(
1574
+ self,
1575
+ project_gid: str,
1576
+ limit: int | None = None,
1577
+ offset: str | None = None,
1578
+ **kwargs
1579
+ ) -> ProjectSectionsListResult:
1580
+ """
1581
+ Returns all sections in a project
1582
+
1583
+ Args:
1584
+ project_gid: Project GID to list sections from
1585
+ limit: Number of items to return per page
1586
+ offset: Pagination offset token
1587
+ **kwargs: Additional parameters
1588
+
1589
+ Returns:
1590
+ ProjectSectionsListResult
1591
+ """
1592
+ params = {k: v for k, v in {
1593
+ "project_gid": project_gid,
1594
+ "limit": limit,
1595
+ "offset": offset,
1596
+ **kwargs
1597
+ }.items() if v is not None}
1598
+
1599
+ result = await self._connector.execute("project_sections", "list", params)
1600
+ # Cast generic envelope to concrete typed result
1601
+ return ProjectSectionsListResult(
1602
+ data=result.data,
1603
+ meta=result.meta )
1604
+
1605
+
1606
+
1607
+ class SectionsQuery:
1608
+ """
1609
+ Query class for Sections entity operations.
1610
+ """
1611
+
1612
+ def __init__(self, connector: AsanaConnector):
1613
+ """Initialize query with connector reference."""
1614
+ self._connector = connector
1615
+
1616
+ async def get(
1617
+ self,
1618
+ section_gid: str,
1619
+ **kwargs
1620
+ ) -> SectionsGetResult:
1621
+ """
1622
+ Get a single section by its ID
1623
+
1624
+ Args:
1625
+ section_gid: Section GID
1626
+ **kwargs: Additional parameters
1627
+
1628
+ Returns:
1629
+ SectionsGetResult
1630
+ """
1631
+ params = {k: v for k, v in {
1632
+ "section_gid": section_gid,
1633
+ **kwargs
1634
+ }.items() if v is not None}
1635
+
1636
+ result = await self._connector.execute("sections", "get", params)
1637
+ # Cast generic envelope to concrete typed result
1638
+ return SectionsGetResult(
1639
+ data=result.data )
1640
+
1641
+
1642
+
1643
+ class TaskSubtasksQuery:
1644
+ """
1645
+ Query class for TaskSubtasks entity operations.
1646
+ """
1647
+
1648
+ def __init__(self, connector: AsanaConnector):
1649
+ """Initialize query with connector reference."""
1650
+ self._connector = connector
1651
+
1652
+ async def list(
1653
+ self,
1654
+ task_gid: str,
1655
+ limit: int | None = None,
1656
+ offset: str | None = None,
1657
+ **kwargs
1658
+ ) -> TaskSubtasksListResult:
1659
+ """
1660
+ Returns all subtasks of a task
1661
+
1662
+ Args:
1663
+ task_gid: Task GID to list subtasks from
1664
+ limit: Number of items to return per page
1665
+ offset: Pagination offset token
1666
+ **kwargs: Additional parameters
1667
+
1668
+ Returns:
1669
+ TaskSubtasksListResult
1670
+ """
1671
+ params = {k: v for k, v in {
1672
+ "task_gid": task_gid,
1673
+ "limit": limit,
1674
+ "offset": offset,
1675
+ **kwargs
1676
+ }.items() if v is not None}
1677
+
1678
+ result = await self._connector.execute("task_subtasks", "list", params)
1679
+ # Cast generic envelope to concrete typed result
1680
+ return TaskSubtasksListResult(
1681
+ data=result.data,
1682
+ meta=result.meta )
1683
+
1684
+
1685
+
1686
+ class TaskDependenciesQuery:
1687
+ """
1688
+ Query class for TaskDependencies entity operations.
1689
+ """
1690
+
1691
+ def __init__(self, connector: AsanaConnector):
1692
+ """Initialize query with connector reference."""
1693
+ self._connector = connector
1694
+
1695
+ async def list(
1696
+ self,
1697
+ task_gid: str,
1698
+ limit: int | None = None,
1699
+ offset: str | None = None,
1700
+ **kwargs
1701
+ ) -> TaskDependenciesListResult:
1702
+ """
1703
+ Returns all tasks that this task depends on
1704
+
1705
+ Args:
1706
+ task_gid: Task GID to list dependencies from
1707
+ limit: Number of items to return per page
1708
+ offset: Pagination offset token
1709
+ **kwargs: Additional parameters
1710
+
1711
+ Returns:
1712
+ TaskDependenciesListResult
1713
+ """
1714
+ params = {k: v for k, v in {
1715
+ "task_gid": task_gid,
1716
+ "limit": limit,
1717
+ "offset": offset,
1718
+ **kwargs
1719
+ }.items() if v is not None}
1720
+
1721
+ result = await self._connector.execute("task_dependencies", "list", params)
1722
+ # Cast generic envelope to concrete typed result
1723
+ return TaskDependenciesListResult(
1724
+ data=result.data,
1725
+ meta=result.meta )
1726
+
1727
+
1728
+
1729
+ class TaskDependentsQuery:
1730
+ """
1731
+ Query class for TaskDependents entity operations.
1732
+ """
1733
+
1734
+ def __init__(self, connector: AsanaConnector):
1735
+ """Initialize query with connector reference."""
1736
+ self._connector = connector
1737
+
1738
+ async def list(
1739
+ self,
1740
+ task_gid: str,
1741
+ limit: int | None = None,
1742
+ offset: str | None = None,
1743
+ **kwargs
1744
+ ) -> TaskDependentsListResult:
1745
+ """
1746
+ Returns all tasks that depend on this task
1747
+
1748
+ Args:
1749
+ task_gid: Task GID to list dependents from
1750
+ limit: Number of items to return per page
1751
+ offset: Pagination offset token
1752
+ **kwargs: Additional parameters
1753
+
1754
+ Returns:
1755
+ TaskDependentsListResult
1756
+ """
1757
+ params = {k: v for k, v in {
1758
+ "task_gid": task_gid,
1759
+ "limit": limit,
1760
+ "offset": offset,
1761
+ **kwargs
1762
+ }.items() if v is not None}
1763
+
1764
+ result = await self._connector.execute("task_dependents", "list", params)
1765
+ # Cast generic envelope to concrete typed result
1766
+ return TaskDependentsListResult(
1767
+ data=result.data,
1768
+ meta=result.meta )
1769
+
1770
+