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

✨ Source Facebook-Marketing: Add Selectable Auth #37625

Conversation

JohnMikkelsonOvative
Copy link
Contributor

@JohnMikkelsonOvative JohnMikkelsonOvative commented Apr 26, 2024

What

The Source Facebook Marketing Connector does not allow for choosing between providing an access token or using oauth, it only allows for users to define a source connection by following an oauth flow. This does not allow for use-cases in which a large organization may provision an access token to be used to authenticate to the Facebook Marketing data, rather than giving a user's personal Facebook account access.

How

The spec defines a credentials field that is a union between newly created OAuthCredentials and ServiceAccountCredentials. This allows for the user to select between using the Oauth flow and inserting Service Account Credentials.

Review guide

  1. spec.py
  2. config.py
  3. source.py
  4. sample_config.json
  5. metadata.yaml
  6. pyproject.toml
  7. request_builder.py
  8. test_ads_insights_action_product_id.py
  9. test_videos.py
  10. test_source.py

User Impact

The user is able to select between using the Oauth flow and inserting Service Account Credentials when setting up the Facebook Marketing Source.

Can this PR be safely reverted and rolled back?

  • YES 💚
  • NO ❌

JohnMikkelsonOvative and others added 5 commits April 24, 2024 16:21
…anges to solve build issues, and changes to solve unit testing issues.
…e config. Updated version to reflect Airbyte versioning standard (this use case is a major version upgrade due to configuration of credentials needed to be changed)
…ebook-marketing-selectable-auth' into JohnMikkelsonOvative/feature-facebook-marketing-selectable-auth
Copy link

vercel bot commented Apr 26, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
airbyte-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 27, 2024 0:42am

@CLAassistant
Copy link

CLAassistant commented Apr 26, 2024

CLA assistant check
All committers have signed the CLA.

@JohnMikkelsonOvative
Copy link
Contributor Author

During the migration to credentials selectable configuration, the decision was made to change the naming of ACCESS_TOKEN to SERVICE_ACCOUNT_INFO. This was due to the same naming convention being used in the Source-Google-Drive. Does this make sense to the Airbyte team, or should we change that naming convention to something more agnostic?

Copy link
Member

@marcosmarxm marcosmarxm left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution @JohnMikkelsonOvative. It was added to the Connector team backlog and it will be reviewed in future sprint. I'll check with the project manager a timeline to make aware, ask your patience until the moment of the review. As this impact in a critical connector the review can take some time and have some back and forward, the goal is to introduce new features with the lower impact possible. In any time, feel free to reach me out in Slack or ping here to get update about the review process.

@@ -95,7 +95,10 @@ def check_connection(self, logger: logging.Logger, config: Mapping[str, Any]) ->
if config.start_date and config.end_date < config.start_date:
return False, "End date must be equal or after start date."

api = API(access_token=config.access_token, page_size=config.page_size)
if config.credentials.auth_type == 'Client':
Copy link
Member

Choose a reason for hiding this comment

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

Just small suggestion is to use maybe Enum class to represent the auth type value.

@@ -2,5 +2,8 @@
"start_date": "2020-09-25T00:00:00Z",
"end_date": "2021-01-01T00:00:00Z",
"account_id": "<YOUR_ACCOUNT_ID_HERE>",
"access_token": "<YOUR_TOKEN_HERE>"
"credentials": {
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. This change will be a breaking change for all of the existing customers.
  2. We should use the config migration to allow the existing Customers to continue using the existing credentials rather than forcing everyone to upgrade.
  3. This will also touch the way the OAuth2.0 works now, because it returns the obtained credentials to the root of the config, here, thus this method should be removed from the flow because the default method returns the obtained access_token/refresh_token under the credentials object, which is exactly what the new flow will require.

oauth_config_specification=OAuthConfigSpecification(
complete_oauth_output_specification={
"type": "object",
"properties": {
"access_token": {
"type": "string",
"path_in_connector_config": ["access_token"],
"path_in_connector_config": ["credentials", "access_token"],
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no backward compatibility for the existing Customers, we should be able to handle the access_token from the root level of the config, alongside the new credentials.access_token path.

@@ -95,7 +95,10 @@ def check_connection(self, logger: logging.Logger, config: Mapping[str, Any]) ->
if config.start_date and config.end_date < config.start_date:
return False, "End date must be equal or after start date."

api = API(access_token=config.access_token, page_size=config.page_size)
if config.credentials.auth_type == 'Client':
api = API(access_token=config.credentials.refresh_token, page_size=config.page_size)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the refresh_token is addressed here? Where we use it in the code?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The OAuth flow, will return the access_token only.
Based on this line of code

@@ -25,7 +25,10 @@ class ConfigBuilder:
def __init__(self) -> None:
self._config: MutableMapping[str, Any] = {
"account_ids": [ACCOUNT_ID],
"access_token": ACCESS_TOKEN,
"credentials": {
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no need to replace the tests, but extend them with the new functionality, using parametrized test case. With this changes, we loose the backward compatibility tests for the existing Customers.

description="Client Secret for the Facebook Marketing API",
airbyte_secret=True,
)
refresh_token: str = Field(
Copy link
Collaborator

Choose a reason for hiding this comment

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

The OAuth2.0 Flow doesn't return the refresh_token, could you please refer to the official doc, where this is possible? Thank you.

Copy link
Collaborator

@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.

Requesting changes, because this change will break the existing Customers.

The path should look like this:

  1. Support both old and new way of authentication to cover the existing Customers
  2. Write the test cases for both scenarios
  3. Make it possible to migrate existing customers to use the new way of authentication by applying the config migration, see the example in the /config_migrations.py
  4. Release this update (at this point, all existing Customers are covered with the config_migration, this will allow to win the time, before we update the OAuth Flow for the platform side, here by removing this method.
  5. Done; the platform will return the access_token back to the credentials.access_token as expected after point 4 is released, which means we are good to go with the new authentication way of things.

@maxi297
Copy link
Contributor

maxi297 commented May 8, 2024

@bazarnov For my personal knowledge, what does removing getDefaultOAuthOutputPath do? It is also deprecated in the interface which seems like we should move away from that

@bazarnov
Copy link
Collaborator

bazarnov commented May 8, 2024

@bazarnov For my personal knowledge, what does removing getDefaultOAuthOutputPath do? It is also deprecated in the interface which seems like we should move away from that

You've looked to the wrong place, see this
Which is inherited from this

@bazarnov
Copy link
Collaborator

bazarnov commented May 8, 2024

We can safely move away from using this method, once the new way is released and the existing Customers are covered with the config_migration prior to the platform change.

@maxi297
Copy link
Contributor

maxi297 commented May 8, 2024

Note that this method is in FacebookOAuthFlow and this will affect Instagram and Facebook Pages as well. Is this an issue? We can probably re-implement this in the specific classes for those connectors

@bazarnov
Copy link
Collaborator

bazarnov commented May 8, 2024

Note that this method is in FacebookOAuthFlow and this will affect Instagram and Facebook Pages as well. Is this an issue? We can probably re-implement this in the specific classes for those connectors

We can add the existing method from the base class to all of the other Facebook-related OAuth flows to keep them running as is for now, then create the follow-up issues, to modify each of them. In this way, the Instagram and Facebook Pages, will not be affected with the platform change.

@bazarnov
Copy link
Collaborator

bazarnov commented May 27, 2024

@cmm-airbyte Could you please fix the conflicts and ensure we pass the CAT successfully before this is finally approved? Thanks.

UPDATE: dismiss this comment, because I missed the fact we work on this within another PR: #38304

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants