Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(low-code): pass refresh headers to oauth #219

Merged
merged 5 commits into from
Jan 16, 2025

Conversation

darynaishchenko
Copy link
Contributor

@darynaishchenko darynaishchenko commented Jan 15, 2025

Problem

Some oauth flows (like in Airtable) require passing specific headers to a refresh token request.

Solution

To make it possible from low code new param refresh_request_headers was added. Default is None.
Updated DeclarativeSingleUseRefreshTokenOauth2Authenticator and DeclarativeOauth2Authenticator with a new param refresh_request_headers and _get_refresh_access_token_response() with a request with headers.

Summary by CodeRabbit

  • New Features

    • Added support for custom headers during OAuth 2.0 token refresh requests.
    • Introduced refresh_request_headers parameter for OAuth authenticators to specify custom headers.
  • Improvements

    • Enhanced OAuth authentication flexibility by allowing dynamic header configuration.
    • Improved error reporting and context for token refresh operations.
  • Tests

    • Added comprehensive test coverage for new refresh request header functionality, ensuring correct header configurations during token refresh.

@darynaishchenko darynaishchenko self-assigned this Jan 15, 2025
@github-actions github-actions bot added the enhancement New feature or request label Jan 15, 2025
Copy link
Contributor

coderabbitai bot commented Jan 15, 2025

📝 Walkthrough

Walkthrough

This pull request introduces a new optional attribute refresh_request_headers to the OAuth authentication system in the Airbyte CDK. The changes span multiple files across the declarative and native authentication modules, enabling users to specify custom headers for OAuth token refresh requests. The modification allows for more flexible configuration of authentication headers while maintaining the existing authentication flow.

Changes

File Change Summary
airbyte_cdk/sources/declarative/auth/oauth.py Added refresh_request_headers attribute and get_refresh_request_headers method to DeclarativeOauth2Authenticator
airbyte_cdk/sources/declarative/declarative_component_schema.yaml Added refresh_request_headers property to OAuthAuthenticator definition
airbyte_cdk/sources/declarative/models/declarative_component_schema.py Added refresh_request_headers field to OAuthAuthenticator class
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py Updated create_oauth_authenticator method signature to include refresh_request_headers
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py Added build_refresh_request_headers and get_refresh_request_headers methods
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py Updated constructors and added get_refresh_request_headers method
unit_tests/sources/declarative/auth/test_oauth.py Added test methods for refresh request headers
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py Added test methods for refresh request headers

Possibly related PRs

Suggested labels

declarative_oauth, oauth_in_builder

Suggested reviewers

  • maxi297
  • aldogonzalez8

What do you think about these changes? The new refresh_request_headers feature looks pretty neat for customizing OAuth token refresh requests. Wdyt? 😊

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
unit_tests/sources/declarative/auth/test_oauth.py (1)

437-442: Consider enhancing error message format?

The error message includes all request details, but wdyt about formatting it more clearly? Maybe something like:

-        f"Error while refreshing access token with request: {method}, {url}, {data}, {headers}"
+        f"Error while refreshing access token with request:\n  Method: {method}\n  URL: {url}\n  Data: {data}\n  Headers: {headers}"
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (1)

168-199: LGTM! The test coverage for refresh request headers looks good.

The test cases cover both scenarios where headers are provided and when they're not. The assertions verify the expected behavior.

Would you consider adding a test case for invalid headers (e.g., malformed Content-Type) to ensure proper error handling? wdyt?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e18e407 and 60ecfbf.

📒 Files selected for processing (8)
  • airbyte_cdk/sources/declarative/auth/oauth.py (4 hunks)
  • airbyte_cdk/sources/declarative/declarative_component_schema.yaml (1 hunks)
  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1 hunks)
  • airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2 hunks)
  • airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py (3 hunks)
  • airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py (6 hunks)
  • unit_tests/sources/declarative/auth/test_oauth.py (3 hunks)
  • unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Check: 'source-pokeapi' (skip=false)
  • GitHub Check: Check: 'source-the-guardian-api' (skip=false)
  • GitHub Check: Check: 'source-shopify' (skip=false)
  • GitHub Check: Check: 'source-hardcoded-records' (skip=false)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Analyze (python)
🔇 Additional comments (18)
airbyte_cdk/sources/declarative/auth/oauth.py (4)

42-42: LGTM! Clear and well-documented attribute in docstring.

The new refresh_request_headers attribute is well-documented with a clear description of its purpose.


62-62: LGTM! Consistent attribute initialization.

The attribute initialization follows the same pattern as other similar attributes in the class.


92-94: LGTM! Consistent initialization in post_init.

The initialization of _refresh_request_headers follows the same pattern as _refresh_request_body.


160-162: LGTM! Well-implemented getter method.

The get_refresh_request_headers method follows the same pattern as other getter methods in the class.

airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py (3)

101-108: LGTM! Clear and concise implementation.

The build_refresh_request_headers method follows a similar pattern to build_refresh_request_body, with appropriate handling of None case.


139-139: LGTM! Proper integration of headers in request.

The headers are correctly integrated into the request call.


254-257: LGTM! Well-defined abstract method.

The abstract method declaration follows the same pattern as other abstract methods in the class.

airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py (3)

39-39: LGTM! Consistent parameter and attribute addition.

The new parameter and its initialization follow the existing patterns in the class.

Also applies to: 54-54


89-91: LGTM! Proper implementation of abstract method.

The get_refresh_request_headers implementation correctly returns the headers.


201-201: LGTM! Consistent parameter passing.

The refresh_request_headers parameter is correctly passed to the superclass.

unit_tests/sources/declarative/auth/test_oauth.py (2)

72-104: LGTM! Comprehensive test coverage for headers.

The test cases cover both scenarios with and without headers, ensuring proper functionality.


227-256: LGTM! Well-structured integration test.

The test verifies that headers are correctly passed through to the request.

unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (2)

245-273: LGTM! Good integration test for headers in refresh token requests.

The test verifies that the headers are correctly passed through to the request. The mocked request verification is thorough.


586-591: Good update to include headers in error messages.

The addition of headers to the error message will help with debugging when requests fail.

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2)

1903-1905: LGTM! Good integration of refresh request headers.

The implementation uses InterpolatedMapping for header evaluation, maintaining consistency with how other similar parameters are handled.


1922-1922: LGTM! Consistent parameter passing.

The refresh_request_headers parameter is correctly passed through to the OAuth authenticator.

airbyte_cdk/sources/declarative/declarative_component_schema.yaml (1)

1114-1121: LGTM! Well-documented schema addition.

The refresh_request_headers property is well-defined with clear examples and proper documentation.

airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1)

550-560: Great addition! Clean implementation of the refresh headers support.

The new refresh_request_headers field is well-documented, properly typed, and maintains backward compatibility. The examples provided are particularly helpful for understanding the usage. This change elegantly solves the Airtable OAuth use case mentioned in the PR description.

@maxi297
Copy link
Contributor

maxi297 commented Jan 15, 2025

I just want to highlight that @bazarnov and @darynaishchenko are playing on the same part of the code. Just want to confirm that there is no conflict here

@bazarnov
Copy link
Contributor

There is no conflict in terms of the code base; we updated different parts of the same components. Once the merge conflicts are fixed, we should be good.

@bazarnov bazarnov added the oauth label Jan 15, 2025
@natikgadzhi
Copy link
Contributor

@bazarnov give this a review please!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (1)

621-626: Consider enhancing the error message format, wdyt?

The error message could be more structured for better readability. Maybe use JSON formatting?

-        f"Error while refreshing access token with request: {method}, {url}, {data}, {headers}"
+        f"Error while refreshing access token with request:\n  Method: {method}\n  URL: {url}\n  Data: {data}\n  Headers: {headers}"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 60ecfbf and 1da0d93.

📒 Files selected for processing (7)
  • airbyte_cdk/sources/declarative/auth/oauth.py (4 hunks)
  • airbyte_cdk/sources/declarative/declarative_component_schema.yaml (1 hunks)
  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1 hunks)
  • airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2 hunks)
  • airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py (3 hunks)
  • airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py (6 hunks)
  • unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py
  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py
  • airbyte_cdk/sources/declarative/declarative_component_schema.yaml
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Check: 'source-pokeapi' (skip=false)
  • GitHub Check: Check: 'source-the-guardian-api' (skip=false)
  • GitHub Check: Check: 'source-shopify' (skip=false)
  • GitHub Check: Check: 'source-hardcoded-records' (skip=false)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Analyze (python)
🔇 Additional comments (11)
airbyte_cdk/sources/declarative/auth/oauth.py (4)

42-42: LGTM! Well-documented new parameter.

The docstring for refresh_request_headers follows the established pattern and clearly explains its purpose.


65-65: LGTM! Clean attribute declaration.

The attribute declaration follows the class's pattern and uses appropriate typing.


106-108: LGTM! Consistent initialization.

The initialization in __post_init__ follows the established pattern and properly handles the None case.


186-188: LGTM! Clean getter implementation.

The get_refresh_request_headers method is simple, focused, and consistent with other getter methods in the class.

airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py (3)

101-108: LGTM! Clean implementation of header building.

The build_refresh_request_headers method is well-documented and handles the None case appropriately.


139-139: LGTM! Proper header integration.

Headers are correctly integrated into the refresh token request.


266-269: LGTM! Well-defined abstract method.

The abstract method get_refresh_request_headers is properly documented and follows the class's pattern.

unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py (2)

168-199: LGTM! Comprehensive header configuration tests.

Good test coverage for both cases: with and without headers.


280-308: LGTM! Thorough header integration test.

The test properly verifies that headers are passed correctly to the refresh token request.

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2)

1922-1924: LGTM! Clean header integration for single-use authenticator.

Headers are properly evaluated using InterpolatedMapping, consistent with other parameters.


1944-1944: LGTM! Consistent header passing.

The refresh headers are passed correctly to the regular authenticator.

@bazarnov bazarnov self-requested a review January 16, 2025 11:12
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
unit_tests/sources/declarative/auth/test_oauth.py (3)

72-107: Great test coverage! Would you consider a few enhancements? 🤔

The test covers both scenarios well. To make it even more robust, what do you think about:

  1. Extracting the common header values into constants at the module level?
  2. Adding a test case for invalid base64 encoding in the Authorization header?

wdyt?

# At module level
CONTENT_TYPE_HEADER = "application/x-www-form-urlencoded"
AUTHORIZATION_PREFIX = "Basic "

# In the test
expected = {
    "Authorization": f"{AUTHORIZATION_PREFIX}c29tZV9jbGllbnRfaWQ6c29tZV9jbGllbnRfc2VjcmV0",
    "Content-Type": CONTENT_TYPE_HEADER,
}

230-259: Solid test! Here's a thought for making it even more comprehensive 💭

The test effectively verifies the happy path. How about adding test cases for:

  1. Malformed headers (e.g., missing Content-Type)?
  2. Verifying that headers aren't modified during the request?

This would help catch potential edge cases. wdyt?

def test_refresh_access_token_when_headers_malformed(self, mocker):
    # Test with missing required headers
    malformed_headers = {
        "Authorization": "Bearer some_access_token",
        # Missing Content-Type
    }
    # ... rest of the test

440-445: How about adding some type hints and documentation? 📝

The error message improvement is great! To make it even more maintainable, what do you think about adding:

  1. Type hints for the parameters and return value
  2. A docstring explaining the function's purpose and parameters

wdyt?

def mock_request(
    method: str,
    url: str,
    data: dict,
    headers: dict | None
) -> requests.Response:
    """Mock the requests.request function for testing.

    Args:
        method: HTTP method
        url: Request URL
        data: Request data
        headers: Request headers

    Returns:
        Response object for successful token refresh
    
    Raises:
        Exception: For any URL other than the refresh endpoint
    """
    if url == "refresh_end":
        return resp
    raise Exception(
        f"Error while refreshing access token with request: {method}, {url}, {data}, {headers}"
    )
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1da0d93 and 464ce0d.

📒 Files selected for processing (1)
  • unit_tests/sources/declarative/auth/test_oauth.py (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Check: 'source-pokeapi' (skip=false)
  • GitHub Check: Check: 'source-the-guardian-api' (skip=false)
  • GitHub Check: Check: 'source-shopify' (skip=false)
  • GitHub Check: Check: 'source-hardcoded-records' (skip=false)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Analyze (python)

Copy link
Contributor

@bazarnov bazarnov left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks @darynaishchenko !

Copy link
Contributor

@lazebnyi lazebnyi left a comment

Choose a reason for hiding this comment

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

the result of get_auth_header is unaffected - https://github.com/airbytehq/airbyte-python-cdk/pull/219/files#r1918454347, then LGTM

@darynaishchenko darynaishchenko merged commit 2185bd9 into main Jan 16, 2025
20 checks passed
@darynaishchenko darynaishchenko deleted the daryna/low-code/pass-refresh-headers-to-oauth branch January 16, 2025 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request oauth
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants