airbyte-agent-hubspot 0.15.28__py3-none-any.whl → 0.15.43__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.
- airbyte_agent_hubspot/__init__.py +101 -26
- airbyte_agent_hubspot/_vendored/connector_sdk/auth_strategies.py +2 -5
- airbyte_agent_hubspot/_vendored/connector_sdk/auth_template.py +1 -1
- airbyte_agent_hubspot/_vendored/connector_sdk/cloud_utils/client.py +26 -26
- airbyte_agent_hubspot/_vendored/connector_sdk/connector_model_loader.py +11 -4
- airbyte_agent_hubspot/_vendored/connector_sdk/constants.py +1 -1
- airbyte_agent_hubspot/_vendored/connector_sdk/executor/hosted_executor.py +10 -11
- airbyte_agent_hubspot/_vendored/connector_sdk/executor/local_executor.py +126 -17
- airbyte_agent_hubspot/_vendored/connector_sdk/extensions.py +43 -5
- airbyte_agent_hubspot/_vendored/connector_sdk/http/response.py +2 -0
- airbyte_agent_hubspot/_vendored/connector_sdk/introspection.py +262 -0
- airbyte_agent_hubspot/_vendored/connector_sdk/logging/logger.py +9 -9
- airbyte_agent_hubspot/_vendored/connector_sdk/logging/types.py +10 -10
- airbyte_agent_hubspot/_vendored/connector_sdk/observability/config.py +179 -0
- airbyte_agent_hubspot/_vendored/connector_sdk/observability/models.py +6 -6
- airbyte_agent_hubspot/_vendored/connector_sdk/observability/session.py +41 -32
- airbyte_agent_hubspot/_vendored/connector_sdk/performance/metrics.py +3 -3
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/base.py +20 -18
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/components.py +59 -58
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/connector.py +22 -33
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/extensions.py +103 -10
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/operations.py +32 -32
- airbyte_agent_hubspot/_vendored/connector_sdk/schema/security.py +44 -34
- airbyte_agent_hubspot/_vendored/connector_sdk/secrets.py +2 -2
- airbyte_agent_hubspot/_vendored/connector_sdk/telemetry/events.py +9 -8
- airbyte_agent_hubspot/_vendored/connector_sdk/telemetry/tracker.py +9 -5
- airbyte_agent_hubspot/_vendored/connector_sdk/types.py +7 -3
- airbyte_agent_hubspot/connector.py +182 -87
- airbyte_agent_hubspot/connector_model.py +17 -12
- airbyte_agent_hubspot/models.py +28 -28
- airbyte_agent_hubspot/types.py +45 -45
- {airbyte_agent_hubspot-0.15.28.dist-info → airbyte_agent_hubspot-0.15.43.dist-info}/METADATA +16 -17
- {airbyte_agent_hubspot-0.15.28.dist-info → airbyte_agent_hubspot-0.15.43.dist-info}/RECORD +34 -32
- {airbyte_agent_hubspot-0.15.28.dist-info → airbyte_agent_hubspot-0.15.43.dist-info}/WHEEL +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
4
|
from datetime import UTC, datetime
|
|
5
|
-
from typing import Any, Dict, List
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator
|
|
8
8
|
|
|
@@ -27,12 +27,12 @@ class RequestLog(BaseModel):
|
|
|
27
27
|
url: str
|
|
28
28
|
path: str
|
|
29
29
|
headers: Dict[str, str] = Field(default_factory=dict)
|
|
30
|
-
params:
|
|
31
|
-
body:
|
|
32
|
-
response_status:
|
|
33
|
-
response_body:
|
|
34
|
-
timing_ms:
|
|
35
|
-
error:
|
|
30
|
+
params: Dict[str, Any] | None = None
|
|
31
|
+
body: Any | None = None
|
|
32
|
+
response_status: int | None = None
|
|
33
|
+
response_body: Any | None = None
|
|
34
|
+
timing_ms: float | None = None
|
|
35
|
+
error: str | None = None
|
|
36
36
|
|
|
37
37
|
@field_serializer("timestamp")
|
|
38
38
|
def serialize_datetime(self, value: datetime) -> str:
|
|
@@ -50,9 +50,9 @@ class LogSession(BaseModel):
|
|
|
50
50
|
|
|
51
51
|
session_id: str
|
|
52
52
|
started_at: datetime = Field(default_factory=_utc_now)
|
|
53
|
-
connector_name:
|
|
53
|
+
connector_name: str | None = None
|
|
54
54
|
logs: List[RequestLog] = Field(default_factory=list)
|
|
55
|
-
max_logs:
|
|
55
|
+
max_logs: int | None = Field(
|
|
56
56
|
default=10000,
|
|
57
57
|
description="Maximum number of logs to keep in memory. "
|
|
58
58
|
"When limit is reached, oldest logs should be flushed before removal. "
|
|
@@ -60,7 +60,7 @@ class LogSession(BaseModel):
|
|
|
60
60
|
)
|
|
61
61
|
chunk_logs: List[bytes] = Field(
|
|
62
62
|
default_factory=list,
|
|
63
|
-
description="Captured chunks from streaming responses.
|
|
63
|
+
description="Captured chunks from streaming responses. Each chunk is logged when log_chunk_fetch() is called.",
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
@field_validator("chunk_logs", mode="before")
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Unified configuration for connector-sdk."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import uuid
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# New config location
|
|
16
|
+
CONFIG_DIR = Path.home() / ".airbyte" / "connector-sdk"
|
|
17
|
+
CONFIG_PATH = CONFIG_DIR / "config.yaml"
|
|
18
|
+
|
|
19
|
+
# Legacy file locations (for migration)
|
|
20
|
+
LEGACY_USER_ID_PATH = Path.home() / ".airbyte" / "ai_sdk_user_id"
|
|
21
|
+
LEGACY_INTERNAL_MARKER_PATH = Path.home() / ".airbyte" / "internal_user"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class SDKConfig:
|
|
26
|
+
"""Connector SDK configuration."""
|
|
27
|
+
|
|
28
|
+
user_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
29
|
+
is_internal_user: bool = False
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> dict[str, Any]:
|
|
32
|
+
"""Convert to dictionary for YAML serialization."""
|
|
33
|
+
return {
|
|
34
|
+
"user_id": self.user_id,
|
|
35
|
+
"is_internal_user": self.is_internal_user,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _delete_legacy_files() -> None:
|
|
40
|
+
"""
|
|
41
|
+
Delete legacy config files after successful migration.
|
|
42
|
+
|
|
43
|
+
Removes:
|
|
44
|
+
- ~/.airbyte/ai_sdk_user_id
|
|
45
|
+
- ~/.airbyte/internal_user
|
|
46
|
+
"""
|
|
47
|
+
for legacy_path in [LEGACY_USER_ID_PATH, LEGACY_INTERNAL_MARKER_PATH]:
|
|
48
|
+
try:
|
|
49
|
+
if legacy_path.exists():
|
|
50
|
+
legacy_path.unlink()
|
|
51
|
+
logger.debug(f"Deleted legacy config file: {legacy_path}")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.debug(f"Could not delete legacy file {legacy_path}: {e}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _migrate_legacy_config() -> SDKConfig | None:
|
|
57
|
+
"""
|
|
58
|
+
Migrate from legacy file-based config to new YAML format.
|
|
59
|
+
|
|
60
|
+
Reads from:
|
|
61
|
+
- ~/.airbyte/ai_sdk_user_id (user_id)
|
|
62
|
+
- ~/.airbyte/internal_user (is_internal_user marker)
|
|
63
|
+
|
|
64
|
+
Returns SDKConfig if migration was successful, None otherwise.
|
|
65
|
+
"""
|
|
66
|
+
user_id = None
|
|
67
|
+
is_internal = False
|
|
68
|
+
|
|
69
|
+
# Try to read legacy user_id
|
|
70
|
+
try:
|
|
71
|
+
if LEGACY_USER_ID_PATH.exists():
|
|
72
|
+
user_id = LEGACY_USER_ID_PATH.read_text().strip()
|
|
73
|
+
if not user_id:
|
|
74
|
+
user_id = None
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
# Check legacy internal_user marker
|
|
79
|
+
try:
|
|
80
|
+
is_internal = LEGACY_INTERNAL_MARKER_PATH.exists()
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
if user_id or is_internal:
|
|
85
|
+
return SDKConfig(
|
|
86
|
+
user_id=user_id or str(uuid.uuid4()),
|
|
87
|
+
is_internal_user=is_internal,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def load_config() -> SDKConfig:
|
|
94
|
+
"""
|
|
95
|
+
Load SDK configuration from config file.
|
|
96
|
+
|
|
97
|
+
Checks (in order):
|
|
98
|
+
1. New config file at ~/.airbyte/connector-sdk/config.yaml
|
|
99
|
+
2. Legacy files at ~/.airbyte/ai_sdk_user_id and ~/.airbyte/internal_user
|
|
100
|
+
3. Creates new config with generated user_id if nothing exists
|
|
101
|
+
|
|
102
|
+
Environment variable AIRBYTE_INTERNAL_USER can override is_internal_user.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
SDKConfig with user_id and is_internal_user
|
|
106
|
+
"""
|
|
107
|
+
config = None
|
|
108
|
+
|
|
109
|
+
# Try to load from new config file
|
|
110
|
+
try:
|
|
111
|
+
if CONFIG_PATH.exists():
|
|
112
|
+
content = CONFIG_PATH.read_text()
|
|
113
|
+
data = yaml.safe_load(content) or {}
|
|
114
|
+
config = SDKConfig(
|
|
115
|
+
user_id=data.get("user_id", str(uuid.uuid4())),
|
|
116
|
+
is_internal_user=data.get("is_internal_user", False),
|
|
117
|
+
)
|
|
118
|
+
# Always clean up legacy files if they exist (even if new config exists)
|
|
119
|
+
_delete_legacy_files()
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.debug(f"Could not load config from {CONFIG_PATH}: {e}")
|
|
122
|
+
|
|
123
|
+
# Try to migrate from legacy files if new config doesn't exist
|
|
124
|
+
if config is None:
|
|
125
|
+
config = _migrate_legacy_config()
|
|
126
|
+
if config:
|
|
127
|
+
# Save migrated config to new location
|
|
128
|
+
try:
|
|
129
|
+
save_config(config)
|
|
130
|
+
logger.debug("Migrated legacy config to new location")
|
|
131
|
+
# Delete legacy files after successful migration
|
|
132
|
+
_delete_legacy_files()
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.debug(f"Could not save migrated config: {e}")
|
|
135
|
+
|
|
136
|
+
# Create new config if nothing exists
|
|
137
|
+
if config is None:
|
|
138
|
+
config = SDKConfig()
|
|
139
|
+
try:
|
|
140
|
+
save_config(config)
|
|
141
|
+
except Exception as e:
|
|
142
|
+
logger.debug(f"Could not save new config: {e}")
|
|
143
|
+
|
|
144
|
+
# Environment variable override for is_internal_user
|
|
145
|
+
env_value = os.getenv("AIRBYTE_INTERNAL_USER", "").lower()
|
|
146
|
+
if env_value in ("true", "1", "yes"):
|
|
147
|
+
config.is_internal_user = True
|
|
148
|
+
elif env_value:
|
|
149
|
+
# Any other non-empty value (including "false", "0", "no") defaults to False
|
|
150
|
+
config.is_internal_user = False
|
|
151
|
+
|
|
152
|
+
return config
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def save_config(config: SDKConfig) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Save SDK configuration to config file.
|
|
158
|
+
|
|
159
|
+
Creates the config directory if it doesn't exist.
|
|
160
|
+
Uses atomic writes to prevent corruption from concurrent access.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
config: SDKConfig to save
|
|
164
|
+
"""
|
|
165
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
# Use atomic write: write to temp file then rename (atomic on POSIX)
|
|
168
|
+
fd, temp_path = tempfile.mkstemp(dir=CONFIG_DIR, suffix=".tmp")
|
|
169
|
+
try:
|
|
170
|
+
with os.fdopen(fd, "w") as f:
|
|
171
|
+
yaml.dump(config.to_dict(), f, default_flow_style=False)
|
|
172
|
+
os.rename(temp_path, CONFIG_PATH)
|
|
173
|
+
except Exception:
|
|
174
|
+
# Clean up temp file on failure
|
|
175
|
+
try:
|
|
176
|
+
os.unlink(temp_path)
|
|
177
|
+
except OSError:
|
|
178
|
+
pass
|
|
179
|
+
raise
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import Any, Dict
|
|
5
|
+
from typing import Any, Dict
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@dataclass
|
|
@@ -12,8 +12,8 @@ class OperationMetadata:
|
|
|
12
12
|
entity: str
|
|
13
13
|
action: str
|
|
14
14
|
timestamp: datetime
|
|
15
|
-
timing_ms:
|
|
16
|
-
status_code:
|
|
17
|
-
error_type:
|
|
18
|
-
error_message:
|
|
19
|
-
params:
|
|
15
|
+
timing_ms: float | None = None
|
|
16
|
+
status_code: int | None = None
|
|
17
|
+
error_type: str | None = None
|
|
18
|
+
error_message: str | None = None
|
|
19
|
+
params: Dict[str, Any] | None = None
|
|
@@ -3,49 +3,43 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
import uuid
|
|
5
5
|
from datetime import UTC, datetime
|
|
6
|
-
from
|
|
7
|
-
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
from .config import SDKConfig, load_config
|
|
8
9
|
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
10
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
|
+
|
|
11
29
|
|
|
12
30
|
def get_persistent_user_id() -> str:
|
|
13
31
|
"""
|
|
14
|
-
Get
|
|
32
|
+
Get the persistent anonymous user ID.
|
|
15
33
|
|
|
16
|
-
|
|
17
|
-
If the file doesn't exist, a new UUID is generated and saved.
|
|
34
|
+
Now reads from ~/.airbyte/connector-sdk/config.yaml
|
|
18
35
|
|
|
19
36
|
Returns:
|
|
20
37
|
An anonymous UUID string that uniquely identifies this user across sessions.
|
|
21
38
|
"""
|
|
22
|
-
|
|
23
|
-
# Create .airbyte directory in home folder if it doesn't exist
|
|
24
|
-
airbyte_dir = Path.home() / ".airbyte"
|
|
25
|
-
airbyte_dir.mkdir(exist_ok=True)
|
|
26
|
-
|
|
27
|
-
# Path to user ID file
|
|
28
|
-
user_id_file = airbyte_dir / "ai_sdk_user_id"
|
|
29
|
-
|
|
30
|
-
# Try to read existing user ID
|
|
31
|
-
if user_id_file.exists():
|
|
32
|
-
user_id = user_id_file.read_text().strip()
|
|
33
|
-
if user_id: # Validate it's not empty
|
|
34
|
-
return user_id
|
|
39
|
+
return _get_config().user_id
|
|
35
40
|
|
|
36
|
-
# Generate new user ID if file doesn't exist or is empty
|
|
37
|
-
user_id = str(uuid.uuid4())
|
|
38
|
-
user_id_file.write_text(user_id)
|
|
39
|
-
logger.debug(f"Generated new anonymous user ID: {user_id}")
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
except Exception as e:
|
|
43
|
-
# If we can't read/write the file, generate a session-only ID
|
|
44
|
-
logger.debug(f"Could not access anonymous user ID file: {e}")
|
|
45
|
-
return str(uuid.uuid4())
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def get_public_ip() -> Optional[str]:
|
|
42
|
+
def get_public_ip() -> str | None:
|
|
49
43
|
"""
|
|
50
44
|
Fetch the public IP address of the user.
|
|
51
45
|
|
|
@@ -53,6 +47,8 @@ def get_public_ip() -> Optional[str]:
|
|
|
53
47
|
Uses httpx for a robust HTTP request to a public IP service.
|
|
54
48
|
"""
|
|
55
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.
|
|
56
52
|
import httpx
|
|
57
53
|
|
|
58
54
|
# Use a short timeout to avoid blocking
|
|
@@ -65,15 +61,27 @@ def get_public_ip() -> Optional[str]:
|
|
|
65
61
|
return None
|
|
66
62
|
|
|
67
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
|
+
|
|
68
76
|
class ObservabilitySession:
|
|
69
77
|
"""Shared session context for both logging and telemetry."""
|
|
70
78
|
|
|
71
79
|
def __init__(
|
|
72
80
|
self,
|
|
73
81
|
connector_name: str,
|
|
74
|
-
connector_version:
|
|
82
|
+
connector_version: str | None = None,
|
|
75
83
|
execution_context: str = "direct",
|
|
76
|
-
session_id:
|
|
84
|
+
session_id: str | None = None,
|
|
77
85
|
):
|
|
78
86
|
self.session_id = session_id or str(uuid.uuid4())
|
|
79
87
|
self.user_id = get_persistent_user_id()
|
|
@@ -84,6 +92,7 @@ class ObservabilitySession:
|
|
|
84
92
|
self.operation_count = 0
|
|
85
93
|
self.metadata: Dict[str, Any] = {}
|
|
86
94
|
self.public_ip = get_public_ip()
|
|
95
|
+
self.is_internal_user = get_is_internal_user()
|
|
87
96
|
|
|
88
97
|
def increment_operations(self):
|
|
89
98
|
"""Increment the operation counter."""
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
|
-
from typing import Dict
|
|
5
|
+
from typing import Dict
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class PerformanceMonitor:
|
|
@@ -33,7 +33,7 @@ class PerformanceMonitor:
|
|
|
33
33
|
metrics["min"] = min(metrics["min"], duration)
|
|
34
34
|
metrics["max"] = max(metrics["max"], duration)
|
|
35
35
|
|
|
36
|
-
def get_stats(self, metric_name: str) ->
|
|
36
|
+
def get_stats(self, metric_name: str) -> Dict[str, float] | None:
|
|
37
37
|
"""Get statistics for a metric.
|
|
38
38
|
|
|
39
39
|
Args:
|
|
@@ -62,7 +62,7 @@ class PerformanceMonitor:
|
|
|
62
62
|
"""
|
|
63
63
|
return {name: self.get_stats(name) for name in self._metrics.keys()}
|
|
64
64
|
|
|
65
|
-
def reset(self, metric_name:
|
|
65
|
+
def reset(self, metric_name: str | None = None):
|
|
66
66
|
"""Reset metrics.
|
|
67
67
|
|
|
68
68
|
Args:
|
|
@@ -7,13 +7,13 @@ References:
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from enum import StrEnum
|
|
10
|
-
from typing import Dict
|
|
10
|
+
from typing import Dict
|
|
11
11
|
from uuid import UUID
|
|
12
12
|
|
|
13
13
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
14
14
|
from pydantic_core import Url
|
|
15
15
|
|
|
16
|
-
from .extensions import RetryConfig
|
|
16
|
+
from .extensions import CacheConfig, RetryConfig
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class ExampleQuestions(BaseModel):
|
|
@@ -45,9 +45,9 @@ class Contact(BaseModel):
|
|
|
45
45
|
|
|
46
46
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
47
47
|
|
|
48
|
-
name:
|
|
49
|
-
url:
|
|
50
|
-
email:
|
|
48
|
+
name: str | None = None
|
|
49
|
+
url: str | None = None
|
|
50
|
+
email: str | None = None
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
class License(BaseModel):
|
|
@@ -60,7 +60,7 @@ class License(BaseModel):
|
|
|
60
60
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
61
61
|
|
|
62
62
|
name: str
|
|
63
|
-
url:
|
|
63
|
+
url: str | None = None
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
class DocUrlType(StrEnum):
|
|
@@ -85,7 +85,7 @@ class DocUrl(BaseModel):
|
|
|
85
85
|
|
|
86
86
|
url: str
|
|
87
87
|
type: DocUrlType
|
|
88
|
-
title:
|
|
88
|
+
title: str | None = None
|
|
89
89
|
|
|
90
90
|
@field_validator("url")
|
|
91
91
|
def validate_url(cls, v):
|
|
@@ -105,23 +105,25 @@ class Info(BaseModel):
|
|
|
105
105
|
- x-airbyte-external-documentation-urls: List of external documentation URLs (Airbyte extension)
|
|
106
106
|
- x-airbyte-retry-config: Retry configuration for transient errors (Airbyte extension)
|
|
107
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)
|
|
108
109
|
"""
|
|
109
110
|
|
|
110
111
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
111
112
|
|
|
112
113
|
title: str
|
|
113
114
|
version: str
|
|
114
|
-
description:
|
|
115
|
-
terms_of_service:
|
|
116
|
-
contact:
|
|
117
|
-
license:
|
|
115
|
+
description: str | None = None
|
|
116
|
+
terms_of_service: str | None = Field(None, alias="termsOfService")
|
|
117
|
+
contact: Contact | None = None
|
|
118
|
+
license: License | None = None
|
|
118
119
|
|
|
119
120
|
# Airbyte extension
|
|
120
|
-
x_airbyte_connector_name:
|
|
121
|
-
x_airbyte_connector_id:
|
|
121
|
+
x_airbyte_connector_name: str | None = Field(None, alias="x-airbyte-connector-name")
|
|
122
|
+
x_airbyte_connector_id: UUID | None = Field(None, alias="x-airbyte-connector-id")
|
|
122
123
|
x_airbyte_external_documentation_urls: list[DocUrl] = Field(..., alias="x-airbyte-external-documentation-urls")
|
|
123
|
-
x_airbyte_retry_config:
|
|
124
|
-
x_airbyte_example_questions:
|
|
124
|
+
x_airbyte_retry_config: RetryConfig | None = Field(None, alias="x-airbyte-retry-config")
|
|
125
|
+
x_airbyte_example_questions: ExampleQuestions | None = Field(None, alias="x-airbyte-example-questions")
|
|
126
|
+
x_airbyte_cache: CacheConfig | None = Field(None, alias="x-airbyte-cache")
|
|
125
127
|
|
|
126
128
|
|
|
127
129
|
class ServerVariable(BaseModel):
|
|
@@ -133,9 +135,9 @@ class ServerVariable(BaseModel):
|
|
|
133
135
|
|
|
134
136
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
135
137
|
|
|
136
|
-
enum:
|
|
138
|
+
enum: list[str] | None = None
|
|
137
139
|
default: str
|
|
138
|
-
description:
|
|
140
|
+
description: str | None = None
|
|
139
141
|
|
|
140
142
|
|
|
141
143
|
class Server(BaseModel):
|
|
@@ -148,7 +150,7 @@ class Server(BaseModel):
|
|
|
148
150
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
149
151
|
|
|
150
152
|
url: str
|
|
151
|
-
description:
|
|
153
|
+
description: str | None = None
|
|
152
154
|
variables: Dict[str, ServerVariable] = Field(default_factory=dict)
|
|
153
155
|
|
|
154
156
|
@field_validator("url")
|