diff --git a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml index ee0a49db83557..416749b0f5ff9 100644 --- a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml @@ -311,7 +311,7 @@ - name: Google Ads sourceDefinitionId: 253487c0-2246-43ba-a21f-5116b20a2c50 dockerRepository: airbyte/source-google-ads - dockerImageTag: 0.1.36 + dockerImageTag: 0.1.37 documentationUrl: https://docs.airbyte.io/integrations/sources/google-ads icon: google-adwords.svg sourceType: api diff --git a/airbyte-config/init/src/main/resources/seed/source_specs.yaml b/airbyte-config/init/src/main/resources/seed/source_specs.yaml index f1611f8874784..5c0fd5ebcea68 100644 --- a/airbyte-config/init/src/main/resources/seed/source_specs.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_specs.yaml @@ -2688,7 +2688,7 @@ supportsNormalization: false supportsDBT: false supported_destination_sync_modes: [] -- dockerImage: "airbyte/source-google-ads:0.1.36" +- dockerImage: "airbyte/source-google-ads:0.1.37" spec: documentationUrl: "https://docs.airbyte.com/integrations/sources/google-ads" connectionSpecification: diff --git a/airbyte-integrations/connectors/source-google-ads/Dockerfile b/airbyte-integrations/connectors/source-google-ads/Dockerfile index 5d34b4e8a4a4c..2bba65c96c907 100644 --- a/airbyte-integrations/connectors/source-google-ads/Dockerfile +++ b/airbyte-integrations/connectors/source-google-ads/Dockerfile @@ -6,12 +6,12 @@ RUN apt-get update && apt-get install -y bash && rm -rf /var/lib/apt/lists/* ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py" WORKDIR /airbyte/integration_code -COPY source_google_ads ./source_google_ads -COPY main.py ./ COPY setup.py ./ RUN pip install . +COPY source_google_ads ./source_google_ads +COPY main.py ./ ENTRYPOINT ["python", "/airbyte/integration_code/main.py"] -LABEL io.airbyte.version=0.1.36 +LABEL io.airbyte.version=0.1.37 LABEL io.airbyte.name=airbyte/source-google-ads diff --git a/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml b/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml index 96968da1853d3..f316cda4576b7 100644 --- a/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml @@ -28,7 +28,9 @@ tests: configured_catalog_path: "integration_tests/configured_catalog_protobuf_msg.json" # expect_records: # path: "integration_tests/expected_records_msg.txt" - # TODO incremental test is disabled because records output from the report streams can be up to 14 days older than the input state + # These tests are disabled because of the issues https://github.com/airbytehq/airbyte/issues/12665 + # and https://github.com/airbytehq/airbyte/issues/12467. Instead, custom integration tests are implemented. + # As soon as the above issues are resolved, standard SATs can be enabled and custom tests removed. # incremental: # - config_path: "secrets/config.json" # configured_catalog_path: "integration_tests/configured_catalog.json" diff --git a/airbyte-integrations/connectors/source-google-ads/integration_tests/abnormal_state.json b/airbyte-integrations/connectors/source-google-ads/integration_tests/abnormal_state.json index 5f7c2ed7bcaea..c9be4d524f3f2 100644 --- a/airbyte-integrations/connectors/source-google-ads/integration_tests/abnormal_state.json +++ b/airbyte-integrations/connectors/source-google-ads/integration_tests/abnormal_state.json @@ -1,5 +1,5 @@ { "ad_group_ad_report": { - "segments.date": "2021-06-07" + "segments.date": "2221-06-07" } } diff --git a/airbyte-integrations/connectors/source-google-ads/integration_tests/test_incremental.py b/airbyte-integrations/connectors/source-google-ads/integration_tests/test_incremental.py index 63687ec61bfef..75b1d2704bd30 100644 --- a/airbyte-integrations/connectors/source-google-ads/integration_tests/test_incremental.py +++ b/airbyte-integrations/connectors/source-google-ads/integration_tests/test_incremental.py @@ -3,93 +3,88 @@ # import pendulum +import pytest from airbyte_cdk.logger import AirbyteLogger from airbyte_cdk.models import ConfiguredAirbyteCatalog, Type from source_google_ads.source import SourceGoogleAds -SAMPLE_CATALOG = { - "streams": [ - { - "stream": { - "name": "ad_group_ad_report", - "json_schema": { - "type": "object", - "title": "Ad Group Ad Report", - "description": "An ad group ad.", - "properties": { - "accent_color": { - "description": "AccentColor", - "type": ["null", "string"], - "field": "ad_group_ad.ad.legacy_responsive_display_ad.accent_color", - }, - "account_currency_code": { - "description": "AccountCurrencyCode", - "type": ["null", "string"], - "field": "customer.currency_code", - }, - "account_descriptive_name": { - "description": "AccountDescriptiveName", - "type": ["null", "string"], - "field": "customer.descriptive_name", - }, - "segments.date": {"description": "Date", "type": ["null", "string"], "field": "segments.date"}, - }, + +@pytest.fixture +def configured_catalog(): + return { + "streams": [ + { + "stream": { + "name": "ad_group_ad_report", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": True, + "default_cursor_field": ["segments.date"], }, - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": True, - "default_cursor_field": ["segments.date"], - }, - "sync_mode": "incremental", - "destination_sync_mode": "overwrite", - "cursor_field": ["segments.date"], - } - ] -} + "sync_mode": "incremental", + "destination_sync_mode": "overwrite", + "cursor_field": ["segments.date"], + } + ] + } + + +GAP_DAYS = 14 + +def test_incremental_sync(config, configured_catalog): + today = pendulum.now().date() + start_date = today.subtract(months=1) + config["start_date"] = start_date.to_date_string() -def test_incremental_sync(config): google_ads_client = SourceGoogleAds() - state = "2021-05-24" - records = google_ads_client.read( - AirbyteLogger(), config, ConfiguredAirbyteCatalog.parse_obj(SAMPLE_CATALOG), {"ad_group_ad_report": {"segments.date": state}} + records = list(google_ads_client.read(AirbyteLogger(), config, ConfiguredAirbyteCatalog.parse_obj(configured_catalog))) + latest_state = None + for record in records[::-1]: + if record and record.type == Type.STATE: + latest_state = record.state.data["ad_group_ad_report"][config["customer_id"]]["segments.date"] + break + + for message in records: + if not message or message.type != Type.RECORD: + continue + cursor_value = message.record.data["segments.date"] + assert cursor_value <= latest_state + assert cursor_value >= start_date.subtract(days=GAP_DAYS).to_date_string() + + # next sync + records = list( + google_ads_client.read( + AirbyteLogger(), + config, + ConfiguredAirbyteCatalog.parse_obj(configured_catalog), + {"ad_group_ad_report": {"segments.date": latest_state}}, + ) ) - current_state = pendulum.parse(state).subtract(days=14).to_date_string() for record in records: - if record and record.type == Type.STATE: - print(record) - temp_state = record.state.data["ad_group_ad_report"] - current_state = ( - temp_state[config["customer_id"]]["segments.date"] if temp_state.get(config["customer_id"]) else temp_state["segments.date"] - ) - if record and record.type == Type.RECORD: - assert record.record.data["segments.date"] >= current_state + if record.type == Type.RECORD: + assert record.record.data["segments.date"] >= pendulum.parse(latest_state).subtract(days=GAP_DAYS).to_date_string() + if record.type == Type.STATE: + assert record.state.data["ad_group_ad_report"][config["customer_id"]]["segments.date"] >= latest_state + - # Next sync - state = "2021-06-04" +def test_abnormally_large_state(config, configured_catalog): + google_ads_client = SourceGoogleAds() records = google_ads_client.read( - AirbyteLogger(), config, ConfiguredAirbyteCatalog.parse_obj(SAMPLE_CATALOG), {"ad_group_ad_report": {"segments.date": state}} + AirbyteLogger(), + config, + ConfiguredAirbyteCatalog.parse_obj(configured_catalog), + {"ad_group_ad_report": {"segments.date": "2222-06-04"}}, ) - current_state = pendulum.parse(state).subtract(days=14).to_date_string() + no_data_records = True + state_records = False for record in records: if record and record.type == Type.STATE: - current_state = record.state.data["ad_group_ad_report"][config["customer_id"]]["segments.date"] + state_records = True if record and record.type == Type.RECORD: - assert record.record.data["segments.date"] >= current_state - - # # Abnormal state - # This part of the test is broken need to understand what is causing this. - # state = "2029-06-04" - # records = google_ads_client.read( - # AirbyteLogger(), config, ConfiguredAirbyteCatalog.parse_obj(SAMPLE_CATALOG), {"ad_group_ad_report": {"segments.date": state}} - # ) - - # no_records = True - # for record in records: - # if record and record.type == Type.STATE: - # assert record.state.data["ad_group_ad_report"]["segments.date"] == state - # if record and record.type == Type.RECORD: - # no_records = False + no_data_records = False - # assert no_records + assert no_data_records + assert state_records diff --git a/airbyte-integrations/connectors/source-google-ads/setup.py b/airbyte-integrations/connectors/source-google-ads/setup.py index 2b828a38b74c1..8d02bb8faaafd 100644 --- a/airbyte-integrations/connectors/source-google-ads/setup.py +++ b/airbyte-integrations/connectors/source-google-ads/setup.py @@ -7,7 +7,7 @@ MAIN_REQUIREMENTS = ["airbyte-cdk~=0.1", "google-ads==14.1.0", "pendulum"] -TEST_REQUIREMENTS = ["pytest~=6.1", "pytest-mock", "freezegun"] +TEST_REQUIREMENTS = ["pytest~=6.1", "pytest-mock", "freezegun", "requests-mock"] setup( name="source_google_ads", diff --git a/airbyte-integrations/connectors/source-google-ads/source_google_ads/custom_query_stream.py b/airbyte-integrations/connectors/source-google-ads/source_google_ads/custom_query_stream.py index 5a6a0270e65d8..82b3a0e5447a2 100644 --- a/airbyte-integrations/connectors/source-google-ads/source_google_ads/custom_query_stream.py +++ b/airbyte-integrations/connectors/source-google-ads/source_google_ads/custom_query_stream.py @@ -34,7 +34,7 @@ def get_query(self, stream_slice: Mapping[str, Any] = None) -> str: return self.insert_segments_date_expr(self.user_defined_query, start_date, end_date) # IncrementalGoogleAdsStream uses get_json_schema a lot while parsing - # responses, caching plaing crucial role for performance here. + # responses, caching playing crucial role for performance here. @lru_cache() def get_json_schema(self) -> Dict[str, Any]: """ diff --git a/airbyte-integrations/connectors/source-google-ads/source_google_ads/streams.py b/airbyte-integrations/connectors/source-google-ads/source_google_ads/streams.py index 43bbdd78c6e63..81b942b56b07c 100644 --- a/airbyte-integrations/connectors/source-google-ads/source_google_ads/streams.py +++ b/airbyte-integrations/connectors/source-google-ads/source_google_ads/streams.py @@ -48,7 +48,7 @@ def chunk_date_range( days_of_data_storage: int = None, range_days: int = None, time_zone=None, -) -> Iterable[Mapping[str, any]]: +) -> Iterable[Optional[Mapping[str, any]]]: """ Passing optional parameter end_date for testing Returns a list of the beginning and ending timestamps of each `range_days` between the start date and now. @@ -64,7 +64,7 @@ def chunk_date_range( # As in to return some state when state in abnormal if start_date > end_date: - start_date = end_date + return [None] # applying conversion window start_date = start_date.subtract(days=conversion_window) @@ -99,7 +99,9 @@ def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs) -> Ite self._customer_id = customer_id yield {} - def read_records(self, sync_mode, stream_slice: Mapping[str, Any] = None, **kwargs) -> Iterable[Mapping[str, Any]]: + def read_records(self, sync_mode, stream_slice: Optional[Mapping[str, Any]] = None, **kwargs) -> Iterable[Mapping[str, Any]]: + if not stream_slice: + return [] account_responses = self.google_ads_client.send_request(self.get_query(stream_slice), customer_id=self._customer_id) for response in account_responses: yield from self.parse_response(response) diff --git a/airbyte-integrations/connectors/source-google-ads/unit_tests/conftest.py b/airbyte-integrations/connectors/source-google-ads/unit_tests/conftest.py index d03c2820311d0..8287466b91bb0 100644 --- a/airbyte-integrations/connectors/source-google-ads/unit_tests/conftest.py +++ b/airbyte-integrations/connectors/source-google-ads/unit_tests/conftest.py @@ -11,3 +11,11 @@ def config_fixture(): with open("secrets/config.json", "r") as config_file: return json.load(config_file) + + +@pytest.fixture(autouse=True) +def mock_oauth_call(requests_mock): + yield requests_mock.post( + "https://accounts.google.com/o/oauth2/token", + json={"access_token": "access_token", "refresh_token": "refresh_token", "expires_in": 0}, + ) diff --git a/airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py b/airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py index bf4bfa8543316..b13ff2336223b 100644 --- a/airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-google-ads/unit_tests/test_source.py @@ -2,6 +2,9 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # +from collections import namedtuple +from unittest.mock import Mock + import pytest from airbyte_cdk import AirbyteLogger from freezegun import freeze_time @@ -14,7 +17,70 @@ from .common import MockErroringGoogleAdsClient, MockGoogleAdsClient -# Test chunck date range without end date +@pytest.fixture +def mock_account_info(mocker): + mocker.patch( + "source_google_ads.source.SourceGoogleAds.get_account_info", + Mock(return_value={"customer.manager": False, "customer.time_zone": "Europe/Berlin"}), + ) + + +@pytest.fixture() +def client_mock(config): + google_api = GoogleAds(credentials=config["credentials"], customer_id=config["customer_id"]) + client = AdGroupAdReport( + start_date=config["start_date"], api=google_api, conversion_window_days=config["conversion_window_days"], time_zone="local" + ) + client._customer_id = "1234567890" + return client + + +@pytest.fixture() +def mock_fields_meta_data(): + Node = namedtuple("Node", ["data_type", "name", "enum_values", "is_repeated"]) + nodes = ( + Node("RESOURCE_NAME", "campaign.accessible_bidding_strategy", [], False), + Node( + "ENUM", + "segments.ad_destination_type", + [ + "APP_DEEP_LINK", + "APP_STORE", + "LEAD_FORM", + "LOCATION_LISTING", + "MAP_DIRECTIONS", + "MESSAGE", + "NOT_APPLICABLE", + "PHONE_CALL", + "UNKNOWN", + "UNMODELED_FOR_CONVERSIONS", + "UNSPECIFIED", + "WEBSITE", + "YOUTUBE", + ], + False, + ), + Node("DATE", "campaign.start_date", [], is_repeated=False), + Node("DATE", "campaign.end_date", [], False), + Node("DATE", "segments.date", [], False), + Node( + "ENUM", + "accessible_bidding_strategy.target_impression_share.location", + ["ABSOLUTE_TOP_OF_PAGE", "ANYWHERE_ON_PAGE", "TOP_OF_PAGE", "UNKNOWN", "UNSPECIFIED"], + False, + ), + Node("STRING", "campaign.name", [], False), + Node("DOUBLE", "campaign.optimization_score", [], False), + Node("RESOURCE_NAME", "campaign.resource_name", [], False), + Node("INT32", "campaign.shopping_setting.campaign_priority", [], False), + Node("INT64", "campaign.shopping_setting.merchant_id", [], False), + Node("BOOLEAN", "campaign_budget.explicitly_shared", [], False), + Node("MESSAGE", "bidding_strategy.enhanced_cpc", [], False), + ) + return Mock(get_fields_metadata=Mock(return_value={node.name: node for node in nodes})) + + +# Test chunk date range without end date @freeze_time("2022-01-30") def test_chunk_date_range_without_end_date(): start_date_str = "2022-01-24" @@ -52,39 +118,33 @@ def test_chunk_date_range(): ] == response -def test_streams_count(config): +def test_streams_count(config, mock_account_info): source = SourceGoogleAds() streams = source.streams(config) expected_streams_number = 19 assert len(streams) == expected_streams_number -def test_non_manager_account(): - mock_account_info = {"customer.manager": False} - source = SourceGoogleAds() - is_manager_account = source.is_manager_account(mock_account_info) - assert not is_manager_account - - -def test_manager_account(): - mock_account_info = {"customer.manager": True} +@pytest.mark.parametrize("is_manager_account", (True, False)) +def test_manager_account(is_manager_account): + mock_account_info = {"customer.manager": is_manager_account} source = SourceGoogleAds() - is_manager_account = source.is_manager_account(mock_account_info) - assert is_manager_account - + assert source.is_manager_account(mock_account_info) is is_manager_account -def test_metrics_in_custom_query(): - mock_query = "SELECT customer.id, metrics.conversions, campaign.start_date FROM campaign" - source = SourceGoogleAds() - is_metrics_in_custom_query = source.is_metrics_in_custom_query(mock_query) - assert is_metrics_in_custom_query - -def test_metrics_not_in_custom_query(): - mock_query = "SELECT segments.ad_destination_type, campaign.start_date, campaign.end_date FROM campaign" +@pytest.mark.parametrize( + ( + "query", + "is_metrics_in_query", + ), + ( + ("SELECT customer.id, metrics.conversions, campaign.start_date FROM campaign", True), + ("SELECT segments.ad_destination_type, campaign.start_date, campaign.end_date FROM campaign", False), + ), +) +def test_metrics_in_custom_query(query, is_metrics_in_query): source = SourceGoogleAds() - is_metrics_in_custom_query = source.is_metrics_in_custom_query(mock_query) - assert not is_metrics_in_custom_query + assert source.is_metrics_in_custom_query(query) is is_metrics_in_query def test_time_zone(): @@ -94,59 +154,33 @@ def test_time_zone(): assert time_zone == "local" -# this requires the config because instantiating a stream creates a google client. TODO refactor so client can be mocked. -def test_get_updated_state(config): - google_api = GoogleAds(credentials=config["credentials"], customer_id=config["customer_id"]) - client = AdGroupAdReport( - start_date=config["start_date"], api=google_api, conversion_window_days=config["conversion_window_days"], time_zone="local" - ) - client._customer_id = "1234567890" - +def test_get_updated_state(client_mock): current_state_stream = {} latest_record = {"segments.date": "2020-01-01"} - new_stream_state = client.get_updated_state(current_state_stream, latest_record) + new_stream_state = client_mock.get_updated_state(current_state_stream, latest_record) assert new_stream_state == {"1234567890": {"segments.date": "2020-01-01"}} current_state_stream = {"segments.date": "2020-01-01"} latest_record = {"segments.date": "2020-02-01"} - new_stream_state = client.get_updated_state(current_state_stream, latest_record) + new_stream_state = client_mock.get_updated_state(current_state_stream, latest_record) assert new_stream_state == {"1234567890": {"segments.date": "2020-02-01"}} current_state_stream = {"1234567890": {"segments.date": "2020-02-01"}} latest_record = {"segments.date": "2021-03-03"} - new_stream_state = client.get_updated_state(current_state_stream, latest_record) + new_stream_state = client_mock.get_updated_state(current_state_stream, latest_record) assert new_stream_state == {"1234567890": {"segments.date": "2021-03-03"}} -def get_instance_from_config(config, query): +def stream_instance(query, api_mock, **kwargs): start_date = "2021-03-04" conversion_window_days = 14 - google_api = GoogleAds(credentials=config["credentials"], customer_id=config["customer_id"]) - instance = CustomQuery( - api=google_api, + api=api_mock, conversion_window_days=conversion_window_days, start_date=start_date, - custom_query_config={"query": query, "table_name": "whatever_table"}, - time_zone="local", - ) - return instance - - -# get he instance with a config -def get_instance_from_config_with_end_date(config, query): - start_date = "2021-03-04" - end_date = "2021-04-04" - conversion_window_days = 14 - google_api = GoogleAds(credentials=config["credentials"], customer_id=config["customer_id"]) - - instance = CustomQuery( - api=google_api, - conversion_window_days=conversion_window_days, - start_date=start_date, - end_date=end_date, time_zone="local", custom_query_config={"query": query, "table_name": "whatever_table"}, + **kwargs, ) return instance @@ -156,25 +190,26 @@ def get_instance_from_config_with_end_date(config, query): [ ( """ - SELecT +SELECT campaign.id, campaign.name, campaign.status, - metrics.impressions FROM campaign -wheRe campaign.status = 'PAUSED' + metrics.impressions +FROM campaign +WHERE campaign.status = 'PAUSED' AND metrics.impressions > 100 -order by campaign.status +ORDER BY campaign.status """, ["campaign.id", "campaign.name", "campaign.status", "metrics.impressions"], ), ( """ - 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 +FROM campaign """, ["campaign.accessible_bidding_strategy", "segments.ad_destination_type", "campaign.start_date", "campaign.end_date"], ), @@ -190,40 +225,42 @@ def test_get_query_fields(query, fields): [ ( """ -SELect +SELECT campaign.id, campaign.name, campaign.status, - metrics.impressions FROM campaign -wheRe campaign.status = 'PAUSED' + metrics.impressions +FROM campaign +WHERE campaign.status = 'PAUSED' AND metrics.impressions > 100 -order by campaign.status +ORDER BY campaign.status """, """ -SELect +SELECT campaign.id, campaign.name, campaign.status, - metrics.impressions , segments.date + metrics.impressions +, segments.date FROM campaign -wheRe campaign.status = 'PAUSED' +WHERE campaign.status = 'PAUSED' AND metrics.impressions > 100 AND segments.date BETWEEN '1980-01-01' AND '2000-01-01' -order by campaign.status +ORDER BY campaign.status """, ), ( """ -SELect +SELECT campaign.id, campaign.name, campaign.status, metrics.impressions FROM campaign -order by campaign.status +ORDER BY campaign.status """, """ -SELect +SELECT campaign.id, campaign.name, campaign.status, @@ -232,35 +269,51 @@ def test_get_query_fields(query, fields): FROM campaign WHERE segments.date BETWEEN '1980-01-01' AND '2000-01-01' -order by campaign.status +ORDER BY campaign.status """, ), ( """ -SELect +SELECT campaign.id, campaign.name, campaign.status, - metrics.impressions FROM campaign -wheRe campaign.status = 'PAUSED' + metrics.impressions +FROM campaign +WHERE campaign.status = 'PAUSED' AND metrics.impressions > 100 """, """ -SELect +SELECT campaign.id, campaign.name, campaign.status, - metrics.impressions , segments.date + metrics.impressions +, segments.date FROM campaign -wheRe campaign.status = 'PAUSED' +WHERE campaign.status = 'PAUSED' AND metrics.impressions > 100 AND segments.date BETWEEN '1980-01-01' AND '2000-01-01' """, ), ( - "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 + """ +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 FROM campaign + WHERE segments.date BETWEEN '1980-01-01' AND '2000-01-01' """, ), @@ -270,7 +323,7 @@ def test_insert_date(original_query, expected_query): assert CustomQuery.insert_segments_date_expr(original_query, "1980-01-01", "2000-01-01") == expected_query -def test_get_json_schema_parse_query(config): +def test_get_json_schema_parse_query(mock_fields_meta_data): query = """ SELECT campaign.accessible_bidding_strategy, @@ -287,14 +340,14 @@ def test_get_json_schema_parse_query(config): "segments.date", ] - instance = get_instance_from_config(config=config, query=query) + instance = stream_instance(query=query, api_mock=mock_fields_meta_data) final_schema = instance.get_json_schema() schema_keys = final_schema["properties"] assert set(schema_keys) == set(final_fields) # test 1 # Test get json schema when start and end date are provided in the config file -def test_get_json_schema_parse_query_with_end_date(config): +def test_get_json_schema_parse_query_with_end_date(mock_fields_meta_data): query = """ SELECT campaign.accessible_bidding_strategy, @@ -311,13 +364,13 @@ def test_get_json_schema_parse_query_with_end_date(config): "segments.date", ] - instance = get_instance_from_config_with_end_date(config=config, query=query) + instance = stream_instance(query=query, api_mock=mock_fields_meta_data, end_date="2021-04-04") final_schema = instance.get_json_schema() schema_keys = final_schema["properties"] assert set(schema_keys) == set(final_fields) # test 1 -def test_google_type_conversion(config): +def test_google_type_conversion(mock_fields_meta_data): """ query may be invalid (fields incompatibility did not checked). But we are just testing types, without submitting the query and further steps. @@ -350,7 +403,7 @@ def test_google_type_conversion(config): bidding_strategy.enhanced_cpc FROM campaign """ - instance = get_instance_from_config(config=config, query=query) + instance = stream_instance(query=query, api_mock=mock_fields_meta_data) final_schema = instance.get_json_schema() schema_properties = final_schema.get("properties") for prop, value in schema_properties.items(): diff --git a/docs/integrations/sources/google-ads.md b/docs/integrations/sources/google-ads.md index c6c2b1a38deaf..d038f0023c142 100644 --- a/docs/integrations/sources/google-ads.md +++ b/docs/integrations/sources/google-ads.md @@ -108,7 +108,8 @@ This source is constrained by whatever API limits are set for the Google Ads tha ## CHANGELOG | Version | Date | Pull Request | Subject | -| :------- | :--------- | :------------------------------------------------------- | :------------------------------------------------------------------------------------------- | +| :------- | :--------- |:---------------------------------------------------------|:---------------------------------------------------------------------------------------------| +| `0.1.37` | 2022-05-06 | [12651](https://github.com/airbytehq/airbyte/pull/12651) | Improve integration and unit tests | | `0.1.36` | 2022-04-19 | [12158](https://github.com/airbytehq/airbyte/pull/12158) | Fix `*_labels` streams data type | | `0.1.35` | 2022-04-18 | [9310](https://github.com/airbytehq/airbyte/pull/9310) | Add new fields to reports | | `0.1.34` | 2022-03-29 | [11602](https://github.com/airbytehq/airbyte/pull/11602) | Add budget amount to campaigns stream. |