airbyte-agent-asana 0.19.27__py3-none-any.whl → 0.19.41__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-asana might be problematic. Click here for more details.
- airbyte_agent_asana/__init__.py +6 -6
- airbyte_agent_asana/_vendored/connector_sdk/auth_strategies.py +2 -5
- airbyte_agent_asana/_vendored/connector_sdk/auth_template.py +1 -1
- airbyte_agent_asana/_vendored/connector_sdk/cloud_utils/client.py +26 -26
- airbyte_agent_asana/_vendored/connector_sdk/connector_model_loader.py +11 -4
- airbyte_agent_asana/_vendored/connector_sdk/constants.py +1 -1
- airbyte_agent_asana/_vendored/connector_sdk/executor/hosted_executor.py +10 -11
- airbyte_agent_asana/_vendored/connector_sdk/executor/local_executor.py +94 -25
- airbyte_agent_asana/_vendored/connector_sdk/extensions.py +43 -5
- airbyte_agent_asana/_vendored/connector_sdk/http/response.py +2 -0
- airbyte_agent_asana/_vendored/connector_sdk/introspection.py +262 -0
- airbyte_agent_asana/_vendored/connector_sdk/logging/logger.py +9 -9
- airbyte_agent_asana/_vendored/connector_sdk/logging/types.py +10 -10
- airbyte_agent_asana/_vendored/connector_sdk/observability/config.py +179 -0
- airbyte_agent_asana/_vendored/connector_sdk/observability/models.py +6 -6
- airbyte_agent_asana/_vendored/connector_sdk/observability/session.py +41 -32
- airbyte_agent_asana/_vendored/connector_sdk/performance/metrics.py +3 -3
- airbyte_agent_asana/_vendored/connector_sdk/schema/base.py +17 -17
- airbyte_agent_asana/_vendored/connector_sdk/schema/components.py +59 -58
- airbyte_agent_asana/_vendored/connector_sdk/schema/extensions.py +9 -9
- airbyte_agent_asana/_vendored/connector_sdk/schema/operations.py +32 -32
- airbyte_agent_asana/_vendored/connector_sdk/schema/security.py +44 -34
- airbyte_agent_asana/_vendored/connector_sdk/secrets.py +2 -2
- airbyte_agent_asana/_vendored/connector_sdk/telemetry/events.py +9 -8
- airbyte_agent_asana/_vendored/connector_sdk/telemetry/tracker.py +9 -5
- airbyte_agent_asana/_vendored/connector_sdk/types.py +7 -3
- airbyte_agent_asana/connector.py +88 -3
- airbyte_agent_asana/connector_model.py +6 -0
- airbyte_agent_asana/models.py +29 -29
- airbyte_agent_asana/types.py +1 -1
- {airbyte_agent_asana-0.19.27.dist-info → airbyte_agent_asana-0.19.41.dist-info}/METADATA +11 -11
- {airbyte_agent_asana-0.19.27.dist-info → airbyte_agent_asana-0.19.41.dist-info}/RECORD +33 -31
- {airbyte_agent_asana-0.19.27.dist-info → airbyte_agent_asana-0.19.41.dist-info}/WHEEL +0 -0
|
@@ -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,7 +7,7 @@ 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
|
|
@@ -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):
|
|
@@ -111,17 +111,17 @@ class Info(BaseModel):
|
|
|
111
111
|
|
|
112
112
|
title: str
|
|
113
113
|
version: str
|
|
114
|
-
description:
|
|
115
|
-
terms_of_service:
|
|
116
|
-
contact:
|
|
117
|
-
license:
|
|
114
|
+
description: str | None = None
|
|
115
|
+
terms_of_service: str | None = Field(None, alias="termsOfService")
|
|
116
|
+
contact: Contact | None = None
|
|
117
|
+
license: License | None = None
|
|
118
118
|
|
|
119
119
|
# Airbyte extension
|
|
120
|
-
x_airbyte_connector_name:
|
|
121
|
-
x_airbyte_connector_id:
|
|
120
|
+
x_airbyte_connector_name: str | None = Field(None, alias="x-airbyte-connector-name")
|
|
121
|
+
x_airbyte_connector_id: UUID | None = Field(None, alias="x-airbyte-connector-id")
|
|
122
122
|
x_airbyte_external_documentation_urls: list[DocUrl] = Field(..., alias="x-airbyte-external-documentation-urls")
|
|
123
|
-
x_airbyte_retry_config:
|
|
124
|
-
x_airbyte_example_questions:
|
|
123
|
+
x_airbyte_retry_config: RetryConfig | None = Field(None, alias="x-airbyte-retry-config")
|
|
124
|
+
x_airbyte_example_questions: ExampleQuestions | None = Field(None, alias="x-airbyte-example-questions")
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
class ServerVariable(BaseModel):
|
|
@@ -133,9 +133,9 @@ class ServerVariable(BaseModel):
|
|
|
133
133
|
|
|
134
134
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
135
135
|
|
|
136
|
-
enum:
|
|
136
|
+
enum: list[str] | None = None
|
|
137
137
|
default: str
|
|
138
|
-
description:
|
|
138
|
+
description: str | None = None
|
|
139
139
|
|
|
140
140
|
|
|
141
141
|
class Server(BaseModel):
|
|
@@ -148,7 +148,7 @@ class Server(BaseModel):
|
|
|
148
148
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
149
149
|
|
|
150
150
|
url: str
|
|
151
|
-
description:
|
|
151
|
+
description: str | None = None
|
|
152
152
|
variables: Dict[str, ServerVariable] = Field(default_factory=dict)
|
|
153
153
|
|
|
154
154
|
@field_validator("url")
|
|
@@ -7,7 +7,7 @@ References:
|
|
|
7
7
|
- https://spec.openapis.org/oas/v3.1.0#parameter-object
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from typing import Any, Dict, List, Literal,
|
|
10
|
+
from typing import Any, Dict, List, Literal, Union
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, ConfigDict, Field
|
|
13
13
|
|
|
@@ -30,43 +30,44 @@ class Schema(BaseModel):
|
|
|
30
30
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
31
31
|
|
|
32
32
|
# Core JSON Schema fields
|
|
33
|
-
type:
|
|
34
|
-
format:
|
|
35
|
-
title:
|
|
36
|
-
description:
|
|
37
|
-
default:
|
|
38
|
-
example:
|
|
33
|
+
type: str | None = None
|
|
34
|
+
format: str | None = None
|
|
35
|
+
title: str | None = None
|
|
36
|
+
description: str | None = None
|
|
37
|
+
default: Any | None = None
|
|
38
|
+
example: Any | None = None
|
|
39
39
|
|
|
40
40
|
# Object properties
|
|
41
41
|
properties: Dict[str, Any] = Field(default_factory=dict) # May contain $ref
|
|
42
42
|
required: List[str] = Field(default_factory=list)
|
|
43
|
-
additional_properties:
|
|
43
|
+
additional_properties: Any | None = Field(None, alias="additionalProperties")
|
|
44
44
|
|
|
45
45
|
# Array properties
|
|
46
|
-
items:
|
|
46
|
+
items: Any | None = None # May be Schema or $ref
|
|
47
47
|
|
|
48
48
|
# Validation
|
|
49
|
-
enum:
|
|
50
|
-
min_length:
|
|
51
|
-
max_length:
|
|
52
|
-
minimum:
|
|
53
|
-
maximum:
|
|
54
|
-
pattern:
|
|
49
|
+
enum: List[Any] | None = None
|
|
50
|
+
min_length: int | None = Field(None, alias="minLength")
|
|
51
|
+
max_length: int | None = Field(None, alias="maxLength")
|
|
52
|
+
minimum: float | None = None
|
|
53
|
+
maximum: float | None = None
|
|
54
|
+
pattern: str | None = None
|
|
55
55
|
|
|
56
56
|
# Composition
|
|
57
|
-
all_of:
|
|
58
|
-
any_of:
|
|
59
|
-
one_of:
|
|
60
|
-
not_:
|
|
57
|
+
all_of: List[Any] | None = Field(None, alias="allOf")
|
|
58
|
+
any_of: List[Any] | None = Field(None, alias="anyOf")
|
|
59
|
+
one_of: List[Any] | None = Field(None, alias="oneOf")
|
|
60
|
+
not_: Any | None = Field(None, alias="not")
|
|
61
61
|
|
|
62
62
|
# Metadata
|
|
63
|
-
nullable:
|
|
64
|
-
read_only:
|
|
65
|
-
write_only:
|
|
66
|
-
deprecated:
|
|
63
|
+
nullable: bool | None = Field(None, deprecated="Use type union with null instead (OpenAPI 3.1)")
|
|
64
|
+
read_only: bool | None = Field(None, alias="readOnly")
|
|
65
|
+
write_only: bool | None = Field(None, alias="writeOnly")
|
|
66
|
+
deprecated: bool | None = None
|
|
67
67
|
|
|
68
|
-
# Airbyte
|
|
69
|
-
x_airbyte_entity_name:
|
|
68
|
+
# Airbyte extensions
|
|
69
|
+
x_airbyte_entity_name: str | None = Field(None, alias="x-airbyte-entity-name")
|
|
70
|
+
x_airbyte_stream_name: str | None = Field(None, alias="x-airbyte-stream-name")
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
class Parameter(BaseModel):
|
|
@@ -80,19 +81,19 @@ class Parameter(BaseModel):
|
|
|
80
81
|
|
|
81
82
|
name: str
|
|
82
83
|
in_: Literal["query", "header", "path", "cookie"] = Field(alias="in")
|
|
83
|
-
description:
|
|
84
|
-
required:
|
|
85
|
-
deprecated:
|
|
86
|
-
allow_empty_value:
|
|
84
|
+
description: str | None = None
|
|
85
|
+
required: bool | None = None
|
|
86
|
+
deprecated: bool | None = None
|
|
87
|
+
allow_empty_value: bool | None = Field(None, alias="allowEmptyValue")
|
|
87
88
|
|
|
88
89
|
# Schema can be inline or reference
|
|
89
|
-
schema_:
|
|
90
|
+
schema_: Dict[str, Any] | None = Field(None, alias="schema")
|
|
90
91
|
|
|
91
92
|
# Style and examples
|
|
92
|
-
style:
|
|
93
|
-
explode:
|
|
94
|
-
example:
|
|
95
|
-
examples:
|
|
93
|
+
style: str | None = None
|
|
94
|
+
explode: bool | None = None
|
|
95
|
+
example: Any | None = None
|
|
96
|
+
examples: Dict[str, Any] | None = None
|
|
96
97
|
|
|
97
98
|
|
|
98
99
|
class MediaType(BaseModel):
|
|
@@ -104,10 +105,10 @@ class MediaType(BaseModel):
|
|
|
104
105
|
|
|
105
106
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
106
107
|
|
|
107
|
-
schema_:
|
|
108
|
-
example:
|
|
109
|
-
examples:
|
|
110
|
-
encoding:
|
|
108
|
+
schema_: Dict[str, Any] | None = Field(None, alias="schema")
|
|
109
|
+
example: Any | None = None
|
|
110
|
+
examples: Dict[str, Any] | None = None
|
|
111
|
+
encoding: Dict[str, Any] | None = None
|
|
111
112
|
|
|
112
113
|
|
|
113
114
|
class GraphQLBodyConfig(BaseModel):
|
|
@@ -124,12 +125,12 @@ class GraphQLBodyConfig(BaseModel):
|
|
|
124
125
|
...,
|
|
125
126
|
description="GraphQL query or mutation string with optional template placeholders (e.g., {{ variable }})",
|
|
126
127
|
)
|
|
127
|
-
variables:
|
|
128
|
+
variables: Dict[str, Any] | None = Field(
|
|
128
129
|
None,
|
|
129
130
|
description="Variables to substitute in the GraphQL query using template syntax (e.g., {{ param_name }})",
|
|
130
131
|
)
|
|
131
|
-
operationName:
|
|
132
|
-
default_fields:
|
|
132
|
+
operationName: str | None = Field(None, description="Operation name for queries with multiple operations")
|
|
133
|
+
default_fields: Union[str, List[str]] | None = Field(
|
|
133
134
|
None,
|
|
134
135
|
description="Default fields to select if not provided in request parameters. Can be a string or array of field names.",
|
|
135
136
|
)
|
|
@@ -155,7 +156,7 @@ class PathOverrideConfig(BaseModel):
|
|
|
155
156
|
|
|
156
157
|
path: str = Field(
|
|
157
158
|
...,
|
|
158
|
-
description=("Actual HTTP path to use for requests (e.g., '/graphql').
|
|
159
|
+
description=("Actual HTTP path to use for requests (e.g., '/graphql'). Must start with '/'"),
|
|
159
160
|
)
|
|
160
161
|
|
|
161
162
|
|
|
@@ -172,17 +173,17 @@ class RequestBody(BaseModel):
|
|
|
172
173
|
|
|
173
174
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
174
175
|
|
|
175
|
-
description:
|
|
176
|
+
description: str | None = None
|
|
176
177
|
content: Dict[str, MediaType] = Field(default_factory=dict)
|
|
177
|
-
required:
|
|
178
|
+
required: bool | None = None
|
|
178
179
|
|
|
179
180
|
# Airbyte extensions for GraphQL support
|
|
180
181
|
# See connector_sdk.extensions for AIRBYTE_BODY_TYPE constant
|
|
181
|
-
x_airbyte_body_type:
|
|
182
|
+
x_airbyte_body_type: BodyTypeConfig | None = Field(
|
|
182
183
|
None,
|
|
183
184
|
alias="x-airbyte-body-type", # AIRBYTE_BODY_TYPE
|
|
184
185
|
description=(
|
|
185
|
-
"Body type and configuration. Contains 'type' field (e.g., 'graphql')
|
|
186
|
+
"Body type and configuration. Contains 'type' field (e.g., 'graphql') and type-specific configuration (query, variables, etc.)."
|
|
186
187
|
),
|
|
187
188
|
)
|
|
188
189
|
|
|
@@ -196,11 +197,11 @@ class Header(BaseModel):
|
|
|
196
197
|
|
|
197
198
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
198
199
|
|
|
199
|
-
description:
|
|
200
|
-
required:
|
|
201
|
-
deprecated:
|
|
202
|
-
schema_:
|
|
203
|
-
example:
|
|
200
|
+
description: str | None = None
|
|
201
|
+
required: bool | None = None
|
|
202
|
+
deprecated: bool | None = None
|
|
203
|
+
schema_: Dict[str, Any] | None = Field(None, alias="schema")
|
|
204
|
+
example: Any | None = None
|
|
204
205
|
|
|
205
206
|
|
|
206
207
|
class Response(BaseModel):
|
|
@@ -213,9 +214,9 @@ class Response(BaseModel):
|
|
|
213
214
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
214
215
|
|
|
215
216
|
description: str
|
|
216
|
-
headers:
|
|
217
|
-
content:
|
|
218
|
-
links:
|
|
217
|
+
headers: Dict[str, Header] | None = None
|
|
218
|
+
content: Dict[str, MediaType] | None = None
|
|
219
|
+
links: Dict[str, Any] | None = None
|
|
219
220
|
|
|
220
221
|
|
|
221
222
|
class Components(BaseModel):
|
|
@@ -230,9 +231,9 @@ class Components(BaseModel):
|
|
|
230
231
|
schemas: Dict[str, Schema] = Field(default_factory=dict)
|
|
231
232
|
responses: Dict[str, Response] = Field(default_factory=dict)
|
|
232
233
|
parameters: Dict[str, Parameter] = Field(default_factory=dict)
|
|
233
|
-
examples:
|
|
234
|
+
examples: Dict[str, Any] | None = None
|
|
234
235
|
request_bodies: Dict[str, RequestBody] = Field(default_factory=dict, alias="requestBodies")
|
|
235
|
-
headers:
|
|
236
|
+
headers: Dict[str, Header] | None = None
|
|
236
237
|
security_schemes: Dict[str, SecurityScheme] = Field(default_factory=dict, alias="securitySchemes")
|
|
237
|
-
links:
|
|
238
|
-
callbacks:
|
|
238
|
+
links: Dict[str, Any] | None = None
|
|
239
|
+
callbacks: Dict[str, Any] | None = None
|
|
@@ -12,7 +12,7 @@ to Operation, Schema, or other models when their respective features
|
|
|
12
12
|
are implemented.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from typing import Literal
|
|
15
|
+
from typing import Literal
|
|
16
16
|
|
|
17
17
|
from pydantic import BaseModel, ConfigDict
|
|
18
18
|
|
|
@@ -33,22 +33,22 @@ class PaginationConfig(BaseModel):
|
|
|
33
33
|
limit_param: str = "limit"
|
|
34
34
|
|
|
35
35
|
# Cursor-based pagination
|
|
36
|
-
cursor_param:
|
|
37
|
-
cursor_source:
|
|
38
|
-
cursor_path:
|
|
36
|
+
cursor_param: str | None = None
|
|
37
|
+
cursor_source: Literal["body", "headers"] | None = "body"
|
|
38
|
+
cursor_path: str | None = None
|
|
39
39
|
|
|
40
40
|
# Offset-based pagination
|
|
41
|
-
offset_param:
|
|
41
|
+
offset_param: str | None = None
|
|
42
42
|
|
|
43
43
|
# Page-based pagination
|
|
44
|
-
page_param:
|
|
44
|
+
page_param: str | None = None
|
|
45
45
|
|
|
46
46
|
# Response parsing
|
|
47
47
|
data_path: str = "data"
|
|
48
|
-
has_more_path:
|
|
48
|
+
has_more_path: str | None = None
|
|
49
49
|
|
|
50
50
|
# Limits
|
|
51
|
-
max_page_size:
|
|
51
|
+
max_page_size: int | None = None
|
|
52
52
|
default_page_size: int = 100
|
|
53
53
|
|
|
54
54
|
|
|
@@ -66,7 +66,7 @@ class RateLimitConfig(BaseModel):
|
|
|
66
66
|
|
|
67
67
|
max_requests: int
|
|
68
68
|
time_window_seconds: int
|
|
69
|
-
retry_after_header:
|
|
69
|
+
retry_after_header: str | None = "Retry-After"
|
|
70
70
|
respect_retry_after: bool = True
|
|
71
71
|
|
|
72
72
|
|
|
@@ -6,7 +6,7 @@ References:
|
|
|
6
6
|
- https://spec.openapis.org/oas/v3.1.0#path-item-object
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from typing import Any, Dict, List
|
|
9
|
+
from typing import Any, Dict, List
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
12
12
|
|
|
@@ -34,38 +34,38 @@ class Operation(BaseModel):
|
|
|
34
34
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
35
35
|
|
|
36
36
|
# Standard OpenAPI fields
|
|
37
|
-
tags:
|
|
38
|
-
summary:
|
|
39
|
-
description:
|
|
40
|
-
external_docs:
|
|
41
|
-
operation_id:
|
|
42
|
-
parameters:
|
|
43
|
-
request_body:
|
|
37
|
+
tags: List[str] | None = None
|
|
38
|
+
summary: str | None = None
|
|
39
|
+
description: str | None = None
|
|
40
|
+
external_docs: Dict[str, Any] | None = Field(None, alias="externalDocs")
|
|
41
|
+
operation_id: str | None = Field(None, alias="operationId")
|
|
42
|
+
parameters: List[Parameter] | None = None
|
|
43
|
+
request_body: RequestBody | None = Field(None, alias="requestBody")
|
|
44
44
|
responses: Dict[str, Response] = Field(default_factory=dict)
|
|
45
|
-
callbacks:
|
|
46
|
-
deprecated:
|
|
47
|
-
security:
|
|
48
|
-
servers:
|
|
45
|
+
callbacks: Dict[str, Any] | None = None
|
|
46
|
+
deprecated: bool | None = None
|
|
47
|
+
security: List[SecurityRequirement] | None = None
|
|
48
|
+
servers: List[Any] | None = None # Can override root servers
|
|
49
49
|
|
|
50
50
|
# Airbyte extensions
|
|
51
51
|
x_airbyte_entity: str = Field(..., alias="x-airbyte-entity")
|
|
52
52
|
x_airbyte_action: ActionTypeLiteral = Field(..., alias="x-airbyte-action")
|
|
53
|
-
x_airbyte_path_override:
|
|
53
|
+
x_airbyte_path_override: PathOverrideConfig | None = Field(
|
|
54
54
|
None,
|
|
55
55
|
alias="x-airbyte-path-override",
|
|
56
|
-
description=("Override path for HTTP requests when OpenAPI path
|
|
56
|
+
description=("Override path for HTTP requests when OpenAPI path differs from actual endpoint"),
|
|
57
57
|
)
|
|
58
|
-
x_airbyte_record_extractor:
|
|
58
|
+
x_airbyte_record_extractor: str | None = Field(
|
|
59
59
|
None,
|
|
60
60
|
alias="x-airbyte-record-extractor",
|
|
61
61
|
description=(
|
|
62
62
|
"JSONPath expression to extract records from API response envelopes. "
|
|
63
63
|
"When specified, executor extracts data at this path instead of returning "
|
|
64
|
-
"full response. Returns array for list/
|
|
64
|
+
"full response. Returns array for list/api_search actions, single record for "
|
|
65
65
|
"get/create/update/delete actions."
|
|
66
66
|
),
|
|
67
67
|
)
|
|
68
|
-
x_airbyte_meta_extractor:
|
|
68
|
+
x_airbyte_meta_extractor: Dict[str, str] | None = Field(
|
|
69
69
|
None,
|
|
70
70
|
alias="x-airbyte-meta-extractor",
|
|
71
71
|
description=(
|
|
@@ -76,8 +76,8 @@ class Operation(BaseModel):
|
|
|
76
76
|
"Example: {'pagination': '$.pagination', 'request_id': '$.requestId'}"
|
|
77
77
|
),
|
|
78
78
|
)
|
|
79
|
-
x_airbyte_file_url:
|
|
80
|
-
x_airbyte_untested:
|
|
79
|
+
x_airbyte_file_url: str | None = Field(None, alias="x-airbyte-file-url")
|
|
80
|
+
x_airbyte_untested: bool | None = Field(
|
|
81
81
|
None,
|
|
82
82
|
alias="x-airbyte-untested",
|
|
83
83
|
description=(
|
|
@@ -127,20 +127,20 @@ class PathItem(BaseModel):
|
|
|
127
127
|
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
128
128
|
|
|
129
129
|
# Common fields for all operations
|
|
130
|
-
summary:
|
|
131
|
-
description:
|
|
132
|
-
servers:
|
|
133
|
-
parameters:
|
|
130
|
+
summary: str | None = None
|
|
131
|
+
description: str | None = None
|
|
132
|
+
servers: List[Any] | None = None
|
|
133
|
+
parameters: List[Parameter] | None = None
|
|
134
134
|
|
|
135
135
|
# HTTP methods (all optional)
|
|
136
|
-
get:
|
|
137
|
-
put:
|
|
138
|
-
post:
|
|
139
|
-
delete:
|
|
140
|
-
options:
|
|
141
|
-
head:
|
|
142
|
-
patch:
|
|
143
|
-
trace:
|
|
136
|
+
get: Operation | None = None
|
|
137
|
+
put: Operation | None = None
|
|
138
|
+
post: Operation | None = None
|
|
139
|
+
delete: Operation | None = None
|
|
140
|
+
options: Operation | None = None
|
|
141
|
+
head: Operation | None = None
|
|
142
|
+
patch: Operation | None = None
|
|
143
|
+
trace: Operation | None = None
|
|
144
144
|
|
|
145
145
|
# Reference support
|
|
146
|
-
ref:
|
|
146
|
+
ref: str | None = Field(None, alias="$ref")
|