Repository navigation
Components nested under a custom component cannot receive required positional-or-keyword factory args #1186
Description
Component
Low-code component factory: ModelToComponentFactory._create_nested_component
Description
A component nested directly under a custom component (any Custom* type with class_name) is built by _create_nested_component. It forwards only the keyword-only args of the child's factory, taken from the parent's kwargs or from the child's $parameters (airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py:2045-2058). A factory whose required arg is not keyword-only, or that only passes its kwargs on to its own children, cannot get that arg there. The build fails at stream creation, so check, discover and read all fail.
Line numbers are at main (0ca75ef4). #500 does not move them.
Two cases remain after #500 (#422):
SelectiveAuthenticatorwith a session branch under a custom component, for exampleCustomRequester.authenticator.create_selective_authenticator(:3459-3472) has no keyword-only args, so nothing is forwarded to it, and it has nothing to pass on tocreate_session_token_authenticator(name,:1212-1213) orcreate_legacy_session_token_authenticator(url_base,:3475-3477). The error hint is wrong: it asks forSelectiveAuthenticator.$parameters.name(or.url_base), but that is filtered out too, because it is not a keyword-only arg ofcreate_selective_authenticator.CursorPaginationorOffsetIncrementunder a custom component, for example a custom partition router field typedPaginationStrategy.create_cursor_pagination(:1831-1833) andcreate_offset_increment(:3126-3133) require a positional-or-keyworddecoder.$parameterscannot hold aDecoderobject, so there is no workaround. The rawTypeErroralso bypasses_extract_missing_parameters(:2020-2025), which only matches "keyword-only", so the user gets no hint.
create_legacy_to_per_partition_state_migration (:1170-1175, required positional-or-keyword declarative_stream) follows the same pattern, but I found no parent that would nest it under a custom component.
Repro
Run from the CDK repo root, which puts unit_tests.sources.declarative.parsers.testing_components on the path:
from airbyte_cdk.sources.declarative.models import CustomPartitionRouter, CustomRequester
from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import ModelToComponentFactory
TESTING = "unit_tests.sources.declarative.parsers.testing_components"
SESSION = {
"type": "SessionTokenAuthenticator",
"login_requester": {"type": "HttpRequester", "url_base": "https://api.test.com", "path": "/login", "http_method": "POST"},
"session_token_path": ["token"],
"request_authentication": {"type": "ApiKey", "inject_into": {"type": "RequestOption", "inject_into": "header", "field_name": "X-Session"}},
}
# Case 1: SelectiveAuthenticator{SessionTokenAuthenticator} under a CustomRequester
ModelToComponentFactory().create_component(
model_type=CustomRequester,
component_definition={
"type": "CustomRequester",
"class_name": f"{TESTING}.TestingRequester",
"url_base": "https://api.test.com",
"path": "/items",
"http_method": "GET",
"authenticator": {
"type": "SelectiveAuthenticator",
"authenticator_selection_path": ["auth_type"],
"authenticators": {"session": SESSION},
"$parameters": {"name": "items"}, # the suggested fix, which is ignored
},
},
config={"auth_type": "session"},
name="items",
)
# Case 2: CursorPagination under a custom partition router
ModelToComponentFactory().create_component(
model_type=CustomPartitionRouter,
component_definition={
"type": "CustomPartitionRouter",
"class_name": f"{TESTING}.TestingCustomSubstreamPartitionRouter",
"custom_field": "x",
"custom_pagination_strategy": {"type": "CursorPagination", "cursor_value": "{{ response.next }}"},
"parent_stream_configs": [],
},
config={},
stream_name="items",
)Output with #500 applied:
Case 1: ValueError: Error creating component 'SelectiveAuthenticator' with parent custom component unit_tests.sources.declarative.parsers.testing_components.TestingRequester: Please provide SelectiveAuthenticator.$parameters.name
(same with and without $parameters.name; a LegacySessionTokenAuthenticator branch asks for $parameters.url_base, same result)
Case 2: TypeError: Error creating component 'CursorPagination' with parent custom component unit_tests.sources.declarative.parsers.testing_components.TestingCustomSubstreamPartitionRouter: ModelToComponentFactory.create_cursor_pagination() missing 1 required positional argument: 'decoder'
(OffsetIncrement: same error from create_offset_increment(); PageIncrement, which has no required decoder, gets past the pagination strategy, and the snippet then stops on the empty parent_stream_configs)
Fleet impact
None found. I scanned 535 manifest.yaml files in the monorepo with $refs resolved. No custom component has a direct CursorPagination, OffsetIncrement, LegacyToPerPartitionStateMigration or session-bearing SelectiveAuthenticator child. 5 connectors (source-asana, source-hubspot, source-linkedin-ads, source-mixpanel, source-slack) put a SelectiveAuthenticator under a CustomRequester, but only with Bearer, OAuth, ApiKey or Basic branches, so they are not affected. Builder and Cloud manifests were not scanned.
Fix options
- Case 1: add keyword-only pass-through params to
create_selective_authenticator(*, name: Optional[str] = None, url_base: Optional[str] = None, decoder: Optional[Decoder] = None) and forward only the non-None ones to the nested authenticators._create_nested_componentthen fills them from the parent's kwargs or fromSelectiveAuthenticator.$parameters, and the existing hint becomes correct. - Case 2: default
decoderincreate_cursor_paginationandcreate_offset_incrementtoJsonDecoder, the fallbackcreate_default_paginatoralready uses (:2453-2459). - Not recommended: widen the
kwonlyargsfilter in_create_nested_componentto positional-or-keyword args. It would start forwarding args such ascreate_http_requester'sdecoderto every component nested under a custom parent, which changes behavior across the fleet.
Precedent
#422, fixed by #500. There, the same filter dropped the positional name of create_session_token_authenticator. #500 made name keyword-only and made create_selective_authenticator forward the requester kwargs to its nested authenticators.
Activity