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 Google Ads: Add support for multiple customer ids in specs #7547

Conversation

t0hai
Copy link
Contributor

@t0hai t0hai commented Nov 1, 2021

What

Fixing issue 6437

How

  • Allow customer_ids field in spec to accept an array of customer ID strings
  • Modify code of google_ads client to iterate over each customer_id and send requests accordingly

Recommended reading order

  1. source_google_ads/spec.json
  2. source_google_ads/source.py
  3. source_google_ads/google_ads.py
  4. source_google_ads/streams.py
  5. unit_tests/test_source.py

Pre-merge Checklist

Updating a connector

Community member or Airbyter

  • Grant edit access to maintainers (instructions)
  • Secrets in the connector's spec are annotated with airbyte_secret
  • Unit & integration tests added and passing.
  • Code reviews completed
  • Documentation updated
    • Changelog updated in docs/integrations/source/google-ads.md including changelog.
  • PR name follows PR naming conventions
  • Connector version bumped like described here

Airbyter

If this is a community PR, the Airbyte engineer reviewing this PR is responsible for the below items.

  • Create a non-forked branch based on this PR and test the below items on it
  • Build is successful
  • Credentials added to Github CI. Instructions.
  • /test connector=connectors/<name> command is passing.
  • New Connector version released on Dockerhub by running the /publish command described here

Unit test passed

Test session starts (platform: linux, Python 3.7.0, pytest 6.2.5, pytest-sugar 0.9.4)
cachedir: .pytest_cache
rootdir: /home/hai/workspace/sss/airbyte, configfile: pytest.ini
plugins: sugar-0.9.4, timeout-1.4.2, mock-3.6.1
collecting ... 
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_google_ads_init ✓                                                                                        5% ▌         
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_send_request ✓                                                                                          11% █▏        
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_get_fields_from_schema ✓                                                                                16% █▋        
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_interval_chunking ✓                                                                                     21% ██▏       
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_get_date_params ✓                                                                                       26% ██▋       
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_convert_schema_into_query ✓                                                                             32% ███▎      
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_get_field_value ✓                                                                                       37% ███▊      
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_google_ads.py::test_parse_single_result ✓                                                                                   42% ████▎     
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_chunk_date_range ✓                                                                                          47% ████▊     
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_get_updated_state ✓                                                                                         53% █████▍    
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_get_query_fields[\n    SELecT\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions FROM campaign\nwheRe campaign.status = 'PAUSED'\nAND metrics.impressions > 100\norder by campaign.status\n    -fields0] ✓58% █████▊    
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_get_query_fields[\n        SELECT\n            campaign.accessible_bidding_strategy,\n            segments.ad_destination_type,\n            campaign.start_date,\n            campaign.end_date\n        FROM campaign\n    -fields1] ✓63% ██████▍   
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_get_query_fields[selet aasdasd from aaa-fields2] ✓                                                          68% ██████▉   
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_insert_date[\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions FROM campaign\nwheRe campaign.status = 'PAUSED'\nAND metrics.impressions > 100\norder by campaign.status\n-\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions , segments.date\nFROM campaign\nwheRe campaign.status = 'PAUSED'\nAND metrics.impressions > 100\n AND segments.date BETWEEN '1980-01-01' AND '2000-01-01'\norder by campaign.status\n] ✓74% ███████▍  
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_insert_date[\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions\nFROM campaign\norder by campaign.status\n-\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions\n, segments.date\nFROM campaign\n\nWHERE segments.date BETWEEN '1980-01-01' AND '2000-01-01'\norder by campaign.status\n] ✓79% ███████▉  
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_insert_date[\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions FROM campaign\nwheRe campaign.status = 'PAUSED'\nAND metrics.impressions > 100\n-\nSELect\n  campaign.id,\n  campaign.name,\n  campaign.status,\n  metrics.impressions , segments.date\nFROM campaign\nwheRe campaign.status = 'PAUSED'\nAND metrics.impressions > 100\n AND segments.date BETWEEN '1980-01-01' AND '2000-01-01'\n] ✓84% ████████▌ 
 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_insert_date[SELECT campaign.accessible_bidding_strategy, segments.ad_destination_type, campaign.start_date, campaign.end_date FROM campaign-SELECT campaign.accessible_bidding_strategy, segments.ad_destination_type, campaign.start_date, campaign.end_date , segments.date\nFROM campaign\nWHERE segments.date BETWEEN '1980-01-01' AND '2000-01-01'\n] ✓89% ████████▉ {"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: None, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsFieldService/SearchGoogleAdsFields, RequestId: bkXITAQWqVCpsK05ryQKLQ, IsFault: False, FaultMessage: None"}}

 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_get_json_schema_parse_query ✓                                                                               95% █████████▌{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: None, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsFieldService/SearchGoogleAdsFields, RequestId: pEQDL2JRvycLZlXl9UYAMQ, IsFault: False, FaultMessage: None"}}

 airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py::test_google_type_conversion ✓                                                                                   100% ██████████

Results (2.55s):
      19 passed

Integration test passed

Test session starts (platform: linux, Python 3.7.0, pytest 6.2.5, pytest-sugar 0.9.4)
cachedir: .pytest_cache
rootdir: /home/hai/workspace/sss/airbyte, configfile: pytest.ini
plugins: sugar-0.9.4, timeout-1.4.2, mock-3.6.1
collecting ... {"type": "LOG", "log": {"level": "INFO", "message": "Starting syncing SourceGoogleAds"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Syncing stream: ad_group_ad_report "}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-10-01'}"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: nxEJnNokHYcs5dxljTjtPQ, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: EbrZ9BwdHfIIpmbCRZG1Fg, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: d86zQqQAGTQCGEw41LWLlw, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: zIJovDEx3xUmPZIlrWqDXw, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 86xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: f6LWRFdRhrfIl3ZHmKg8Sg, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-10-17'}"}}
type=<Type.STATE: 'STATE'> log=None spec=None connectionStatus=None catalog=None record=None state=AirbyteStateMessage(data={'ad_group_ad_report': {'segments.date': '2021-10-17'}})
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: _kuYXQ0XCHJUiwuBgz292g, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: S4mL-3nemjTxP6RsqoCo4Q, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: 3skOf9t2Iv_kHQmwJiyf6A, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 86xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: vWx28sXWTaFGwIL1GLJq8A, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-11-03'}"}}
type=<Type.STATE: 'STATE'> log=None spec=None connectionStatus=None catalog=None record=None state=AirbyteStateMessage(data={'ad_group_ad_report': {'segments.date': '2021-11-03'}})
{"type": "LOG", "log": {"level": "INFO", "message": "Read 5706 records from ad_group_ad_report stream"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Finished syncing SourceGoogleAds"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Starting syncing SourceGoogleAds"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Syncing stream: ad_group_ad_report "}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-10-17'}"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: TWodgRSEWmHktMNK1RCg7g, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: aOvt4F1cfMp3_nlikLEYFA, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: pJ941YX8wF_pPXT08ypXvA, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: _RyKI8z28XptYijkhScXIA, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 86xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: 3tXjgtHX4mepLsbeLS6Ygw, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-11-03'}"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 46xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: Fk22zlOGYmwl-hzlxTRUNQ, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Request made: ClientCustomerId: 86xxxxxxxx, Host: googleads.googleapis.com, Method: /google.ads.googleads.v8.services.GoogleAdsService/Search, RequestId: KhpS5Zzo31kS9p_StLZ7Rw, IsFault: False, FaultMessage: None"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Setting state of ad_group_ad_report stream to {'segments.date': '2021-11-04'}"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Read 3844 records from ad_group_ad_report stream"}}
{"type": "LOG", "log": {"level": "INFO", "message": "Finished syncing SourceGoogleAds"}}

 airbyte-integrations/connectors/source-google-ads/integration_tests/test_incremental.py::test_incremental_sync ✓                                                                             100% ██████████

Results (106.24s):
       1 passed

@github-actions github-actions bot added area/connectors Connector related issues area/documentation Improvements or additions to documentation labels Nov 1, 2021
@t0hai t0hai force-pushed the n0rritt/source-google-ads/add_support_for_multiple_customer_ids_in_specs branch from 1975cd5 to 7a2b746 Compare November 1, 2021 23:23
@harshithmullapudi harshithmullapudi changed the title Add support for multiple customer ids in specs Source Google Ads: Add support for multiple customer ids in specs Nov 2, 2021
@harshithmullapudi harshithmullapudi self-assigned this Nov 2, 2021
@harshithmullapudi
Copy link
Contributor

Hey @n0rritt do you mind sharing screenshot about integration tests from local

@t0hai
Copy link
Contributor Author

t0hai commented Nov 3, 2021

@harshithmullapudi the unit and integration test logs are already included in the PR description as expandables, is that enough or would you need a screenshot of it running in Airflow web UI?

Copy link
Contributor

@yevhenii-ldv yevhenii-ldv left a comment

Choose a reason for hiding this comment

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

Please, see comments

@t0hai t0hai force-pushed the n0rritt/source-google-ads/add_support_for_multiple_customer_ids_in_specs branch from 7a2b746 to a401614 Compare November 4, 2021 10:36
@t0hai t0hai force-pushed the n0rritt/source-google-ads/add_support_for_multiple_customer_ids_in_specs branch from a401614 to f8bdc5b Compare November 4, 2021 10:44
@t0hai
Copy link
Contributor Author

t0hai commented Nov 4, 2021

@yevhenii-ldv I've made the fixes according to your comments, could you have another look again please?

@harshithmullapudi
Copy link
Contributor

@yevhenii-ldv is it a +1 from you ?

},
"login_customer_id": {
"type": "string",
"title": "Login Customer ID",
"description": "If your access to the customer account is through a manager account, this field is required and must be set to the customer ID of the manager account (10-digit number without dashes). More information about this field you can see <a href=\"https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid\">here</a>"
"description": "If your access to the customer account is through a manager account, this field is required and must be set to the customer ID of the manager account (10-digit number without dashes). More information about this field you can see <a href=\"https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid\">here</a>",
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure that for all of the listed Customer ID(s) one specified Login Customer ID will be suitable. Or will we need to make the `Customer Id => Login Customer ID" mapping?

Copy link
Contributor Author

@t0hai t0hai Nov 15, 2021

Choose a reason for hiding this comment

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

I had the same thought in the beginning but multiple (sub) customer accounts belong to one (manager) login_customer_id.
For another manager account a new connection should be created, otherwise it get's messy with the mapping.
https://developers.google.com/google-ads/api/docs/migration/login-customer-id

@yevhenii-ldv
Copy link
Contributor

@n0rritt LGTM in general. Only one quastion to be sure.

@sherifnada
Copy link
Contributor

@yevhenii-ldv this does not seem to correctly take into account state logic for different user accounts

@yevhenii-ldv
Copy link
Contributor

@n0rritt, hello)
As the @sherifnada wrote, we will not work correctly with the state, because now we have created one single state for one custom ID:

{
   "ad_group_ad_report": {
     "segments.date": "2021-06-07"
   }
}

In case of change, we will need to update the logic of saving the state to the following:

{
   "<customer_id>": {
     "ad_group_ad_report": {
       "segments.date": "2021-06-07"
     }
   }
}

But at the same time, preserve backward compatibility for users who have already started this connector and have an state.json file.

@t0hai
Copy link
Contributor Author

t0hai commented Nov 16, 2021

@yevhenii-ldv yes I can make another commit for state per account
thx for pointing out @sherifnada

@harshithmullapudi
Copy link
Contributor

Hey @n0rritt did you get time to fix it ?

@harshithmullapudi
Copy link
Contributor

Hey @n0rritt any updates on this ?

@harshithmullapudi harshithmullapudi removed their assignment Dec 2, 2021
@marcosmarxm
Copy link
Member

@n0rritt I'll close this due to inactivity. If you want to return this work you can reopen the PR.

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 blocked community
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants