From 92fc5b4230a935042f5f7326c3f1334e27235213 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 9 Oct 2025 15:13:38 -0500 Subject: [PATCH 1/2] feat: Add CustomErrorHandler to CompositeErrorHandler list --- .../declarative_component_schema.yaml | 1 + .../models/declarative_component_schema.py | 4 +- .../test_model_to_component_factory.py | 38 +++++++++++++++++++ .../declarative/parsers/testing_components.py | 9 +++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/airbyte_cdk/sources/declarative/declarative_component_schema.yaml b/airbyte_cdk/sources/declarative/declarative_component_schema.yaml index 7068105cb1..a77bc34ea4 100644 --- a/airbyte_cdk/sources/declarative/declarative_component_schema.yaml +++ b/airbyte_cdk/sources/declarative/declarative_component_schema.yaml @@ -391,6 +391,7 @@ definitions: anyOf: - "$ref": "#/definitions/CompositeErrorHandler" - "$ref": "#/definitions/DefaultErrorHandler" + - "$ref": "#/definitions/CustomErrorHandler" $parameters: type: object additionalProperties: true diff --git a/airbyte_cdk/sources/declarative/models/declarative_component_schema.py b/airbyte_cdk/sources/declarative/models/declarative_component_schema.py index 49b48a2320..d3fd62ab1a 100644 --- a/airbyte_cdk/sources/declarative/models/declarative_component_schema.py +++ b/airbyte_cdk/sources/declarative/models/declarative_component_schema.py @@ -2149,7 +2149,9 @@ class ConfigAddFields(BaseModel): class CompositeErrorHandler(BaseModel): type: Literal["CompositeErrorHandler"] - error_handlers: List[Union[CompositeErrorHandler, DefaultErrorHandler]] = Field( + error_handlers: List[ + Union[CompositeErrorHandler, DefaultErrorHandler, CustomErrorHandler] + ] = Field( ..., description="List of error handlers to iterate on to determine how to handle a failed response.", title="Error Handlers", diff --git a/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py b/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py index 5c4d2c8d58..903eeb14c4 100644 --- a/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +++ b/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py @@ -2027,6 +2027,44 @@ def test_create_composite_error_handler(): assert error_handler_1.response_filters[0].action == ResponseAction.RETRY +def test_create_composite_error_handler_with_custom_error_handler(): + content = """ + error_handler: + type: "CompositeErrorHandler" + error_handlers: + - type: "CustomErrorHandler" + class_name: "unit_tests.sources.declarative.parsers.testing_components.TestingCustomErrorHandler" + - response_filters: + - http_codes: [ 429 ] + action: RETRY + """ + parsed_manifest = YamlDeclarativeSource._parse(content) + resolved_manifest = resolver.preprocess_manifest(parsed_manifest) + error_handler_manifest = transformer.propagate_types_and_parameters( + "", resolved_manifest["error_handler"], {} + ) + + error_handler = factory.create_component( + model_type=CompositeErrorHandlerModel, + component_definition=error_handler_manifest, + config=input_config, + ) + + assert isinstance(error_handler, CompositeErrorHandler) + assert len(error_handler.error_handlers) == 2 + + # First error handler should be a custom error handler + error_handler_0 = error_handler.error_handlers[0] + assert error_handler_0.__class__.__name__ == "TestingCustomErrorHandler" + + # Second error handler should be a default error handler + error_handler_1 = error_handler.error_handlers[1] + assert isinstance(error_handler_1, DefaultErrorHandler) + assert isinstance(error_handler_1.response_filters[0], HttpResponseFilter) + assert error_handler_1.response_filters[0].http_codes == {429} + assert error_handler_1.response_filters[0].action == ResponseAction.RETRY + + # This might be a better test for the manifest transformer but also worth testing end-to-end here as well def test_config_with_defaults(): content = """ diff --git a/unit_tests/sources/declarative/parsers/testing_components.py b/unit_tests/sources/declarative/parsers/testing_components.py index 88316b521c..6269acb8e3 100644 --- a/unit_tests/sources/declarative/parsers/testing_components.py +++ b/unit_tests/sources/declarative/parsers/testing_components.py @@ -90,6 +90,15 @@ def migrate(self, stream_state: Mapping[str, Any]) -> Mapping[str, Any]: return stream_state +@dataclass +class TestingCustomErrorHandler(DefaultErrorHandler): + """ + A test class based on DefaultErrorHandler used for testing manifests that use custom error handlers. + """ + + __test__: ClassVar[bool] = False # Tell Pytest this is not a Pytest class, despite its name + + @dataclass class TestingRequester(HttpRequester): request_parameters: Optional[RequestInput] = None From c437cc15a70c2dde3f5d3b848fc1984498aab153 Mon Sep 17 00:00:00 2001 From: octavia-squidington-iii Date: Thu, 9 Oct 2025 20:21:41 +0000 Subject: [PATCH 2/2] Auto-fix lint and format issues --- .../models/declarative_component_schema.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/airbyte_cdk/sources/declarative/models/declarative_component_schema.py b/airbyte_cdk/sources/declarative/models/declarative_component_schema.py index d3fd62ab1a..915f942d05 100644 --- a/airbyte_cdk/sources/declarative/models/declarative_component_schema.py +++ b/airbyte_cdk/sources/declarative/models/declarative_component_schema.py @@ -2149,12 +2149,12 @@ class ConfigAddFields(BaseModel): class CompositeErrorHandler(BaseModel): type: Literal["CompositeErrorHandler"] - error_handlers: List[ - Union[CompositeErrorHandler, DefaultErrorHandler, CustomErrorHandler] - ] = Field( - ..., - description="List of error handlers to iterate on to determine how to handle a failed response.", - title="Error Handlers", + error_handlers: List[Union[CompositeErrorHandler, DefaultErrorHandler, CustomErrorHandler]] = ( + Field( + ..., + description="List of error handlers to iterate on to determine how to handle a failed response.", + title="Error Handlers", + ) ) parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")