-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Low code CDK: Allow query param / body injection for api key authenticator #26953
Conversation
airbyte-cdk/python/airbyte_cdk/sources/declarative/auth/declarative_authenticator.py
Outdated
Show resolved
Hide resolved
airbyte-cdk/python/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py
Outdated
Show resolved
Hide resolved
…ameter-authenticator
@@ -191,6 +197,8 @@ def request_headers( | |||
self.requester.get_request_headers, | |||
self.paginator.get_request_headers, | |||
self.stream_slicer.get_request_headers, | |||
# auth headers are handled separately by passing the authenticator to the HttpStream constructor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open question: would it be preferable to pass None
to the HttpStream
constructor and handle request headers like the query params and body for consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to split this out from the main change as it would require changes to the other authenticators as well. Let's get the changes in to unblock downstream changes and come back to it
airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py
Show resolved
Hide resolved
if model.inject_into | ||
else RequestOption( | ||
inject_into=RequestOptionType.header, | ||
field_name=InterpolatedString.create(model.header, parameters=model.parameters).eval(config), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the field_name evaluated here instead of in the authenticator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, that's not necessary
@@ -110,14 +109,23 @@ definitions: | |||
- "Token token={{ config['api_key'] }}" | |||
header: | |||
title: Header Name | |||
description: The name of the HTTP header that will be set to the API key. | |||
description: The name of the HTTP header that will be set to the API key. This setting is deprecated, use inject_into instead. If header and inject_into is defined at the same time, header will be ignored. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we fail if both are set to avoid suprises?
""" | ||
Interface used to associate which authenticators can be used as part of the declarative framework | ||
""" | ||
|
||
def get_request_params(self) -> MutableMapping[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this method returning a MutableMapping
?
"""HTTP request parameter to add to the requests""" | ||
return {} | ||
|
||
def get_request_body_data(self) -> Union[Mapping, str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the type is incomplete. It should be Union[Mapping[str, Any], str]
"""Form-encoded body data to set on the requests""" | ||
return {} | ||
|
||
def get_request_body_json(self) -> Mapping: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the type is incomplete
def get_request_body_data(self) -> Union[Mapping, str, None]: | ||
return self._get_request_options(RequestOptionType.body_data) | ||
|
||
def get_request_body_json(self) -> Union[Mapping, None]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Optional[Mapping[str, Any]]
def get_request_params(self) -> Union[MutableMapping[str, Any], None]: | ||
return self._get_request_options(RequestOptionType.request_parameter) | ||
|
||
def get_request_body_data(self) -> Union[Mapping, str, None]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Optional[Union[Mapping[str, Any], str]
options[self._field_name.eval(self.config)] = self.token | ||
return options | ||
|
||
def get_request_params(self) -> Union[MutableMapping[str, Any], None]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Optional[Mapping[str, Any]]
@girarda Adjusted the PR, could you have another look?
|
/test connector=connectors/source-zenloop local_cdk=1
Build PassedTest summary info:
|
/test connector=connectors/source-lokalise local_cdk=1
Build PassedTest summary info:
|
What
Part of #20790
This PR deprecates the header param of the api key authenticator and introduces a request_option param that allows to also inject the auth key into a query param or the body
How
To allow this I had to change the class hierarchy a bit - the important part is that the simple retriever can spread in authenticator-defined query params and body params into outgoing requests. To do so the interface of the authenticator known to the retriever has to include methods for getting query params and body params (not for headers as this is already handled by the current setup using the Session class from the requests package). Currently this is set to
AuthBase
. As every authenticator can theoretically be able to do this,DeclarativeAuthenticator
is extended with the required methods and the authenticator type in the requester is changed toDeclarativeAuthenticator
.This removes a bunch of double inheritance for other declarative connectors which is a nice side effect.
As an image:
Note that the python class
ApiKeyAuthenticator
is changed to only support the request option, the deprecated header property is handled in the component factory to isolate this behavior to the yaml layer.Theoretically, custom authenticators used to work fine as long as they implemented AuthBase, now they have to implement
DeclarativeAuthenticator
. I checked existing custom authenticators and they all do this already, I guess it was implied that a custom authenticator needs to do this in the first place (it just didn't bear any weight so far). I clarified this in the description.🚨 User Impact 🚨
Low code CDK users can now inject the token from the api key authenticator in request params or the body:
The
header
property is still supported, but should not be used anymore.The
ApiKeyAuthenticator
class fromairbyte_cdk.sources.declarative.auth.token
does not support the header parameter anymore.