From edd57b5781e4d4c4a3a3f0a34968d5b2a8007f5c Mon Sep 17 00:00:00 2001 From: Eason09053360 <185830721+Eason09053360@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:52:05 +0800 Subject: [PATCH] Strip only the literal properties/ prefix from GA property link IDs lstrip removes any leading character belonging to the given set, not a prefix, so the property ID itself could lose leading characters. This is correct today only because GA4 property IDs happen to be numeric; the intent is a prefix strip and should be expressed as one. --- .../operators/analytics_admin.py | 2 +- .../operators/test_analytics_admin.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/providers/google/src/airflow/providers/google/marketing_platform/operators/analytics_admin.py b/providers/google/src/airflow/providers/google/marketing_platform/operators/analytics_admin.py index 037480e5b5cee..e6be9c9f90217 100644 --- a/providers/google/src/airflow/providers/google/marketing_platform/operators/analytics_admin.py +++ b/providers/google/src/airflow/providers/google/marketing_platform/operators/analytics_admin.py @@ -194,7 +194,7 @@ def execute( self.log.info("The Google Analytics property %s was created successfully.", prop.name) GoogleAnalyticsPropertyLink.persist( context=context, - property_id=prop.name.lstrip("properties/"), + property_id=prop.name.removeprefix("properties/"), ) return Property.to_dict(prop) diff --git a/providers/google/tests/unit/google/marketing_platform/operators/test_analytics_admin.py b/providers/google/tests/unit/google/marketing_platform/operators/test_analytics_admin.py index 8c65b0e4788af..502de8ba2f44c 100644 --- a/providers/google/tests/unit/google/marketing_platform/operators/test_analytics_admin.py +++ b/providers/google/tests/unit/google/marketing_platform/operators/test_analytics_admin.py @@ -117,6 +117,31 @@ def test_execute(self, property_to_dict_mock, hook_mock, _): property_to_dict_mock.assert_called_once_with(property_returned) assert property_created == property_serialized + @pytest.mark.parametrize( + ("property_name", "expected_property_id"), + [ + (TEST_PROPERTY_NAME, TEST_PROPERTY_ID), + # Stripping a character set instead of the literal prefix would also eat the leading "s". + ("properties/s123", "s123"), + ], + ) + @mock.patch(f"{ANALYTICS_PATH}.GoogleAnalyticsPropertyLink") + @mock.patch(f"{ANALYTICS_PATH}.GoogleAnalyticsAdminHook") + @mock.patch(f"{ANALYTICS_PATH}.Property.to_dict") + def test_execute_persists_link_with_property_id( + self, _, hook_mock, property_link_mock, property_name, expected_property_id + ): + hook_mock.return_value.create_property.return_value.name = property_name + + GoogleAnalyticsAdminCreatePropertyOperator( + task_id="test_task", + analytics_property=mock.MagicMock(), + gcp_conn_id=GCP_CONN_ID, + impersonation_chain=IMPERSONATION_CHAIN, + ).execute(context=None) + + property_link_mock.persist.assert_called_once_with(context=None, property_id=expected_property_id) + class TestGoogleAnalyticsAdminDeletePropertyOperator: @mock.patch(f"{ANALYTICS_PATH}.GoogleAnalyticsAdminHook")