Skip to content

fix(low-code): build session authenticators and paginators nested in SelectiveAuthenticator or custom components - #500

Merged
Baz (bazarnov) merged 5 commits into
airbytehq:mainfrom
hai-ld:patch-1
Oct 2, 2026
Merged

Baz (bazarnov) merged 5 commits into
airbytehq:mainfrom
hai-ld:patch-1

Conversation

@hai-ld

@hai-ld hai-ld commented Apr 21, 2025 •

Copy link
Copy Markdown
Contributor

This builds on hai-ld's original change in this PR: name becomes keyword-only in create_session_token_authenticator, so a SessionTokenAuthenticator can be created as a subcomponent of a custom component. I merged main, added the SelectiveAuthenticator half of #422, the remaining nested-under-custom-component cases from #1186, and tests for all of them.

Summary

Three related defects in how the factory passes args to nested components. A and B end in the same TypeError from create_session_token_authenticator:

  • A. SelectiveAuthenticator drops the requester kwargs (the [SelectiveAuthenticator] TypeError when attempting to create SessionTokenAuthenticator #422 body). create_selective_authenticator built every nested authenticator without kwargs. It forwarded **kwargs only into SelectiveAuthenticator(...), whose __new__ discards them and returns the selected child. So the name, url_base and decoder that create_http_requester passes never reached the nested authenticators. SessionTokenAuthenticator (requires name) and LegacySessionTokenAuthenticator (requires url_base) failed at stream build time in check, discover and read. All branches are built eagerly, so the error also happened when the session branch was not selected.
  • B. A positional name is filtered out under custom components ([SelectiveAuthenticator] TypeError when attempting to create SessionTokenAuthenticator #422 comment 1, and the original case of this PR). _create_nested_component forwards only keyword-only constructor args, from the parent's kwargs or from $parameters. name was positional-or-keyword, so it was always dropped, including the documented $parameters.name escape hatch.
  • C. The same filter starves other children of custom components (Components nested under a custom component cannot receive required positional-or-keyword factory args #1186). create_selective_authenticator had no keyword-only args, so a SelectiveAuthenticator directly under a custom component (e.g. CustomRequester.authenticator) got nothing to pass to a session branch, and the hint it raised (Please provide SelectiveAuthenticator.$parameters.name) could not be followed. CursorPagination and OffsetIncrement require a positional decoder, which a custom parent cannot pass and $parameters cannot hold.

Changes

  • create_selective_authenticator forwards **kwargs to each nested _create_component_from_model. The comprehension variable is renamed name >> key, so the selector key is not confused with the stream name. The **kwargs passed to SelectiveAuthenticator(...) stays as it was.
  • create_session_token_authenticator takes name as keyword-only (hai-ld's change). When the parent passes name, as a CustomAuthenticator on an HttpRequester does, the parent's stream name wins over $parameters.name (model_to_component_factory.py:2057). $parameters.name applies only when no parent passes name.
  • create_selective_authenticator takes name and url_base as keyword-only args and forwards the non-None ones. Under a custom component they come from the parent's kwargs or from SelectiveAuthenticator.$parameters, so the existing hint now works; a missing value still raises it. decoder is not added: no built-in authenticator reads it.
  • create_cursor_pagination and create_offset_increment default decoder to JsonDecoder, the fallback create_default_paginator already uses. Explicit decoders are unchanged.

Why the stream name and not the selector key

#422 comment 2 suggests passing the selector key as name. I forwarded the requester kwargs instead, so a nested authenticator gets exactly what it gets when set directly on an HttpRequester:

  • The login requester is named <stream>_login_requester, as it is without SelectiveAuthenticator. That name sets the airbyte.HttpRequester.<name> logger (requesters/http_requester.py:272) and the StreamDescriptor on traced errors (streams/http/http_client.py:355,363) and on the rate-limited stream status (http_client.py:588). The selector key would name every stream's login requester session_login_requester.
  • name is the context in the REDUCE_PAGE_SIZE rejection message for the login requester (model_to_component_factory.py:1215-1217).
  • LegacySessionTokenAuthenticator needs url_base (model_to_component_factory.py:3475-3477), which a selector key cannot supply.

Compatibility

  • Monorepo scan with $refs resolved: 29 connectors use SelectiveAuthenticator, and none nests SessionTokenAuthenticator or LegacySessionTokenAuthenticator (branches: OAuth 23, Bearer 16, Basic 7, ApiKey 5, Custom 3). Every factory in the SelectiveAuthenticator.authenticators union accepts **kwargs. Only the two session factories use name or url_base, so the other branches build as before.
  • A CustomAuthenticator inside a SelectiveAuthenticator now receives name, url_base and decoder and passes them to its nested components, the same as a CustomAuthenticator set directly on an HttpRequester. create_custom_component lets these override same-named manifest fields (model_to_component_factory.py:1887-1890), and a nested requester's $parameters.name is replaced by the stream name, as it already is when the CustomAuthenticator sits on the HttpRequester directly. The 3 connectors with this shape (source-jira, source-okta, source-salesloft) declare none of these fields and have no nested components in them.
  • SelectiveAuthenticator directly under a custom component: 5 connectors (source-asana, source-hubspot, source-linkedin-ads, source-mixpanel, source-slack), all with OAuth, Bearer, ApiKey or Basic branches, whose factories ignore name and url_base. No monorepo CursorPagination or OffsetIncrement sits directly under a custom component.
  • Making name keyword-only only affects positional callers of create_session_token_authenticator. There are none: the CDK calls it through the constructor map with name=, and no monorepo connector calls it.

Tests

  • parsers/test_model_to_component_factory.py, next to test_create_requester_with_selective_authenticator:
    • test_create_requester_with_selective_authenticator_nesting_session_authenticators[session|legacy|token|oauth]: one SelectiveAuthenticator with all four branches. Each selection builds, which also covers the eager build of unselected session branches. The legacy case also checks that the requester's url_base reaches LegacySessionTokenAuthenticator.
    • test_create_requester_with_selective_authenticator_passes_stream_name_to_session_token_authenticator: login_requester.name == "lists_login_requester".
    • test_create_requester_with_custom_authenticator_nesting_session_token_authenticator: the parent's name reaches the nested SessionTokenAuthenticator.
    • test_create_custom_authenticator_nesting_session_token_authenticator_with_name_in_parameters: $parameters.name works when the parent passes no name.
    • test_create_requester_with_selective_authenticator_nesting_custom_authenticator: SelectiveAuthenticator >> CustomAuthenticator >> SessionTokenAuthenticator, which needs both changes.
  • test_concurrent_declarative_source.py::test_given_selective_authenticator_nesting_session_token_authenticator_when_read_then_authenticate: end to end through ConcurrentDeclarativeSource and HttpMocker. With session selected there is one POST /login and the session header on the data request. With token selected, records are returned and no login request is sent.
  • parsers/testing_components.py: TestingCustomAuthenticator, a custom authenticator that wraps a nested one.
  • Components nested under a custom component cannot receive required positional-or-keyword factory args #1186, in parsers/test_model_to_component_factory.py: test_create_custom_requester_with_selective_authenticator_* (session branch gets the stream name; legacy branch gets $parameters.url_base; without it, the hint is raised) and test_parse_custom_component_fields_if_subcomponent[test_cursor_pagination_without_decoder|test_offset_increment_without_decoder]. 4 fail before this change; the hint case is a guard.

All 10 #422 cases fail on main. The existing test_create_requester_with_selective_authenticator cases still pass.

Mutation check, each production change reverted or altered alone:

Mutation New cases failing
No kwargs forwarded in create_selective_authenticator 8 of 10 (every SelectiveAuthenticator case, both end-to-end cases)
name positional again 3 of 10 (every CustomAuthenticator case)
Selector key passed as name 2 of 10 (both stream-name assertions under SelectiveAuthenticator)
Only name forwarded, no url_base 4 of 10 (every nesting_session_authenticators case)
Wrong url_base forwarded 1 of 10 (the legacy case)

unit_tests/sources/declarative/parsers, auth and requesters/paginators: 715 passed. ruff format, ruff check and mypy on the factory: clean.

Fixes #422
Fixes #1186

@coderabbitai

coderabbitai Bot commented Apr 21, 2025 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The change updates the method signature of create_session_token_authenticator in the ModelToComponentFactory class. The name parameter, which was previously a positional argument, is now required to be passed as a keyword argument. No internal logic or return type modifications were made to the method.

Changes

File Change Summary
Authenticator Factory Update
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py
Made the name parameter of create_session_token_authenticator a keyword-only argument.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant ModelToComponentFactory

    Caller->>ModelToComponentFactory: create_session_token_authenticator(model, config, name=...)
    ModelToComponentFactory-->>Caller: Returns ApiKeyAuthenticator or BearerAuthenticator
Loading

Would you like to update any related documentation or usage examples to reflect the new keyword-only requirement for the name parameter, wdyt?

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86519d0 and ef67b08.

📒 Files selected for processing (1)
  • airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: SDM Docker Image Build
  • GitHub Check: preview_docs
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (Fast)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@hai-ld hai-ld changed the title Allow SessionTokenAuthenticator to be created as a subcomponent to a custom component fix: Allow SessionTokenAuthenticator to be created as a subcomponent to a custom component Apr 21, 2025
@hai-ld

hai-ld commented Jul 16, 2025

Copy link
Copy Markdown
Contributor Author

Hi Airbyte team Brian Lai (@brianjlai) Aldo Gonzalez (@aldogonzalez8),

Could you help with reviewing this PR please? I'm not sure how to config the GCP_GSM_CREDENTIALS env var required by the tests. Thanks!

…custom component

Change `name` argument in the function `create_session_token_authenticator` to a keyword-only argument so that it can be injected via `$parameters`. Other `name` arguments in the same module are already keyword-only.
@devin-ai-integration devin-ai-integration Bot added the community PRs and issues from community contributors label Dec 10, 2025
@bazarnov
Baz (bazarnov) requested a review from a team as a code owner September 28, 2026 15:20
@bazarnov Baz (bazarnov) changed the title fix: Allow SessionTokenAuthenticator to be created as a subcomponent to a custom component fix(low-code): build session token authenticators nested in SelectiveAuthenticator or custom components Sep 28, 2026

@bazarnov Baz (bazarnov) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks hai-ld for the original fix, and sorry it waited this long.

I extended it to close #422 as well:

  • merged main;
  • create_selective_authenticator now forwards the requester kwargs to its nested authenticators, so SessionTokenAuthenticator (name) and LegacySessionTokenAuthenticator (url_base) build inside a SelectiveAuthenticator, with the stream name, the same as on an HttpRequester;
  • factory tests for both halves plus one end-to-end read through ConcurrentDeclarativeSource; every new case fails on main, and each production line is pinned by a mutation.

No monorepo manifest changes behavior (0 of 29 SelectiveAuthenticator users nest a session authenticator). The remaining nesting gaps under custom components are tracked in #1186.

The connector checks on this PR fail only because GSM secrets are not available to fork PRs (No Google Cloud credentials found in secrets fetch); the same commit passed all of them in a base-repo run: https://github.com/airbytehq/airbyte-python-cdk/actions/runs/36428824962. destination-motherduck is red on main as well.

@bazarnov Baz (bazarnov) changed the title fix(low-code): build session token authenticators nested in SelectiveAuthenticator or custom components fix(low-code): build session authenticators and paginators nested in SelectiveAuthenticator or custom components Oct 2, 2026
@bazarnov
Baz (bazarnov) merged commit 9ac8aa6 into airbytehq:main Oct 2, 2026
17 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community PRs and issues from community contributors

Projects

None yet

3 participants