Skip to content

Commit 8dd1200

Browse files
author
Oleksandr Bazarnov
committed
added HttpRequester deprecations
1 parent f63f195 commit 8dd1200

File tree

6 files changed

+195
-72
lines changed

6 files changed

+195
-72
lines changed

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,14 +1863,16 @@ definitions:
18631863
type: object
18641864
required:
18651865
- type
1866-
- url_base
18671866
properties:
18681867
type:
18691868
type: string
18701869
enum: [HttpRequester]
18711870
url_base:
1871+
deprecated: true
1872+
deprecation_message: "Use `url` field instead."
1873+
sharable: true
18721874
title: API Base URL
1873-
description: Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
1875+
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.
18741876
type: string
18751877
interpolation_context:
18761878
- config
@@ -1886,9 +1888,30 @@ definitions:
18861888
- "{{ config['base_url'] or 'https://app.posthog.com'}}/api"
18871889
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
18881890
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
1891+
url:
1892+
sharable: true
1893+
title: API URL
1894+
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.
1895+
type: string
1896+
interpolation_context:
1897+
- config
1898+
- next_page_token
1899+
- stream_interval
1900+
- stream_partition
1901+
- stream_slice
1902+
- creation_response
1903+
- polling_response
1904+
- download_target
1905+
examples:
1906+
- "https://connect.squareup.com/v2"
1907+
- "{{ config['url'] or 'https://app.posthog.com'}}/api"
1908+
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
1909+
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
18891910
path:
1911+
deprecated: true
1912+
deprecation_message: "Use `url` field instead."
18901913
title: URL Path
1891-
description: 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.
1914+
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.
18921915
type: string
18931916
interpolation_context:
18941917
- config
@@ -4158,4 +4181,4 @@ interpolation:
41584181
regex: The regular expression to search for. It must include a capture group.
41594182
return_type: str
41604183
examples:
4161-
- '{{ "goodbye, cruel world" | regex_search("goodbye,\s(.*)$") }} -> "cruel world"'
4184+
- '{{ "goodbye, cruel world" | regex_search("goodbye,\s(.*)$") }} -> "cruel world"'

airbyte_cdk/sources/declarative/models/base_model_with_deprecations.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,39 @@ class Config:
4646

4747
extra = "allow"
4848

49-
def __init__(self, **data: Any) -> None:
49+
def __init__(self, **model_data: Any) -> None:
5050
"""
5151
Show warnings for deprecated fields during component initialization.
5252
"""
53-
# placeholder for deprecation logs
53+
54+
# call the parent constructor first to initialize Pydantic internals
55+
super().__init__(**model_data)
56+
57+
# set the placeholder for the deprecation logs
5458
self._deprecation_logs: List[AirbyteLogMessage] = []
5559

60+
# process deprecated fields, if present
61+
self._process_fields(model_data)
62+
63+
# set the deprecation logs attribute to the model
64+
self._set_deprecation_logs_attr_to_model()
65+
66+
def _process_fields(self, model_data: Any) -> None:
67+
"""
68+
Processes the fields in the provided model data, checking for deprecated fields.
69+
70+
For each field in the input `model_data`, this method checks if the field exists in the model's defined fields.
71+
If the field is marked as deprecated (using the `DEPRECATED` flag in its metadata), it triggers a deprecation warning
72+
by calling the `_deprecated_warning` method with the field name and an optional deprecation message.
73+
74+
Args:
75+
model_data (Any): The data containing fields to be processed.
76+
77+
Returns:
78+
None
79+
"""
5680
model_fields = self.__fields__
57-
for field_name in data:
81+
for field_name in model_data.keys():
5882
if field_name in model_fields:
5983
is_deprecated_field = model_fields[field_name].field_info.extra.get(
6084
DEPRECATED, False
@@ -65,28 +89,18 @@ def __init__(self, **data: Any) -> None:
6589
)
6690
self._deprecated_warning(field_name, deprecation_message)
6791

68-
# Call the parent constructor
69-
super().__init__(**data)
70-
71-
def __getattribute__(self, name: str) -> Any:
72-
"""
73-
Show warnings for deprecated fields during field usage.
92+
def _set_deprecation_logs_attr_to_model(self) -> None:
7493
"""
94+
Sets the deprecation logs attribute on the model instance.
7595
76-
value = super().__getattribute__(name)
77-
try:
78-
model_fields = super().__getattribute__(FIELDS_TAG)
79-
field_info = model_fields.get(name)
80-
is_deprecated_field = (
81-
field_info.field_info.extra.get(DEPRECATED, False) if field_info else False
82-
)
83-
if is_deprecated_field:
84-
deprecation_message = field_info.field_info.extra.get(DEPRECATION_MESSAGE, "")
85-
self._deprecated_warning(name, deprecation_message)
86-
except (AttributeError, KeyError):
87-
pass
88-
89-
return value
96+
This method attaches the current instance's deprecation logs to the model by setting
97+
an attribute named by `DEPRECATION_LOGS_TAG` to the value of `self._deprecation_logs`.
98+
This is typically used to track or log deprecated features or configurations within the model.
99+
100+
Returns:
101+
None
102+
"""
103+
setattr(self, DEPRECATION_LOGS_TAG, self._deprecation_logs)
90104

91105
def _deprecated_warning(self, field_name: str, message: str) -> None:
92106
"""
@@ -97,12 +111,12 @@ def _deprecated_warning(self, field_name: str, message: str) -> None:
97111
"""
98112

99113
message = f"Component type: `{self.__class__.__name__}`. Field '{field_name}' is deprecated. {message}"
100-
101114
# Emit a warning message for deprecated fields (to stdout) (Python Default behavior)
102115
warnings.warn(message, DeprecationWarning)
103-
116+
# Create an Airbyte deprecation log message
117+
deprecation_log_message = AirbyteLogMessage(level=Level.WARN, message=message)
104118
# Add the deprecation message to the Airbyte log messages,
105119
# this logs are displayed in the Connector Builder.
106-
self._deprecation_logs.append(
107-
AirbyteLogMessage(level=Level.WARN, message=message),
108-
)
120+
if deprecation_log_message not in self._deprecation_logs:
121+
# Avoid duplicates in the deprecation logs
122+
self._deprecation_logs.append(deprecation_log_message)

airbyte_cdk/sources/declarative/models/declarative_component_schema.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2-
31
# generated by datamodel-codegen:
42
# filename: declarative_component_schema.yaml
53

@@ -10,6 +8,10 @@
108

119
from pydantic.v1 import BaseModel, Extra, Field
1210

11+
from airbyte_cdk.sources.declarative.models.base_model_with_deprecations import (
12+
BaseModelWithDeprecations,
13+
)
14+
1315

1416
class AuthFlowType(Enum):
1517
oauth2_0 = "oauth2.0"
@@ -880,20 +882,17 @@ class FlattenFields(BaseModel):
880882

881883

882884
class KeyTransformation(BaseModel):
883-
prefix: Optional[Union[str, None]] = Field(
885+
type: Literal["KeyTransformation"]
886+
prefix: Optional[str] = Field(
884887
None,
885888
description="Prefix to add for object keys. If not provided original keys remain unchanged.",
886-
examples=[
887-
"flattened_",
888-
],
889+
examples=["flattened_"],
889890
title="Key Prefix",
890891
)
891-
suffix: Optional[Union[str, None]] = Field(
892+
suffix: Optional[str] = Field(
892893
None,
893894
description="Suffix to add for object keys. If not provided original keys remain unchanged.",
894-
examples=[
895-
"_flattened",
896-
],
895+
examples=["_flattened"],
897896
title="Key Suffix",
898897
)
899898

@@ -916,7 +915,7 @@ class DpathFlattenFields(BaseModel):
916915
description="Whether to replace the origin record or not. Default is False.",
917916
title="Replace Origin Record",
918917
)
919-
key_transformation: Optional[Union[KeyTransformation, None]] = Field(
918+
key_transformation: Optional[KeyTransformation] = Field(
920919
None,
921920
description="Transformation for object keys. If not provided, original key will be used.",
922921
title="Key transformation",
@@ -2171,11 +2170,13 @@ class SessionTokenAuthenticator(BaseModel):
21712170
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
21722171

21732172

2174-
class HttpRequester(BaseModel):
2173+
class HttpRequester(BaseModelWithDeprecations):
21752174
type: Literal["HttpRequester"]
2176-
url_base: str = Field(
2177-
...,
2178-
description="Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2175+
url_base: Optional[str] = Field(
2176+
None,
2177+
deprecated=True,
2178+
deprecation_message="Use `url` field instead.",
2179+
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.",
21792180
examples=[
21802181
"https://connect.squareup.com/v2",
21812182
"{{ config['base_url'] or 'https://app.posthog.com'}}/api",
@@ -2184,9 +2185,22 @@ class HttpRequester(BaseModel):
21842185
],
21852186
title="API Base URL",
21862187
)
2188+
url: Optional[str] = Field(
2189+
None,
2190+
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.",
2191+
examples=[
2192+
"https://connect.squareup.com/v2",
2193+
"{{ config['url'] or 'https://app.posthog.com'}}/api",
2194+
"https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups",
2195+
"https://example.com/api/v1/resource/{{ next_page_token['id'] }}",
2196+
],
2197+
title="API URL",
2198+
)
21872199
path: Optional[str] = Field(
21882200
None,
2189-
description="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.",
2201+
deprecated=True,
2202+
deprecation_message="Use `url` field instead.",
2203+
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.",
21902204
examples=[
21912205
"/products",
21922206
"/quotes/{{ stream_partition['id'] }}/quote_line_groups",

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,7 +2194,7 @@ def create_http_requester(
21942194
self._create_component_from_model(
21952195
model=model.authenticator,
21962196
config=config,
2197-
url_base=model.url_base,
2197+
url_base=model.url or model.url_base,
21982198
name=name,
21992199
decoder=decoder,
22002200
)
@@ -2231,6 +2231,7 @@ def create_http_requester(
22312231

22322232
return HttpRequester(
22332233
name=name,
2234+
url=model.url,
22342235
url_base=model.url_base,
22352236
path=model.path,
22362237
authenticator=authenticator,
@@ -2928,6 +2929,25 @@ def create_simple_retriever(
29282929
use_cache: Optional[bool] = None,
29292930
**kwargs: Any,
29302931
) -> SimpleRetriever:
2932+
def _get_url() -> str:
2933+
"""
2934+
Closure to get the URL from the requester. This is used to get the URL in the case of a lazy retriever.
2935+
This is needed because the URL is not set until the requester is created.
2936+
"""
2937+
2938+
_url = (
2939+
model.requester.url
2940+
if hasattr(model.requester, "url") and model.requester.url is not None
2941+
else requester.get_url()
2942+
)
2943+
_url_base = (
2944+
model.requester.url_base
2945+
if hasattr(model.requester, "url_base") and model.requester.url_base is not None
2946+
else requester.get_url_base()
2947+
)
2948+
2949+
return _url or _url_base
2950+
29312951
decoder = (
29322952
self._create_component_from_model(model=model.decoder, config=config)
29332953
if model.decoder
@@ -2992,11 +3012,6 @@ def create_simple_retriever(
29923012
use_cache=use_cache,
29933013
config=config,
29943014
)
2995-
url_base = (
2996-
model.requester.url_base
2997-
if hasattr(model.requester, "url_base")
2998-
else requester.get_url_base()
2999-
)
30003015

30013016
# Define cursor only if per partition or common incremental support is needed
30023017
cursor = stream_slicer if isinstance(stream_slicer, DeclarativeCursor) else None
@@ -3020,7 +3035,7 @@ def create_simple_retriever(
30203035
self._create_component_from_model(
30213036
model=model.paginator,
30223037
config=config,
3023-
url_base=url_base,
3038+
url_base=_get_url(),
30243039
extractor_model=model.record_selector.extractor,
30253040
decoder=decoder,
30263041
cursor_used_for_stop_condition=cursor_used_for_stop_condition,

0 commit comments

Comments
 (0)