airbyte-cdk 6.48.0__py3-none-any.whl → 6.48.2__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_cdk/connector_builder/connector_builder_handler.py +11 -0
- airbyte_cdk/connector_builder/test_reader/reader.py +11 -0
- airbyte_cdk/manifest_migrations/README.md +73 -0
- airbyte_cdk/manifest_migrations/__init__.py +3 -0
- airbyte_cdk/manifest_migrations/exceptions.py +12 -0
- airbyte_cdk/manifest_migrations/manifest_migration.py +134 -0
- airbyte_cdk/manifest_migrations/migration_handler.py +163 -0
- airbyte_cdk/manifest_migrations/migrations/__init__.py +4 -0
- airbyte_cdk/manifest_migrations/migrations/http_requester_path_to_url.py +57 -0
- airbyte_cdk/manifest_migrations/migrations/http_requester_request_body_json_data_to_request_body.py +51 -0
- airbyte_cdk/manifest_migrations/migrations/http_requester_url_base_to_url.py +41 -0
- airbyte_cdk/manifest_migrations/migrations/registry.yaml +22 -0
- airbyte_cdk/manifest_migrations/migrations_registry.py +76 -0
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +80 -5
- airbyte_cdk/sources/declarative/declarative_source.py +10 -1
- airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +1 -1
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +32 -7
- airbyte_cdk/sources/declarative/models/base_model_with_deprecations.py +144 -0
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +54 -5
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +66 -15
- airbyte_cdk/sources/declarative/requesters/http_requester.py +62 -17
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +24 -2
- airbyte_cdk/sources/declarative/requesters/requester.py +12 -0
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/METADATA +1 -1
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/RECORD +29 -17
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/LICENSE_SHORT +0 -0
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.48.0.dist-info → airbyte_cdk-6.48.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
|
6
|
+
import importlib
|
7
|
+
import inspect
|
8
|
+
import os
|
9
|
+
from pathlib import Path
|
10
|
+
from types import ModuleType
|
11
|
+
from typing import Dict, List, Type
|
12
|
+
|
13
|
+
import yaml
|
14
|
+
|
15
|
+
from airbyte_cdk.manifest_migrations.manifest_migration import (
|
16
|
+
ManifestMigration,
|
17
|
+
)
|
18
|
+
|
19
|
+
DiscoveredMigrations = Dict[str, List[Type[ManifestMigration]]]
|
20
|
+
|
21
|
+
MIGRATIONS_PATH = Path(__file__).parent / "migrations"
|
22
|
+
REGISTRY_PATH = MIGRATIONS_PATH / "registry.yaml"
|
23
|
+
|
24
|
+
|
25
|
+
def _find_migration_module(name: str) -> str:
|
26
|
+
"""
|
27
|
+
Finds the migration module by name in the migrations directory.
|
28
|
+
The name should match the file name of the migration module (without the .py extension).
|
29
|
+
Raises ImportError if the module is not found.
|
30
|
+
"""
|
31
|
+
|
32
|
+
for migration_file in os.listdir(MIGRATIONS_PATH):
|
33
|
+
migration_name = name + ".py"
|
34
|
+
if migration_file == migration_name:
|
35
|
+
return migration_file.replace(".py", "")
|
36
|
+
|
37
|
+
raise ImportError(f"Migration module '{name}' not found in {MIGRATIONS_PATH}.")
|
38
|
+
|
39
|
+
|
40
|
+
def _get_migration_class(module: ModuleType) -> Type[ManifestMigration]:
|
41
|
+
"""
|
42
|
+
Returns the ManifestMigration subclass defined in the module.
|
43
|
+
"""
|
44
|
+
for _, obj in inspect.getmembers(module, inspect.isclass):
|
45
|
+
if issubclass(obj, ManifestMigration):
|
46
|
+
return obj
|
47
|
+
|
48
|
+
raise ImportError(f"No ManifestMigration subclass found in module {module.__name__}.")
|
49
|
+
|
50
|
+
|
51
|
+
def _discover_migrations() -> DiscoveredMigrations:
|
52
|
+
"""
|
53
|
+
Discovers and returns a list of ManifestMigration subclasses in the order specified by registry.yaml.
|
54
|
+
"""
|
55
|
+
with open(REGISTRY_PATH, "r") as f:
|
56
|
+
registry = yaml.safe_load(f)
|
57
|
+
migrations: DiscoveredMigrations = {}
|
58
|
+
# Iterate through the registry and import the migration classes
|
59
|
+
# based on the version and order specified in the registry.yaml
|
60
|
+
for version_entry in registry.get("manifest_migrations", []):
|
61
|
+
migration_version = version_entry.get("version", "0.0.0")
|
62
|
+
if not migration_version in migrations:
|
63
|
+
migrations[migration_version] = []
|
64
|
+
|
65
|
+
for migration in sorted(version_entry.get("migrations", []), key=lambda m: m["order"]):
|
66
|
+
module = importlib.import_module(
|
67
|
+
f"airbyte_cdk.manifest_migrations.migrations.{_find_migration_module(migration['name'])}"
|
68
|
+
)
|
69
|
+
migration_class = _get_migration_class(module)
|
70
|
+
migrations[migration_version].append(migration_class)
|
71
|
+
|
72
|
+
return migrations
|
73
|
+
|
74
|
+
|
75
|
+
# registered migrations
|
76
|
+
MANIFEST_MIGRATIONS: DiscoveredMigrations = _discover_migrations()
|
@@ -1911,15 +1911,16 @@ definitions:
|
|
1911
1911
|
type: object
|
1912
1912
|
required:
|
1913
1913
|
- type
|
1914
|
-
- url_base
|
1915
1914
|
properties:
|
1916
1915
|
type:
|
1917
1916
|
type: string
|
1918
1917
|
enum: [HttpRequester]
|
1919
1918
|
url_base:
|
1920
|
-
|
1919
|
+
deprecated: true
|
1920
|
+
deprecation_message: "Use `url` field instead."
|
1921
1921
|
title: API Base URL
|
1922
|
-
description:
|
1922
|
+
description: Deprecated, use the `url` instead. Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
|
1923
|
+
linkable: true
|
1923
1924
|
type: string
|
1924
1925
|
interpolation_context:
|
1925
1926
|
- config
|
@@ -1935,9 +1936,29 @@ definitions:
|
|
1935
1936
|
- "{{ config['base_url'] or 'https://app.posthog.com'}}/api"
|
1936
1937
|
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
|
1937
1938
|
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
|
1939
|
+
url:
|
1940
|
+
title: The URL of an API endpoint
|
1941
|
+
description: The URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
|
1942
|
+
type: string
|
1943
|
+
interpolation_context:
|
1944
|
+
- config
|
1945
|
+
- next_page_token
|
1946
|
+
- stream_interval
|
1947
|
+
- stream_partition
|
1948
|
+
- stream_slice
|
1949
|
+
- creation_response
|
1950
|
+
- polling_response
|
1951
|
+
- download_target
|
1952
|
+
examples:
|
1953
|
+
- "https://connect.squareup.com/v2"
|
1954
|
+
- "{{ config['url'] or 'https://app.posthog.com'}}/api"
|
1955
|
+
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
|
1956
|
+
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
|
1938
1957
|
path:
|
1958
|
+
deprecated: true
|
1959
|
+
deprecation_message: "Use `url` field instead."
|
1939
1960
|
title: URL Path
|
1940
|
-
description:
|
1961
|
+
description: Deprecated, use the `url` instead. Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
|
1941
1962
|
type: string
|
1942
1963
|
interpolation_context:
|
1943
1964
|
- config
|
@@ -1983,6 +2004,8 @@ definitions:
|
|
1983
2004
|
description: Allows for retrieving a dynamic set of properties from an API endpoint which can be injected into outbound request using the stream_partition.extra_fields.
|
1984
2005
|
"$ref": "#/definitions/PropertiesFromEndpoint"
|
1985
2006
|
request_body_data:
|
2007
|
+
deprecated: true
|
2008
|
+
deprecation_message: "Use `request_body` field instead."
|
1986
2009
|
title: Request Body Payload (Non-JSON)
|
1987
2010
|
description: Specifies how to populate the body of the request with a non-JSON payload. Plain text will be sent as is, whereas objects will be converted to a urlencoded form.
|
1988
2011
|
anyOf:
|
@@ -2001,6 +2024,8 @@ definitions:
|
|
2001
2024
|
[{"value": {{ stream_interval['start_time'] | int * 1000 }} }]
|
2002
2025
|
}, "orderBy": 1, "columnName": "Timestamp"}]/
|
2003
2026
|
request_body_json:
|
2027
|
+
deprecated: true
|
2028
|
+
deprecation_message: "Use `request_body` field instead."
|
2004
2029
|
title: Request Body JSON Payload
|
2005
2030
|
description: Specifies how to populate the body of the request with a JSON payload. Can contain nested objects.
|
2006
2031
|
anyOf:
|
@@ -2019,6 +2044,35 @@ definitions:
|
|
2019
2044
|
- sort:
|
2020
2045
|
field: "updated_at"
|
2021
2046
|
order: "ascending"
|
2047
|
+
request_body:
|
2048
|
+
title: Request Body Payload to be send as a part of the API request.
|
2049
|
+
description: Specifies how to populate the body of the request with a payload. Can contain nested objects.
|
2050
|
+
anyOf:
|
2051
|
+
- "$ref": "#/definitions/RequestBody"
|
2052
|
+
interpolation_context:
|
2053
|
+
- next_page_token
|
2054
|
+
- stream_interval
|
2055
|
+
- stream_partition
|
2056
|
+
- stream_slice
|
2057
|
+
examples:
|
2058
|
+
- type: RequestBodyJson
|
2059
|
+
value:
|
2060
|
+
sort_order: "ASC"
|
2061
|
+
sort_field: "CREATED_AT"
|
2062
|
+
- type: RequestBodyJson
|
2063
|
+
value:
|
2064
|
+
key: "{{ config['value'] }}"
|
2065
|
+
- type: RequestBodyJson
|
2066
|
+
value:
|
2067
|
+
sort:
|
2068
|
+
field: "updated_at"
|
2069
|
+
order: "ascending"
|
2070
|
+
- type: RequestBodyData
|
2071
|
+
value: "plain_text_body"
|
2072
|
+
- type: RequestBodyData
|
2073
|
+
value:
|
2074
|
+
param1: "value1"
|
2075
|
+
param2: "{{ config['param2_value'] }}"
|
2022
2076
|
request_headers:
|
2023
2077
|
title: Request Headers
|
2024
2078
|
description: Return any non-auth headers. Authentication headers will overwrite any overlapping headers returned from this method.
|
@@ -4019,6 +4073,27 @@ definitions:
|
|
4019
4073
|
- type
|
4020
4074
|
- stream_template
|
4021
4075
|
- components_resolver
|
4076
|
+
RequestBody:
|
4077
|
+
type: object
|
4078
|
+
description: The request body payload. Can be either URL encoded data or JSON.
|
4079
|
+
properties:
|
4080
|
+
type:
|
4081
|
+
anyOf:
|
4082
|
+
- type: string
|
4083
|
+
enum: [RequestBodyData]
|
4084
|
+
- type: string
|
4085
|
+
enum: [RequestBodyJson]
|
4086
|
+
value:
|
4087
|
+
anyOf:
|
4088
|
+
- type: string
|
4089
|
+
description: The request body payload as a string.
|
4090
|
+
- type: object
|
4091
|
+
description: The request body payload as a Non-JSON object (url-encoded data).
|
4092
|
+
additionalProperties:
|
4093
|
+
type: string
|
4094
|
+
- type: object
|
4095
|
+
description: The request body payload as a JSON object (json-encoded data).
|
4096
|
+
additionalProperties: true
|
4022
4097
|
interpolation:
|
4023
4098
|
variables:
|
4024
4099
|
- title: config
|
@@ -4227,4 +4302,4 @@ interpolation:
|
|
4227
4302
|
regex: The regular expression to search for. It must include a capture group.
|
4228
4303
|
return_type: str
|
4229
4304
|
examples:
|
4230
|
-
- '{{ "goodbye, cruel world" | regex_search("goodbye,\s(.*)$") }} -> "cruel world"'
|
4305
|
+
- '{{ "goodbye, cruel world" | regex_search("goodbye,\s(.*)$") }} -> "cruel world"'
|
@@ -4,8 +4,11 @@
|
|
4
4
|
|
5
5
|
import logging
|
6
6
|
from abc import abstractmethod
|
7
|
-
from typing import Any, Mapping, Tuple
|
7
|
+
from typing import Any, List, Mapping, Tuple
|
8
8
|
|
9
|
+
from airbyte_cdk.connector_builder.models import (
|
10
|
+
LogMessage as ConnectorBuilderLogMessage,
|
11
|
+
)
|
9
12
|
from airbyte_cdk.sources.abstract_source import AbstractSource
|
10
13
|
from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
|
11
14
|
|
@@ -34,3 +37,9 @@ class DeclarativeSource(AbstractSource):
|
|
34
37
|
The error object will be cast to string to display the problem to the user.
|
35
38
|
"""
|
36
39
|
return self.connection_checker.check_connection(self, logger, config)
|
40
|
+
|
41
|
+
def deprecation_warnings(self) -> List[ConnectorBuilderLogMessage]:
|
42
|
+
"""
|
43
|
+
Returns a list of deprecation warnings for the source.
|
44
|
+
"""
|
45
|
+
return []
|
@@ -12,7 +12,7 @@ from airbyte_cdk.sources.types import Config
|
|
12
12
|
NestedMappingEntry = Union[
|
13
13
|
dict[str, "NestedMapping"], list["NestedMapping"], str, int, float, bool, None
|
14
14
|
]
|
15
|
-
NestedMapping = Union[dict[str, NestedMappingEntry], str]
|
15
|
+
NestedMapping = Union[dict[str, NestedMappingEntry], str, dict[str, Any]]
|
16
16
|
|
17
17
|
|
18
18
|
@dataclass
|
@@ -15,6 +15,12 @@ from jsonschema.exceptions import ValidationError
|
|
15
15
|
from jsonschema.validators import validate
|
16
16
|
from packaging.version import InvalidVersion, Version
|
17
17
|
|
18
|
+
from airbyte_cdk.connector_builder.models import (
|
19
|
+
LogMessage as ConnectorBuilderLogMessage,
|
20
|
+
)
|
21
|
+
from airbyte_cdk.manifest_migrations.migration_handler import (
|
22
|
+
ManifestMigrationHandler,
|
23
|
+
)
|
18
24
|
from airbyte_cdk.models import (
|
19
25
|
AirbyteConnectionStatus,
|
20
26
|
AirbyteMessage,
|
@@ -91,6 +97,7 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
91
97
|
debug: bool = False,
|
92
98
|
emit_connector_builder_messages: bool = False,
|
93
99
|
component_factory: Optional[ModelToComponentFactory] = None,
|
100
|
+
migrate_manifest: Optional[bool] = False,
|
94
101
|
normalize_manifest: Optional[bool] = False,
|
95
102
|
) -> None:
|
96
103
|
"""
|
@@ -104,12 +111,11 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
104
111
|
"""
|
105
112
|
self.logger = logging.getLogger(f"airbyte.{self.name}")
|
106
113
|
self._should_normalize = normalize_manifest
|
114
|
+
self._should_migrate = migrate_manifest
|
107
115
|
self._declarative_component_schema = _get_declarative_component_schema()
|
108
116
|
# If custom components are needed, locate and/or register them.
|
109
117
|
self.components_module: ModuleType | None = get_registered_components_module(config=config)
|
110
|
-
#
|
111
|
-
self._source_config = self._preprocess_manifest(dict(source_config))
|
112
|
-
|
118
|
+
# set additional attributes
|
113
119
|
self._debug = debug
|
114
120
|
self._emit_connector_builder_messages = emit_connector_builder_messages
|
115
121
|
self._constructor = (
|
@@ -126,11 +132,12 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
126
132
|
)
|
127
133
|
self._config = config or {}
|
128
134
|
|
135
|
+
# resolve all components in the manifest
|
136
|
+
self._source_config = self._pre_process_manifest(dict(source_config))
|
129
137
|
# validate resolved manifest against the declarative component schema
|
130
138
|
self._validate_source()
|
131
|
-
|
132
139
|
# apply additional post-processing to the manifest
|
133
|
-
self.
|
140
|
+
self._post_process_manifest()
|
134
141
|
|
135
142
|
@property
|
136
143
|
def resolved_manifest(self) -> Mapping[str, Any]:
|
@@ -145,7 +152,7 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
145
152
|
"""
|
146
153
|
return self._source_config
|
147
154
|
|
148
|
-
def
|
155
|
+
def _pre_process_manifest(self, manifest: Dict[str, Any]) -> Dict[str, Any]:
|
149
156
|
"""
|
150
157
|
Preprocesses the provided manifest dictionary by resolving any manifest references.
|
151
158
|
|
@@ -169,12 +176,14 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
169
176
|
|
170
177
|
return propagated_manifest
|
171
178
|
|
172
|
-
def
|
179
|
+
def _post_process_manifest(self) -> None:
|
173
180
|
"""
|
174
181
|
Post-processes the manifest after validation.
|
175
182
|
This method is responsible for any additional modifications or transformations needed
|
176
183
|
after the manifest has been validated and before it is used in the source.
|
177
184
|
"""
|
185
|
+
# apply manifest migration, if required
|
186
|
+
self._migrate_manifest()
|
178
187
|
# apply manifest normalization, if required
|
179
188
|
self._normalize_manifest()
|
180
189
|
|
@@ -190,6 +199,19 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
190
199
|
normalizer = ManifestNormalizer(self._source_config, self._declarative_component_schema)
|
191
200
|
self._source_config = normalizer.normalize()
|
192
201
|
|
202
|
+
def _migrate_manifest(self) -> None:
|
203
|
+
"""
|
204
|
+
This method is used to migrate the manifest. It should be called after the manifest has been validated.
|
205
|
+
The migration is done in place, so the original manifest is modified.
|
206
|
+
|
207
|
+
The original manifest is returned if any error occurs during migration.
|
208
|
+
"""
|
209
|
+
if self._should_migrate:
|
210
|
+
manifest_migrator = ManifestMigrationHandler(self._source_config)
|
211
|
+
self._source_config = manifest_migrator.apply_migrations()
|
212
|
+
# validate migrated manifest against the declarative component schema
|
213
|
+
self._validate_source()
|
214
|
+
|
193
215
|
def _fix_source_type(self, manifest: Dict[str, Any]) -> Dict[str, Any]:
|
194
216
|
"""
|
195
217
|
Fix the source type in the manifest. This is necessary because the source type is not always set in the manifest.
|
@@ -211,6 +233,9 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
211
233
|
with_dynamic_stream_name=True,
|
212
234
|
)
|
213
235
|
|
236
|
+
def deprecation_warnings(self) -> List[ConnectorBuilderLogMessage]:
|
237
|
+
return self._constructor.get_model_deprecations()
|
238
|
+
|
214
239
|
@property
|
215
240
|
def connection_checker(self) -> ConnectionChecker:
|
216
241
|
check = self._source_config["check"]
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
2
|
+
|
3
|
+
# THIS IS A STATIC CLASS MODEL USED TO DISPLAY DEPRECATION WARNINGS
|
4
|
+
# WHEN DEPRECATED FIELDS ARE ACCESSED
|
5
|
+
|
6
|
+
import warnings
|
7
|
+
from typing import Any, List
|
8
|
+
|
9
|
+
from pydantic.v1 import BaseModel
|
10
|
+
|
11
|
+
from airbyte_cdk.connector_builder.models import LogMessage as ConnectorBuilderLogMessage
|
12
|
+
|
13
|
+
# format the warning message
|
14
|
+
warnings.formatwarning = (
|
15
|
+
lambda message, category, *args, **kwargs: f"{category.__name__}: {message}"
|
16
|
+
)
|
17
|
+
|
18
|
+
FIELDS_TAG = "__fields__"
|
19
|
+
DEPRECATED = "deprecated"
|
20
|
+
DEPRECATION_MESSAGE = "deprecation_message"
|
21
|
+
DEPRECATION_LOGS_TAG = "_deprecation_logs"
|
22
|
+
|
23
|
+
|
24
|
+
class BaseModelWithDeprecations(BaseModel):
|
25
|
+
"""
|
26
|
+
Pydantic BaseModel that warns when deprecated fields are accessed.
|
27
|
+
The deprecation message is stored in the field's extra attributes.
|
28
|
+
This class is used to create models that can have deprecated fields
|
29
|
+
and show warnings when those fields are accessed or initialized.
|
30
|
+
|
31
|
+
The `_deprecation_logs` attribute is stored in the model itself.
|
32
|
+
The collected deprecation warnings are further propagated to the Airbyte log messages,
|
33
|
+
during the component creation process, in `model_to_component._collect_model_deprecations()`.
|
34
|
+
|
35
|
+
The component implementation is not responsible for handling the deprecation warnings,
|
36
|
+
since the deprecation warnings are already handled in the model itself.
|
37
|
+
"""
|
38
|
+
|
39
|
+
class Config:
|
40
|
+
"""
|
41
|
+
Allow extra fields in the model. In case the model restricts extra fields.
|
42
|
+
"""
|
43
|
+
|
44
|
+
extra = "allow"
|
45
|
+
|
46
|
+
def __init__(self, **model_fields: Any) -> None:
|
47
|
+
"""
|
48
|
+
Show warnings for deprecated fields during component initialization.
|
49
|
+
"""
|
50
|
+
# call the parent constructor first to initialize Pydantic internals
|
51
|
+
super().__init__(**model_fields)
|
52
|
+
# set the placeholder for the default deprecation messages
|
53
|
+
self._default_deprecation_messages: List[str] = []
|
54
|
+
# set the placeholder for the deprecation logs
|
55
|
+
self._deprecation_logs: List[ConnectorBuilderLogMessage] = []
|
56
|
+
# process deprecated fields, if present
|
57
|
+
self._process_fields(model_fields)
|
58
|
+
# emit default deprecation messages
|
59
|
+
self._emit_default_deprecation_messages()
|
60
|
+
# set the deprecation logs attribute to the model
|
61
|
+
self._set_deprecation_logs_attr_to_model()
|
62
|
+
|
63
|
+
def _is_deprecated_field(self, field_name: str) -> bool:
|
64
|
+
return (
|
65
|
+
self.__fields__[field_name].field_info.extra.get(DEPRECATED, False)
|
66
|
+
if field_name in self.__fields__.keys()
|
67
|
+
else False
|
68
|
+
)
|
69
|
+
|
70
|
+
def _get_deprecation_message(self, field_name: str) -> str:
|
71
|
+
return (
|
72
|
+
self.__fields__[field_name].field_info.extra.get(
|
73
|
+
DEPRECATION_MESSAGE, "<missing_deprecation_message>"
|
74
|
+
)
|
75
|
+
if field_name in self.__fields__.keys()
|
76
|
+
else "<missing_deprecation_message>"
|
77
|
+
)
|
78
|
+
|
79
|
+
def _process_fields(self, model_fields: Any) -> None:
|
80
|
+
"""
|
81
|
+
Processes the fields in the provided model data, checking for deprecated fields.
|
82
|
+
|
83
|
+
For each field in the input `model_fields`, this method checks if the field exists in the model's defined fields.
|
84
|
+
If the field is marked as deprecated (using the `DEPRECATED` flag in its metadata), it triggers a deprecation warning
|
85
|
+
by calling the `_create_warning` method with the field name and an optional deprecation message.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
model_fields (Any): The data containing fields to be processed.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
None
|
92
|
+
"""
|
93
|
+
|
94
|
+
if hasattr(self, FIELDS_TAG):
|
95
|
+
for field_name in model_fields.keys():
|
96
|
+
if self._is_deprecated_field(field_name):
|
97
|
+
self._create_warning(
|
98
|
+
field_name,
|
99
|
+
self._get_deprecation_message(field_name),
|
100
|
+
)
|
101
|
+
|
102
|
+
def _set_deprecation_logs_attr_to_model(self) -> None:
|
103
|
+
"""
|
104
|
+
Sets the deprecation logs attribute on the model instance.
|
105
|
+
|
106
|
+
This method attaches the current instance's deprecation logs to the model by setting
|
107
|
+
an attribute named by `DEPRECATION_LOGS_TAG` to the value of `self._deprecation_logs`.
|
108
|
+
This is typically used to track or log deprecated features or configurations within the model.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
None
|
112
|
+
"""
|
113
|
+
setattr(self, DEPRECATION_LOGS_TAG, self._deprecation_logs)
|
114
|
+
|
115
|
+
def _create_warning(self, field_name: str, message: str) -> None:
|
116
|
+
"""
|
117
|
+
Show a warning message for deprecated fields (to stdout).
|
118
|
+
Args:
|
119
|
+
field_name (str): Name of the deprecated field.
|
120
|
+
message (str): Warning message to be displayed.
|
121
|
+
"""
|
122
|
+
|
123
|
+
deprecated_message = f"Component type: `{self.__class__.__name__}`. Field '{field_name}' is deprecated. {message}"
|
124
|
+
|
125
|
+
if deprecated_message not in self._default_deprecation_messages:
|
126
|
+
# Avoid duplicates in the default deprecation messages
|
127
|
+
self._default_deprecation_messages.append(deprecated_message)
|
128
|
+
|
129
|
+
# Create an Airbyte deprecation log message
|
130
|
+
deprecation_log_message = ConnectorBuilderLogMessage(
|
131
|
+
level="WARN", message=deprecated_message
|
132
|
+
)
|
133
|
+
# Add the deprecation message to the Airbyte log messages,
|
134
|
+
# this logs are displayed in the Connector Builder.
|
135
|
+
if deprecation_log_message not in self._deprecation_logs:
|
136
|
+
# Avoid duplicates in the deprecation logs
|
137
|
+
self._deprecation_logs.append(deprecation_log_message)
|
138
|
+
|
139
|
+
def _emit_default_deprecation_messages(self) -> None:
|
140
|
+
"""
|
141
|
+
Emit default deprecation messages for deprecated fields to STDOUT.
|
142
|
+
"""
|
143
|
+
for message in self._default_deprecation_messages:
|
144
|
+
warnings.warn(message, DeprecationWarning)
|
@@ -10,6 +10,10 @@ from typing import Any, Dict, List, Literal, Optional, Union
|
|
10
10
|
|
11
11
|
from pydantic.v1 import BaseModel, Extra, Field
|
12
12
|
|
13
|
+
from airbyte_cdk.sources.declarative.models.base_model_with_deprecations import (
|
14
|
+
BaseModelWithDeprecations,
|
15
|
+
)
|
16
|
+
|
13
17
|
|
14
18
|
class AuthFlowType(Enum):
|
15
19
|
oauth2_0 = "oauth2.0"
|
@@ -1497,6 +1501,11 @@ class ConfigComponentsResolver(BaseModel):
|
|
1497
1501
|
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
1498
1502
|
|
1499
1503
|
|
1504
|
+
class RequestBody(BaseModel):
|
1505
|
+
type: Optional[Union[Literal["RequestBodyData"], Literal["RequestBodyJson"]]] = None
|
1506
|
+
value: Optional[Union[str, Dict[str, str], Dict[str, Any]]] = None
|
1507
|
+
|
1508
|
+
|
1500
1509
|
class AddedFieldDefinition(BaseModel):
|
1501
1510
|
type: Literal["AddedFieldDefinition"]
|
1502
1511
|
path: List[str] = Field(
|
@@ -2207,11 +2216,13 @@ class SessionTokenAuthenticator(BaseModel):
|
|
2207
2216
|
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
2208
2217
|
|
2209
2218
|
|
2210
|
-
class HttpRequester(
|
2219
|
+
class HttpRequester(BaseModelWithDeprecations):
|
2211
2220
|
type: Literal["HttpRequester"]
|
2212
|
-
url_base: str = Field(
|
2213
|
-
|
2214
|
-
|
2221
|
+
url_base: Optional[str] = Field(
|
2222
|
+
None,
|
2223
|
+
deprecated=True,
|
2224
|
+
deprecation_message="Use `url` field instead.",
|
2225
|
+
description="Deprecated, use the `url` instead. Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
|
2215
2226
|
examples=[
|
2216
2227
|
"https://connect.squareup.com/v2",
|
2217
2228
|
"{{ config['base_url'] or 'https://app.posthog.com'}}/api",
|
@@ -2220,9 +2231,22 @@ class HttpRequester(BaseModel):
|
|
2220
2231
|
],
|
2221
2232
|
title="API Base URL",
|
2222
2233
|
)
|
2234
|
+
url: Optional[str] = Field(
|
2235
|
+
None,
|
2236
|
+
description="The URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
|
2237
|
+
examples=[
|
2238
|
+
"https://connect.squareup.com/v2",
|
2239
|
+
"{{ config['url'] or 'https://app.posthog.com'}}/api",
|
2240
|
+
"https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups",
|
2241
|
+
"https://example.com/api/v1/resource/{{ next_page_token['id'] }}",
|
2242
|
+
],
|
2243
|
+
title="The URL of an API endpoint",
|
2244
|
+
)
|
2223
2245
|
path: Optional[str] = Field(
|
2224
2246
|
None,
|
2225
|
-
|
2247
|
+
deprecated=True,
|
2248
|
+
deprecation_message="Use `url` field instead.",
|
2249
|
+
description="Deprecated, use the `url` instead. Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
|
2226
2250
|
examples=[
|
2227
2251
|
"/products",
|
2228
2252
|
"/quotes/{{ stream_partition['id'] }}/quote_line_groups",
|
@@ -2261,6 +2285,8 @@ class HttpRequester(BaseModel):
|
|
2261
2285
|
)
|
2262
2286
|
request_body_data: Optional[Union[Dict[str, str], str]] = Field(
|
2263
2287
|
None,
|
2288
|
+
deprecated=True,
|
2289
|
+
deprecation_message="Use `request_body` field instead.",
|
2264
2290
|
description="Specifies how to populate the body of the request with a non-JSON payload. Plain text will be sent as is, whereas objects will be converted to a urlencoded form.",
|
2265
2291
|
examples=[
|
2266
2292
|
'[{"clause": {"type": "timestamp", "operator": 10, "parameters":\n [{"value": {{ stream_interval[\'start_time\'] | int * 1000 }} }]\n }, "orderBy": 1, "columnName": "Timestamp"}]/\n'
|
@@ -2269,6 +2295,8 @@ class HttpRequester(BaseModel):
|
|
2269
2295
|
)
|
2270
2296
|
request_body_json: Optional[Union[Dict[str, Any], str]] = Field(
|
2271
2297
|
None,
|
2298
|
+
deprecated=True,
|
2299
|
+
deprecation_message="Use `request_body` field instead.",
|
2272
2300
|
description="Specifies how to populate the body of the request with a JSON payload. Can contain nested objects.",
|
2273
2301
|
examples=[
|
2274
2302
|
{"sort_order": "ASC", "sort_field": "CREATED_AT"},
|
@@ -2277,6 +2305,27 @@ class HttpRequester(BaseModel):
|
|
2277
2305
|
],
|
2278
2306
|
title="Request Body JSON Payload",
|
2279
2307
|
)
|
2308
|
+
request_body: Optional[RequestBody] = Field(
|
2309
|
+
None,
|
2310
|
+
description="Specifies how to populate the body of the request with a payload. Can contain nested objects.",
|
2311
|
+
examples=[
|
2312
|
+
{
|
2313
|
+
"type": "RequestBodyJson",
|
2314
|
+
"value": {"sort_order": "ASC", "sort_field": "CREATED_AT"},
|
2315
|
+
},
|
2316
|
+
{"type": "RequestBodyJson", "value": {"key": "{{ config['value'] }}"}},
|
2317
|
+
{
|
2318
|
+
"type": "RequestBodyJson",
|
2319
|
+
"value": {"sort": {"field": "updated_at", "order": "ascending"}},
|
2320
|
+
},
|
2321
|
+
{"type": "RequestBodyData", "value": "plain_text_body"},
|
2322
|
+
{
|
2323
|
+
"type": "RequestBodyData",
|
2324
|
+
"value": {"param1": "value1", "param2": "{{ config['param2_value'] }}"},
|
2325
|
+
},
|
2326
|
+
],
|
2327
|
+
title="Request Body Payload to be send as a part of the API request.",
|
2328
|
+
)
|
2280
2329
|
request_headers: Optional[Union[Dict[str, str], str]] = Field(
|
2281
2330
|
None,
|
2282
2331
|
description="Return any non-auth headers. Authentication headers will overwrite any overlapping headers returned from this method.",
|