airbyte-agent-amazon-ads 0.1.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 (57) hide show
  1. airbyte_agent_amazon_ads/__init__.py +75 -0
  2. airbyte_agent_amazon_ads/_vendored/__init__.py +1 -0
  3. airbyte_agent_amazon_ads/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_amazon_ads/_vendored/connector_sdk/auth_strategies.py +1171 -0
  5. airbyte_agent_amazon_ads/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_amazon_ads/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_amazon_ads/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_amazon_ads/_vendored/connector_sdk/connector_model_loader.py +1116 -0
  9. airbyte_agent_amazon_ads/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_amazon_ads/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_amazon_ads/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_amazon_ads/_vendored/connector_sdk/executor/hosted_executor.py +196 -0
  13. airbyte_agent_amazon_ads/_vendored/connector_sdk/executor/local_executor.py +1773 -0
  14. airbyte_agent_amazon_ads/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_amazon_ads/_vendored/connector_sdk/extensions.py +693 -0
  16. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_amazon_ads/_vendored/connector_sdk/http/response.py +104 -0
  23. airbyte_agent_amazon_ads/_vendored/connector_sdk/http_client.py +693 -0
  24. airbyte_agent_amazon_ads/_vendored/connector_sdk/introspection.py +481 -0
  25. airbyte_agent_amazon_ads/_vendored/connector_sdk/logging/__init__.py +11 -0
  26. airbyte_agent_amazon_ads/_vendored/connector_sdk/logging/logger.py +273 -0
  27. airbyte_agent_amazon_ads/_vendored/connector_sdk/logging/types.py +93 -0
  28. airbyte_agent_amazon_ads/_vendored/connector_sdk/observability/__init__.py +11 -0
  29. airbyte_agent_amazon_ads/_vendored/connector_sdk/observability/config.py +179 -0
  30. airbyte_agent_amazon_ads/_vendored/connector_sdk/observability/models.py +19 -0
  31. airbyte_agent_amazon_ads/_vendored/connector_sdk/observability/redactor.py +81 -0
  32. airbyte_agent_amazon_ads/_vendored/connector_sdk/observability/session.py +103 -0
  33. airbyte_agent_amazon_ads/_vendored/connector_sdk/performance/__init__.py +6 -0
  34. airbyte_agent_amazon_ads/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  35. airbyte_agent_amazon_ads/_vendored/connector_sdk/performance/metrics.py +93 -0
  36. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/__init__.py +75 -0
  37. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/base.py +201 -0
  38. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/components.py +244 -0
  39. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/connector.py +120 -0
  40. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/extensions.py +301 -0
  41. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/operations.py +146 -0
  42. airbyte_agent_amazon_ads/_vendored/connector_sdk/schema/security.py +236 -0
  43. airbyte_agent_amazon_ads/_vendored/connector_sdk/secrets.py +182 -0
  44. airbyte_agent_amazon_ads/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  45. airbyte_agent_amazon_ads/_vendored/connector_sdk/telemetry/config.py +32 -0
  46. airbyte_agent_amazon_ads/_vendored/connector_sdk/telemetry/events.py +59 -0
  47. airbyte_agent_amazon_ads/_vendored/connector_sdk/telemetry/tracker.py +155 -0
  48. airbyte_agent_amazon_ads/_vendored/connector_sdk/types.py +255 -0
  49. airbyte_agent_amazon_ads/_vendored/connector_sdk/utils.py +60 -0
  50. airbyte_agent_amazon_ads/_vendored/connector_sdk/validation.py +828 -0
  51. airbyte_agent_amazon_ads/connector.py +711 -0
  52. airbyte_agent_amazon_ads/connector_model.py +2213 -0
  53. airbyte_agent_amazon_ads/models.py +225 -0
  54. airbyte_agent_amazon_ads/types.py +238 -0
  55. airbyte_agent_amazon_ads-0.1.18.dist-info/METADATA +140 -0
  56. airbyte_agent_amazon_ads-0.1.18.dist-info/RECORD +57 -0
  57. airbyte_agent_amazon_ads-0.1.18.dist-info/WHEEL +4 -0
@@ -0,0 +1,711 @@
1
+ """
2
+ Amazon-Ads connector.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import inspect
8
+ import json
9
+ import logging
10
+ from functools import wraps
11
+ from typing import TYPE_CHECKING, Any, Callable, Mapping, TypeVar, overload
12
+ try:
13
+ from typing import Literal
14
+ except ImportError:
15
+ from typing_extensions import Literal
16
+
17
+ from .connector_model import AmazonAdsConnectorModel
18
+ from ._vendored.connector_sdk.introspection import describe_entities, generate_tool_description
19
+ from .types import (
20
+ PortfoliosGetParams,
21
+ PortfoliosListParams,
22
+ ProfilesGetParams,
23
+ ProfilesListParams,
24
+ SponsoredProductCampaignsGetParams,
25
+ SponsoredProductCampaignsListParams,
26
+ SponsoredProductCampaignsListParamsStatefilter,
27
+ AirbyteSearchParams,
28
+ ProfilesSearchFilter,
29
+ ProfilesSearchQuery,
30
+ )
31
+ if TYPE_CHECKING:
32
+ from .models import AmazonAdsAuthConfig
33
+ # Import response models and envelope models at runtime
34
+ from .models import (
35
+ AmazonAdsExecuteResult,
36
+ AmazonAdsExecuteResultWithMeta,
37
+ ProfilesListResult,
38
+ PortfoliosListResult,
39
+ SponsoredProductCampaignsListResult,
40
+ Portfolio,
41
+ Profile,
42
+ SponsoredProductCampaign,
43
+ AirbyteSearchHit,
44
+ AirbyteSearchResult,
45
+ ProfilesSearchData,
46
+ ProfilesSearchResult,
47
+ )
48
+
49
+ # TypeVar for decorator type preservation
50
+ _F = TypeVar("_F", bound=Callable[..., Any])
51
+
52
+ DEFAULT_MAX_OUTPUT_CHARS = 50_000 # ~50KB default, configurable per-tool
53
+
54
+
55
+ def _raise_output_too_large(message: str) -> None:
56
+ try:
57
+ from pydantic_ai import ModelRetry # type: ignore[import-not-found]
58
+ except Exception as exc:
59
+ raise RuntimeError(message) from exc
60
+ raise ModelRetry(message)
61
+
62
+
63
+ def _check_output_size(result: Any, max_chars: int | None, tool_name: str) -> Any:
64
+ if max_chars is None or max_chars <= 0:
65
+ return result
66
+
67
+ try:
68
+ serialized = json.dumps(result, default=str)
69
+ except (TypeError, ValueError):
70
+ return result
71
+
72
+ if len(serialized) > max_chars:
73
+ truncated_preview = serialized[:500] + "..." if len(serialized) > 500 else serialized
74
+ _raise_output_too_large(
75
+ f"Tool '{tool_name}' output too large ({len(serialized):,} chars, limit {max_chars:,}). "
76
+ "Please narrow your query by: using the 'fields' parameter to select only needed fields, "
77
+ "adding filters, or reducing the 'limit'. "
78
+ f"Preview: {truncated_preview}"
79
+ )
80
+
81
+ return result
82
+
83
+
84
+
85
+
86
+ class AmazonAdsConnector:
87
+ """
88
+ Type-safe Amazon-Ads API connector.
89
+
90
+ Auto-generated from OpenAPI specification with full type safety.
91
+ """
92
+
93
+ connector_name = "amazon-ads"
94
+ connector_version = "1.0.4"
95
+ vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
96
+
97
+ # Map of (entity, action) -> needs_envelope for envelope wrapping decision
98
+ _ENVELOPE_MAP = {
99
+ ("profiles", "list"): True,
100
+ ("profiles", "get"): None,
101
+ ("portfolios", "list"): True,
102
+ ("portfolios", "get"): None,
103
+ ("sponsored_product_campaigns", "list"): True,
104
+ ("sponsored_product_campaigns", "get"): None,
105
+ }
106
+
107
+ # Map of (entity, action) -> {python_param_name: api_param_name}
108
+ # Used to convert snake_case TypedDict keys to API parameter names in execute()
109
+ _PARAM_MAP = {
110
+ ('profiles', 'list'): {'profile_type_filter': 'profileTypeFilter'},
111
+ ('profiles', 'get'): {'profile_id': 'profileId'},
112
+ ('portfolios', 'list'): {'include_extended_data_fields': 'includeExtendedDataFields'},
113
+ ('portfolios', 'get'): {'portfolio_id': 'portfolioId'},
114
+ ('sponsored_product_campaigns', 'list'): {'state_filter': 'stateFilter', 'max_results': 'maxResults', 'next_token': 'nextToken'},
115
+ ('sponsored_product_campaigns', 'get'): {'campaign_id': 'campaignId'},
116
+ }
117
+
118
+ def __init__(
119
+ self,
120
+ auth_config: AmazonAdsAuthConfig | None = None,
121
+ external_user_id: str | None = None,
122
+ airbyte_client_id: str | None = None,
123
+ airbyte_client_secret: str | None = None,
124
+ on_token_refresh: Any | None = None,
125
+ region: str | None = None ):
126
+ """
127
+ Initialize a new amazon-ads connector instance.
128
+
129
+ Supports both local and hosted execution modes:
130
+ - Local mode: Provide `auth_config` for direct API calls
131
+ - Hosted mode: Provide `external_user_id`, `airbyte_client_id`, and `airbyte_client_secret` for hosted execution
132
+
133
+ Args:
134
+ auth_config: Typed authentication configuration (required for local mode)
135
+ external_user_id: External user ID (required for hosted mode)
136
+ airbyte_client_id: Airbyte OAuth client ID (required for hosted mode)
137
+ airbyte_client_secret: Airbyte OAuth client secret (required for hosted mode)
138
+ on_token_refresh: Optional callback for OAuth2 token refresh persistence.
139
+ Called with new_tokens dict when tokens are refreshed. Can be sync or async.
140
+ Example: lambda tokens: save_to_database(tokens) region: The Amazon Ads API endpoint URL based on region:
141
+ - NA (North America): https://advertising-api.amazon.com
142
+ - EU (Europe): https://advertising-api-eu.amazon.com
143
+ - FE (Far East): https://advertising-api-fe.amazon.com
144
+
145
+ Examples:
146
+ # Local mode (direct API calls)
147
+ connector = AmazonAdsConnector(auth_config=AmazonAdsAuthConfig(client_id="...", client_secret="...", refresh_token="..."))
148
+ # Hosted mode (executed on Airbyte cloud)
149
+ connector = AmazonAdsConnector(
150
+ external_user_id="user-123",
151
+ airbyte_client_id="client_abc123",
152
+ airbyte_client_secret="secret_xyz789"
153
+ )
154
+
155
+ # Local mode with OAuth2 token refresh callback
156
+ def save_tokens(new_tokens: dict) -> None:
157
+ # Persist updated tokens to your storage (file, database, etc.)
158
+ with open("tokens.json", "w") as f:
159
+ json.dump(new_tokens, f)
160
+
161
+ connector = AmazonAdsConnector(
162
+ auth_config=AmazonAdsAuthConfig(access_token="...", refresh_token="..."),
163
+ on_token_refresh=save_tokens
164
+ )
165
+ """
166
+ # Hosted mode: external_user_id, airbyte_client_id, and airbyte_client_secret provided
167
+ if external_user_id and airbyte_client_id and airbyte_client_secret:
168
+ from ._vendored.connector_sdk.executor import HostedExecutor
169
+ self._executor = HostedExecutor(
170
+ external_user_id=external_user_id,
171
+ airbyte_client_id=airbyte_client_id,
172
+ airbyte_client_secret=airbyte_client_secret,
173
+ connector_definition_id=str(AmazonAdsConnectorModel.id),
174
+ )
175
+ else:
176
+ # Local mode: auth_config required
177
+ if not auth_config:
178
+ raise ValueError(
179
+ "Either provide (external_user_id, airbyte_client_id, airbyte_client_secret) for hosted mode "
180
+ "or auth_config for local mode"
181
+ )
182
+
183
+ from ._vendored.connector_sdk.executor import LocalExecutor
184
+
185
+ # Build config_values dict from server variables
186
+ config_values: dict[str, str] = {}
187
+ if region:
188
+ config_values["region"] = region
189
+
190
+ self._executor = LocalExecutor(
191
+ model=AmazonAdsConnectorModel,
192
+ auth_config=auth_config.model_dump() if auth_config else None,
193
+ config_values=config_values,
194
+ on_token_refresh=on_token_refresh
195
+ )
196
+
197
+ # Update base_url with server variables if provided
198
+ base_url = self._executor.http_client.base_url
199
+ if region:
200
+ base_url = base_url.replace("{region}", region)
201
+ self._executor.http_client.base_url = base_url
202
+
203
+ # Initialize entity query objects
204
+ self.profiles = ProfilesQuery(self)
205
+ self.portfolios = PortfoliosQuery(self)
206
+ self.sponsored_product_campaigns = SponsoredProductCampaignsQuery(self)
207
+
208
+ # ===== TYPED EXECUTE METHOD (Recommended Interface) =====
209
+
210
+ @overload
211
+ async def execute(
212
+ self,
213
+ entity: Literal["profiles"],
214
+ action: Literal["list"],
215
+ params: "ProfilesListParams"
216
+ ) -> "ProfilesListResult": ...
217
+
218
+ @overload
219
+ async def execute(
220
+ self,
221
+ entity: Literal["profiles"],
222
+ action: Literal["get"],
223
+ params: "ProfilesGetParams"
224
+ ) -> "Profile": ...
225
+
226
+ @overload
227
+ async def execute(
228
+ self,
229
+ entity: Literal["portfolios"],
230
+ action: Literal["list"],
231
+ params: "PortfoliosListParams"
232
+ ) -> "PortfoliosListResult": ...
233
+
234
+ @overload
235
+ async def execute(
236
+ self,
237
+ entity: Literal["portfolios"],
238
+ action: Literal["get"],
239
+ params: "PortfoliosGetParams"
240
+ ) -> "Portfolio": ...
241
+
242
+ @overload
243
+ async def execute(
244
+ self,
245
+ entity: Literal["sponsored_product_campaigns"],
246
+ action: Literal["list"],
247
+ params: "SponsoredProductCampaignsListParams"
248
+ ) -> "SponsoredProductCampaignsListResult": ...
249
+
250
+ @overload
251
+ async def execute(
252
+ self,
253
+ entity: Literal["sponsored_product_campaigns"],
254
+ action: Literal["get"],
255
+ params: "SponsoredProductCampaignsGetParams"
256
+ ) -> "SponsoredProductCampaign": ...
257
+
258
+
259
+ @overload
260
+ async def execute(
261
+ self,
262
+ entity: str,
263
+ action: Literal["list", "get", "search"],
264
+ params: Mapping[str, Any]
265
+ ) -> AmazonAdsExecuteResult[Any] | AmazonAdsExecuteResultWithMeta[Any, Any] | Any: ...
266
+
267
+ async def execute(
268
+ self,
269
+ entity: str,
270
+ action: Literal["list", "get", "search"],
271
+ params: Mapping[str, Any] | None = None
272
+ ) -> Any:
273
+ """
274
+ Execute an entity operation with full type safety.
275
+
276
+ This is the recommended interface for blessed connectors as it:
277
+ - Uses the same signature as non-blessed connectors
278
+ - Provides full IDE autocomplete for entity/action/params
279
+ - Makes migration from generic to blessed connectors seamless
280
+
281
+ Args:
282
+ entity: Entity name (e.g., "customers")
283
+ action: Operation action (e.g., "create", "get", "list")
284
+ params: Operation parameters (typed based on entity+action)
285
+
286
+ Returns:
287
+ Typed response based on the operation
288
+
289
+ Example:
290
+ customer = await connector.execute(
291
+ entity="customers",
292
+ action="get",
293
+ params={"id": "cus_123"}
294
+ )
295
+ """
296
+ from ._vendored.connector_sdk.executor import ExecutionConfig
297
+
298
+ # Remap parameter names from snake_case (TypedDict keys) to API parameter names
299
+ resolved_params = dict(params) if params is not None else None
300
+ if resolved_params:
301
+ param_map = self._PARAM_MAP.get((entity, action), {})
302
+ if param_map:
303
+ resolved_params = {param_map.get(k, k): v for k, v in resolved_params.items()}
304
+
305
+ # Use ExecutionConfig for both local and hosted executors
306
+ config = ExecutionConfig(
307
+ entity=entity,
308
+ action=action,
309
+ params=resolved_params
310
+ )
311
+
312
+ result = await self._executor.execute(config)
313
+
314
+ if not result.success:
315
+ raise RuntimeError(f"Execution failed: {result.error}")
316
+
317
+ # Check if this operation has extractors configured
318
+ has_extractors = self._ENVELOPE_MAP.get((entity, action), False)
319
+
320
+ if has_extractors:
321
+ # With extractors - return Pydantic envelope with data and meta
322
+ if result.meta is not None:
323
+ return AmazonAdsExecuteResultWithMeta[Any, Any](
324
+ data=result.data,
325
+ meta=result.meta
326
+ )
327
+ else:
328
+ return AmazonAdsExecuteResult[Any](data=result.data)
329
+ else:
330
+ # No extractors - return raw response data
331
+ return result.data
332
+
333
+ # ===== INTROSPECTION METHODS =====
334
+
335
+ @classmethod
336
+ def tool_utils(
337
+ cls,
338
+ func: _F | None = None,
339
+ *,
340
+ update_docstring: bool = True,
341
+ enable_hosted_mode_features: bool = True,
342
+ max_output_chars: int | None = DEFAULT_MAX_OUTPUT_CHARS,
343
+ ) -> _F | Callable[[_F], _F]:
344
+ """
345
+ Decorator that adds tool utilities like docstring augmentation and output limits.
346
+
347
+ Usage:
348
+ @mcp.tool()
349
+ @AmazonAdsConnector.tool_utils
350
+ async def execute(entity: str, action: str, params: dict):
351
+ ...
352
+
353
+ @mcp.tool()
354
+ @AmazonAdsConnector.tool_utils(update_docstring=False, max_output_chars=None)
355
+ async def execute(entity: str, action: str, params: dict):
356
+ ...
357
+
358
+ Args:
359
+ update_docstring: When True, append connector capabilities to __doc__.
360
+ enable_hosted_mode_features: When False, omit hosted-mode search sections from docstrings.
361
+ max_output_chars: Max serialized output size before raising. Use None to disable.
362
+ """
363
+
364
+ def decorate(inner: _F) -> _F:
365
+ if update_docstring:
366
+ description = generate_tool_description(
367
+ AmazonAdsConnectorModel,
368
+ enable_hosted_mode_features=enable_hosted_mode_features,
369
+ )
370
+ original_doc = inner.__doc__ or ""
371
+ if original_doc.strip():
372
+ full_doc = f"{original_doc.strip()}\n{description}"
373
+ else:
374
+ full_doc = description
375
+ else:
376
+ full_doc = ""
377
+
378
+ if inspect.iscoroutinefunction(inner):
379
+
380
+ @wraps(inner)
381
+ async def aw(*args: Any, **kwargs: Any) -> Any:
382
+ result = await inner(*args, **kwargs)
383
+ return _check_output_size(result, max_output_chars, inner.__name__)
384
+
385
+ wrapped = aw
386
+ else:
387
+
388
+ @wraps(inner)
389
+ def sw(*args: Any, **kwargs: Any) -> Any:
390
+ result = inner(*args, **kwargs)
391
+ return _check_output_size(result, max_output_chars, inner.__name__)
392
+
393
+ wrapped = sw
394
+
395
+ if update_docstring:
396
+ wrapped.__doc__ = full_doc
397
+ return wrapped # type: ignore[return-value]
398
+
399
+ if func is not None:
400
+ return decorate(func)
401
+ return decorate
402
+
403
+ def list_entities(self) -> list[dict[str, Any]]:
404
+ """
405
+ Get structured data about available entities, actions, and parameters.
406
+
407
+ Returns a list of entity descriptions with:
408
+ - entity_name: Name of the entity (e.g., "contacts", "deals")
409
+ - description: Entity description from the first endpoint
410
+ - available_actions: List of actions (e.g., ["list", "get", "create"])
411
+ - parameters: Dict mapping action -> list of parameter dicts
412
+
413
+ Example:
414
+ entities = connector.list_entities()
415
+ for entity in entities:
416
+ print(f"{entity['entity_name']}: {entity['available_actions']}")
417
+ """
418
+ return describe_entities(AmazonAdsConnectorModel)
419
+
420
+ def entity_schema(self, entity: str) -> dict[str, Any] | None:
421
+ """
422
+ Get the JSON schema for an entity.
423
+
424
+ Args:
425
+ entity: Entity name (e.g., "contacts", "companies")
426
+
427
+ Returns:
428
+ JSON schema dict describing the entity structure, or None if not found.
429
+
430
+ Example:
431
+ schema = connector.entity_schema("contacts")
432
+ if schema:
433
+ print(f"Contact properties: {list(schema.get('properties', {}).keys())}")
434
+ """
435
+ entity_def = next(
436
+ (e for e in AmazonAdsConnectorModel.entities if e.name == entity),
437
+ None
438
+ )
439
+ if entity_def is None:
440
+ logging.getLogger(__name__).warning(
441
+ f"Entity '{entity}' not found. Available entities: "
442
+ f"{[e.name for e in AmazonAdsConnectorModel.entities]}"
443
+ )
444
+ return entity_def.entity_schema if entity_def else None
445
+
446
+
447
+
448
+ class ProfilesQuery:
449
+ """
450
+ Query class for Profiles entity operations.
451
+ """
452
+
453
+ def __init__(self, connector: AmazonAdsConnector):
454
+ """Initialize query with connector reference."""
455
+ self._connector = connector
456
+
457
+ async def list(
458
+ self,
459
+ profile_type_filter: str | None = None,
460
+ **kwargs
461
+ ) -> ProfilesListResult:
462
+ """
463
+ Returns a list of advertising profiles associated with the authenticated user.
464
+ Profiles represent an advertiser's account in a specific marketplace. Advertisers
465
+ may have a single profile if they advertise in only one marketplace, or a separate
466
+ profile for each marketplace if they advertise regionally or globally.
467
+
468
+
469
+ Args:
470
+ profile_type_filter: Filter profiles by type. Comma-separated list of profile types.
471
+ Valid values: seller, vendor, agency
472
+
473
+ **kwargs: Additional parameters
474
+
475
+ Returns:
476
+ ProfilesListResult
477
+ """
478
+ params = {k: v for k, v in {
479
+ "profileTypeFilter": profile_type_filter,
480
+ **kwargs
481
+ }.items() if v is not None}
482
+
483
+ result = await self._connector.execute("profiles", "list", params)
484
+ # Cast generic envelope to concrete typed result
485
+ return ProfilesListResult(
486
+ data=result.data
487
+ )
488
+
489
+
490
+
491
+ async def get(
492
+ self,
493
+ profile_id: str,
494
+ **kwargs
495
+ ) -> Profile:
496
+ """
497
+ Retrieves a single advertising profile by its ID. The profile contains
498
+ information about the advertiser's account in a specific marketplace.
499
+
500
+
501
+ Args:
502
+ profile_id: The unique identifier of the profile
503
+ **kwargs: Additional parameters
504
+
505
+ Returns:
506
+ Profile
507
+ """
508
+ params = {k: v for k, v in {
509
+ "profileId": profile_id,
510
+ **kwargs
511
+ }.items() if v is not None}
512
+
513
+ result = await self._connector.execute("profiles", "get", params)
514
+ return result
515
+
516
+
517
+
518
+ async def search(
519
+ self,
520
+ query: ProfilesSearchQuery,
521
+ limit: int | None = None,
522
+ cursor: str | None = None,
523
+ fields: list[list[str]] | None = None,
524
+ ) -> ProfilesSearchResult:
525
+ """
526
+ Search profiles records from Airbyte cache.
527
+
528
+ This operation searches cached data from Airbyte syncs.
529
+ Only available in hosted execution mode.
530
+
531
+ Available filter fields (ProfilesSearchFilter):
532
+ - account_info:
533
+ - country_code:
534
+ - currency_code:
535
+ - daily_budget:
536
+ - profile_id:
537
+ - timezone:
538
+
539
+ Args:
540
+ query: Filter and sort conditions. Supports operators like eq, neq, gt, gte, lt, lte,
541
+ in, like, fuzzy, keyword, not, and, or. Example: {"filter": {"eq": {"status": "active"}}}
542
+ limit: Maximum results to return (default 1000)
543
+ cursor: Pagination cursor from previous response's next_cursor
544
+ fields: Field paths to include in results. Each path is a list of keys for nested access.
545
+ Example: [["id"], ["user", "name"]] returns id and user.name fields.
546
+
547
+ Returns:
548
+ ProfilesSearchResult with hits (list of AirbyteSearchHit[ProfilesSearchData]) and pagination info
549
+
550
+ Raises:
551
+ NotImplementedError: If called in local execution mode
552
+ """
553
+ params: dict[str, Any] = {"query": query}
554
+ if limit is not None:
555
+ params["limit"] = limit
556
+ if cursor is not None:
557
+ params["cursor"] = cursor
558
+ if fields is not None:
559
+ params["fields"] = fields
560
+
561
+ result = await self._connector.execute("profiles", "search", params)
562
+
563
+ # Parse response into typed result
564
+ return ProfilesSearchResult(
565
+ hits=[
566
+ AirbyteSearchHit[ProfilesSearchData](
567
+ id=hit.get("id"),
568
+ score=hit.get("score"),
569
+ data=ProfilesSearchData(**hit.get("data", {}))
570
+ )
571
+ for hit in result.get("hits", [])
572
+ ],
573
+ next_cursor=result.get("next_cursor"),
574
+ took_ms=result.get("took_ms")
575
+ )
576
+
577
+ class PortfoliosQuery:
578
+ """
579
+ Query class for Portfolios entity operations.
580
+ """
581
+
582
+ def __init__(self, connector: AmazonAdsConnector):
583
+ """Initialize query with connector reference."""
584
+ self._connector = connector
585
+
586
+ async def list(
587
+ self,
588
+ include_extended_data_fields: str | None = None,
589
+ **kwargs
590
+ ) -> PortfoliosListResult:
591
+ """
592
+ Returns a list of portfolios for the specified profile. Portfolios are used to
593
+ group campaigns together for organizational and budget management purposes.
594
+
595
+
596
+ Args:
597
+ include_extended_data_fields: Whether to include extended data fields in the response
598
+ **kwargs: Additional parameters
599
+
600
+ Returns:
601
+ PortfoliosListResult
602
+ """
603
+ params = {k: v for k, v in {
604
+ "includeExtendedDataFields": include_extended_data_fields,
605
+ **kwargs
606
+ }.items() if v is not None}
607
+
608
+ result = await self._connector.execute("portfolios", "list", params)
609
+ # Cast generic envelope to concrete typed result
610
+ return PortfoliosListResult(
611
+ data=result.data
612
+ )
613
+
614
+
615
+
616
+ async def get(
617
+ self,
618
+ portfolio_id: str,
619
+ **kwargs
620
+ ) -> Portfolio:
621
+ """
622
+ Retrieves a single portfolio by its ID using the v2 API.
623
+
624
+
625
+ Args:
626
+ portfolio_id: The unique identifier of the portfolio
627
+ **kwargs: Additional parameters
628
+
629
+ Returns:
630
+ Portfolio
631
+ """
632
+ params = {k: v for k, v in {
633
+ "portfolioId": portfolio_id,
634
+ **kwargs
635
+ }.items() if v is not None}
636
+
637
+ result = await self._connector.execute("portfolios", "get", params)
638
+ return result
639
+
640
+
641
+
642
+ class SponsoredProductCampaignsQuery:
643
+ """
644
+ Query class for SponsoredProductCampaigns entity operations.
645
+ """
646
+
647
+ def __init__(self, connector: AmazonAdsConnector):
648
+ """Initialize query with connector reference."""
649
+ self._connector = connector
650
+
651
+ async def list(
652
+ self,
653
+ state_filter: SponsoredProductCampaignsListParamsStatefilter | None = None,
654
+ max_results: int | None = None,
655
+ next_token: str | None = None,
656
+ **kwargs
657
+ ) -> SponsoredProductCampaignsListResult:
658
+ """
659
+ Returns a list of sponsored product campaigns for the specified profile.
660
+ Sponsored Products campaigns promote individual product listings on Amazon.
661
+
662
+
663
+ Args:
664
+ state_filter: Parameter stateFilter
665
+ max_results: Maximum number of results to return
666
+ next_token: Token for pagination
667
+ **kwargs: Additional parameters
668
+
669
+ Returns:
670
+ SponsoredProductCampaignsListResult
671
+ """
672
+ params = {k: v for k, v in {
673
+ "stateFilter": state_filter,
674
+ "maxResults": max_results,
675
+ "nextToken": next_token,
676
+ **kwargs
677
+ }.items() if v is not None}
678
+
679
+ result = await self._connector.execute("sponsored_product_campaigns", "list", params)
680
+ # Cast generic envelope to concrete typed result
681
+ return SponsoredProductCampaignsListResult(
682
+ data=result.data
683
+ )
684
+
685
+
686
+
687
+ async def get(
688
+ self,
689
+ campaign_id: str,
690
+ **kwargs
691
+ ) -> SponsoredProductCampaign:
692
+ """
693
+ Retrieves a single sponsored product campaign by its ID using the v2 API.
694
+
695
+
696
+ Args:
697
+ campaign_id: The unique identifier of the campaign
698
+ **kwargs: Additional parameters
699
+
700
+ Returns:
701
+ SponsoredProductCampaign
702
+ """
703
+ params = {k: v for k, v in {
704
+ "campaignId": campaign_id,
705
+ **kwargs
706
+ }.items() if v is not None}
707
+
708
+ result = await self._connector.execute("sponsored_product_campaigns", "get", params)
709
+ return result
710
+
711
+