airbyte-agent-greenhouse 0.17.48__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_greenhouse/__init__.py +105 -0
  2. airbyte_agent_greenhouse/_vendored/__init__.py +1 -0
  3. airbyte_agent_greenhouse/_vendored/connector_sdk/__init__.py +82 -0
  4. airbyte_agent_greenhouse/_vendored/connector_sdk/auth_strategies.py +1120 -0
  5. airbyte_agent_greenhouse/_vendored/connector_sdk/auth_template.py +135 -0
  6. airbyte_agent_greenhouse/_vendored/connector_sdk/cloud_utils/__init__.py +5 -0
  7. airbyte_agent_greenhouse/_vendored/connector_sdk/cloud_utils/client.py +213 -0
  8. airbyte_agent_greenhouse/_vendored/connector_sdk/connector_model_loader.py +965 -0
  9. airbyte_agent_greenhouse/_vendored/connector_sdk/constants.py +78 -0
  10. airbyte_agent_greenhouse/_vendored/connector_sdk/exceptions.py +23 -0
  11. airbyte_agent_greenhouse/_vendored/connector_sdk/executor/__init__.py +31 -0
  12. airbyte_agent_greenhouse/_vendored/connector_sdk/executor/hosted_executor.py +196 -0
  13. airbyte_agent_greenhouse/_vendored/connector_sdk/executor/local_executor.py +1724 -0
  14. airbyte_agent_greenhouse/_vendored/connector_sdk/executor/models.py +190 -0
  15. airbyte_agent_greenhouse/_vendored/connector_sdk/extensions.py +693 -0
  16. airbyte_agent_greenhouse/_vendored/connector_sdk/http/__init__.py +37 -0
  17. airbyte_agent_greenhouse/_vendored/connector_sdk/http/adapters/__init__.py +9 -0
  18. airbyte_agent_greenhouse/_vendored/connector_sdk/http/adapters/httpx_adapter.py +251 -0
  19. airbyte_agent_greenhouse/_vendored/connector_sdk/http/config.py +98 -0
  20. airbyte_agent_greenhouse/_vendored/connector_sdk/http/exceptions.py +119 -0
  21. airbyte_agent_greenhouse/_vendored/connector_sdk/http/protocols.py +114 -0
  22. airbyte_agent_greenhouse/_vendored/connector_sdk/http/response.py +104 -0
  23. airbyte_agent_greenhouse/_vendored/connector_sdk/http_client.py +693 -0
  24. airbyte_agent_greenhouse/_vendored/connector_sdk/introspection.py +262 -0
  25. airbyte_agent_greenhouse/_vendored/connector_sdk/logging/__init__.py +11 -0
  26. airbyte_agent_greenhouse/_vendored/connector_sdk/logging/logger.py +273 -0
  27. airbyte_agent_greenhouse/_vendored/connector_sdk/logging/types.py +93 -0
  28. airbyte_agent_greenhouse/_vendored/connector_sdk/observability/__init__.py +11 -0
  29. airbyte_agent_greenhouse/_vendored/connector_sdk/observability/config.py +179 -0
  30. airbyte_agent_greenhouse/_vendored/connector_sdk/observability/models.py +19 -0
  31. airbyte_agent_greenhouse/_vendored/connector_sdk/observability/redactor.py +81 -0
  32. airbyte_agent_greenhouse/_vendored/connector_sdk/observability/session.py +103 -0
  33. airbyte_agent_greenhouse/_vendored/connector_sdk/performance/__init__.py +6 -0
  34. airbyte_agent_greenhouse/_vendored/connector_sdk/performance/instrumentation.py +57 -0
  35. airbyte_agent_greenhouse/_vendored/connector_sdk/performance/metrics.py +93 -0
  36. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/__init__.py +75 -0
  37. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/base.py +164 -0
  38. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/components.py +239 -0
  39. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/connector.py +120 -0
  40. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/extensions.py +230 -0
  41. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/operations.py +146 -0
  42. airbyte_agent_greenhouse/_vendored/connector_sdk/schema/security.py +223 -0
  43. airbyte_agent_greenhouse/_vendored/connector_sdk/secrets.py +182 -0
  44. airbyte_agent_greenhouse/_vendored/connector_sdk/telemetry/__init__.py +10 -0
  45. airbyte_agent_greenhouse/_vendored/connector_sdk/telemetry/config.py +32 -0
  46. airbyte_agent_greenhouse/_vendored/connector_sdk/telemetry/events.py +59 -0
  47. airbyte_agent_greenhouse/_vendored/connector_sdk/telemetry/tracker.py +155 -0
  48. airbyte_agent_greenhouse/_vendored/connector_sdk/types.py +245 -0
  49. airbyte_agent_greenhouse/_vendored/connector_sdk/utils.py +60 -0
  50. airbyte_agent_greenhouse/_vendored/connector_sdk/validation.py +828 -0
  51. airbyte_agent_greenhouse/connector.py +1391 -0
  52. airbyte_agent_greenhouse/connector_model.py +2356 -0
  53. airbyte_agent_greenhouse/models.py +281 -0
  54. airbyte_agent_greenhouse/types.py +136 -0
  55. airbyte_agent_greenhouse-0.17.48.dist-info/METADATA +116 -0
  56. airbyte_agent_greenhouse-0.17.48.dist-info/RECORD +57 -0
  57. airbyte_agent_greenhouse-0.17.48.dist-info/WHEEL +4 -0
@@ -0,0 +1,164 @@
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 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, 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
+ """
110
+
111
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
112
+
113
+ title: str
114
+ version: str
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
119
+
120
+ # Airbyte extension
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")
123
+ x_airbyte_external_documentation_urls: list[DocUrl] = Field(..., alias="x-airbyte-external-documentation-urls")
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")
127
+
128
+
129
+ class ServerVariable(BaseModel):
130
+ """
131
+ Variable for server URL templating.
132
+
133
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#server-variable-object
134
+ """
135
+
136
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
137
+
138
+ enum: list[str] | None = None
139
+ default: str
140
+ description: str | None = None
141
+
142
+
143
+ class Server(BaseModel):
144
+ """
145
+ Server URL and variable definitions.
146
+
147
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#server-object
148
+ """
149
+
150
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
151
+
152
+ url: str
153
+ description: str | None = None
154
+ variables: Dict[str, ServerVariable] = Field(default_factory=dict)
155
+ x_airbyte_replication_user_config_mapping: Dict[str, str] | None = Field(default=None, alias="x-airbyte-replication-user-config-mapping")
156
+
157
+ @field_validator("url")
158
+ @classmethod
159
+ def validate_url(cls, v: str) -> str:
160
+ """Validate that server URL is properly formatted."""
161
+ if not v:
162
+ raise ValueError("Server URL cannot be empty")
163
+ # Allow both absolute URLs and relative paths
164
+ return v
@@ -0,0 +1,239 @@
1
+ """
2
+ Component models for OpenAPI 3.1: Schema, Parameter, RequestBody, Response, Components.
3
+
4
+ References:
5
+ - https://spec.openapis.org/oas/v3.1.0#components-object
6
+ - https://spec.openapis.org/oas/v3.1.0#schema-object
7
+ - https://spec.openapis.org/oas/v3.1.0#parameter-object
8
+ """
9
+
10
+ from typing import Any, Dict, List, Literal, Union
11
+
12
+ from pydantic import BaseModel, ConfigDict, Field
13
+
14
+ from .security import SecurityScheme
15
+
16
+
17
+ class Schema(BaseModel):
18
+ """
19
+ JSON Schema definition for data models.
20
+
21
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#schema-object
22
+
23
+ Note: Uses Dict[str, Any] for properties to support nested schemas and $ref.
24
+ Reference resolution happens at runtime in config_loader.py.
25
+
26
+ Extensions:
27
+ - x-airbyte-resource-name: Name of the resource this schema represents (Airbyte extension)
28
+ """
29
+
30
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
31
+
32
+ # Core JSON Schema fields
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
+
40
+ # Object properties
41
+ properties: Dict[str, Any] = Field(default_factory=dict) # May contain $ref
42
+ required: List[str] = Field(default_factory=list)
43
+ additional_properties: Any | None = Field(None, alias="additionalProperties")
44
+
45
+ # Array properties
46
+ items: Any | None = None # May be Schema or $ref
47
+
48
+ # Validation
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
+
56
+ # Composition
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
+
62
+ # Metadata
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
+
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")
71
+
72
+
73
+ class Parameter(BaseModel):
74
+ """
75
+ Operation parameter definition.
76
+
77
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#parameter-object
78
+ """
79
+
80
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
81
+
82
+ name: str
83
+ in_: Literal["query", "header", "path", "cookie"] = Field(alias="in")
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")
88
+
89
+ # Schema can be inline or reference
90
+ schema_: Dict[str, Any] | None = Field(None, alias="schema")
91
+
92
+ # Style and examples
93
+ style: str | None = None
94
+ explode: bool | None = None
95
+ example: Any | None = None
96
+ examples: Dict[str, Any] | None = None
97
+
98
+
99
+ class MediaType(BaseModel):
100
+ """
101
+ Media type object for request/response content.
102
+
103
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#media-type-object
104
+ """
105
+
106
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
107
+
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
112
+
113
+
114
+ class GraphQLBodyConfig(BaseModel):
115
+ """
116
+ GraphQL body type configuration for x-airbyte-body-type extension.
117
+
118
+ Used when x-airbyte-body-type.type = "graphql"
119
+ """
120
+
121
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
122
+
123
+ type: Literal["graphql"] = Field(..., description="Body type identifier (must be 'graphql')")
124
+ query: str = Field(
125
+ ...,
126
+ description="GraphQL query or mutation string with optional template placeholders (e.g., {{ variable }})",
127
+ )
128
+ variables: Dict[str, Any] | None = Field(
129
+ None,
130
+ description="Variables to substitute in the GraphQL query using template syntax (e.g., {{ param_name }})",
131
+ )
132
+ operationName: str | None = Field(None, description="Operation name for queries with multiple operations")
133
+ default_fields: Union[str, List[str]] | None = Field(
134
+ None,
135
+ description="Default fields to select if not provided in request parameters. Can be a string or array of field names.",
136
+ )
137
+
138
+
139
+ # Union type for all body type configs (extensible for future types like XML, SOAP, etc.)
140
+ BodyTypeConfig = Union[GraphQLBodyConfig]
141
+
142
+
143
+ class PathOverrideConfig(BaseModel):
144
+ """
145
+ Path override configuration for x-airbyte-path-override extension.
146
+
147
+ Used when the OpenAPI path differs from the actual HTTP endpoint path.
148
+ Common for GraphQL APIs where multiple resources share the same endpoint (e.g., /graphql).
149
+
150
+ Example:
151
+ OpenAPI path: /graphql:repositories (for uniqueness)
152
+ Actual HTTP path: /graphql (configured here)
153
+ """
154
+
155
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
156
+
157
+ path: str = Field(
158
+ ...,
159
+ description=("Actual HTTP path to use for requests (e.g., '/graphql'). Must start with '/'"),
160
+ )
161
+
162
+
163
+ class RequestBody(BaseModel):
164
+ """
165
+ Request body definition.
166
+
167
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#request-body-object
168
+
169
+ Airbyte Extensions:
170
+ See connector_sdk.extensions for documentation:
171
+ - AIRBYTE_BODY_TYPE: Body type and configuration (nested structure)
172
+ """
173
+
174
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
175
+
176
+ description: str | None = None
177
+ content: Dict[str, MediaType] = Field(default_factory=dict)
178
+ required: bool | None = None
179
+
180
+ # Airbyte extensions for GraphQL support
181
+ # See connector_sdk.extensions for AIRBYTE_BODY_TYPE constant
182
+ x_airbyte_body_type: BodyTypeConfig | None = Field(
183
+ None,
184
+ alias="x-airbyte-body-type", # AIRBYTE_BODY_TYPE
185
+ description=(
186
+ "Body type and configuration. Contains 'type' field (e.g., 'graphql') and type-specific configuration (query, variables, etc.)."
187
+ ),
188
+ )
189
+
190
+
191
+ class Header(BaseModel):
192
+ """
193
+ Header definition.
194
+
195
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#header-object
196
+ """
197
+
198
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
199
+
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
205
+
206
+
207
+ class Response(BaseModel):
208
+ """
209
+ Response definition.
210
+
211
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#response-object
212
+ """
213
+
214
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
215
+
216
+ description: str
217
+ headers: Dict[str, Header] | None = None
218
+ content: Dict[str, MediaType] | None = None
219
+ links: Dict[str, Any] | None = None
220
+
221
+
222
+ class Components(BaseModel):
223
+ """
224
+ Reusable component definitions.
225
+
226
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#components-object
227
+ """
228
+
229
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
230
+
231
+ schemas: Dict[str, Schema] = Field(default_factory=dict)
232
+ responses: Dict[str, Response] = Field(default_factory=dict)
233
+ parameters: Dict[str, Parameter] = Field(default_factory=dict)
234
+ examples: Dict[str, Any] | None = None
235
+ request_bodies: Dict[str, RequestBody] = Field(default_factory=dict, alias="requestBodies")
236
+ headers: Dict[str, Header] | None = None
237
+ security_schemes: Dict[str, SecurityScheme] = Field(default_factory=dict, alias="securitySchemes")
238
+ links: Dict[str, Any] | None = None
239
+ callbacks: Dict[str, Any] | None = None
@@ -0,0 +1,120 @@
1
+ """
2
+ Root OpenAPI 3.1 connector specification model.
3
+
4
+ References:
5
+ - https://spec.openapis.org/oas/v3.1.0#openapi-object
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Iterator
11
+ from typing import Any
12
+
13
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
14
+
15
+ from ..constants import OPENAPI_VERSION_PREFIX
16
+
17
+ from .base import Info, Server
18
+ from .components import Components
19
+ from .operations import Operation, PathItem
20
+ from .security import SecurityRequirement
21
+
22
+
23
+ class Tag(BaseModel):
24
+ """
25
+ Tag metadata for grouping operations.
26
+
27
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#tag-object
28
+ """
29
+
30
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
31
+
32
+ name: str
33
+ description: str | None = None
34
+ external_docs: dict[str, Any] | None = Field(None, alias="externalDocs")
35
+
36
+
37
+ # HTTP methods supported by OpenAPI operations
38
+ HTTP_METHODS = frozenset({"get", "post", "put", "patch", "delete", "options", "head", "trace"})
39
+
40
+
41
+ class ExternalDocs(BaseModel):
42
+ """
43
+ External documentation reference.
44
+
45
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#external-documentation-object
46
+ """
47
+
48
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
49
+
50
+ description: str | None = None
51
+ url: str
52
+
53
+
54
+ class OpenAPIConnector(BaseModel):
55
+ """
56
+ Root OpenAPI 3.1 connector specification.
57
+
58
+ OpenAPI Reference: https://spec.openapis.org/oas/v3.1.0#openapi-object
59
+
60
+ This is the top-level model that represents a complete OpenAPI 3.1 specification
61
+ for an Airbyte connector. It enforces strict validation (extra='forbid') to catch
62
+ typos and unknown extensions.
63
+ """
64
+
65
+ model_config = ConfigDict(populate_by_name=True, extra="forbid", validate_default=True)
66
+
67
+ # Required fields
68
+ openapi: str
69
+ info: Info
70
+ paths: dict[str, PathItem] = Field(default_factory=dict)
71
+
72
+ # Optional fields
73
+ servers: list[Server] = Field(default_factory=list)
74
+ components: Components | None = None
75
+ security: list[SecurityRequirement] | None = None
76
+ tags: list[Tag] | None = None
77
+ external_docs: ExternalDocs | None = Field(None, alias="externalDocs")
78
+
79
+ @field_validator("openapi")
80
+ @classmethod
81
+ def validate_openapi_version(cls, v: str) -> str:
82
+ """Validate that OpenAPI version is 3.1.x."""
83
+ if not v.startswith(OPENAPI_VERSION_PREFIX):
84
+ raise ValueError(f"OpenAPI version must be {OPENAPI_VERSION_PREFIX}x, got: {v}")
85
+ return v
86
+
87
+ def get_entity_operations(self, entity_name: str) -> list[tuple[str, str, Operation]]:
88
+ """
89
+ Get all operations for a specific entity.
90
+
91
+ Args:
92
+ entity_name: The x-airbyte-entity value to filter by
93
+
94
+ Returns:
95
+ List of tuples: (path, method, operation)
96
+ """
97
+ return [(path, method, op) for path, method, op in self._iter_operations() if op.x_airbyte_entity == entity_name]
98
+
99
+ def list_entities(self) -> list[str]:
100
+ """
101
+ List all unique entity names defined in x-airbyte-entity extensions.
102
+
103
+ Returns:
104
+ Sorted list of unique entity names
105
+ """
106
+ entities = {op.x_airbyte_entity for _, _, op in self._iter_operations() if op.x_airbyte_entity}
107
+ return sorted(entities)
108
+
109
+ def _iter_operations(self) -> Iterator[tuple[str, str, Operation]]:
110
+ """
111
+ Iterate over all operations in the spec.
112
+
113
+ Yields:
114
+ Tuples of (path, method, operation) for each defined operation
115
+ """
116
+ for path, path_item in self.paths.items():
117
+ for method in HTTP_METHODS:
118
+ operation = getattr(path_item, method, None)
119
+ if operation:
120
+ yield path, method, operation