airbyte-agent-facebook-marketing 0.1.0__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-facebook-marketing might be problematic. Click here for more details.
- airbyte_agent_facebook_marketing/__init__.py +221 -0
- airbyte_agent_facebook_marketing/_vendored/__init__.py +1 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/__init__.py +82 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/auth_strategies.py +1171 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/auth_template.py +135 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/cloud_utils/client.py +213 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/connector_model_loader.py +1120 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/constants.py +78 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/exceptions.py +23 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/executor/__init__.py +31 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/executor/hosted_executor.py +201 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/executor/local_executor.py +1854 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/executor/models.py +202 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/extensions.py +693 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/__init__.py +37 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/config.py +98 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/exceptions.py +119 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/protocols.py +114 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http/response.py +104 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/http_client.py +693 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/introspection.py +481 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/logging/__init__.py +11 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/logging/logger.py +273 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/logging/types.py +93 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/observability/__init__.py +11 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/observability/config.py +179 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/observability/models.py +19 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/observability/redactor.py +81 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/observability/session.py +103 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/performance/__init__.py +6 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/performance/instrumentation.py +57 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/performance/metrics.py +93 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/__init__.py +75 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/base.py +201 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/components.py +244 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/connector.py +120 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/extensions.py +301 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/operations.py +156 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/schema/security.py +236 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/secrets.py +182 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/telemetry/__init__.py +10 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/telemetry/config.py +32 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/telemetry/events.py +59 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/telemetry/tracker.py +155 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/types.py +270 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/utils.py +60 -0
- airbyte_agent_facebook_marketing/_vendored/connector_sdk/validation.py +848 -0
- airbyte_agent_facebook_marketing/connector.py +1553 -0
- airbyte_agent_facebook_marketing/connector_model.py +3120 -0
- airbyte_agent_facebook_marketing/models.py +814 -0
- airbyte_agent_facebook_marketing/types.py +1957 -0
- airbyte_agent_facebook_marketing-0.1.0.dist-info/METADATA +148 -0
- airbyte_agent_facebook_marketing-0.1.0.dist-info/RECORD +57 -0
- airbyte_agent_facebook_marketing-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Shared session context for both logging and telemetry."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
from .config import SDKConfig, load_config
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
# Cache the config at module level to avoid repeated reads
|
|
13
|
+
_cached_config: SDKConfig | None = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_config() -> SDKConfig:
|
|
17
|
+
"""Get cached SDK config or load from file."""
|
|
18
|
+
global _cached_config
|
|
19
|
+
if _cached_config is None:
|
|
20
|
+
_cached_config = load_config()
|
|
21
|
+
return _cached_config
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _clear_config_cache() -> None:
|
|
25
|
+
"""Clear the cached config. Used for testing."""
|
|
26
|
+
global _cached_config
|
|
27
|
+
_cached_config = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_persistent_user_id() -> str:
|
|
31
|
+
"""
|
|
32
|
+
Get the persistent anonymous user ID.
|
|
33
|
+
|
|
34
|
+
Now reads from ~/.airbyte/connector-sdk/config.yaml
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
An anonymous UUID string that uniquely identifies this user across sessions.
|
|
38
|
+
"""
|
|
39
|
+
return _get_config().user_id
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_public_ip() -> str | None:
|
|
43
|
+
"""
|
|
44
|
+
Fetch the public IP address of the user.
|
|
45
|
+
|
|
46
|
+
Returns None if unable to fetch (network issues, etc).
|
|
47
|
+
Uses httpx for a robust HTTP request to a public IP service.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
# NOTE: Import here intentionally - this is a non-critical network call
|
|
51
|
+
# that may fail. Importing at module level would make httpx a hard dependency.
|
|
52
|
+
import httpx
|
|
53
|
+
|
|
54
|
+
# Use a short timeout to avoid blocking
|
|
55
|
+
with httpx.Client(timeout=2.0) as client:
|
|
56
|
+
response = client.get("https://api.ipify.org?format=text")
|
|
57
|
+
response.raise_for_status()
|
|
58
|
+
return response.text.strip()
|
|
59
|
+
except Exception:
|
|
60
|
+
# Never fail - just return None
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_is_internal_user() -> bool:
|
|
65
|
+
"""
|
|
66
|
+
Check if the current user is an internal Airbyte user.
|
|
67
|
+
|
|
68
|
+
Now reads from ~/.airbyte/connector-sdk/config.yaml
|
|
69
|
+
Environment variable AIRBYTE_INTERNAL_USER can override.
|
|
70
|
+
|
|
71
|
+
Returns False if not set or on any error.
|
|
72
|
+
"""
|
|
73
|
+
return _get_config().is_internal_user
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ObservabilitySession:
|
|
77
|
+
"""Shared session context for both logging and telemetry."""
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
connector_name: str,
|
|
82
|
+
connector_version: str | None = None,
|
|
83
|
+
execution_context: str = "direct",
|
|
84
|
+
session_id: str | None = None,
|
|
85
|
+
):
|
|
86
|
+
self.session_id = session_id or str(uuid.uuid4())
|
|
87
|
+
self.user_id = get_persistent_user_id()
|
|
88
|
+
self.connector_name = connector_name
|
|
89
|
+
self.connector_version = connector_version
|
|
90
|
+
self.execution_context = execution_context
|
|
91
|
+
self.started_at = datetime.now(UTC)
|
|
92
|
+
self.operation_count = 0
|
|
93
|
+
self.metadata: Dict[str, Any] = {}
|
|
94
|
+
self.public_ip = get_public_ip()
|
|
95
|
+
self.is_internal_user = get_is_internal_user()
|
|
96
|
+
|
|
97
|
+
def increment_operations(self):
|
|
98
|
+
"""Increment the operation counter."""
|
|
99
|
+
self.operation_count += 1
|
|
100
|
+
|
|
101
|
+
def duration_seconds(self) -> float:
|
|
102
|
+
"""Calculate session duration in seconds."""
|
|
103
|
+
return (datetime.now(UTC) - self.started_at).total_seconds()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Performance instrumentation decorator for async functions."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Callable, TypeVar
|
|
7
|
+
|
|
8
|
+
# Type variable for generic function decoration
|
|
9
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def instrument(metric_name: str) -> Callable[[F], F]:
|
|
15
|
+
"""Decorator to instrument async functions with performance tracking.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
metric_name: Name of the metric to track
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Decorator function
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
@instrument("stripe.customer.list")
|
|
25
|
+
async def list_customers():
|
|
26
|
+
...
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def decorator(func: F) -> F:
|
|
30
|
+
@functools.wraps(func)
|
|
31
|
+
async def wrapper(*args, **kwargs):
|
|
32
|
+
start_time = time.time()
|
|
33
|
+
success = True
|
|
34
|
+
error = None
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
result = await func(*args, **kwargs)
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
success = False
|
|
42
|
+
error = e
|
|
43
|
+
raise
|
|
44
|
+
|
|
45
|
+
finally:
|
|
46
|
+
duration = time.time() - start_time
|
|
47
|
+
duration_ms = duration * 1000
|
|
48
|
+
|
|
49
|
+
# Log performance metrics
|
|
50
|
+
if success:
|
|
51
|
+
logger.debug(f"[{metric_name}] completed in {duration_ms:.2f}ms")
|
|
52
|
+
else:
|
|
53
|
+
logger.warning(f"[{metric_name}] failed after {duration_ms:.2f}ms: {error}")
|
|
54
|
+
|
|
55
|
+
return wrapper # type: ignore
|
|
56
|
+
|
|
57
|
+
return decorator
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Performance metrics tracking."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PerformanceMonitor:
|
|
9
|
+
"""Monitor and track performance metrics for operations."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize performance monitor."""
|
|
13
|
+
self._metrics: Dict[str, Dict[str, float]] = {}
|
|
14
|
+
|
|
15
|
+
def record(self, metric_name: str, duration: float):
|
|
16
|
+
"""Record a metric.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
metric_name: Name of the metric
|
|
20
|
+
duration: Duration in seconds
|
|
21
|
+
"""
|
|
22
|
+
if metric_name not in self._metrics:
|
|
23
|
+
self._metrics[metric_name] = {
|
|
24
|
+
"count": 0,
|
|
25
|
+
"total": 0.0,
|
|
26
|
+
"min": float("inf"),
|
|
27
|
+
"max": 0.0,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
metrics = self._metrics[metric_name]
|
|
31
|
+
metrics["count"] += 1
|
|
32
|
+
metrics["total"] += duration
|
|
33
|
+
metrics["min"] = min(metrics["min"], duration)
|
|
34
|
+
metrics["max"] = max(metrics["max"], duration)
|
|
35
|
+
|
|
36
|
+
def get_stats(self, metric_name: str) -> Dict[str, float] | None:
|
|
37
|
+
"""Get statistics for a metric.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
metric_name: Name of the metric
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dictionary with count, total, mean, min, max or None if metric not found
|
|
44
|
+
"""
|
|
45
|
+
if metric_name not in self._metrics:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
metrics = self._metrics[metric_name]
|
|
49
|
+
return {
|
|
50
|
+
"count": metrics["count"],
|
|
51
|
+
"total": metrics["total"],
|
|
52
|
+
"mean": metrics["total"] / metrics["count"] if metrics["count"] > 0 else 0.0,
|
|
53
|
+
"min": metrics["min"] if metrics["min"] != float("inf") else 0.0,
|
|
54
|
+
"max": metrics["max"],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def get_all_stats(self) -> Dict[str, Dict[str, float]]:
|
|
58
|
+
"""Get statistics for all metrics.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dictionary mapping metric names to their statistics
|
|
62
|
+
"""
|
|
63
|
+
return {name: self.get_stats(name) for name in self._metrics.keys()}
|
|
64
|
+
|
|
65
|
+
def reset(self, metric_name: str | None = None):
|
|
66
|
+
"""Reset metrics.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
metric_name: Specific metric to reset, or None to reset all
|
|
70
|
+
"""
|
|
71
|
+
if metric_name:
|
|
72
|
+
if metric_name in self._metrics:
|
|
73
|
+
del self._metrics[metric_name]
|
|
74
|
+
else:
|
|
75
|
+
self._metrics.clear()
|
|
76
|
+
|
|
77
|
+
@asynccontextmanager
|
|
78
|
+
async def track(self, metric_name: str):
|
|
79
|
+
"""Context manager for tracking operation duration.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
metric_name: Name of the metric to track
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
async with monitor.track("api_call"):
|
|
86
|
+
result = await some_async_operation()
|
|
87
|
+
"""
|
|
88
|
+
start_time = time.time()
|
|
89
|
+
try:
|
|
90
|
+
yield
|
|
91
|
+
finally:
|
|
92
|
+
duration = time.time() - start_time
|
|
93
|
+
self.record(metric_name, duration)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic 2 schema models for OpenAPI 3.0 connector specifications.
|
|
3
|
+
|
|
4
|
+
This package provides strongly-typed Pydantic models that mirror the OpenAPI 3.0
|
|
5
|
+
specification while supporting Airbyte-specific extensions.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
import yaml
|
|
9
|
+
from . import OpenAPIConnector
|
|
10
|
+
|
|
11
|
+
with open('connector.yaml') as f:
|
|
12
|
+
data = yaml.safe_load(f)
|
|
13
|
+
|
|
14
|
+
connector = OpenAPIConnector(**data)
|
|
15
|
+
print(connector.list_resources())
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .base import Contact, Info, License, Server, ServerVariable
|
|
19
|
+
from .components import (
|
|
20
|
+
Components,
|
|
21
|
+
Header,
|
|
22
|
+
MediaType,
|
|
23
|
+
Parameter,
|
|
24
|
+
RequestBody,
|
|
25
|
+
Response,
|
|
26
|
+
Schema,
|
|
27
|
+
)
|
|
28
|
+
from .connector import ExternalDocs, OpenAPIConnector, Tag
|
|
29
|
+
from .extensions import PaginationConfig, RateLimitConfig, RetryConfig
|
|
30
|
+
from .operations import Operation, PathItem
|
|
31
|
+
from .security import (
|
|
32
|
+
AirbyteAuthConfig,
|
|
33
|
+
AuthConfigFieldSpec,
|
|
34
|
+
AuthConfigOption,
|
|
35
|
+
OAuth2Flow,
|
|
36
|
+
OAuth2Flows,
|
|
37
|
+
SecurityRequirement,
|
|
38
|
+
SecurityScheme,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
# Root model
|
|
43
|
+
"OpenAPIConnector",
|
|
44
|
+
"Tag",
|
|
45
|
+
"ExternalDocs",
|
|
46
|
+
# Base models
|
|
47
|
+
"Info",
|
|
48
|
+
"Server",
|
|
49
|
+
"ServerVariable",
|
|
50
|
+
"Contact",
|
|
51
|
+
"License",
|
|
52
|
+
# Security models
|
|
53
|
+
"SecurityScheme",
|
|
54
|
+
"SecurityRequirement",
|
|
55
|
+
"OAuth2Flow",
|
|
56
|
+
"OAuth2Flows",
|
|
57
|
+
"AirbyteAuthConfig",
|
|
58
|
+
"AuthConfigOption",
|
|
59
|
+
"AuthConfigFieldSpec",
|
|
60
|
+
# Component models
|
|
61
|
+
"Components",
|
|
62
|
+
"Schema",
|
|
63
|
+
"Parameter",
|
|
64
|
+
"RequestBody",
|
|
65
|
+
"Response",
|
|
66
|
+
"MediaType",
|
|
67
|
+
"Header",
|
|
68
|
+
# Operation models
|
|
69
|
+
"PathItem",
|
|
70
|
+
"Operation",
|
|
71
|
+
# Extension models (for future use)
|
|
72
|
+
"PaginationConfig",
|
|
73
|
+
"RateLimitConfig",
|
|
74
|
+
"RetryConfig",
|
|
75
|
+
]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base OpenAPI 3.1 models: Info, Server, Contact, License.
|
|
3
|
+
|
|
4
|
+
References:
|
|
5
|
+
- https://spec.openapis.org/oas/v3.1.0#info-object
|
|
6
|
+
- https://spec.openapis.org/oas/v3.1.0#server-object
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
14
|
+
from pydantic_core import Url
|
|
15
|
+
|
|
16
|
+
from .extensions import CacheConfig, ReplicationConfig, RetryConfig
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExampleQuestions(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Example questions for AI connector documentation.
|
|
22
|
+
|
|
23
|
+
Used to generate supported_questions.md and unsupported_questions.md files
|
|
24
|
+
that appear in the connector's README.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
28
|
+
|
|
29
|
+
supported: list[str] = Field(
|
|
30
|
+
default_factory=list,
|
|
31
|
+
description="Example questions the connector can handle",
|
|
32
|
+
)
|
|
33
|
+
unsupported: list[str] = Field(
|
|
34
|
+
default_factory=list,
|
|
35
|
+
description="Example questions the connector cannot handle",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Contact(BaseModel):
|
|
40
|
+
"""
|
|
41
|
+
Contact information for the API.
|
|
42
|
+
|
|
43
|
+
OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#contact-object
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
47
|
+
|
|
48
|
+
name: str | None = None
|
|
49
|
+
url: str | None = None
|
|
50
|
+
email: str | None = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class License(BaseModel):
|
|
54
|
+
"""
|
|
55
|
+
License information for the API.
|
|
56
|
+
|
|
57
|
+
OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#license-object
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
61
|
+
|
|
62
|
+
name: str
|
|
63
|
+
url: str | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class DocUrlType(StrEnum):
|
|
67
|
+
API_DEPRECATIONS = "api_deprecations"
|
|
68
|
+
API_REFERENCE = "api_reference"
|
|
69
|
+
API_RELEASE_HISTORY = "api_release_history"
|
|
70
|
+
AUTHENTICATION_GUIDE = "authentication_guide"
|
|
71
|
+
CHANGELOG = "changelog"
|
|
72
|
+
DATA_MODEL_REFERENCE = "data_model_reference"
|
|
73
|
+
DEVELOPER_COMMUNITY = "developer_community"
|
|
74
|
+
MIGRATION_GUIDE = "migration_guide"
|
|
75
|
+
OPENAPI_SPEC = "openapi_spec"
|
|
76
|
+
OTHER = "other"
|
|
77
|
+
PERMISSIONS_SCOPES = "permissions_scopes"
|
|
78
|
+
RATE_LIMITS = "rate_limits"
|
|
79
|
+
SQL_REFERENCE = "sql_reference"
|
|
80
|
+
STATUS_PAGE = "status_page"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DocUrl(BaseModel):
|
|
84
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
85
|
+
|
|
86
|
+
url: str
|
|
87
|
+
type: DocUrlType
|
|
88
|
+
title: str | None = None
|
|
89
|
+
|
|
90
|
+
@field_validator("url")
|
|
91
|
+
def validate_url(cls, v):
|
|
92
|
+
Url(v)
|
|
93
|
+
return v
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Info(BaseModel):
|
|
97
|
+
"""
|
|
98
|
+
API metadata information.
|
|
99
|
+
|
|
100
|
+
OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#info-object
|
|
101
|
+
|
|
102
|
+
Extensions:
|
|
103
|
+
- x-airbyte-connector-name: Name of the connector (Airbyte extension)
|
|
104
|
+
- x-airbyte-connector-id: UUID of the connector (Airbyte extension)
|
|
105
|
+
- x-airbyte-external-documentation-urls: List of external documentation URLs (Airbyte extension)
|
|
106
|
+
- x-airbyte-retry-config: Retry configuration for transient errors (Airbyte extension)
|
|
107
|
+
- x-airbyte-example-questions: Example questions for AI connector README (Airbyte extension)
|
|
108
|
+
- x-airbyte-cache: Cache configuration for field mapping between API and cache schemas (Airbyte extension)
|
|
109
|
+
- x-airbyte-replication-config: Replication configuration for MULTI mode connectors (Airbyte extension)
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
113
|
+
|
|
114
|
+
title: str
|
|
115
|
+
version: str
|
|
116
|
+
description: str | None = None
|
|
117
|
+
terms_of_service: str | None = Field(None, alias="termsOfService")
|
|
118
|
+
contact: Contact | None = None
|
|
119
|
+
license: License | None = None
|
|
120
|
+
|
|
121
|
+
# Airbyte extension
|
|
122
|
+
x_airbyte_connector_name: str | None = Field(None, alias="x-airbyte-connector-name")
|
|
123
|
+
x_airbyte_connector_id: UUID | None = Field(None, alias="x-airbyte-connector-id")
|
|
124
|
+
x_airbyte_external_documentation_urls: list[DocUrl] = Field(..., alias="x-airbyte-external-documentation-urls")
|
|
125
|
+
x_airbyte_retry_config: RetryConfig | None = Field(None, alias="x-airbyte-retry-config")
|
|
126
|
+
x_airbyte_example_questions: ExampleQuestions | None = Field(None, alias="x-airbyte-example-questions")
|
|
127
|
+
x_airbyte_cache: CacheConfig | None = Field(None, alias="x-airbyte-cache")
|
|
128
|
+
x_airbyte_replication_config: ReplicationConfig | None = Field(None, alias="x-airbyte-replication-config")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ServerVariable(BaseModel):
|
|
132
|
+
"""
|
|
133
|
+
Variable for server URL templating.
|
|
134
|
+
|
|
135
|
+
OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#server-variable-object
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
139
|
+
|
|
140
|
+
enum: list[str] | None = None
|
|
141
|
+
default: str
|
|
142
|
+
description: str | None = None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class EnvironmentMappingTransform(BaseModel):
|
|
146
|
+
"""
|
|
147
|
+
Structured transform for environment mapping values.
|
|
148
|
+
|
|
149
|
+
Allows transforming environment values before storing in source_config.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
source: subdomain
|
|
153
|
+
format: "{value}.atlassian.net"
|
|
154
|
+
|
|
155
|
+
The format string uses {value} as a placeholder for the source value.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
159
|
+
|
|
160
|
+
source: str = Field(description="The environment config key to read the value from")
|
|
161
|
+
format: str | None = Field(
|
|
162
|
+
default=None,
|
|
163
|
+
description="Optional format string to transform the value. Use {value} as placeholder.",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Type alias for environment mapping values: either a simple string (config key)
|
|
168
|
+
# or a structured transform with source and optional transform template
|
|
169
|
+
EnvironmentMappingValue = str | EnvironmentMappingTransform
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class Server(BaseModel):
|
|
173
|
+
"""
|
|
174
|
+
Server URL and variable definitions.
|
|
175
|
+
|
|
176
|
+
OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#server-object
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
180
|
+
|
|
181
|
+
url: str
|
|
182
|
+
description: str | None = None
|
|
183
|
+
variables: Dict[str, ServerVariable] = Field(default_factory=dict)
|
|
184
|
+
x_airbyte_replication_environment_mapping: Dict[str, EnvironmentMappingValue] | None = Field(
|
|
185
|
+
default=None,
|
|
186
|
+
alias="x-airbyte-replication-environment-mapping",
|
|
187
|
+
)
|
|
188
|
+
x_airbyte_replication_environment_constants: Dict[str, Any] | None = Field(
|
|
189
|
+
default=None,
|
|
190
|
+
alias="x-airbyte-replication-environment-constants",
|
|
191
|
+
description="Constant values to always inject at environment config paths (e.g., 'region': 'us-east-1')",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@field_validator("url")
|
|
195
|
+
@classmethod
|
|
196
|
+
def validate_url(cls, v: str) -> str:
|
|
197
|
+
"""Validate that server URL is properly formatted."""
|
|
198
|
+
if not v:
|
|
199
|
+
raise ValueError("Server URL cannot be empty")
|
|
200
|
+
# Allow both absolute URLs and relative paths
|
|
201
|
+
return v
|