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 (with migration & tests) #38304

Open
wants to merge 29 commits into
base: master
Choose a base branch
from

Conversation

cmm-airbyte
Copy link
Contributor

@cmm-airbyte cmm-airbyte commented May 16, 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 ❌

Source PR here: #37625

JohnMikkelsonOvative and others added 9 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 May 16, 2024

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

1 Ignored Deployment
Name Status Preview Comments Updated (UTC)
airbyte-docs ⬜️ Ignored (Inspect) Visit Preview May 31, 2024 6:24pm

@CLAassistant
Copy link

CLAassistant commented May 16, 2024

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
2 out of 4 committers have signed the CLA.

✅ JohnMikkelsonOvative
✅ marcosmarxm
❌ cristina.mariscal
❌ cmm-airbyte


cristina.mariscal seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@cmm-airbyte cmm-airbyte changed the title FB oauth origin ✨ Source Facebook-Marketing: Add Selectable Auth (with migration & tests) May 21, 2024
@cmm-airbyte cmm-airbyte marked this pull request as ready for review May 21, 2024 15:01
@cmm-airbyte cmm-airbyte requested a review from a team as a code owner May 21, 2024 15:01
@bazarnov
Copy link
Collaborator

@cmm-airbyte We need to update the config in the GSM for the change to work, I believe.

@cmm-airbyte cmm-airbyte requested a review from a team as a code owner May 21, 2024 18:43
@octavia-squidington-iii octavia-squidington-iii added the area/documentation Improvements or additions to documentation label May 21, 2024
@maxi297
Copy link
Contributor

maxi297 commented May 21, 2024

We need to update the config in the GSM for the change to work, I believe.

I haven't check the change but it shouldn't be if the change is non-breaking with the config migration

Copy link
Contributor

@maxi297 maxi297 left a comment

Choose a reason for hiding this comment

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

I'll test manually tomorrow morning but I don't see obvious issues with the code

@@ -44,6 +44,9 @@ data:
- "ads_insights_region"
- "ads_insights_dma"
- "ads_insights_action_product_id"
3.0.0:
Copy link
Contributor

Choose a reason for hiding this comment

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

What makes it breaking if we have the config migration? Does that mean that the user will have to opt-in on that change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, I was just guessing this because the PR cannot be directly reverted. I can do only a 2.2.0 version increment. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think 2.2.0 makes sense, yes! We should still document that the config format changes but there is a migration for backward compatibility but none for forward compatiblity

Copy link
Contributor Author

Choose a reason for hiding this comment

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

great. Thanks Max!

"""
credentials = config.get("credentials", None)
return credentials is None or (
"access_token" not in credentials and not ("client_id" in credentials and "client_id" in credentials)
Copy link
Contributor

Choose a reason for hiding this comment

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

This condition feels hard to maintain. If something adds a new auth method, they will need to know that they have to update an old migration script

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mmm.. I think if there is a new change, an iteration over this migration will be required. isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's take the following scenario: on the 2025-01-04, a developer is doing the work to add username/password as an authentication mechanism. To do so, they update the spec to have credentials.username and credentials.password and configure the authenticator properly in the source.py. Now, this condition will always be triggered which means that the GSM secrets and the database entries will be updated on every operation of the connector. For the source in practice, I don't think it changes that much but I fear that those updates on the platform or GSM side could create issues in the long run. Hence, I would try to make the condition specific to this update. Is there a case where only credentials not being present wouldn't be enough to trigger this migration?

Note if we keep the condition as-is: the "client_id" in credentials and "client_id" in credentials part of the condition should probably be "client_id" in credentials and "client_secret" in credentials

Starting from `3.0.0`, the `client_id`, `client_secret` and `access_token` will be placed at `credentials` path.
"""

message_repository: MessageRepository = InMemoryMessageRepository()
Copy link
Contributor

Choose a reason for hiding this comment

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

This field is useless right now. We can just do print(create_connector_config_control_message(migrated_config).json(exclude_unset=True))

message_repository: MessageRepository = InMemoryMessageRepository()

@classmethod
def should_migrate(cls, config: Mapping[str, Any]) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

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

It feels like this should be private as it is only used internally. What do you think? The same comment applies to transform, modify_and_save and emit_control_message

def transform(cls, config: Mapping[str, Any]) -> Mapping[str, Any]:
# transform the config
config["credentials"] = {}
if "client_id" in config:
Copy link
Contributor

Choose a reason for hiding this comment

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

Set the type here so we can distinguish which type of authentication

Copy link
Contributor

Choose a reason for hiding this comment

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

If client_id and client_secret are not defined, we should use the auth Service

title = "Authenticate via Facebook Marketing (Oauth)"
discriminator = "auth_type"

auth_type: Literal["Client"] = Field("Client", const=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

This probably needs an access_token

Copy link
Contributor

@maxi297 maxi297 left a comment

Choose a reason for hiding this comment

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

Can you confirm that the case I mentioned in the _should_migrate method is covered or not?

"airbyte_secret": true,
"type": "string"
"credentials": {
"title": "authentication credentials",
Copy link
Contributor

Choose a reason for hiding this comment

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

> False, otherwise.
"""
credentials = config.get("credentials", None)
return credentials is None or (
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this condition needs to change with the new release process. At first, we wanted to release both the platform and the source at the same time. This meant that we only had to bother about the migration once as:

  • Before the update, access_token would be at the root but source would migrate this to credentials.access_token
  • After the update:
    • when configuring the source, the access_token would be on credentials.access_token
    • when logging in through oauth, the platform would set the access_token would be on credentials.access_token

However, our new plan have those releases split. This means that we will have a state where the source is updated but not the platform. In this case, we could have the following steps:

  • The source is updated
  • The migration happens so access_token is moved to credentials.access_token
  • A user does oauth, the access token is set to access_token
  • Given this condition, there will be credentials.access_token and we won't leverage the new token from oauth

I think the condition here should just be "is there an access token on the root level". Does that make sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/connectors Connector related issues area/documentation Improvements or additions to documentation connectors/source/facebook-marketing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants