apache-airflow-providers-google 14.0.0__py3-none-any.whl → 19.1.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. airflow/providers/google/3rd-party-licenses/LICENSES.txt +14 -0
  2. airflow/providers/google/3rd-party-licenses/NOTICE +5 -0
  3. airflow/providers/google/__init__.py +3 -3
  4. airflow/providers/google/_vendor/__init__.py +0 -0
  5. airflow/providers/google/_vendor/json_merge_patch.py +91 -0
  6. airflow/providers/google/ads/hooks/ads.py +52 -43
  7. airflow/providers/google/ads/operators/ads.py +2 -2
  8. airflow/providers/google/ads/transfers/ads_to_gcs.py +3 -19
  9. airflow/providers/google/assets/gcs.py +1 -11
  10. airflow/providers/google/cloud/_internal_client/secret_manager_client.py +3 -2
  11. airflow/providers/google/cloud/bundles/gcs.py +161 -0
  12. airflow/providers/google/cloud/hooks/alloy_db.py +2 -3
  13. airflow/providers/google/cloud/hooks/bigquery.py +195 -318
  14. airflow/providers/google/cloud/hooks/bigquery_dts.py +8 -8
  15. airflow/providers/google/cloud/hooks/bigtable.py +3 -2
  16. airflow/providers/google/cloud/hooks/cloud_batch.py +8 -9
  17. airflow/providers/google/cloud/hooks/cloud_build.py +6 -65
  18. airflow/providers/google/cloud/hooks/cloud_composer.py +292 -24
  19. airflow/providers/google/cloud/hooks/cloud_logging.py +109 -0
  20. airflow/providers/google/cloud/hooks/cloud_memorystore.py +4 -3
  21. airflow/providers/google/cloud/hooks/cloud_run.py +20 -11
  22. airflow/providers/google/cloud/hooks/cloud_sql.py +136 -64
  23. airflow/providers/google/cloud/hooks/cloud_storage_transfer_service.py +35 -15
  24. airflow/providers/google/cloud/hooks/compute.py +7 -6
  25. airflow/providers/google/cloud/hooks/compute_ssh.py +7 -4
  26. airflow/providers/google/cloud/hooks/datacatalog.py +12 -3
  27. airflow/providers/google/cloud/hooks/dataflow.py +87 -242
  28. airflow/providers/google/cloud/hooks/dataform.py +9 -14
  29. airflow/providers/google/cloud/hooks/datafusion.py +7 -9
  30. airflow/providers/google/cloud/hooks/dataplex.py +13 -12
  31. airflow/providers/google/cloud/hooks/dataprep.py +2 -2
  32. airflow/providers/google/cloud/hooks/dataproc.py +76 -74
  33. airflow/providers/google/cloud/hooks/dataproc_metastore.py +4 -3
  34. airflow/providers/google/cloud/hooks/dlp.py +5 -4
  35. airflow/providers/google/cloud/hooks/gcs.py +144 -33
  36. airflow/providers/google/cloud/hooks/gen_ai.py +196 -0
  37. airflow/providers/google/cloud/hooks/kms.py +3 -2
  38. airflow/providers/google/cloud/hooks/kubernetes_engine.py +22 -17
  39. airflow/providers/google/cloud/hooks/looker.py +6 -1
  40. airflow/providers/google/cloud/hooks/managed_kafka.py +227 -3
  41. airflow/providers/google/cloud/hooks/mlengine.py +7 -8
  42. airflow/providers/google/cloud/hooks/natural_language.py +3 -2
  43. airflow/providers/google/cloud/hooks/os_login.py +3 -2
  44. airflow/providers/google/cloud/hooks/pubsub.py +6 -6
  45. airflow/providers/google/cloud/hooks/secret_manager.py +105 -12
  46. airflow/providers/google/cloud/hooks/spanner.py +75 -10
  47. airflow/providers/google/cloud/hooks/speech_to_text.py +3 -2
  48. airflow/providers/google/cloud/hooks/stackdriver.py +18 -18
  49. airflow/providers/google/cloud/hooks/tasks.py +4 -3
  50. airflow/providers/google/cloud/hooks/text_to_speech.py +3 -2
  51. airflow/providers/google/cloud/hooks/translate.py +8 -17
  52. airflow/providers/google/cloud/hooks/vertex_ai/auto_ml.py +8 -222
  53. airflow/providers/google/cloud/hooks/vertex_ai/batch_prediction_job.py +9 -15
  54. airflow/providers/google/cloud/hooks/vertex_ai/custom_job.py +33 -283
  55. airflow/providers/google/cloud/hooks/vertex_ai/dataset.py +5 -12
  56. airflow/providers/google/cloud/hooks/vertex_ai/endpoint_service.py +6 -12
  57. airflow/providers/google/cloud/hooks/vertex_ai/experiment_service.py +202 -0
  58. airflow/providers/google/cloud/hooks/vertex_ai/feature_store.py +311 -10
  59. airflow/providers/google/cloud/hooks/vertex_ai/generative_model.py +79 -75
  60. airflow/providers/google/cloud/hooks/vertex_ai/hyperparameter_tuning_job.py +7 -13
  61. airflow/providers/google/cloud/hooks/vertex_ai/model_service.py +8 -12
  62. airflow/providers/google/cloud/hooks/vertex_ai/pipeline_job.py +6 -12
  63. airflow/providers/google/cloud/hooks/vertex_ai/prediction_service.py +3 -2
  64. airflow/providers/google/cloud/hooks/vertex_ai/ray.py +223 -0
  65. airflow/providers/google/cloud/hooks/video_intelligence.py +3 -2
  66. airflow/providers/google/cloud/hooks/vision.py +7 -7
  67. airflow/providers/google/cloud/hooks/workflows.py +4 -3
  68. airflow/providers/google/cloud/links/alloy_db.py +0 -46
  69. airflow/providers/google/cloud/links/base.py +77 -7
  70. airflow/providers/google/cloud/links/bigquery.py +0 -47
  71. airflow/providers/google/cloud/links/bigquery_dts.py +0 -20
  72. airflow/providers/google/cloud/links/bigtable.py +0 -48
  73. airflow/providers/google/cloud/links/cloud_build.py +0 -73
  74. airflow/providers/google/cloud/links/cloud_functions.py +0 -33
  75. airflow/providers/google/cloud/links/cloud_memorystore.py +0 -58
  76. airflow/providers/google/cloud/links/{life_sciences.py → cloud_run.py} +5 -27
  77. airflow/providers/google/cloud/links/cloud_sql.py +0 -33
  78. airflow/providers/google/cloud/links/cloud_storage_transfer.py +17 -46
  79. airflow/providers/google/cloud/links/cloud_tasks.py +7 -26
  80. airflow/providers/google/cloud/links/compute.py +0 -58
  81. airflow/providers/google/cloud/links/data_loss_prevention.py +0 -169
  82. airflow/providers/google/cloud/links/datacatalog.py +23 -54
  83. airflow/providers/google/cloud/links/dataflow.py +0 -34
  84. airflow/providers/google/cloud/links/dataform.py +0 -64
  85. airflow/providers/google/cloud/links/datafusion.py +1 -90
  86. airflow/providers/google/cloud/links/dataplex.py +0 -154
  87. airflow/providers/google/cloud/links/dataprep.py +0 -24
  88. airflow/providers/google/cloud/links/dataproc.py +11 -89
  89. airflow/providers/google/cloud/links/datastore.py +0 -31
  90. airflow/providers/google/cloud/links/kubernetes_engine.py +11 -61
  91. airflow/providers/google/cloud/links/managed_kafka.py +11 -51
  92. airflow/providers/google/cloud/links/mlengine.py +0 -70
  93. airflow/providers/google/cloud/links/pubsub.py +0 -32
  94. airflow/providers/google/cloud/links/spanner.py +0 -33
  95. airflow/providers/google/cloud/links/stackdriver.py +0 -30
  96. airflow/providers/google/cloud/links/translate.py +17 -187
  97. airflow/providers/google/cloud/links/vertex_ai.py +28 -195
  98. airflow/providers/google/cloud/links/workflows.py +0 -52
  99. airflow/providers/google/cloud/log/gcs_task_handler.py +166 -118
  100. airflow/providers/google/cloud/log/stackdriver_task_handler.py +14 -9
  101. airflow/providers/google/cloud/openlineage/CloudStorageTransferJobFacet.json +68 -0
  102. airflow/providers/google/cloud/openlineage/CloudStorageTransferRunFacet.json +60 -0
  103. airflow/providers/google/cloud/openlineage/DataFusionRunFacet.json +32 -0
  104. airflow/providers/google/cloud/openlineage/facets.py +141 -40
  105. airflow/providers/google/cloud/openlineage/mixins.py +14 -13
  106. airflow/providers/google/cloud/openlineage/utils.py +19 -3
  107. airflow/providers/google/cloud/operators/alloy_db.py +76 -61
  108. airflow/providers/google/cloud/operators/bigquery.py +104 -667
  109. airflow/providers/google/cloud/operators/bigquery_dts.py +12 -12
  110. airflow/providers/google/cloud/operators/bigtable.py +38 -7
  111. airflow/providers/google/cloud/operators/cloud_base.py +22 -1
  112. airflow/providers/google/cloud/operators/cloud_batch.py +18 -18
  113. airflow/providers/google/cloud/operators/cloud_build.py +80 -36
  114. airflow/providers/google/cloud/operators/cloud_composer.py +157 -71
  115. airflow/providers/google/cloud/operators/cloud_logging_sink.py +341 -0
  116. airflow/providers/google/cloud/operators/cloud_memorystore.py +74 -46
  117. airflow/providers/google/cloud/operators/cloud_run.py +39 -20
  118. airflow/providers/google/cloud/operators/cloud_sql.py +46 -61
  119. airflow/providers/google/cloud/operators/cloud_storage_transfer_service.py +92 -14
  120. airflow/providers/google/cloud/operators/compute.py +18 -50
  121. airflow/providers/google/cloud/operators/datacatalog.py +167 -29
  122. airflow/providers/google/cloud/operators/dataflow.py +38 -15
  123. airflow/providers/google/cloud/operators/dataform.py +19 -7
  124. airflow/providers/google/cloud/operators/datafusion.py +43 -43
  125. airflow/providers/google/cloud/operators/dataplex.py +212 -126
  126. airflow/providers/google/cloud/operators/dataprep.py +1 -5
  127. airflow/providers/google/cloud/operators/dataproc.py +134 -207
  128. airflow/providers/google/cloud/operators/dataproc_metastore.py +102 -84
  129. airflow/providers/google/cloud/operators/datastore.py +22 -6
  130. airflow/providers/google/cloud/operators/dlp.py +24 -45
  131. airflow/providers/google/cloud/operators/functions.py +21 -14
  132. airflow/providers/google/cloud/operators/gcs.py +15 -12
  133. airflow/providers/google/cloud/operators/gen_ai.py +389 -0
  134. airflow/providers/google/cloud/operators/kubernetes_engine.py +115 -106
  135. airflow/providers/google/cloud/operators/looker.py +1 -1
  136. airflow/providers/google/cloud/operators/managed_kafka.py +362 -40
  137. airflow/providers/google/cloud/operators/natural_language.py +5 -3
  138. airflow/providers/google/cloud/operators/pubsub.py +69 -21
  139. airflow/providers/google/cloud/operators/spanner.py +53 -45
  140. airflow/providers/google/cloud/operators/speech_to_text.py +5 -4
  141. airflow/providers/google/cloud/operators/stackdriver.py +5 -11
  142. airflow/providers/google/cloud/operators/tasks.py +6 -15
  143. airflow/providers/google/cloud/operators/text_to_speech.py +4 -3
  144. airflow/providers/google/cloud/operators/translate.py +46 -20
  145. airflow/providers/google/cloud/operators/translate_speech.py +4 -3
  146. airflow/providers/google/cloud/operators/vertex_ai/auto_ml.py +44 -34
  147. airflow/providers/google/cloud/operators/vertex_ai/batch_prediction_job.py +34 -12
  148. airflow/providers/google/cloud/operators/vertex_ai/custom_job.py +62 -53
  149. airflow/providers/google/cloud/operators/vertex_ai/dataset.py +75 -11
  150. airflow/providers/google/cloud/operators/vertex_ai/endpoint_service.py +48 -12
  151. airflow/providers/google/cloud/operators/vertex_ai/experiment_service.py +435 -0
  152. airflow/providers/google/cloud/operators/vertex_ai/feature_store.py +532 -1
  153. airflow/providers/google/cloud/operators/vertex_ai/generative_model.py +135 -116
  154. airflow/providers/google/cloud/operators/vertex_ai/hyperparameter_tuning_job.py +16 -12
  155. airflow/providers/google/cloud/operators/vertex_ai/model_service.py +62 -14
  156. airflow/providers/google/cloud/operators/vertex_ai/pipeline_job.py +35 -10
  157. airflow/providers/google/cloud/operators/vertex_ai/ray.py +393 -0
  158. airflow/providers/google/cloud/operators/video_intelligence.py +5 -3
  159. airflow/providers/google/cloud/operators/vision.py +7 -5
  160. airflow/providers/google/cloud/operators/workflows.py +24 -19
  161. airflow/providers/google/cloud/secrets/secret_manager.py +2 -1
  162. airflow/providers/google/cloud/sensors/bigquery.py +2 -2
  163. airflow/providers/google/cloud/sensors/bigquery_dts.py +6 -4
  164. airflow/providers/google/cloud/sensors/bigtable.py +14 -6
  165. airflow/providers/google/cloud/sensors/cloud_composer.py +535 -33
  166. airflow/providers/google/cloud/sensors/cloud_storage_transfer_service.py +6 -5
  167. airflow/providers/google/cloud/sensors/dataflow.py +27 -10
  168. airflow/providers/google/cloud/sensors/dataform.py +2 -2
  169. airflow/providers/google/cloud/sensors/datafusion.py +4 -4
  170. airflow/providers/google/cloud/sensors/dataplex.py +7 -5
  171. airflow/providers/google/cloud/sensors/dataprep.py +2 -2
  172. airflow/providers/google/cloud/sensors/dataproc.py +10 -9
  173. airflow/providers/google/cloud/sensors/dataproc_metastore.py +4 -3
  174. airflow/providers/google/cloud/sensors/gcs.py +22 -21
  175. airflow/providers/google/cloud/sensors/looker.py +5 -5
  176. airflow/providers/google/cloud/sensors/pubsub.py +20 -20
  177. airflow/providers/google/cloud/sensors/tasks.py +2 -2
  178. airflow/providers/google/cloud/sensors/vertex_ai/feature_store.py +2 -2
  179. airflow/providers/google/cloud/sensors/workflows.py +6 -4
  180. airflow/providers/google/cloud/transfers/adls_to_gcs.py +1 -1
  181. airflow/providers/google/cloud/transfers/azure_blob_to_gcs.py +2 -2
  182. airflow/providers/google/cloud/transfers/azure_fileshare_to_gcs.py +2 -2
  183. airflow/providers/google/cloud/transfers/bigquery_to_bigquery.py +11 -8
  184. airflow/providers/google/cloud/transfers/bigquery_to_gcs.py +14 -13
  185. airflow/providers/google/cloud/transfers/bigquery_to_mssql.py +7 -3
  186. airflow/providers/google/cloud/transfers/bigquery_to_mysql.py +12 -1
  187. airflow/providers/google/cloud/transfers/bigquery_to_postgres.py +24 -10
  188. airflow/providers/google/cloud/transfers/bigquery_to_sql.py +104 -5
  189. airflow/providers/google/cloud/transfers/calendar_to_gcs.py +1 -1
  190. airflow/providers/google/cloud/transfers/cassandra_to_gcs.py +18 -22
  191. airflow/providers/google/cloud/transfers/facebook_ads_to_gcs.py +4 -5
  192. airflow/providers/google/cloud/transfers/gcs_to_bigquery.py +45 -38
  193. airflow/providers/google/cloud/transfers/gcs_to_gcs.py +2 -2
  194. airflow/providers/google/cloud/transfers/gcs_to_local.py +5 -3
  195. airflow/providers/google/cloud/transfers/gcs_to_sftp.py +10 -4
  196. airflow/providers/google/cloud/transfers/gdrive_to_gcs.py +6 -2
  197. airflow/providers/google/cloud/transfers/gdrive_to_local.py +2 -2
  198. airflow/providers/google/cloud/transfers/http_to_gcs.py +193 -0
  199. airflow/providers/google/cloud/transfers/local_to_gcs.py +2 -2
  200. airflow/providers/google/cloud/transfers/mssql_to_gcs.py +1 -1
  201. airflow/providers/google/cloud/transfers/oracle_to_gcs.py +36 -11
  202. airflow/providers/google/cloud/transfers/postgres_to_gcs.py +44 -12
  203. airflow/providers/google/cloud/transfers/s3_to_gcs.py +12 -6
  204. airflow/providers/google/cloud/transfers/salesforce_to_gcs.py +2 -2
  205. airflow/providers/google/cloud/transfers/sftp_to_gcs.py +36 -14
  206. airflow/providers/google/cloud/transfers/sheets_to_gcs.py +3 -3
  207. airflow/providers/google/cloud/transfers/sql_to_gcs.py +10 -10
  208. airflow/providers/google/cloud/triggers/bigquery.py +75 -34
  209. airflow/providers/google/cloud/triggers/bigquery_dts.py +2 -1
  210. airflow/providers/google/cloud/triggers/cloud_batch.py +2 -1
  211. airflow/providers/google/cloud/triggers/cloud_build.py +3 -2
  212. airflow/providers/google/cloud/triggers/cloud_composer.py +303 -47
  213. airflow/providers/google/cloud/triggers/cloud_run.py +2 -2
  214. airflow/providers/google/cloud/triggers/cloud_storage_transfer_service.py +96 -5
  215. airflow/providers/google/cloud/triggers/dataflow.py +125 -2
  216. airflow/providers/google/cloud/triggers/datafusion.py +1 -1
  217. airflow/providers/google/cloud/triggers/dataplex.py +16 -3
  218. airflow/providers/google/cloud/triggers/dataproc.py +124 -53
  219. airflow/providers/google/cloud/triggers/kubernetes_engine.py +46 -28
  220. airflow/providers/google/cloud/triggers/mlengine.py +1 -1
  221. airflow/providers/google/cloud/triggers/pubsub.py +17 -20
  222. airflow/providers/google/cloud/triggers/vertex_ai.py +8 -7
  223. airflow/providers/google/cloud/utils/bigquery.py +5 -7
  224. airflow/providers/google/cloud/utils/bigquery_get_data.py +1 -1
  225. airflow/providers/google/cloud/utils/credentials_provider.py +4 -3
  226. airflow/providers/google/cloud/utils/dataform.py +1 -1
  227. airflow/providers/google/cloud/utils/external_token_supplier.py +0 -1
  228. airflow/providers/google/cloud/utils/field_validator.py +1 -2
  229. airflow/providers/google/cloud/utils/validators.py +43 -0
  230. airflow/providers/google/common/auth_backend/google_openid.py +26 -9
  231. airflow/providers/google/common/consts.py +2 -1
  232. airflow/providers/google/common/deprecated.py +2 -1
  233. airflow/providers/google/common/hooks/base_google.py +40 -43
  234. airflow/providers/google/common/hooks/operation_helpers.py +78 -0
  235. airflow/providers/google/common/links/storage.py +0 -22
  236. airflow/providers/google/common/utils/get_secret.py +31 -0
  237. airflow/providers/google/common/utils/id_token_credentials.py +4 -5
  238. airflow/providers/google/firebase/operators/firestore.py +2 -2
  239. airflow/providers/google/get_provider_info.py +61 -216
  240. airflow/providers/google/go_module_utils.py +35 -3
  241. airflow/providers/google/leveldb/hooks/leveldb.py +30 -6
  242. airflow/providers/google/leveldb/operators/leveldb.py +2 -2
  243. airflow/providers/google/marketing_platform/hooks/analytics_admin.py +3 -2
  244. airflow/providers/google/marketing_platform/hooks/display_video.py +3 -109
  245. airflow/providers/google/marketing_platform/hooks/search_ads.py +1 -1
  246. airflow/providers/google/marketing_platform/links/analytics_admin.py +4 -5
  247. airflow/providers/google/marketing_platform/operators/analytics_admin.py +7 -6
  248. airflow/providers/google/marketing_platform/operators/campaign_manager.py +5 -5
  249. airflow/providers/google/marketing_platform/operators/display_video.py +28 -489
  250. airflow/providers/google/marketing_platform/operators/search_ads.py +2 -2
  251. airflow/providers/google/marketing_platform/sensors/campaign_manager.py +2 -2
  252. airflow/providers/google/marketing_platform/sensors/display_video.py +4 -64
  253. airflow/providers/google/suite/hooks/calendar.py +1 -1
  254. airflow/providers/google/suite/hooks/drive.py +2 -2
  255. airflow/providers/google/suite/hooks/sheets.py +15 -1
  256. airflow/providers/google/suite/operators/sheets.py +8 -3
  257. airflow/providers/google/suite/sensors/drive.py +2 -2
  258. airflow/providers/google/suite/transfers/gcs_to_gdrive.py +2 -2
  259. airflow/providers/google/suite/transfers/gcs_to_sheets.py +1 -1
  260. airflow/providers/google/suite/transfers/local_to_drive.py +3 -3
  261. airflow/providers/google/suite/transfers/sql_to_sheets.py +5 -4
  262. airflow/providers/google/version_compat.py +15 -1
  263. {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/METADATA +117 -72
  264. apache_airflow_providers_google-19.1.0rc1.dist-info/RECORD +331 -0
  265. {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/WHEEL +1 -1
  266. apache_airflow_providers_google-19.1.0rc1.dist-info/licenses/NOTICE +5 -0
  267. airflow/providers/google/cloud/example_dags/example_cloud_task.py +0 -54
  268. airflow/providers/google/cloud/hooks/automl.py +0 -679
  269. airflow/providers/google/cloud/hooks/life_sciences.py +0 -159
  270. airflow/providers/google/cloud/links/automl.py +0 -193
  271. airflow/providers/google/cloud/operators/automl.py +0 -1360
  272. airflow/providers/google/cloud/operators/life_sciences.py +0 -119
  273. airflow/providers/google/cloud/operators/mlengine.py +0 -1515
  274. airflow/providers/google/cloud/utils/mlengine_operator_utils.py +0 -273
  275. apache_airflow_providers_google-14.0.0.dist-info/RECORD +0 -318
  276. /airflow/providers/google/cloud/{example_dags → bundles}/__init__.py +0 -0
  277. {apache_airflow_providers_google-14.0.0.dist-info → apache_airflow_providers_google-19.1.0rc1.dist-info}/entry_points.txt +0 -0
  278. {airflow/providers/google → apache_airflow_providers_google-19.1.0rc1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,109 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ from __future__ import annotations
19
+
20
+ from collections.abc import Sequence
21
+ from typing import TYPE_CHECKING
22
+
23
+ from google.cloud.logging_v2.services.config_service_v2 import ConfigServiceV2Client
24
+ from google.cloud.logging_v2.types import (
25
+ CreateSinkRequest,
26
+ DeleteSinkRequest,
27
+ GetSinkRequest,
28
+ ListSinksRequest,
29
+ LogSink,
30
+ UpdateSinkRequest,
31
+ )
32
+
33
+ from airflow.providers.google.common.consts import CLIENT_INFO
34
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID, GoogleBaseHook
35
+
36
+ if TYPE_CHECKING:
37
+ from google.protobuf.field_mask_pb2 import FieldMask
38
+
39
+
40
+ class CloudLoggingHook(GoogleBaseHook):
41
+ """
42
+ Hook for Google Cloud Logging Log Sinks API.
43
+
44
+ :param gcp_conn_id: The connection ID to use when fetching connection info.
45
+ :param impersonation_chain: Optional service account to impersonate.
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ gcp_conn_id: str = "google_cloud_default",
51
+ impersonation_chain: str | Sequence[str] | None = None,
52
+ **kwargs,
53
+ ) -> None:
54
+ super().__init__(gcp_conn_id=gcp_conn_id, impersonation_chain=impersonation_chain, **kwargs)
55
+ self._client: ConfigServiceV2Client | None = None
56
+
57
+ def get_conn(self) -> ConfigServiceV2Client:
58
+ """Return the Google Cloud Logging Config client."""
59
+ if not self._client:
60
+ self._client = ConfigServiceV2Client(credentials=self.get_credentials(), client_info=CLIENT_INFO)
61
+ return self._client
62
+
63
+ def get_parent(self, project_id):
64
+ return f"projects/{project_id}"
65
+
66
+ @GoogleBaseHook.fallback_to_default_project_id
67
+ def create_sink(
68
+ self, sink: LogSink | dict, unique_writer_identity: bool = True, project_id: str = PROVIDE_PROJECT_ID
69
+ ) -> LogSink:
70
+ if isinstance(sink, dict):
71
+ sink = LogSink(**sink)
72
+ request = CreateSinkRequest(
73
+ parent=self.get_parent(project_id), sink=sink, unique_writer_identity=unique_writer_identity
74
+ )
75
+ return self.get_conn().create_sink(request=request)
76
+
77
+ @GoogleBaseHook.fallback_to_default_project_id
78
+ def get_sink(self, sink_name: str, project_id: str = PROVIDE_PROJECT_ID) -> LogSink:
79
+ request = GetSinkRequest(sink_name=f"projects/{project_id}/sinks/{sink_name}")
80
+ return self.get_conn().get_sink(request=request)
81
+
82
+ @GoogleBaseHook.fallback_to_default_project_id
83
+ def list_sinks(self, page_size: int | None = None, project_id: str = PROVIDE_PROJECT_ID) -> list[LogSink]:
84
+ request = ListSinksRequest(parent=self.get_parent(project_id), page_size=page_size)
85
+ return list(self.get_conn().list_sinks(request=request))
86
+
87
+ @GoogleBaseHook.fallback_to_default_project_id
88
+ def delete_sink(self, sink_name: str, project_id: str = PROVIDE_PROJECT_ID) -> None:
89
+ request = DeleteSinkRequest(sink_name=f"projects/{project_id}/sinks/{sink_name}")
90
+ self.get_conn().delete_sink(request=request)
91
+
92
+ @GoogleBaseHook.fallback_to_default_project_id
93
+ def update_sink(
94
+ self,
95
+ sink_name: str,
96
+ sink: LogSink | dict,
97
+ unique_writer_identity: bool,
98
+ update_mask: FieldMask | dict,
99
+ project_id: str = PROVIDE_PROJECT_ID,
100
+ ) -> LogSink:
101
+ if isinstance(sink, dict):
102
+ sink = LogSink(**sink)
103
+ request = UpdateSinkRequest(
104
+ sink_name=f"projects/{project_id}/sinks/{sink_name}",
105
+ sink=sink,
106
+ unique_writer_identity=unique_writer_identity,
107
+ update_mask=update_mask,
108
+ )
109
+ return self.get_conn().update_sink(request=request)
@@ -31,9 +31,6 @@ from __future__ import annotations
31
31
  from collections.abc import Sequence
32
32
  from typing import TYPE_CHECKING
33
33
 
34
- from airflow import version
35
- from airflow.exceptions import AirflowException
36
- from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID, GoogleBaseHook
37
34
  from google.api_core import path_template
38
35
  from google.api_core.exceptions import NotFound
39
36
  from google.api_core.gapic_v1.method import DEFAULT, _MethodDefault
@@ -47,6 +44,10 @@ from google.cloud.redis_v1 import (
47
44
  OutputConfig,
48
45
  )
49
46
 
47
+ from airflow import version
48
+ from airflow.exceptions import AirflowException
49
+ from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID, GoogleBaseHook
50
+
50
51
  if TYPE_CHECKING:
51
52
  from google.api_core.retry import Retry
52
53
  from google.protobuf.field_mask_pb2 import FieldMask
@@ -21,9 +21,6 @@ import itertools
21
21
  from collections.abc import Iterable, Sequence
22
22
  from typing import TYPE_CHECKING, Any
23
23
 
24
- from airflow.exceptions import AirflowException
25
- from airflow.providers.google.common.consts import CLIENT_INFO
26
- from airflow.providers.google.common.hooks.base_google import PROVIDE_PROJECT_ID, GoogleBaseHook
27
24
  from google.cloud.run_v2 import (
28
25
  CreateJobRequest,
29
26
  CreateServiceRequest,
@@ -41,7 +38,15 @@ from google.cloud.run_v2 import (
41
38
  ServicesClient,
42
39
  UpdateJobRequest,
43
40
  )
44
- from google.longrunning import operations_pb2 # type: ignore[attr-defined]
41
+ from google.longrunning import operations_pb2
42
+
43
+ from airflow.exceptions import AirflowException
44
+ from airflow.providers.google.common.consts import CLIENT_INFO
45
+ from airflow.providers.google.common.hooks.base_google import (
46
+ PROVIDE_PROJECT_ID,
47
+ GoogleBaseAsyncHook,
48
+ GoogleBaseHook,
49
+ )
45
50
 
46
51
  if TYPE_CHECKING:
47
52
  from google.api_core import operation
@@ -158,7 +163,7 @@ class CloudRunHook(GoogleBaseHook):
158
163
  return list(itertools.islice(jobs, limit))
159
164
 
160
165
 
161
- class CloudRunAsyncHook(GoogleBaseHook):
166
+ class CloudRunAsyncHook(GoogleBaseAsyncHook):
162
167
  """
163
168
  Async hook for the Google Cloud Run service.
164
169
 
@@ -173,6 +178,8 @@ class CloudRunAsyncHook(GoogleBaseHook):
173
178
  account from the list granting this role to the originating account.
174
179
  """
175
180
 
181
+ sync_hook_class = CloudRunHook
182
+
176
183
  def __init__(
177
184
  self,
178
185
  gcp_conn_id: str = "google_cloud_default",
@@ -182,16 +189,16 @@ class CloudRunAsyncHook(GoogleBaseHook):
182
189
  self._client: JobsAsyncClient | None = None
183
190
  super().__init__(gcp_conn_id=gcp_conn_id, impersonation_chain=impersonation_chain, **kwargs)
184
191
 
185
- def get_conn(self):
192
+ async def get_conn(self):
186
193
  if self._client is None:
187
- self._client = JobsAsyncClient(credentials=self.get_credentials(), client_info=CLIENT_INFO)
194
+ sync_hook = await self.get_sync_hook()
195
+ self._client = JobsAsyncClient(credentials=sync_hook.get_credentials(), client_info=CLIENT_INFO)
188
196
 
189
197
  return self._client
190
198
 
191
199
  async def get_operation(self, operation_name: str) -> operations_pb2.Operation:
192
- return await self.get_conn().get_operation(
193
- operations_pb2.GetOperationRequest(name=operation_name), timeout=120
194
- )
200
+ conn = await self.get_conn()
201
+ return await conn.get_operation(operations_pb2.GetOperationRequest(name=operation_name), timeout=120)
195
202
 
196
203
 
197
204
  class CloudRunServiceHook(GoogleBaseHook):
@@ -257,7 +264,7 @@ class CloudRunServiceHook(GoogleBaseHook):
257
264
  return operation.result()
258
265
 
259
266
 
260
- class CloudRunServiceAsyncHook(GoogleBaseHook):
267
+ class CloudRunServiceAsyncHook(GoogleBaseAsyncHook):
261
268
  """
262
269
  Async hook for the Google Cloud Run services.
263
270
 
@@ -272,6 +279,8 @@ class CloudRunServiceAsyncHook(GoogleBaseHook):
272
279
  account from the list granting this role to the originating account.
273
280
  """
274
281
 
282
+ sync_hook_class = CloudRunServiceHook
283
+
275
284
  def __init__(
276
285
  self,
277
286
  gcp_conn_id: str = "google_cloud_default",
@@ -38,7 +38,7 @@ from inspect import signature
38
38
  from pathlib import Path
39
39
  from subprocess import PIPE, Popen
40
40
  from tempfile import NamedTemporaryFile, _TemporaryFileWrapper, gettempdir
41
- from typing import TYPE_CHECKING, Any
41
+ from typing import TYPE_CHECKING, Any, cast
42
42
  from urllib.parse import quote_plus
43
43
 
44
44
  import httpx
@@ -50,8 +50,14 @@ from googleapiclient.errors import HttpError
50
50
  # Number of retries - used by googleapiclient method calls to perform retries
51
51
  # For requests that are "retriable"
52
52
  from airflow.exceptions import AirflowException
53
- from airflow.hooks.base import BaseHook
54
- from airflow.models import Connection
53
+ from airflow.providers.google.version_compat import AIRFLOW_V_3_1_PLUS
54
+
55
+ if AIRFLOW_V_3_1_PLUS:
56
+ from airflow.sdk import Connection
57
+ else:
58
+ from airflow.models import Connection # type: ignore[assignment,attr-defined,no-redef]
59
+
60
+ from airflow.providers.common.compat.sdk import BaseHook
55
61
  from airflow.providers.google.cloud.hooks.secret_manager import (
56
62
  GoogleCloudSecretManagerHook,
57
63
  )
@@ -64,10 +70,10 @@ from airflow.providers.google.common.hooks.base_google import (
64
70
  from airflow.utils.log.logging_mixin import LoggingMixin
65
71
 
66
72
  if TYPE_CHECKING:
73
+ from google.cloud.secretmanager_v1 import AccessSecretVersionResponse
67
74
  from requests import Session
68
75
 
69
76
  from airflow.providers.common.sql.hooks.sql import DbApiHook
70
- from google.cloud.secretmanager_v1 import AccessSecretVersionResponse
71
77
 
72
78
  UNIX_PATH_MAX = 108
73
79
 
@@ -502,7 +508,7 @@ class CloudSqlProxyRunner(LoggingMixin):
502
508
  :param project_id: Optional id of the Google Cloud project to connect to - it overwrites
503
509
  default project id taken from the Google Cloud connection.
504
510
  :param sql_proxy_version: Specific version of SQL proxy to download
505
- (for example 'v1.13'). By default latest version is downloaded.
511
+ (for example 'v1.13'). By default, latest version is downloaded.
506
512
  :param sql_proxy_binary_path: If specified, then proxy will be
507
513
  used from the path specified rather than dynamically generated. This means
508
514
  that if the binary is not present in that path it will also be downloaded.
@@ -634,35 +640,32 @@ class CloudSqlProxyRunner(LoggingMixin):
634
640
  self._download_sql_proxy_if_needed()
635
641
  if self.sql_proxy_process:
636
642
  raise AirflowException(f"The sql proxy is already running: {self.sql_proxy_process}")
637
- else:
638
- command_to_run = [self.sql_proxy_path]
639
- command_to_run.extend(self.command_line_parameters)
640
- self.log.info("Creating directory %s", self.cloud_sql_proxy_socket_directory)
641
- Path(self.cloud_sql_proxy_socket_directory).mkdir(parents=True, exist_ok=True)
642
- command_to_run.extend(self._get_credential_parameters())
643
- self.log.info("Running the command: `%s`", " ".join(command_to_run))
644
-
645
- self.sql_proxy_process = Popen(command_to_run, stdin=PIPE, stdout=PIPE, stderr=PIPE)
646
- self.log.info("The pid of cloud_sql_proxy: %s", self.sql_proxy_process.pid)
647
- while True:
648
- line = (
649
- self.sql_proxy_process.stderr.readline().decode("utf-8")
650
- if self.sql_proxy_process.stderr
651
- else ""
652
- )
653
- return_code = self.sql_proxy_process.poll()
654
- if line == "" and return_code is not None:
655
- self.sql_proxy_process = None
656
- raise AirflowException(
657
- f"The cloud_sql_proxy finished early with return code {return_code}!"
658
- )
659
- if line != "":
660
- self.log.info(line)
661
- if "googleapi: Error" in line or "invalid instance name:" in line:
662
- self.stop_proxy()
663
- raise AirflowException(f"Error when starting the cloud_sql_proxy {line}!")
664
- if "Ready for new connections" in line:
665
- return
643
+ command_to_run = [self.sql_proxy_path]
644
+ command_to_run.extend(self.command_line_parameters)
645
+ self.log.info("Creating directory %s", self.cloud_sql_proxy_socket_directory)
646
+ Path(self.cloud_sql_proxy_socket_directory).mkdir(parents=True, exist_ok=True)
647
+ command_to_run.extend(self._get_credential_parameters())
648
+ self.log.info("Running the command: `%s`", " ".join(command_to_run))
649
+
650
+ self.sql_proxy_process = Popen(command_to_run, stdin=PIPE, stdout=PIPE, stderr=PIPE)
651
+ self.log.info("The pid of cloud_sql_proxy: %s", self.sql_proxy_process.pid)
652
+ while True:
653
+ line = (
654
+ self.sql_proxy_process.stderr.readline().decode("utf-8")
655
+ if self.sql_proxy_process.stderr
656
+ else ""
657
+ )
658
+ return_code = self.sql_proxy_process.poll()
659
+ if line == "" and return_code is not None:
660
+ self.sql_proxy_process = None
661
+ raise AirflowException(f"The cloud_sql_proxy finished early with return code {return_code}!")
662
+ if line != "":
663
+ self.log.info(line)
664
+ if "googleapi: Error" in line or "invalid instance name:" in line:
665
+ self.stop_proxy()
666
+ raise AirflowException(f"Error when starting the cloud_sql_proxy {line}!")
667
+ if "Ready for new connections" in line:
668
+ return
666
669
 
667
670
  def stop_proxy(self) -> None:
668
671
  """
@@ -672,10 +675,9 @@ class CloudSqlProxyRunner(LoggingMixin):
672
675
  """
673
676
  if not self.sql_proxy_process:
674
677
  raise AirflowException("The sql proxy is not started yet")
675
- else:
676
- self.log.info("Stopping the cloud_sql_proxy pid: %s", self.sql_proxy_process.pid)
677
- self.sql_proxy_process.kill()
678
- self.sql_proxy_process = None
678
+ self.log.info("Stopping the cloud_sql_proxy pid: %s", self.sql_proxy_process.pid)
679
+ self.sql_proxy_process.kill()
680
+ self.sql_proxy_process = None
679
681
  # Cleanup!
680
682
  self.log.info("Removing the socket directory: %s", self.cloud_sql_proxy_socket_directory)
681
683
  shutil.rmtree(self.cloud_sql_proxy_socket_directory, ignore_errors=True)
@@ -691,7 +693,7 @@ class CloudSqlProxyRunner(LoggingMixin):
691
693
  self.log.info("Skipped removing proxy - it was not downloaded: %s", self.sql_proxy_path)
692
694
  if os.path.isfile(self.credentials_path):
693
695
  self.log.info("Removing generated credentials file %s", self.credentials_path)
694
- # Here file cannot be delete by concurrent task (each task has its own copy)
696
+ # Here file cannot be deleted by concurrent task (each task has its own copy)
695
697
  os.remove(self.credentials_path)
696
698
 
697
699
  def get_proxy_version(self) -> str | None:
@@ -704,8 +706,7 @@ class CloudSqlProxyRunner(LoggingMixin):
704
706
  matched = re.search("[Vv]ersion (.*?);", result)
705
707
  if matched:
706
708
  return matched.group(1)
707
- else:
708
- return None
709
+ return None
709
710
 
710
711
  def get_socket_path(self) -> str:
711
712
  """
@@ -852,8 +853,8 @@ class CloudSQLDatabaseHook(BaseHook):
852
853
  self.user = self._get_iam_db_login()
853
854
  self.password = self._generate_login_token(service_account=self.cloudsql_connection.login)
854
855
  else:
855
- self.user = self.cloudsql_connection.login
856
- self.password = self.cloudsql_connection.password
856
+ self.user = cast("str", self.cloudsql_connection.login)
857
+ self.password = cast("str", self.cloudsql_connection.password)
857
858
  self.public_ip = self.cloudsql_connection.host
858
859
  self.public_port = self.cloudsql_connection.port
859
860
  self.ssl_cert = ssl_cert
@@ -908,10 +909,9 @@ class CloudSQLDatabaseHook(BaseHook):
908
909
  secret_data = json.loads(base64.b64decode(secret.payload.data))
909
910
  if cert_name in secret_data:
910
911
  return secret_data[cert_name]
911
- else:
912
- raise AirflowException(
913
- "Invalid secret format. Expected dictionary with keys: `sslcert`, `sslkey`, `sslrootcert`"
914
- )
912
+ raise AirflowException(
913
+ "Invalid secret format. Expected dictionary with keys: `sslcert`, `sslkey`, `sslrootcert`"
914
+ )
915
915
 
916
916
  def _set_temporary_ssl_file(
917
917
  self, cert_name: str, cert_path: str | None = None, cert_value: str | None = None
@@ -1051,15 +1051,26 @@ class CloudSQLDatabaseHook(BaseHook):
1051
1051
  def _quote(value) -> str | None:
1052
1052
  return quote_plus(value) if value else None
1053
1053
 
1054
- def _generate_connection_uri(self) -> str:
1054
+ def _reserve_port(self):
1055
1055
  if self.use_proxy:
1056
1056
  if self.sql_proxy_use_tcp:
1057
1057
  if not self.sql_proxy_tcp_port:
1058
1058
  self.reserve_free_tcp_port()
1059
1059
  if not self.sql_proxy_unique_path:
1060
1060
  self.sql_proxy_unique_path = self._generate_unique_path()
1061
+
1062
+ def _generate_connection_uri(self) -> str:
1063
+ self._reserve_port()
1061
1064
  if not self.database_type:
1062
1065
  raise ValueError("The database_type should be set")
1066
+ if not self.user:
1067
+ raise AirflowException("The login parameter needs to be set in connection")
1068
+ if not self.public_ip:
1069
+ raise AirflowException("The location parameter needs to be set in connection")
1070
+ if not self.password:
1071
+ raise AirflowException("The password parameter needs to be set in connection")
1072
+ if not self.database:
1073
+ raise AirflowException("The database parameter needs to be set in connection")
1063
1074
 
1064
1075
  database_uris = CONNECTION_URIS[self.database_type]
1065
1076
  ssl_spec = None
@@ -1078,14 +1089,6 @@ class CloudSQLDatabaseHook(BaseHook):
1078
1089
  ssl_spec = {"cert": self.sslcert, "key": self.sslkey, "ca": self.sslrootcert}
1079
1090
  else:
1080
1091
  format_string = public_uris["non-ssl"]
1081
- if not self.user:
1082
- raise AirflowException("The login parameter needs to be set in connection")
1083
- if not self.public_ip:
1084
- raise AirflowException("The location parameter needs to be set in connection")
1085
- if not self.password:
1086
- raise AirflowException("The password parameter needs to be set in connection")
1087
- if not self.database:
1088
- raise AirflowException("The database parameter needs to be set in connection")
1089
1092
 
1090
1093
  connection_uri = format_string.format(
1091
1094
  user=quote_plus(self.user) if self.user else "",
@@ -1109,6 +1112,8 @@ class CloudSQLDatabaseHook(BaseHook):
1109
1112
  return connection_uri
1110
1113
 
1111
1114
  def _get_instance_socket_name(self) -> str:
1115
+ if self.project_id is None:
1116
+ raise ValueError("The project_id should not be none")
1112
1117
  return self.project_id + ":" + self.location + ":" + self.instance
1113
1118
 
1114
1119
  def _get_sqlproxy_instance_specification(self) -> str:
@@ -1117,6 +1122,69 @@ class CloudSQLDatabaseHook(BaseHook):
1117
1122
  instance_specification += f"=tcp:{self.sql_proxy_tcp_port}"
1118
1123
  return instance_specification
1119
1124
 
1125
+ def _generate_connection_parameters(self) -> dict:
1126
+ self._reserve_port()
1127
+ if not self.database_type:
1128
+ raise ValueError("The database_type should be set")
1129
+ if not self.user:
1130
+ raise AirflowException("The login parameter needs to be set in connection")
1131
+ if not self.public_ip:
1132
+ raise AirflowException("The location parameter needs to be set in connection")
1133
+ if not self.password:
1134
+ raise AirflowException("The password parameter needs to be set in connection")
1135
+ if not self.database:
1136
+ raise AirflowException("The database parameter needs to be set in connection")
1137
+
1138
+ connection_parameters = {}
1139
+
1140
+ connection_parameters["conn_type"] = self.database_type
1141
+ connection_parameters["login"] = self.user
1142
+ connection_parameters["password"] = self.password
1143
+ connection_parameters["schema"] = self.database
1144
+ connection_parameters["extra"] = {}
1145
+
1146
+ database_uris = CONNECTION_URIS[self.database_type]
1147
+ if self.use_proxy:
1148
+ proxy_uris = database_uris["proxy"]
1149
+ if self.sql_proxy_use_tcp:
1150
+ connection_parameters["host"] = "127.0.0.1"
1151
+ connection_parameters["port"] = self.sql_proxy_tcp_port
1152
+ else:
1153
+ socket_path = f"{self.sql_proxy_unique_path}/{self._get_instance_socket_name()}"
1154
+ if "localhost" in proxy_uris["socket"]:
1155
+ connection_parameters["host"] = "localhost"
1156
+ connection_parameters["extra"].update({"unix_socket": socket_path})
1157
+ else:
1158
+ connection_parameters["host"] = socket_path
1159
+ else:
1160
+ public_uris = database_uris["public"]
1161
+ if self.use_ssl:
1162
+ connection_parameters["host"] = self.public_ip
1163
+ connection_parameters["port"] = self.public_port
1164
+ if "ssl_spec" in public_uris["ssl"]:
1165
+ connection_parameters["extra"].update(
1166
+ {
1167
+ "ssl": json.dumps(
1168
+ {"cert": self.sslcert, "key": self.sslkey, "ca": self.sslrootcert}
1169
+ )
1170
+ }
1171
+ )
1172
+ else:
1173
+ connection_parameters["extra"].update(
1174
+ {
1175
+ "sslmode": "verify-ca",
1176
+ "sslcert": self.sslcert,
1177
+ "sslkey": self.sslkey,
1178
+ "sslrootcert": self.sslrootcert,
1179
+ }
1180
+ )
1181
+ else:
1182
+ connection_parameters["host"] = self.public_ip
1183
+ connection_parameters["port"] = self.public_port
1184
+ if connection_parameters.get("extra"):
1185
+ connection_parameters["extra"] = json.dumps(connection_parameters["extra"])
1186
+ return connection_parameters
1187
+
1120
1188
  def create_connection(self) -> Connection:
1121
1189
  """
1122
1190
  Create a connection.
@@ -1124,8 +1192,11 @@ class CloudSQLDatabaseHook(BaseHook):
1124
1192
  Connection ID will be randomly generated according to whether it uses
1125
1193
  proxy, TCP, UNIX sockets, SSL.
1126
1194
  """
1127
- uri = self._generate_connection_uri()
1128
- connection = Connection(conn_id=self.db_conn_id, uri=uri)
1195
+ if AIRFLOW_V_3_1_PLUS:
1196
+ kwargs = self._generate_connection_parameters()
1197
+ else:
1198
+ kwargs = {"uri": self._generate_connection_uri()}
1199
+ connection = Connection(conn_id=self.db_conn_id, **kwargs)
1129
1200
  self.log.info("Creating connection %s", self.db_conn_id)
1130
1201
  return connection
1131
1202
 
@@ -1141,6 +1212,8 @@ class CloudSQLDatabaseHook(BaseHook):
1141
1212
  raise ValueError("Proxy runner can only be retrieved in case of use_proxy = True")
1142
1213
  if not self.sql_proxy_unique_path:
1143
1214
  raise ValueError("The sql_proxy_unique_path should be set")
1215
+ if self.project_id is None:
1216
+ raise ValueError("The project_id should not be None")
1144
1217
  return CloudSqlProxyRunner(
1145
1218
  path_prefix=self.sql_proxy_unique_path,
1146
1219
  instance_specification=self._get_sqlproxy_instance_specification(),
@@ -1177,9 +1250,9 @@ class CloudSQLDatabaseHook(BaseHook):
1177
1250
  raise ValueError("The db_hook should be set")
1178
1251
  if not isinstance(self.db_hook, PostgresHook):
1179
1252
  raise ValueError(f"The db_hook should be PostgresHook and is {type(self.db_hook)}")
1180
- conn = getattr(self.db_hook, "conn")
1181
- if conn and conn.notices:
1182
- for output in self.db_hook.conn.notices:
1253
+ conn = getattr(self.db_hook, "conn", None)
1254
+ if conn and hasattr(conn, "notices") and conn.notices:
1255
+ for output in conn.notices:
1183
1256
  self.log.info(output)
1184
1257
 
1185
1258
  def reserve_free_tcp_port(self) -> None:
@@ -1205,8 +1278,7 @@ class CloudSQLDatabaseHook(BaseHook):
1205
1278
 
1206
1279
  if self.database_type == "postgres":
1207
1280
  return self.cloudsql_connection.login.split(".gserviceaccount.com")[0]
1208
- else:
1209
- return self.cloudsql_connection.login.split("@")[0]
1281
+ return self.cloudsql_connection.login.split("@")[0]
1210
1282
 
1211
1283
  def _generate_login_token(self, service_account) -> str:
1212
1284
  """Generate an IAM login token for Cloud SQL and return the token."""
@@ -1215,7 +1287,7 @@ class CloudSQLDatabaseHook(BaseHook):
1215
1287
  cloud_sql_hook = CloudSQLHook(api_version="v1", gcp_conn_id=self.gcp_conn_id)
1216
1288
 
1217
1289
  with cloud_sql_hook.provide_authorized_gcloud():
1218
- proc = subprocess.run(cmd, capture_output=True)
1290
+ proc = subprocess.run(cmd, check=False, capture_output=True)
1219
1291
 
1220
1292
  if proc.returncode != 0:
1221
1293
  stderr_last_20_lines = "\n".join(proc.stderr.decode().strip().splitlines()[-20:])
@@ -36,6 +36,14 @@ from copy import deepcopy
36
36
  from datetime import timedelta
37
37
  from typing import TYPE_CHECKING, Any
38
38
 
39
+ from google.cloud.storage_transfer_v1 import (
40
+ ListTransferJobsRequest,
41
+ RunTransferJobRequest,
42
+ StorageTransferServiceAsyncClient,
43
+ TransferJob,
44
+ TransferOperation,
45
+ )
46
+ from google.protobuf.json_format import MessageToDict
39
47
  from googleapiclient.discovery import Resource, build
40
48
  from googleapiclient.errors import HttpError
41
49
 
@@ -46,21 +54,14 @@ from airflow.providers.google.common.hooks.base_google import (
46
54
  GoogleBaseAsyncHook,
47
55
  GoogleBaseHook,
48
56
  )
49
- from google.api_core import protobuf_helpers
50
- from google.cloud.storage_transfer_v1 import (
51
- ListTransferJobsRequest,
52
- StorageTransferServiceAsyncClient,
53
- TransferJob,
54
- TransferOperation,
55
- )
56
57
 
57
58
  if TYPE_CHECKING:
58
- from proto import Message
59
-
59
+ from google.api_core import operation_async
60
60
  from google.cloud.storage_transfer_v1.services.storage_transfer_service.pagers import (
61
61
  ListTransferJobsAsyncPager,
62
62
  )
63
- from google.longrunning import operations_pb2 # type: ignore[attr-defined]
63
+ from google.longrunning import operations_pb2
64
+ from proto import Message
64
65
 
65
66
  log = logging.getLogger(__name__)
66
67
 
@@ -219,7 +220,7 @@ class CloudDataTransferServiceHook(GoogleBaseHook):
219
220
  return (
220
221
  self.get_conn().transferJobs().create(body=body).execute(num_retries=self.num_retries)
221
222
  )
222
- elif transfer_job.get(STATUS) == GcpTransferJobsStatus.DISABLED:
223
+ if transfer_job.get(STATUS) == GcpTransferJobsStatus.DISABLED:
223
224
  return self.enable_transfer_job(job_name=job_name, project_id=body.get(PROJECT_ID))
224
225
  else:
225
226
  raise e
@@ -604,7 +605,7 @@ class CloudDataTransferServiceAsyncHook(GoogleBaseAsyncHook):
604
605
  self,
605
606
  request_filter: dict | None = None,
606
607
  **kwargs,
607
- ) -> list[TransferOperation]:
608
+ ) -> list[dict[str, Any]]:
608
609
  """
609
610
  Get a transfer operation in Google Storage Transfer Service.
610
611
 
@@ -661,7 +662,12 @@ class CloudDataTransferServiceAsyncHook(GoogleBaseAsyncHook):
661
662
  )
662
663
 
663
664
  transfer_operations = [
664
- protobuf_helpers.from_any_pb(TransferOperation, op.metadata) for op in operations
665
+ MessageToDict(
666
+ getattr(op, "_pb", op),
667
+ preserving_proto_field_name=True,
668
+ use_integers_for_enums=True,
669
+ )
670
+ for op in operations
665
671
  ]
666
672
 
667
673
  return transfer_operations
@@ -678,7 +684,7 @@ class CloudDataTransferServiceAsyncHook(GoogleBaseAsyncHook):
678
684
 
679
685
  @staticmethod
680
686
  async def operations_contain_expected_statuses(
681
- operations: list[TransferOperation], expected_statuses: set[str] | str
687
+ operations: list[dict[str, Any]], expected_statuses: set[str] | str
682
688
  ) -> bool:
683
689
  """
684
690
  Check whether an operation exists with the expected status.
@@ -697,7 +703,7 @@ class CloudDataTransferServiceAsyncHook(GoogleBaseAsyncHook):
697
703
  if not operations:
698
704
  return False
699
705
 
700
- current_statuses = {operation.status.name for operation in operations}
706
+ current_statuses = {TransferOperation.Status(op["metadata"]["status"]).name for op in operations}
701
707
 
702
708
  if len(current_statuses - expected_statuses_set) != len(current_statuses):
703
709
  return True
@@ -708,3 +714,17 @@ class CloudDataTransferServiceAsyncHook(GoogleBaseAsyncHook):
708
714
  f"Expected: {', '.join(expected_statuses_set)}"
709
715
  )
710
716
  return False
717
+
718
+ async def run_transfer_job(self, job_name: str) -> operation_async.AsyncOperation:
719
+ """
720
+ Run Google Storage Transfer Service job.
721
+
722
+ :param job_name: (Required) Name of the job to run.
723
+ """
724
+ client = await self.get_conn()
725
+ request = RunTransferJobRequest(
726
+ job_name=job_name,
727
+ project_id=self.project_id,
728
+ )
729
+ operation = await client.run_transfer_job(request=request)
730
+ return operation