airbyte-agent-stripe 0.5.28__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_stripe/__init__.py +237 -0
  2. airbyte_agent_stripe/_vendored/__init__.py +1 -0
  3. airbyte_agent_stripe/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_stripe/_vendored/connector_sdk/auth_strategies.py +1123 -0
  5. airbyte_agent_stripe/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_stripe/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_stripe/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_stripe/_vendored/connector_sdk/connector_model_loader.py +957 -0
  9. airbyte_agent_stripe/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_stripe/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_stripe/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_stripe/_vendored/connector_sdk/executor/hosted_executor.py +197 -0
  13. airbyte_agent_stripe/_vendored/connector_sdk/executor/local_executor.py +1524 -0
  14. airbyte_agent_stripe/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_stripe/_vendored/connector_sdk/extensions.py +655 -0
  16. airbyte_agent_stripe/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_stripe/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_stripe/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_stripe/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_stripe/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_stripe/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_stripe/_vendored/connector_sdk/http/response.py +102 -0
  23. airbyte_agent_stripe/_vendored/connector_sdk/http_client.py +686 -0
  24. airbyte_agent_stripe/_vendored/connector_sdk/logging/__init__.py +11 -0
  25. airbyte_agent_stripe/_vendored/connector_sdk/logging/logger.py +264 -0
  26. airbyte_agent_stripe/_vendored/connector_sdk/logging/types.py +92 -0
  27. airbyte_agent_stripe/_vendored/connector_sdk/observability/__init__.py +11 -0
  28. airbyte_agent_stripe/_vendored/connector_sdk/observability/models.py +19 -0
  29. airbyte_agent_stripe/_vendored/connector_sdk/observability/redactor.py +81 -0
  30. airbyte_agent_stripe/_vendored/connector_sdk/observability/session.py +94 -0
  31. airbyte_agent_stripe/_vendored/connector_sdk/performance/__init__.py +6 -0
  32. airbyte_agent_stripe/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  33. airbyte_agent_stripe/_vendored/connector_sdk/performance/metrics.py +93 -0
  34. airbyte_agent_stripe/_vendored/connector_sdk/schema/__init__.py +75 -0
  35. airbyte_agent_stripe/_vendored/connector_sdk/schema/base.py +161 -0
  36. airbyte_agent_stripe/_vendored/connector_sdk/schema/components.py +238 -0
  37. airbyte_agent_stripe/_vendored/connector_sdk/schema/connector.py +131 -0
  38. airbyte_agent_stripe/_vendored/connector_sdk/schema/extensions.py +109 -0
  39. airbyte_agent_stripe/_vendored/connector_sdk/schema/operations.py +146 -0
  40. airbyte_agent_stripe/_vendored/connector_sdk/schema/security.py +213 -0
  41. airbyte_agent_stripe/_vendored/connector_sdk/secrets.py +182 -0
  42. airbyte_agent_stripe/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  43. airbyte_agent_stripe/_vendored/connector_sdk/telemetry/config.py +32 -0
  44. airbyte_agent_stripe/_vendored/connector_sdk/telemetry/events.py +58 -0
  45. airbyte_agent_stripe/_vendored/connector_sdk/telemetry/tracker.py +151 -0
  46. airbyte_agent_stripe/_vendored/connector_sdk/types.py +241 -0
  47. airbyte_agent_stripe/_vendored/connector_sdk/utils.py +60 -0
  48. airbyte_agent_stripe/_vendored/connector_sdk/validation.py +822 -0
  49. airbyte_agent_stripe/connector.py +1579 -0
  50. airbyte_agent_stripe/connector_model.py +14869 -0
  51. airbyte_agent_stripe/models.py +2353 -0
  52. airbyte_agent_stripe/types.py +295 -0
  53. airbyte_agent_stripe-0.5.28.dist-info/METADATA +114 -0
  54. airbyte_agent_stripe-0.5.28.dist-info/RECORD +55 -0
  55. airbyte_agent_stripe-0.5.28.dist-info/WHEEL +4 -0
@@ -0,0 +1,135 @@
1
+ """
2
+ Template engine for auth_mapping in x-airbyte-auth-config.
3
+
4
+ Handles template substitution for mapping user-provided config values to auth parameters.
5
+ """
6
+
7
+ import re
8
+ from typing import Dict
9
+
10
+
11
+ class MissingVariableError(ValueError):
12
+ """Raised when a template variable is not found in config.
13
+
14
+ Extends ValueError for backwards compatibility with code that catches ValueError.
15
+ """
16
+
17
+ def __init__(self, var_name: str, available_fields: list):
18
+ self.var_name = var_name
19
+ self.available_fields = available_fields
20
+ super().__init__(f"Template variable '${{{var_name}}}' not found in config. " f"Available fields: {available_fields}")
21
+
22
+
23
+ def apply_template(template: str, values: Dict[str, str]) -> str:
24
+ """
25
+ Apply template substitution for auth_mapping.
26
+
27
+ Template syntax:
28
+ - ${variable}: Replaced with value from the values dict
29
+ - Any other text: Used as-is (constants or concatenation)
30
+
31
+ Examples:
32
+ >>> apply_template("${api_key}", {"api_key": "abc123"})
33
+ 'abc123'
34
+
35
+ >>> apply_template("${email}/token", {"email": "user@example.com"})
36
+ 'user@example.com/token'
37
+
38
+ >>> apply_template("api_token", {})
39
+ 'api_token'
40
+
41
+ >>> apply_template("", {})
42
+ ''
43
+
44
+ Args:
45
+ template: Template string with ${variable} placeholders
46
+ values: Dict of variable names to values
47
+
48
+ Returns:
49
+ Resolved template string
50
+
51
+ Raises:
52
+ MissingVariableError: If template contains unresolved variables
53
+ """
54
+ if not template:
55
+ return ""
56
+
57
+ # Check if it's a pure constant (no variables)
58
+ if "${" not in template:
59
+ return template
60
+
61
+ # Find all variable references
62
+ variable_pattern = re.compile(r"\$\{([^}]+)\}")
63
+ matches = variable_pattern.findall(template)
64
+
65
+ # Substitute all ${var} with values
66
+ result = template
67
+ for var_name in matches:
68
+ if var_name not in values:
69
+ raise MissingVariableError(var_name, list(values.keys()))
70
+ # Replace the variable with its value
71
+ result = result.replace(f"${{{var_name}}}", values[var_name])
72
+
73
+ return result
74
+
75
+
76
+ def apply_auth_mapping(
77
+ auth_mapping: Dict[str, str],
78
+ user_config: Dict[str, str],
79
+ required_fields: list | None = None,
80
+ ) -> Dict[str, str]:
81
+ """
82
+ Apply auth_mapping templates to user config.
83
+
84
+ Takes the auth_mapping from x-airbyte-auth-config and user-provided config,
85
+ and returns the mapped auth parameters. Optional fields (not in required_fields)
86
+ are skipped if their template variables are not provided.
87
+
88
+ Example:
89
+ >>> auth_mapping = {
90
+ ... "username": "${api_key}",
91
+ ... "password": ""
92
+ ... }
93
+ >>> user_config = {"api_key": "my_key_123"}
94
+ >>> apply_auth_mapping(auth_mapping, user_config)
95
+ {'username': 'my_key_123', 'password': ''}
96
+
97
+ >>> # Optional fields are skipped if not provided
98
+ >>> auth_mapping = {
99
+ ... "access_token": "${access_token}",
100
+ ... "refresh_token": "${refresh_token}",
101
+ ... }
102
+ >>> user_config = {"access_token": "abc123"}
103
+ >>> apply_auth_mapping(auth_mapping, user_config, required_fields=["access_token"])
104
+ {'access_token': 'abc123'}
105
+
106
+ Args:
107
+ auth_mapping: Dict mapping auth parameters to template strings
108
+ user_config: Dict of user-provided field values
109
+ required_fields: List of required field names. If a template references
110
+ a variable not in user_config and that variable is not required,
111
+ the mapping is skipped. Behavior:
112
+ - None: all fields are treated as required (backward compatible)
113
+ - []: no fields are required (all optional)
114
+ - ["foo"]: only "foo" is required
115
+
116
+ Returns:
117
+ Dict of resolved auth parameters
118
+
119
+ Raises:
120
+ MissingVariableError: If a required template variable is not found
121
+ """
122
+ resolved = {}
123
+ required_set = set(required_fields) if required_fields is not None else None
124
+
125
+ for param, template in auth_mapping.items():
126
+ try:
127
+ resolved[param] = apply_template(template, user_config)
128
+ except MissingVariableError as e:
129
+ # If the missing variable is not in required fields, skip this mapping
130
+ if required_set is not None and e.var_name not in required_set:
131
+ continue
132
+ # Otherwise, re-raise the error
133
+ raise
134
+
135
+ return resolved
@@ -0,0 +1,5 @@
1
+ """Cloud API utilities for Airbyte Platform integration."""
2
+
3
+ from .client import AirbyteCloudClient
4
+
5
+ __all__ = ["AirbyteCloudClient"]
@@ -0,0 +1,213 @@
1
+ """AirbyteCloudClient for Airbyte Platform API integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timedelta
6
+ from typing import Any
7
+
8
+ import httpx
9
+
10
+
11
+ class AirbyteCloudClient:
12
+ """Client for interacting with Airbyte Platform APIs.
13
+
14
+ Handles authentication, token caching, and API calls to:
15
+ - Get bearer tokens for authentication
16
+ - Look up connector instances for users
17
+ - Execute connectors via the cloud API
18
+
19
+ Example:
20
+ client = AirbyteCloudClient(
21
+ client_id="your-client-id",
22
+ client_secret="your-client-secret"
23
+ )
24
+
25
+ # Get a connector instance
26
+ instance_id = await client.get_connector_instance_id(
27
+ external_user_id="user-123",
28
+ connector_definition_id="stripe-def-456"
29
+ )
30
+
31
+ # Execute the connector
32
+ result = await client.execute_connector(
33
+ instance_id=instance_id,
34
+ entity="customers",
35
+ action="list",
36
+ params={"limit": 10}
37
+ )
38
+ """
39
+
40
+ AUTH_BASE_URL = "https://cloud.airbyte.com" # For token endpoint
41
+ API_BASE_URL = "https://api.airbyte.ai" # For instance lookup & execution
42
+
43
+ def __init__(self, client_id: str, client_secret: str):
44
+ """Initialize AirbyteCloudClient.
45
+
46
+ Args:
47
+ client_id: Airbyte client ID for authentication
48
+ client_secret: Airbyte client secret for authentication
49
+ """
50
+ self._client_id = client_id
51
+ self._client_secret = client_secret
52
+
53
+ # Token cache (instance-level)
54
+ self._cached_token: str | None = None
55
+ self._token_expires_at: datetime | None = None
56
+ self._http_client = httpx.AsyncClient(
57
+ timeout=httpx.Timeout(300.0), # 5 minute timeout
58
+ follow_redirects=True,
59
+ )
60
+
61
+ async def get_bearer_token(self) -> str:
62
+ """Get bearer token for API authentication.
63
+
64
+ Caches the token and only requests a new one when the cached token
65
+ is expired or missing. Adds a 60-second buffer before expiration
66
+ to avoid edge cases.
67
+
68
+ Returns:
69
+ Bearer token string
70
+
71
+ Raises:
72
+ httpx.HTTPStatusError: If the token request fails with 4xx/5xx
73
+ httpx.RequestError: If the network request fails
74
+
75
+ Example:
76
+ token = await client.get_bearer_token()
77
+ # Use token in Authorization header: f"Bearer {token}"
78
+ """
79
+ # Check if we have a cached token that hasn't expired
80
+ if self._cached_token and self._token_expires_at:
81
+ # Add 60 second buffer before expiration to avoid edge cases
82
+ now = datetime.now()
83
+ if now < self._token_expires_at:
84
+ # Token is still valid, return cached version
85
+ return self._cached_token
86
+
87
+ # Token is missing or expired, fetch a new one
88
+ url = f"{self.AUTH_BASE_URL}/api/v1/applications/token"
89
+ request_body = {
90
+ "client_id": self._client_id,
91
+ "client_secret": self._client_secret,
92
+ }
93
+
94
+ response = await self._http_client.post(url, json=request_body)
95
+ response.raise_for_status()
96
+
97
+ data = response.json()
98
+ access_token = data["access_token"]
99
+ expires_in = 15 * 60 # default 15 min expiry time * 60 seconds
100
+
101
+ # Calculate expiration time with 60 second buffer
102
+ expires_at = datetime.now() + timedelta(seconds=expires_in - 60)
103
+ self._cached_token = access_token
104
+ self._token_expires_at = expires_at
105
+
106
+ return access_token
107
+
108
+ async def get_connector_instance_id(
109
+ self,
110
+ external_user_id: str,
111
+ connector_definition_id: str,
112
+ ) -> str:
113
+ """Get connector instance ID for a user.
114
+
115
+ Looks up the connector instance that belongs to the specified user
116
+ and connector definition. Validates that exactly one instance exists.
117
+
118
+ Args:
119
+ external_user_id: User identifier in the Airbyte system
120
+ connector_definition_id: UUID of the connector definition
121
+
122
+ Returns:
123
+ Connector instance ID (UUID string)
124
+
125
+ Raises:
126
+ ValueError: If 0 or more than 1 instance is found
127
+ httpx.HTTPStatusError: If API returns 4xx/5xx status code
128
+ httpx.RequestError: If network request fails
129
+
130
+ Example:
131
+ instance_id = await client.get_connector_instance_id(
132
+ external_user_id="user-123",
133
+ connector_definition_id="550e8400-e29b-41d4-a716-446655440000"
134
+ )
135
+ """
136
+
137
+ token = await self.get_bearer_token()
138
+ url = f"{self.API_BASE_URL}/api/v1/connectors/instances_for_user"
139
+ params = {
140
+ "external_user_id": external_user_id,
141
+ "definition_id": connector_definition_id,
142
+ }
143
+
144
+ headers = {"Authorization": f"Bearer {token}"}
145
+ response = await self._http_client.get(url, params=params, headers=headers)
146
+ response.raise_for_status()
147
+
148
+ data = response.json()
149
+ instances = data["instances"]
150
+
151
+ if len(instances) == 0:
152
+ raise ValueError(f"No connector instance found for user '{external_user_id}' " f"and connector '{connector_definition_id}'")
153
+
154
+ if len(instances) > 1:
155
+ raise ValueError(
156
+ f"Multiple connector instances found for user '{external_user_id}' "
157
+ f"and connector '{connector_definition_id}'. Expected exactly 1, "
158
+ f"found {len(instances)}"
159
+ )
160
+
161
+ instance_id = instances[0]["id"]
162
+ return instance_id
163
+
164
+ async def execute_connector(
165
+ self,
166
+ instance_id: str,
167
+ entity: str,
168
+ action: str,
169
+ params: dict[str, Any] | None,
170
+ ) -> dict[str, Any]:
171
+ """Execute a connector operation.
172
+
173
+ Args:
174
+ instance_id: Connector instance UUID
175
+ entity: Entity name (e.g., "customers", "invoices")
176
+ action: Operation action (e.g., "list", "get", "create")
177
+ params: Optional parameters for the operation
178
+
179
+ Returns:
180
+ Raw JSON response dict from the API
181
+
182
+ Raises:
183
+ httpx.HTTPStatusError: If API returns 4xx/5xx status code
184
+ httpx.RequestError: If network request fails
185
+
186
+ Example:
187
+ result = await client.execute_connector(
188
+ instance_id="inst-123",
189
+ entity="customers",
190
+ action="list",
191
+ params={"limit": 10}
192
+ )
193
+ """
194
+ token = await self.get_bearer_token()
195
+ url = f"{self.API_BASE_URL}/api/v1/connectors/instances/{instance_id}/execute"
196
+ headers = {"Authorization": f"Bearer {token}"}
197
+ request_body = {
198
+ "entity": entity,
199
+ "action": action,
200
+ "params": params,
201
+ }
202
+
203
+ response = await self._http_client.post(url, json=request_body, headers=headers)
204
+ response.raise_for_status()
205
+
206
+ return response.json()
207
+
208
+ async def close(self):
209
+ """Close the HTTP client.
210
+
211
+ Call this when you're done using the client to clean up resources.
212
+ """
213
+ await self._http_client.aclose()