From a984b753c2dae2b4286e788212556219e82c532c Mon Sep 17 00:00:00 2001 From: Serhii Lazebnyi <53845333+lazebnyi@users.noreply.github.com> Date: Fri, 15 Sep 2023 21:38:42 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8Source=20Google=20Analytics=20(GAv4):?= =?UTF-8?q?=20change=20start=20date=20to=20optional;=20add=20suggested=20s?= =?UTF-8?q?treams=20and=20update=20error=E2=80=A6=20(#30417)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dockerfile | 2 +- .../metadata.yaml | 13 ++- .../source.py | 50 ++++++++--- .../spec.json | 2 +- .../unit_tests/conftest.py | 83 +++++++++++++++++++ .../unit_tests/test_source.py | 54 ------------ .../unit_tests/test_streams.py | 47 ++--------- .../sources/google-analytics-data-api.md | 75 +++++++++-------- 8 files changed, 184 insertions(+), 142 deletions(-) create mode 100644 airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/conftest.py diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/Dockerfile b/airbyte-integrations/connectors/source-google-analytics-data-api/Dockerfile index f67eff6f9be2..09953f0e5dbf 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/Dockerfile +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/Dockerfile @@ -28,5 +28,5 @@ COPY source_google_analytics_data_api ./source_google_analytics_data_api ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py" ENTRYPOINT ["python", "/airbyte/integration_code/main.py"] -LABEL io.airbyte.version=1.3.1 +LABEL io.airbyte.version=1.4.0 LABEL io.airbyte.name=airbyte/source-google-analytics-data-api diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/metadata.yaml b/airbyte-integrations/connectors/source-google-analytics-data-api/metadata.yaml index da99cb4566c0..1431d9caa5d3 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/metadata.yaml +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/metadata.yaml @@ -7,7 +7,7 @@ data: connectorSubtype: api connectorType: source definitionId: 3cc2eafd-84aa-4dca-93af-322d9dfeec1a - dockerImageTag: 1.3.1 + dockerImageTag: 1.4.0 dockerRepository: airbyte/source-google-analytics-data-api githubIssueLabel: source-google-analytics-data-api icon: google-analytics.svg @@ -19,6 +19,17 @@ data: oss: enabled: true releaseStage: generally_available + suggestedStreams: + streams: + - website_overview + - daily_active_users + - traffic_sources + - pages + - weekly_active_users + - devices + - locations + - four_weekly_active_users + - sessions documentationUrl: https://docs.airbyte.com/integrations/sources/google-analytics-data-api tags: - language:python diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/source.py b/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/source.py index ab8e76e29893..dfdbff5711ae 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/source.py +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/source.py @@ -13,6 +13,7 @@ import dpath import jsonschema +import pendulum import requests from airbyte_cdk.models import FailureType, SyncMode from airbyte_cdk.sources import AbstractSource @@ -52,7 +53,19 @@ def __init__(self): def __get__(self, instance, owner): if not self._metadata: stream = GoogleAnalyticsDataApiMetadataStream(config=instance.config, authenticator=instance.config["authenticator"]) - metadata = next(stream.read_records(sync_mode=SyncMode.full_refresh), None) + + try: + metadata = next(stream.read_records(sync_mode=SyncMode.full_refresh), None) + except HTTPError as e: + if e.response.status_code == HTTPStatus.UNAUTHORIZED: + internal_message = "Unauthorized error reached." + message = "Can not get metadata with unauthorized credentials. Try to re-authenticate in source settings." + + unauthorized_error = AirbyteTracedException( + message=message, internal_message=internal_message, failure_type=FailureType.config_error + ) + raise unauthorized_error + if not metadata: raise Exception("failed to get metadata, over quota, try later") self._metadata = { @@ -156,7 +169,10 @@ def get_json_schema(self) -> Mapping[str, Any]: schema["properties"].update( { - d: {"type": get_dimensions_type(d), "description": self.metadata["dimensions"].get(d, {}).get("description", d)} + d.replace(":", "_"): { + "type": get_dimensions_type(d), + "description": self.metadata["dimensions"].get(d, {}).get("description", d), + } for d in self.config["dimensions"] } ) @@ -171,7 +187,7 @@ def get_json_schema(self) -> Mapping[str, Any]: schema["properties"].update( { - m: { + m.replace(":", "_"): { "type": ["null", get_metrics_type(self.metadata["metrics"].get(m, {}).get("type"))], "description": self.metadata["metrics"].get(m, {}).get("description", m), } @@ -213,9 +229,9 @@ def parse_response( ) -> Iterable[Mapping]: r = response.json() - dimensions = [h.get("name") for h in r.get("dimensionHeaders", [{}])] - metrics = [h.get("name") for h in r.get("metricHeaders", [{}])] - metrics_type_map = {h.get("name"): h.get("type") for h in r.get("metricHeaders", [{}])} + dimensions = [h.get("name").replace(":", "_") for h in r.get("dimensionHeaders", [{}])] + metrics = [h.get("name").replace(":", "_") for h in r.get("metricHeaders", [{}])] + metrics_type_map = {h.get("name").replace(":", "_"): h.get("type") for h in r.get("metricHeaders", [{}])} for row in r.get("rows", []): record = { @@ -252,7 +268,6 @@ def request_body_json( stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None, ) -> Optional[Mapping]: - payload = { "metrics": [{"name": m} for m in self.config["metrics"]], "dimensions": [{"name": d} for d in self.config["dimensions"]], @@ -268,7 +283,6 @@ def request_body_json( def stream_slices( self, *, sync_mode: SyncMode, cursor_field: List[str] = None, stream_state: Mapping[str, Any] = None ) -> Iterable[Optional[Mapping[str, Any]]]: - today: datetime.date = datetime.date.today() start_date = stream_state and stream_state.get(self.cursor_field) @@ -358,6 +372,21 @@ def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapp class SourceGoogleAnalyticsDataApi(AbstractSource): + @property + def default_date_ranges_start_date(self) -> str: + # set default date ranges start date to 2 years ago + return pendulum.now(tz="UTC").subtract(years=2).format("YYYY-MM-DD") + + def _validate_and_transform_start_date(self, start_date: str) -> datetime.date: + start_date = self.default_date_ranges_start_date if not start_date else start_date + + try: + start_date = utils.string_to_date(start_date) + except ValueError as e: + raise ConfigurationError(str(e)) + + return start_date + def _validate_and_transform(self, config: Mapping[str, Any], report_names: Set[str]): if "custom_reports" in config: if isinstance(config["custom_reports"], str): @@ -396,10 +425,7 @@ def _validate_and_transform(self, config: Mapping[str, Any], report_names: Set[s except ValueError: raise ConfigurationError("credentials.credentials_json is not valid JSON") - try: - config["date_ranges_start_date"] = utils.string_to_date(config["date_ranges_start_date"]) - except ValueError as e: - raise ConfigurationError(str(e)) + config["date_ranges_start_date"] = self._validate_and_transform_start_date(config.get("date_ranges_start_date")) if not config.get("window_in_days"): source_spec = self.spec(logging.getLogger("airbyte")) diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/spec.json b/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/spec.json index b0eda7a01fb4..6d5703a4d63c 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/spec.json +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/source_google_analytics_data_api/spec.json @@ -4,7 +4,7 @@ "$schema": "https://json-schema.org/draft-07/schema#", "title": "Google Analytics (Data API) Spec", "type": "object", - "required": ["property_ids", "date_ranges_start_date"], + "required": ["property_ids"], "additionalProperties": true, "properties": { "credentials": { diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/conftest.py b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/conftest.py new file mode 100644 index 000000000000..ac7e7ef2f28c --- /dev/null +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/conftest.py @@ -0,0 +1,83 @@ +# +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. +# + + +import datetime +import json +from copy import deepcopy + +import pytest + +# json credentials with fake private key +json_credentials = """ +{ + "type": "service_account", + "project_id": "unittest-project-id", + "private_key_id": "9qf98e52oda52g5ne23al6evnf13649c2u077162c", + "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA3slcXL+dA36ESmOi\\n1xBhZmp5Hn0WkaHDtW4naba3plva0ibloBNWhFhjQOh7Ff01PVjhT4D5jgqXBIgc\\nz9Gv3QIDAQABAkEArlhYPoD5SB2/O1PjwHgiMPrL1C9B9S/pr1cH4vPJnpY3VKE3\\n5hvdil14YwRrcbmIxMkK2iRLi9lM4mJmdWPy4QIhAPsRFXZSGx0TZsDxD9V0ZJmZ\\n0AuDCj/NF1xB5KPLmp7pAiEA4yoFox6w7ql/a1pUVaLt0NJkDfE+22pxYGNQaiXU\\nuNUCIQCsFLaIJZiN4jlgbxlyLVeya9lLuqIwvqqPQl6q4ad12QIgS9gG48xmdHig\\n8z3IdIMedZ8ZCtKmEun6Cp1+BsK0wDUCIF0nHfSuU+eTQ2qAON2SHIrJf8UeFO7N\\nzdTN1IwwQqjI\\n-----END PRIVATE KEY-----\\n", + "client_email": "google-analytics-access@unittest-project-id.iam.gserviceaccount.com", + "client_id": "213243192021686092537", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-analytics-access%40unittest-project-id.iam.gserviceaccount.com" +} +""" + + +@pytest.fixture +def one_year_ago(): + return datetime.datetime.strftime((datetime.datetime.now() - datetime.timedelta(days=1)), "%Y-%m-%d") + + +@pytest.fixture +def config(one_year_ago): + return { + "property_id": "108176369", + "property_ids": ["108176369"], + "credentials": {"auth_type": "Service", "credentials_json": json_credentials}, + "date_ranges_start_date": one_year_ago, + "dimensions": ["date", "deviceCategory", "operatingSystem", "browser"], + "metrics": [ + "totalUsers", + "newUsers", + "sessions", + "sessionsPerUser", + "averageSessionDuration", + "screenPageViews", + "screenPageViewsPerSession", + "bounceRate", + ], + "custom_reports": json.dumps([{ + "name": "report1", + "dimensions": ["date", "browser"], + "metrics": ["totalUsers", "sessions", "screenPageViews"], + }]), + } + + +@pytest.fixture +def config_without_date_range(): + return { + "property_id": "108176369", + "property_ids": ["108176369"], + "credentials": {"auth_type": "Service", "credentials_json": json_credentials}, + "custom_reports": [], + } + + +@pytest.fixture +def patch_base_class(one_year_ago, config_without_date_range): + return {"config": config_without_date_range} + + +@pytest.fixture +def config_gen(config): + def inner(**kwargs): + new_config = deepcopy(config) + # WARNING, no support deep dictionaries + new_config.update(kwargs) + return {k: v for k, v in new_config.items() if v is not ...} + + return inner diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_source.py b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_source.py index 37546a7edd24..697d999085ec 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_source.py @@ -2,9 +2,6 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # -import datetime -import json -from copy import deepcopy from unittest.mock import MagicMock import pytest @@ -13,57 +10,6 @@ from source_google_analytics_data_api import SourceGoogleAnalyticsDataApi from source_google_analytics_data_api.utils import NO_DIMENSIONS, NO_METRICS, NO_NAME, WRONG_JSON_SYNTAX -json_credentials = """ -{ - "type": "service_account", - "project_id": "unittest-project-id", - "private_key_id": "9qf98e52oda52g5ne23al6evnf13649c2u077162c", - "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA3slcXL+dA36ESmOi\\n1xBhZmp5Hn0WkaHDtW4naba3plva0ibloBNWhFhjQOh7Ff01PVjhT4D5jgqXBIgc\\nz9Gv3QIDAQABAkEArlhYPoD5SB2/O1PjwHgiMPrL1C9B9S/pr1cH4vPJnpY3VKE3\\n5hvdil14YwRrcbmIxMkK2iRLi9lM4mJmdWPy4QIhAPsRFXZSGx0TZsDxD9V0ZJmZ\\n0AuDCj/NF1xB5KPLmp7pAiEA4yoFox6w7ql/a1pUVaLt0NJkDfE+22pxYGNQaiXU\\nuNUCIQCsFLaIJZiN4jlgbxlyLVeya9lLuqIwvqqPQl6q4ad12QIgS9gG48xmdHig\\n8z3IdIMedZ8ZCtKmEun6Cp1+BsK0wDUCIF0nHfSuU+eTQ2qAON2SHIrJf8UeFO7N\\nzdTN1IwwQqjI\\n-----END PRIVATE KEY-----\\n", - "client_email": "google-analytics-access@unittest-project-id.iam.gserviceaccount.com", - "client_id": "213243192021686092537", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-analytics-access%40unittest-project-id.iam.gserviceaccount.com" -} -""" - - -@pytest.fixture -def patch_base_class(): - return { - "config": { - "property_ids": ["108176369"], - "credentials": {"auth_type": "Service", "credentials_json": json_credentials}, - "date_ranges_start_date": datetime.datetime.strftime((datetime.datetime.now() - datetime.timedelta(days=1)), "%Y-%m-%d"), - } - } - - -@pytest.fixture -def config(): - return { - "property_ids": ["108176369"], - "credentials": {"auth_type": "Service", "credentials_json": json_credentials}, - "date_ranges_start_date": datetime.datetime.strftime((datetime.datetime.now() - datetime.timedelta(days=1)), "%Y-%m-%d"), - "custom_reports": json.dumps([{ - "name": "report1", - "dimensions": ["date", "country"], - "metrics": ["totalUsers", "screenPageViews"] - }]), - } - - -@pytest.fixture -def config_gen(config): - def inner(**kwargs): - new_config = deepcopy(config) - # WARNING, no support deep dictionaries - new_config.update(kwargs) - return {k: v for k, v in new_config.items() if v is not ...} - - return inner - @pytest.mark.parametrize( "config_values, is_successful, message", diff --git a/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_streams.py index 4a4bd6bf513e..eabbf0d5991c 100644 --- a/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_streams.py +++ b/airbyte-integrations/connectors/source-google-analytics-data-api/unit_tests/test_streams.py @@ -14,48 +14,15 @@ from .utils import read_incremental -json_credentials = """ -{ - "type": "service_account", - "project_id": "unittest-project-id", - "private_key_id": "9qf98e52oda52g5ne23al6evnf13649c2u077162c", - "private_key": "", - "client_email": "google-analytics-access@unittest-project-id.iam.gserviceaccount.com", - "client_id": "213243192021686092537", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-analytics-access%40unittest-project-id.iam.gserviceaccount.com" -} -""" - @pytest.fixture -def patch_base_class(mocker): +def patch_base_class(mocker, config): # Mock abstract methods to enable instantiating abstract class mocker.patch.object(GoogleAnalyticsDataApiBaseStream, "path", f"{random.randint(100000000, 999999999)}:runReport") mocker.patch.object(GoogleAnalyticsDataApiBaseStream, "primary_key", "test_primary_key") mocker.patch.object(GoogleAnalyticsDataApiBaseStream, "__abstractmethods__", set()) - return { - "config": { - "property_ids": ["496180525"], - "property_id": "496180525", - "credentials": {"auth_type": "Service", "credentials_json": json_credentials}, - "dimensions": ["date", "deviceCategory", "operatingSystem", "browser"], - "metrics": [ - "totalUsers", - "newUsers", - "sessions", - "sessionsPerUser", - "averageSessionDuration", - "screenPageViews", - "screenPageViewsPerSession", - "bounceRate", - ], - "date_ranges_start_date": datetime.datetime.strftime((datetime.datetime.now() - datetime.timedelta(days=1)), "%Y-%m-%d"), - } - } + return {"config": config} def test_request_params(patch_base_class): @@ -160,7 +127,7 @@ def test_parse_response(patch_base_class): {"name": "totalUsers", "type": "TYPE_INTEGER"}, {"name": "newUsers", "type": "TYPE_INTEGER"}, {"name": "sessions", "type": "TYPE_INTEGER"}, - {"name": "sessionsPerUser", "type": "TYPE_FLOAT"}, + {"name": "sessionsPerUser:", "type": "TYPE_FLOAT"}, {"name": "averageSessionDuration", "type": "TYPE_SECONDS"}, {"name": "screenPageViews", "type": "TYPE_INTEGER"}, {"name": "screenPageViewsPerSession", "type": "TYPE_FLOAT"}, @@ -201,7 +168,7 @@ def test_parse_response(patch_base_class): expected_data = [ { - "property_id": "496180525", + "property_id": "108176369", "date": "20220731", "deviceCategory": "desktop", "operatingSystem": "Macintosh", @@ -209,14 +176,14 @@ def test_parse_response(patch_base_class): "totalUsers": 344, "newUsers": 169, "sessions": 420, - "sessionsPerUser": 1.2209302325581395, + "sessionsPerUser_": 1.2209302325581395, "averageSessionDuration": 194.76313766428572, "screenPageViews": 614, "screenPageViewsPerSession": 1.4619047619047618, "bounceRate": 0.47857142857142859, }, { - "property_id": "496180525", + "property_id": "108176369", "date": "20220731", "deviceCategory": "desktop", "operatingSystem": "Windows", @@ -224,7 +191,7 @@ def test_parse_response(patch_base_class): "totalUsers": 322, "newUsers": 211, "sessions": 387, - "sessionsPerUser": 1.2018633540372672, + "sessionsPerUser_": 1.2018633540372672, "averageSessionDuration": 249.21595714211884, "screenPageViews": 669, "screenPageViewsPerSession": 1.7286821705426356, diff --git a/docs/integrations/sources/google-analytics-data-api.md b/docs/integrations/sources/google-analytics-data-api.md index a7282013cf74..2d90d3e67598 100644 --- a/docs/integrations/sources/google-analytics-data-api.md +++ b/docs/integrations/sources/google-analytics-data-api.md @@ -29,7 +29,7 @@ For **Airbyte Cloud** users, we highly recommend using OAuth for authentication, If the Property Settings shows a "Tracking Id" such as "UA-123...-1", this denotes that the property is a Universal Analytics property, and the Analytics data for that property cannot be reported on using this connector. You can create a new Google Analytics 4 property by following [these instructions](https://support.google.com/analytics/answer/9744165?hl=en). ::: -7. In the **Start Date** field, use the provided datepicker or enter a date programmatically in the format `YYYY-MM-DD`. All data added from this date onward will be replicated. Note that this setting is _not_ applied to custom Cohort reports. +7. (Optional) In the **Start Date** field, use the provided datepicker or enter a date programmatically in the format `YYYY-MM-DD`. All data added from this date onward will be replicated. Note that this setting is _not_ applied to custom Cohort reports. 8. (Optional) In the **Custom Reports** field, you may optionally provide a JSON array describing any custom reports you want to sync from Google Analytics. See the [Custom Reports](#custom-reports) section below for more information on formulating these reports. 9. (Optional) In the **Data Request Interval (Days)** field, you can specify the interval in days (ranging from 1 to 364) used when requesting data from the Google Analytics API. The bigger this value is, the faster the sync will be, but the more likely that sampling will be applied to your data, potentially causing inaccuracies in the returned results. We recommend setting this to 1 unless you have a hard requirement to make the sync faster at the expense of accuracy. This field does not apply to custom Cohort reports. See the [Data Sampling](#data-sampling-and-data-request-intervals) section below for more context on this field. @@ -80,7 +80,16 @@ Before you can use the service account to access Google Analytics data, you need If the Property Settings shows a "Tracking Id" such as "UA-123...-1", this denotes that the property is a Universal Analytics property, and the Analytics data for that property cannot be reported on in the Data API. You can create a new Google Analytics 4 property by following [these instructions](https://support.google.com/analytics/answer/9744165?hl=en). ::: -6. In the **Start Date** field, use the provided datepicker or enter a date programmatically in the format `YYYY-MM-DD`. All data added from this date onward will be replicated. Note that this setting is _not_ applied to custom Cohort reports. +6. (Optional) In the **Start Date** field, use the provided datepicker or enter a date programmatically in the format `YYYY-MM-DD`. All data added from this date onward will be replicated. Note that this setting is _not_ applied to custom Cohort reports. + +:::note +If the start date is not provided, the default value will be used, which is two years from the initial sync. +::: + +:::caution +Many analyses and data investigations may require 24-48 hours to process information from your website or app. To ensure the accuracy of the data, we subtract two days from the starting date. For more details, please refer to [Google's documentation](https://support.google.com/analytics/answer/9333790?hl=en). +::: + 7. (Optional) In the **Custom Reports** field, you may optionally provide a JSON array describing any custom reports you want to sync from Google Analytics. See the [Custom Reports](#custom-reports) section below for more information on formulating these reports. 8. (Optional) In the **Data Request Interval (Days)** field, you can specify the interval in days (ranging from 1 to 364) used when requesting data from the Google Analytics API. The bigger this value is, the faster the sync will be, but the more likely that sampling will be applied to your data, potentially causing inaccuracies in the returned results. We recommend setting this to 1 unless you have a hard requirement to make the sync faster at the expense of accuracy. This field does not apply to custom Cohort reports. See the [Data Sampling](#data-sampling-and-data-request-intervals) section below for more context on this field. @@ -254,37 +263,37 @@ The Google Analytics connector is subject to Google Analytics Data API quotas. P ## Data type map -| Integration Type | Airbyte Type | Notes | -|:-----------------|:-------------|:------| -| `string` | `string` | | -| `number` | `number` | | -| `array` | `array` | | -| `object` | `object` | | +| Integration Type | Airbyte Type | +|:-----------------|:-------------| +| `string` | `string` | +| `number` | `number` | +| `array` | `array` | +| `object` | `object` | ## Changelog -| Version | Date | Pull Request | Subject | -|:--------|:-----------|:---------------------------------------------------------|:------------------------------------------------------------------------------| -| 1.3.1 | 2023-09-14 | [30424](https://github.com/airbytehq/airbyte/pull/30424) | Fixed duplicated stream issue | -| 1.3.0 | 2023-09-13 | [30152](https://github.com/airbytehq/airbyte/pull/30152) | Ability to add multiple property ids | -| 1.2.0 | 2023-09-11 | [30290](https://github.com/airbytehq/airbyte/pull/30290) | Add new preconfigured reports | -| 1.1.3 | 2023-08-04 | [29103](https://github.com/airbytehq/airbyte/pull/29103) | Update input field descriptions | -| 1.1.2 | 2023-07-03 | [27909](https://github.com/airbytehq/airbyte/pull/27909) | Limit the page size of custom report streams | -| 1.1.1 | 2023-06-26 | [27718](https://github.com/airbytehq/airbyte/pull/27718) | Limit the page size when calling `check()` | -| 1.1.0 | 2023-06-26 | [27738](https://github.com/airbytehq/airbyte/pull/27738) | License Update: Elv2 | -| 1.0.0 | 2023-06-22 | [26283](https://github.com/airbytehq/airbyte/pull/26283) | Added primary_key and lookback window | -| 0.2.7 | 2023-06-21 | [27531](https://github.com/airbytehq/airbyte/pull/27531) | Fix formatting | -| 0.2.6 | 2023-06-09 | [27207](https://github.com/airbytehq/airbyte/pull/27207) | Improve api rate limit messages | -| 0.2.5 | 2023-06-08 | [27175](https://github.com/airbytehq/airbyte/pull/27175) | Improve Error Messages | -| 0.2.4 | 2023-06-01 | [26887](https://github.com/airbytehq/airbyte/pull/26887) | Remove `authSpecification` from connector spec in favour of `advancedAuth` | -| 0.2.3 | 2023-05-16 | [26126](https://github.com/airbytehq/airbyte/pull/26126) | Fix pagination | -| 0.2.2 | 2023-05-12 | [25987](https://github.com/airbytehq/airbyte/pull/25987) | Categorized Config Errors Accurately | -| 0.2.1 | 2023-05-11 | [26008](https://github.com/airbytehq/airbyte/pull/26008) | Added handling for `429 - potentiallyThresholdedRequestsPerHour` error | -| 0.2.0 | 2023-04-13 | [25179](https://github.com/airbytehq/airbyte/pull/25179) | Implement support for custom Cohort and Pivot reports | -| 0.1.3 | 2023-03-10 | [23872](https://github.com/airbytehq/airbyte/pull/23872) | Fix parse + cursor for custom reports | -| 0.1.2 | 2023-03-07 | [23822](https://github.com/airbytehq/airbyte/pull/23822) | Improve `rate limits` customer faced error messages and retry logic for `429` | -| 0.1.1 | 2023-01-10 | [21169](https://github.com/airbytehq/airbyte/pull/21169) | Slicer updated, unit tests added | -| 0.1.0 | 2023-01-08 | [20889](https://github.com/airbytehq/airbyte/pull/20889) | Improved config validation, SAT | -| 0.0.3 | 2022-08-15 | [15229](https://github.com/airbytehq/airbyte/pull/15229) | Source Google Analytics Data Api: code refactoring | -| 0.0.2 | 2022-07-27 | [15087](https://github.com/airbytehq/airbyte/pull/15087) | fix documentationUrl | -| 0.0.1 | 2022-05-09 | [12701](https://github.com/airbytehq/airbyte/pull/12701) | Introduce Google Analytics Data API source | +| Version | Date | Pull Request | Subject | +|:--------|:-----------|:---------------------------------------------------------|:--------------------------------------------------------------------------------| +| 1.4.0 | 2023-09-15 | [30417](https://github.com/airbytehq/airbyte/pull/30417) | Change start date to optional; add suggested streams and update errors handling | +| 1.3.1 | 2023-09-14 | [30424](https://github.com/airbytehq/airbyte/pull/30424) | Fixed duplicated stream issue | +| 1.2.0 | 2023-09-11 | [30290](https://github.com/airbytehq/airbyte/pull/30290) | Add new preconfigured reports | +| 1.1.3 | 2023-08-04 | [29103](https://github.com/airbytehq/airbyte/pull/29103) | Update input field descriptions | +| 1.1.2 | 2023-07-03 | [27909](https://github.com/airbytehq/airbyte/pull/27909) | Limit the page size of custom report streams | +| 1.1.1 | 2023-06-26 | [27718](https://github.com/airbytehq/airbyte/pull/27718) | Limit the page size when calling `check()` | +| 1.1.0 | 2023-06-26 | [27738](https://github.com/airbytehq/airbyte/pull/27738) | License Update: Elv2 | +| 1.0.0 | 2023-06-22 | [26283](https://github.com/airbytehq/airbyte/pull/26283) | Added primary_key and lookback window | +| 0.2.7 | 2023-06-21 | [27531](https://github.com/airbytehq/airbyte/pull/27531) | Fix formatting | +| 0.2.6 | 2023-06-09 | [27207](https://github.com/airbytehq/airbyte/pull/27207) | Improve api rate limit messages | +| 0.2.5 | 2023-06-08 | [27175](https://github.com/airbytehq/airbyte/pull/27175) | Improve Error Messages | +| 0.2.4 | 2023-06-01 | [26887](https://github.com/airbytehq/airbyte/pull/26887) | Remove `authSpecification` from connector spec in favour of `advancedAuth` | +| 0.2.3 | 2023-05-16 | [26126](https://github.com/airbytehq/airbyte/pull/26126) | Fix pagination | +| 0.2.2 | 2023-05-12 | [25987](https://github.com/airbytehq/airbyte/pull/25987) | Categorized Config Errors Accurately | +| 0.2.1 | 2023-05-11 | [26008](https://github.com/airbytehq/airbyte/pull/26008) | Added handling for `429 - potentiallyThresholdedRequestsPerHour` error | +| 0.2.0 | 2023-04-13 | [25179](https://github.com/airbytehq/airbyte/pull/25179) | Implement support for custom Cohort and Pivot reports | +| 0.1.3 | 2023-03-10 | [23872](https://github.com/airbytehq/airbyte/pull/23872) | Fix parse + cursor for custom reports | +| 0.1.2 | 2023-03-07 | [23822](https://github.com/airbytehq/airbyte/pull/23822) | Improve `rate limits` customer faced error messages and retry logic for `429` | +| 0.1.1 | 2023-01-10 | [21169](https://github.com/airbytehq/airbyte/pull/21169) | Slicer updated, unit tests added | +| 0.1.0 | 2023-01-08 | [20889](https://github.com/airbytehq/airbyte/pull/20889) | Improved config validation, SAT | +| 0.0.3 | 2022-08-15 | [15229](https://github.com/airbytehq/airbyte/pull/15229) | Source Google Analytics Data Api: code refactoring | +| 0.0.2 | 2022-07-27 | [15087](https://github.com/airbytehq/airbyte/pull/15087) | fix documentationUrl | +| 0.0.1 | 2022-05-09 | [12701](https://github.com/airbytehq/airbyte/pull/12701) | Introduce Google Analytics Data API source |