airbyte-agent-linear 0.19.18__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_linear/__init__.py +43 -0
  2. airbyte_agent_linear/_vendored/__init__.py +1 -0
  3. airbyte_agent_linear/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_linear/_vendored/connector_sdk/auth_strategies.py +1123 -0
  5. airbyte_agent_linear/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_linear/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_linear/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_linear/_vendored/connector_sdk/connector_model_loader.py +957 -0
  9. airbyte_agent_linear/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_linear/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_linear/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_linear/_vendored/connector_sdk/executor/hosted_executor.py +197 -0
  13. airbyte_agent_linear/_vendored/connector_sdk/executor/local_executor.py +1504 -0
  14. airbyte_agent_linear/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_linear/_vendored/connector_sdk/extensions.py +655 -0
  16. airbyte_agent_linear/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_linear/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_linear/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_linear/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_linear/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_linear/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_linear/_vendored/connector_sdk/http/response.py +102 -0
  23. airbyte_agent_linear/_vendored/connector_sdk/http_client.py +679 -0
  24. airbyte_agent_linear/_vendored/connector_sdk/logging/__init__.py +11 -0
  25. airbyte_agent_linear/_vendored/connector_sdk/logging/logger.py +264 -0
  26. airbyte_agent_linear/_vendored/connector_sdk/logging/types.py +92 -0
  27. airbyte_agent_linear/_vendored/connector_sdk/observability/__init__.py +11 -0
  28. airbyte_agent_linear/_vendored/connector_sdk/observability/models.py +19 -0
  29. airbyte_agent_linear/_vendored/connector_sdk/observability/redactor.py +81 -0
  30. airbyte_agent_linear/_vendored/connector_sdk/observability/session.py +94 -0
  31. airbyte_agent_linear/_vendored/connector_sdk/performance/__init__.py +6 -0
  32. airbyte_agent_linear/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  33. airbyte_agent_linear/_vendored/connector_sdk/performance/metrics.py +93 -0
  34. airbyte_agent_linear/_vendored/connector_sdk/schema/__init__.py +75 -0
  35. airbyte_agent_linear/_vendored/connector_sdk/schema/base.py +161 -0
  36. airbyte_agent_linear/_vendored/connector_sdk/schema/components.py +238 -0
  37. airbyte_agent_linear/_vendored/connector_sdk/schema/connector.py +131 -0
  38. airbyte_agent_linear/_vendored/connector_sdk/schema/extensions.py +109 -0
  39. airbyte_agent_linear/_vendored/connector_sdk/schema/operations.py +146 -0
  40. airbyte_agent_linear/_vendored/connector_sdk/schema/security.py +213 -0
  41. airbyte_agent_linear/_vendored/connector_sdk/secrets.py +182 -0
  42. airbyte_agent_linear/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  43. airbyte_agent_linear/_vendored/connector_sdk/telemetry/config.py +32 -0
  44. airbyte_agent_linear/_vendored/connector_sdk/telemetry/events.py +58 -0
  45. airbyte_agent_linear/_vendored/connector_sdk/telemetry/tracker.py +151 -0
  46. airbyte_agent_linear/_vendored/connector_sdk/types.py +241 -0
  47. airbyte_agent_linear/_vendored/connector_sdk/utils.py +60 -0
  48. airbyte_agent_linear/_vendored/connector_sdk/validation.py +822 -0
  49. airbyte_agent_linear/connector.py +460 -0
  50. airbyte_agent_linear/connector_model.py +780 -0
  51. airbyte_agent_linear/models.py +221 -0
  52. airbyte_agent_linear/types.py +44 -0
  53. airbyte_agent_linear-0.19.18.dist-info/METADATA +101 -0
  54. airbyte_agent_linear-0.19.18.dist-info/RECORD +55 -0
  55. airbyte_agent_linear-0.19.18.dist-info/WHEEL +4 -0
@@ -0,0 +1,460 @@
1
+ """
2
+ linear connector.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Any, overload
8
+ try:
9
+ from typing import Literal
10
+ except ImportError:
11
+ from typing_extensions import Literal
12
+
13
+ from .connector_model import LinearConnectorModel
14
+
15
+ from .types import (
16
+ IssuesGetParams,
17
+ IssuesListParams,
18
+ ProjectsGetParams,
19
+ ProjectsListParams,
20
+ TeamsGetParams,
21
+ TeamsListParams,
22
+ )
23
+
24
+ if TYPE_CHECKING:
25
+ from .models import LinearAuthConfig
26
+ # Import response models and envelope models at runtime
27
+ from .models import (
28
+ LinearExecuteResult,
29
+ LinearExecuteResultWithMeta,
30
+ IssueResponse,
31
+ IssuesListResponse,
32
+ ProjectResponse,
33
+ ProjectsListResponse,
34
+ TeamResponse,
35
+ TeamsListResponse,
36
+ )
37
+
38
+
39
+ class LinearConnector:
40
+ """
41
+ Type-safe Linear API connector.
42
+
43
+ Auto-generated from OpenAPI specification with full type safety.
44
+ """
45
+
46
+ connector_name = "linear"
47
+ connector_version = "0.1.1"
48
+ vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
49
+
50
+ # Map of (entity, action) -> has_extractors for envelope wrapping decision
51
+ _EXTRACTOR_MAP = {
52
+ ("issues", "list"): False,
53
+ ("issues", "get"): False,
54
+ ("projects", "list"): False,
55
+ ("projects", "get"): False,
56
+ ("teams", "list"): False,
57
+ ("teams", "get"): False,
58
+ }
59
+
60
+ # Map of (entity, action) -> {python_param_name: api_param_name}
61
+ # Used to convert snake_case TypedDict keys to API parameter names in execute()
62
+ _PARAM_MAP = {
63
+ ('issues', 'list'): {'first': 'first', 'after': 'after'},
64
+ ('issues', 'get'): {'id': 'id'},
65
+ ('projects', 'list'): {'first': 'first', 'after': 'after'},
66
+ ('projects', 'get'): {'id': 'id'},
67
+ ('teams', 'list'): {'first': 'first', 'after': 'after'},
68
+ ('teams', 'get'): {'id': 'id'},
69
+ }
70
+
71
+ def __init__(
72
+ self,
73
+ auth_config: LinearAuthConfig | None = None,
74
+ external_user_id: str | None = None,
75
+ airbyte_client_id: str | None = None,
76
+ airbyte_client_secret: str | None = None,
77
+ on_token_refresh: Any | None = None ):
78
+ """
79
+ Initialize a new linear connector instance.
80
+
81
+ Supports both local and hosted execution modes:
82
+ - Local mode: Provide `auth_config` for direct API calls
83
+ - Hosted mode: Provide `external_user_id`, `airbyte_client_id`, and `airbyte_client_secret` for hosted execution
84
+
85
+ Args:
86
+ auth_config: Typed authentication configuration (required for local mode)
87
+ external_user_id: External user ID (required for hosted mode)
88
+ airbyte_client_id: Airbyte OAuth client ID (required for hosted mode)
89
+ airbyte_client_secret: Airbyte OAuth client secret (required for hosted mode)
90
+ on_token_refresh: Optional callback for OAuth2 token refresh persistence.
91
+ Called with new_tokens dict when tokens are refreshed. Can be sync or async.
92
+ Example: lambda tokens: save_to_database(tokens)
93
+ Examples:
94
+ # Local mode (direct API calls)
95
+ connector = LinearConnector(auth_config=LinearAuthConfig(api_key="..."))
96
+ # Hosted mode (executed on Airbyte cloud)
97
+ connector = LinearConnector(
98
+ external_user_id="user-123",
99
+ airbyte_client_id="client_abc123",
100
+ airbyte_client_secret="secret_xyz789"
101
+ )
102
+
103
+ # Local mode with OAuth2 token refresh callback
104
+ def save_tokens(new_tokens: dict) -> None:
105
+ # Persist updated tokens to your storage (file, database, etc.)
106
+ with open("tokens.json", "w") as f:
107
+ json.dump(new_tokens, f)
108
+
109
+ connector = LinearConnector(
110
+ auth_config=LinearAuthConfig(access_token="...", refresh_token="..."),
111
+ on_token_refresh=save_tokens
112
+ )
113
+ """
114
+ # Hosted mode: external_user_id, airbyte_client_id, and airbyte_client_secret provided
115
+ if external_user_id and airbyte_client_id and airbyte_client_secret:
116
+ from ._vendored.connector_sdk.executor import HostedExecutor
117
+ self._executor = HostedExecutor(
118
+ external_user_id=external_user_id,
119
+ airbyte_client_id=airbyte_client_id,
120
+ airbyte_client_secret=airbyte_client_secret,
121
+ connector_definition_id=str(LinearConnectorModel.id),
122
+ )
123
+ else:
124
+ # Local mode: auth_config required
125
+ if not auth_config:
126
+ raise ValueError(
127
+ "Either provide (external_user_id, airbyte_client_id, airbyte_client_secret) for hosted mode "
128
+ "or auth_config for local mode"
129
+ )
130
+
131
+ from ._vendored.connector_sdk.executor import LocalExecutor
132
+
133
+ # Build config_values dict from server variables
134
+ config_values = None
135
+
136
+ self._executor = LocalExecutor(
137
+ model=LinearConnectorModel,
138
+ auth_config=auth_config.model_dump() if auth_config else None,
139
+ config_values=config_values,
140
+ on_token_refresh=on_token_refresh
141
+ )
142
+
143
+ # Update base_url with server variables if provided
144
+
145
+ # Initialize entity query objects
146
+ self.issues = IssuesQuery(self)
147
+ self.projects = ProjectsQuery(self)
148
+ self.teams = TeamsQuery(self)
149
+
150
+ # ===== TYPED EXECUTE METHOD (Recommended Interface) =====
151
+
152
+ @overload
153
+ async def execute(
154
+ self,
155
+ entity: Literal["issues"],
156
+ action: Literal["list"],
157
+ params: "IssuesListParams"
158
+ ) -> "IssuesListResponse": ...
159
+
160
+ @overload
161
+ async def execute(
162
+ self,
163
+ entity: Literal["issues"],
164
+ action: Literal["get"],
165
+ params: "IssuesGetParams"
166
+ ) -> "IssueResponse": ...
167
+
168
+ @overload
169
+ async def execute(
170
+ self,
171
+ entity: Literal["projects"],
172
+ action: Literal["list"],
173
+ params: "ProjectsListParams"
174
+ ) -> "ProjectsListResponse": ...
175
+
176
+ @overload
177
+ async def execute(
178
+ self,
179
+ entity: Literal["projects"],
180
+ action: Literal["get"],
181
+ params: "ProjectsGetParams"
182
+ ) -> "ProjectResponse": ...
183
+
184
+ @overload
185
+ async def execute(
186
+ self,
187
+ entity: Literal["teams"],
188
+ action: Literal["list"],
189
+ params: "TeamsListParams"
190
+ ) -> "TeamsListResponse": ...
191
+
192
+ @overload
193
+ async def execute(
194
+ self,
195
+ entity: Literal["teams"],
196
+ action: Literal["get"],
197
+ params: "TeamsGetParams"
198
+ ) -> "TeamResponse": ...
199
+
200
+
201
+ @overload
202
+ async def execute(
203
+ self,
204
+ entity: str,
205
+ action: str,
206
+ params: dict[str, Any]
207
+ ) -> LinearExecuteResult[Any] | LinearExecuteResultWithMeta[Any, Any] | Any: ...
208
+
209
+ async def execute(
210
+ self,
211
+ entity: str,
212
+ action: str,
213
+ params: dict[str, Any] | None = None
214
+ ) -> Any:
215
+ """
216
+ Execute an entity operation with full type safety.
217
+
218
+ This is the recommended interface for blessed connectors as it:
219
+ - Uses the same signature as non-blessed connectors
220
+ - Provides full IDE autocomplete for entity/action/params
221
+ - Makes migration from generic to blessed connectors seamless
222
+
223
+ Args:
224
+ entity: Entity name (e.g., "customers")
225
+ action: Operation action (e.g., "create", "get", "list")
226
+ params: Operation parameters (typed based on entity+action)
227
+
228
+ Returns:
229
+ Typed response based on the operation
230
+
231
+ Example:
232
+ customer = await connector.execute(
233
+ entity="customers",
234
+ action="get",
235
+ params={"id": "cus_123"}
236
+ )
237
+ """
238
+ from ._vendored.connector_sdk.executor import ExecutionConfig
239
+
240
+ # Remap parameter names from snake_case (TypedDict keys) to API parameter names
241
+ if params:
242
+ param_map = self._PARAM_MAP.get((entity, action), {})
243
+ if param_map:
244
+ params = {param_map.get(k, k): v for k, v in params.items()}
245
+
246
+ # Use ExecutionConfig for both local and hosted executors
247
+ config = ExecutionConfig(
248
+ entity=entity,
249
+ action=action,
250
+ params=params
251
+ )
252
+
253
+ result = await self._executor.execute(config)
254
+
255
+ if not result.success:
256
+ raise RuntimeError(f"Execution failed: {result.error}")
257
+
258
+ # Check if this operation has extractors configured
259
+ has_extractors = self._EXTRACTOR_MAP.get((entity, action), False)
260
+
261
+ if has_extractors:
262
+ # With extractors - return Pydantic envelope with data and meta
263
+ if result.meta is not None:
264
+ return LinearExecuteResultWithMeta[Any, Any](
265
+ data=result.data,
266
+ meta=result.meta
267
+ )
268
+ else:
269
+ return LinearExecuteResult[Any](data=result.data)
270
+ else:
271
+ # No extractors - return raw response data
272
+ return result.data
273
+
274
+
275
+
276
+ class IssuesQuery:
277
+ """
278
+ Query class for Issues entity operations.
279
+ """
280
+
281
+ def __init__(self, connector: LinearConnector):
282
+ """Initialize query with connector reference."""
283
+ self._connector = connector
284
+
285
+ async def list(
286
+ self,
287
+ first: int | None = None,
288
+ after: str | None = None,
289
+ **kwargs
290
+ ) -> IssuesListResponse:
291
+ """
292
+ Returns a paginated list of issues via GraphQL with pagination support
293
+
294
+ Args:
295
+ first: Number of items to return (max 250)
296
+ after: Cursor to start after (for pagination)
297
+ **kwargs: Additional parameters
298
+
299
+ Returns:
300
+ IssuesListResponse
301
+ """
302
+ params = {k: v for k, v in {
303
+ "first": first,
304
+ "after": after,
305
+ **kwargs
306
+ }.items() if v is not None}
307
+
308
+ result = await self._connector.execute("issues", "list", params)
309
+ return result
310
+
311
+
312
+
313
+ async def get(
314
+ self,
315
+ id: str | None = None,
316
+ **kwargs
317
+ ) -> IssueResponse:
318
+ """
319
+ Get a single issue by ID via GraphQL
320
+
321
+ Args:
322
+ id: Issue ID
323
+ **kwargs: Additional parameters
324
+
325
+ Returns:
326
+ IssueResponse
327
+ """
328
+ params = {k: v for k, v in {
329
+ "id": id,
330
+ **kwargs
331
+ }.items() if v is not None}
332
+
333
+ result = await self._connector.execute("issues", "get", params)
334
+ return result
335
+
336
+
337
+
338
+ class ProjectsQuery:
339
+ """
340
+ Query class for Projects entity operations.
341
+ """
342
+
343
+ def __init__(self, connector: LinearConnector):
344
+ """Initialize query with connector reference."""
345
+ self._connector = connector
346
+
347
+ async def list(
348
+ self,
349
+ first: int | None = None,
350
+ after: str | None = None,
351
+ **kwargs
352
+ ) -> ProjectsListResponse:
353
+ """
354
+ Returns a paginated list of projects via GraphQL with pagination support
355
+
356
+ Args:
357
+ first: Number of items to return (max 250)
358
+ after: Cursor to start after (for pagination)
359
+ **kwargs: Additional parameters
360
+
361
+ Returns:
362
+ ProjectsListResponse
363
+ """
364
+ params = {k: v for k, v in {
365
+ "first": first,
366
+ "after": after,
367
+ **kwargs
368
+ }.items() if v is not None}
369
+
370
+ result = await self._connector.execute("projects", "list", params)
371
+ return result
372
+
373
+
374
+
375
+ async def get(
376
+ self,
377
+ id: str | None = None,
378
+ **kwargs
379
+ ) -> ProjectResponse:
380
+ """
381
+ Get a single project by ID via GraphQL
382
+
383
+ Args:
384
+ id: Project ID
385
+ **kwargs: Additional parameters
386
+
387
+ Returns:
388
+ ProjectResponse
389
+ """
390
+ params = {k: v for k, v in {
391
+ "id": id,
392
+ **kwargs
393
+ }.items() if v is not None}
394
+
395
+ result = await self._connector.execute("projects", "get", params)
396
+ return result
397
+
398
+
399
+
400
+ class TeamsQuery:
401
+ """
402
+ Query class for Teams entity operations.
403
+ """
404
+
405
+ def __init__(self, connector: LinearConnector):
406
+ """Initialize query with connector reference."""
407
+ self._connector = connector
408
+
409
+ async def list(
410
+ self,
411
+ first: int | None = None,
412
+ after: str | None = None,
413
+ **kwargs
414
+ ) -> TeamsListResponse:
415
+ """
416
+ Returns a list of teams via GraphQL with pagination support
417
+
418
+ Args:
419
+ first: Number of items to return (max 250)
420
+ after: Cursor to start after (for pagination)
421
+ **kwargs: Additional parameters
422
+
423
+ Returns:
424
+ TeamsListResponse
425
+ """
426
+ params = {k: v for k, v in {
427
+ "first": first,
428
+ "after": after,
429
+ **kwargs
430
+ }.items() if v is not None}
431
+
432
+ result = await self._connector.execute("teams", "list", params)
433
+ return result
434
+
435
+
436
+
437
+ async def get(
438
+ self,
439
+ id: str | None = None,
440
+ **kwargs
441
+ ) -> TeamResponse:
442
+ """
443
+ Get a single team by ID via GraphQL
444
+
445
+ Args:
446
+ id: Team ID
447
+ **kwargs: Additional parameters
448
+
449
+ Returns:
450
+ TeamResponse
451
+ """
452
+ params = {k: v for k, v in {
453
+ "id": id,
454
+ **kwargs
455
+ }.items() if v is not None}
456
+
457
+ result = await self._connector.execute("teams", "get", params)
458
+ return result
459
+
460
+