This is an automated email from the ASF dual-hosted git repository.
jason810496 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 43038e01182 Fix Azure Data Factory hook broken with
azure-mgmt-datafactory 10 (#69800)
43038e01182 is described below
commit 43038e011829eae8082f25a233dade5deef0c145
Author: PoAn Yang <[email protected]>
AuthorDate: Wed Jul 15 20:26:26 2026 +0900
Fix Azure Data Factory hook broken with azure-mgmt-datafactory 10 (#69800)
* Fix Azure Data Factory hook broken by azure-mgmt-datafactory 10
Signed-off-by: PoAn Yang <[email protected]>
* Fix Azure Data Factory hook broken by azure-mgmt-datafactory 10
Signed-off-by: PoAn Yang <[email protected]>
---------
Signed-off-by: PoAn Yang <[email protected]>
---
providers/microsoft/azure/README.rst | 2 +-
providers/microsoft/azure/docs/index.rst | 2 +-
providers/microsoft/azure/pyproject.toml | 2 +-
.../microsoft/azure/hooks/data_factory.py | 46 ++++++++++++--
.../microsoft/azure/hooks/test_data_factory.py | 73 ++++++++++++++++------
uv.lock | 8 +--
6 files changed, 102 insertions(+), 31 deletions(-)
diff --git a/providers/microsoft/azure/README.rst
b/providers/microsoft/azure/README.rst
index 63f0e97e181..be404f636ee 100644
--- a/providers/microsoft/azure/README.rst
+++ b/providers/microsoft/azure/README.rst
@@ -74,7 +74,7 @@ PIP package Version required
``azure-synapse-artifacts`` ``>=0.17.0``
``azure-storage-file-datalake`` ``>=12.9.1``
``azure-kusto-data`` ``>=4.1.0,!=5.0.0``
-``azure-mgmt-datafactory`` ``>=2.0.0``
+``azure-mgmt-datafactory`` ``>=10.0.0``
``azure-mgmt-containerregistry`` ``>=8.0.0``
``azure-mgmt-compute`` ``>=33.0.0``
``azure-mgmt-containerinstance`` ``>=10.1.0``
diff --git a/providers/microsoft/azure/docs/index.rst
b/providers/microsoft/azure/docs/index.rst
index 0ad9ca9512f..b326188e24e 100644
--- a/providers/microsoft/azure/docs/index.rst
+++ b/providers/microsoft/azure/docs/index.rst
@@ -128,7 +128,7 @@ PIP package Version required
``azure-synapse-artifacts`` ``>=0.17.0``
``azure-storage-file-datalake`` ``>=12.9.1``
``azure-kusto-data`` ``>=4.1.0,!=5.0.0``
-``azure-mgmt-datafactory`` ``>=2.0.0``
+``azure-mgmt-datafactory`` ``>=10.0.0``
``azure-mgmt-containerregistry`` ``>=8.0.0``
``azure-mgmt-compute`` ``>=33.0.0``
``azure-mgmt-containerinstance`` ``>=10.1.0``
diff --git a/providers/microsoft/azure/pyproject.toml
b/providers/microsoft/azure/pyproject.toml
index 9b2d117593b..91327c2c74d 100644
--- a/providers/microsoft/azure/pyproject.toml
+++ b/providers/microsoft/azure/pyproject.toml
@@ -84,7 +84,7 @@ dependencies = [
"azure-storage-file-datalake>=12.9.1",
# azure-kusto-data 5.0.0 pins requests to a specific version which makes
resolving dependencies harder
"azure-kusto-data>=4.1.0,!=5.0.0",
- "azure-mgmt-datafactory>=2.0.0",
+ "azure-mgmt-datafactory>=10.0.0",
"azure-mgmt-containerregistry>=8.0.0",
"azure-mgmt-compute>=33.0.0",
"azure-mgmt-containerinstance>=10.1.0",
diff --git
a/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/data_factory.py
b/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/data_factory.py
index dd408bed515..61ae3c8848b 100644
---
a/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/data_factory.py
+++
b/providers/microsoft/azure/src/airflow/providers/microsoft/azure/hooks/data_factory.py
@@ -39,6 +39,7 @@ from collections.abc import Callable
from functools import wraps
from typing import IO, TYPE_CHECKING, Any, TypeVar, cast
+from azure.core import MatchConditions
from azure.identity import ClientSecretCredential, DefaultAzureCredential
from azure.identity.aio import (
ClientSecretCredential as AsyncClientSecretCredential,
@@ -278,7 +279,12 @@ class AzureDataFactoryHook(BaseHook):
raise AirflowException(f"Factory {factory!r} does not exist.")
return self.get_conn().factories.create_or_update(
- resource_group_name, factory_name, factory, if_match, **config
+ resource_group_name,
+ factory_name,
+ factory,
+ etag=if_match,
+ match_condition=MatchConditions.IfNotModified if if_match else
None,
+ **config,
)
@provide_targeted_factory
@@ -339,7 +345,12 @@ class AzureDataFactoryHook(BaseHook):
:return: The linked service.
"""
return self.get_conn().linked_services.get(
- resource_group_name, factory_name, linked_service_name,
if_none_match, **config
+ resource_group_name,
+ factory_name,
+ linked_service_name,
+ etag=if_none_match,
+ match_condition=MatchConditions.IfModified if if_none_match else
None,
+ **config,
)
def _linked_service_exists(self, resource_group_name, factory_name,
linked_service_name) -> bool:
@@ -549,7 +560,12 @@ class AzureDataFactoryHook(BaseHook):
:return: The DataFlowResource.
"""
return self.get_conn().data_flows.get(
- resource_group_name, factory_name, dataflow_name, if_none_match,
**config
+ resource_group_name,
+ factory_name,
+ dataflow_name,
+ etag=if_none_match,
+ match_condition=MatchConditions.IfModified if if_none_match else
None,
+ **config,
)
def _dataflow_exists(
@@ -597,7 +613,13 @@ class AzureDataFactoryHook(BaseHook):
raise AirflowException(f"Dataflow {dataflow_name!r} does not
exist.")
return self.get_conn().data_flows.create_or_update(
- resource_group_name, factory_name, dataflow_name, dataflow,
if_match, **config
+ resource_group_name,
+ factory_name,
+ dataflow_name,
+ dataflow,
+ etag=if_match,
+ match_condition=MatchConditions.IfNotModified if if_match else
None,
+ **config,
)
@provide_targeted_factory
@@ -627,7 +649,13 @@ class AzureDataFactoryHook(BaseHook):
raise AirflowException(f"Dataflow {dataflow_name!r} already
exists.")
return self.get_conn().data_flows.create_or_update(
- resource_group_name, factory_name, dataflow_name, dataflow,
if_match, **config
+ resource_group_name,
+ factory_name,
+ dataflow_name,
+ dataflow,
+ etag=if_match,
+ match_condition=MatchConditions.IfNotModified if if_match else
None,
+ **config,
)
@provide_targeted_factory
@@ -923,7 +951,13 @@ class AzureDataFactoryHook(BaseHook):
raise AirflowException(f"Trigger {trigger_name!r} does not exist.")
return self.get_conn().triggers.create_or_update(
- resource_group_name, factory_name, trigger_name, trigger,
if_match, **config
+ resource_group_name,
+ factory_name,
+ trigger_name,
+ trigger,
+ etag=if_match,
+ match_condition=MatchConditions.IfNotModified if if_match else
None,
+ **config,
)
@provide_targeted_factory
diff --git
a/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_data_factory.py
b/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_data_factory.py
index 92608f6101a..1b64d29c3ce 100644
---
a/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_data_factory.py
+++
b/providers/microsoft/azure/tests/unit/microsoft/azure/hooks/test_data_factory.py
@@ -21,6 +21,7 @@ from unittest import mock
from unittest.mock import MagicMock, PropertyMock, patch
import pytest
+from azure.core import MatchConditions
from azure.mgmt.datafactory.aio import DataFactoryManagementClient
from airflow.models.connection import Connection
@@ -242,12 +243,18 @@ def test_create_factory(hook: AzureDataFactoryHook):
hook._conn.factories.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, MODEL)
-def test_update_factory(hook: AzureDataFactoryHook):
[email protected](
+ ("if_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfNotModified)],
+)
+def test_update_factory(hook: AzureDataFactoryHook, if_match,
expected_match_condition):
with patch.object(hook, "_factory_exists") as mock_factory_exists:
mock_factory_exists.return_value = True
- hook.update_factory(MODEL, RESOURCE_GROUP, FACTORY)
+ hook.update_factory(MODEL, RESOURCE_GROUP, FACTORY, if_match)
- hook._conn.factories.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, MODEL, None)
+ hook._conn.factories.create_or_update.assert_called_with(
+ RESOURCE_GROUP, FACTORY, MODEL, etag=if_match,
match_condition=expected_match_condition
+ )
def test_update_factory_non_existent(hook: AzureDataFactoryHook):
@@ -264,10 +271,16 @@ def test_delete_factory(hook: AzureDataFactoryHook):
hook._conn.factories.delete.assert_called_with(RESOURCE_GROUP, FACTORY)
-def test_get_linked_service(hook: AzureDataFactoryHook):
- hook.get_linked_service(NAME, RESOURCE_GROUP, FACTORY)
[email protected](
+ ("if_none_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfModified)],
+)
+def test_get_linked_service(hook: AzureDataFactoryHook, if_none_match,
expected_match_condition):
+ hook.get_linked_service(NAME, RESOURCE_GROUP, FACTORY, if_none_match)
- hook._conn.linked_services.get.assert_called_with(RESOURCE_GROUP, FACTORY,
NAME, None)
+ hook._conn.linked_services.get.assert_called_with(
+ RESOURCE_GROUP, FACTORY, NAME, etag=if_none_match,
match_condition=expected_match_condition
+ )
def test_create_linked_service(hook: AzureDataFactoryHook):
@@ -332,24 +345,42 @@ def test_delete_dataset(hook: AzureDataFactoryHook):
hook._conn.datasets.delete.assert_called_with(RESOURCE_GROUP, FACTORY,
NAME)
-def test_get_dataflow(hook: AzureDataFactoryHook):
- hook.get_dataflow(NAME, RESOURCE_GROUP, FACTORY)
[email protected](
+ ("if_none_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfModified)],
+)
+def test_get_dataflow(hook: AzureDataFactoryHook, if_none_match,
expected_match_condition):
+ hook.get_dataflow(NAME, RESOURCE_GROUP, FACTORY, if_none_match)
- hook._conn.data_flows.get.assert_called_with(RESOURCE_GROUP, FACTORY,
NAME, None)
+ hook._conn.data_flows.get.assert_called_with(
+ RESOURCE_GROUP, FACTORY, NAME, etag=if_none_match,
match_condition=expected_match_condition
+ )
-def test_create_dataflow(hook: AzureDataFactoryHook):
- hook.create_dataflow(NAME, MODEL, RESOURCE_GROUP, FACTORY)
[email protected](
+ ("if_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfNotModified)],
+)
+def test_create_dataflow(hook: AzureDataFactoryHook, if_match,
expected_match_condition):
+ hook.create_dataflow(NAME, MODEL, RESOURCE_GROUP, FACTORY, if_match)
- hook._conn.data_flows.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, NAME, MODEL, None)
+ hook._conn.data_flows.create_or_update.assert_called_with(
+ RESOURCE_GROUP, FACTORY, NAME, MODEL, etag=if_match,
match_condition=expected_match_condition
+ )
-def test_update_dataflow(hook: AzureDataFactoryHook):
[email protected](
+ ("if_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfNotModified)],
+)
+def test_update_dataflow(hook: AzureDataFactoryHook, if_match,
expected_match_condition):
with patch.object(hook, "_dataflow_exists") as mock_dataflow_exists:
mock_dataflow_exists.return_value = True
- hook.update_dataflow(NAME, MODEL, RESOURCE_GROUP, FACTORY)
+ hook.update_dataflow(NAME, MODEL, RESOURCE_GROUP, FACTORY, if_match)
- hook._conn.data_flows.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, NAME, MODEL, None)
+ hook._conn.data_flows.create_or_update.assert_called_with(
+ RESOURCE_GROUP, FACTORY, NAME, MODEL, etag=if_match,
match_condition=expected_match_condition
+ )
def test_update_dataflow_non_existent(hook: AzureDataFactoryHook):
@@ -473,12 +504,18 @@ def test_create_trigger(hook: AzureDataFactoryHook):
hook._conn.triggers.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, NAME, MODEL)
-def test_update_trigger(hook: AzureDataFactoryHook):
[email protected](
+ ("if_match", "expected_match_condition"),
+ [(None, None), ("etag-value", MatchConditions.IfNotModified)],
+)
+def test_update_trigger(hook: AzureDataFactoryHook, if_match,
expected_match_condition):
with patch.object(hook, "_trigger_exists") as mock_trigger_exists:
mock_trigger_exists.return_value = True
- hook.update_trigger(NAME, MODEL, RESOURCE_GROUP, FACTORY)
+ hook.update_trigger(NAME, MODEL, RESOURCE_GROUP, FACTORY, if_match)
- hook._conn.triggers.create_or_update.assert_called_with(RESOURCE_GROUP,
FACTORY, NAME, MODEL, None)
+ hook._conn.triggers.create_or_update.assert_called_with(
+ RESOURCE_GROUP, FACTORY, NAME, MODEL, etag=if_match,
match_condition=expected_match_condition
+ )
def test_update_trigger_non_existent(hook: AzureDataFactoryHook):
diff --git a/uv.lock b/uv.lock
index aeb4d3f9de6..b5e8f242a27 100644
--- a/uv.lock
+++ b/uv.lock
@@ -6364,7 +6364,7 @@ requires-dist = [
{ name = "azure-mgmt-containerinstance", specifier = ">=10.1.0" },
{ name = "azure-mgmt-containerregistry", specifier = ">=8.0.0" },
{ name = "azure-mgmt-cosmosdb", specifier = ">=3.0.0" },
- { name = "azure-mgmt-datafactory", specifier = ">=2.0.0" },
+ { name = "azure-mgmt-datafactory", specifier = ">=10.0.0" },
{ name = "azure-mgmt-datalake-store", specifier = ">=0.5.0" },
{ name = "azure-mgmt-resource", specifier = ">=2.2.0" },
{ name = "azure-mgmt-storage", specifier = ">=16.0.0" },
@@ -9726,16 +9726,16 @@ wheels = [
[[package]]
name = "azure-mgmt-datafactory"
-version = "9.3.0"
+version = "10.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "azure-mgmt-core" },
{ name = "isodate" },
{ name = "typing-extensions" },
]
-sdist = { url =
"https://files.pythonhosted.org/packages/e8/5e/c28a8a50885185a9a06ff659594fec24f1a840622f21883b376358dd1188/azure_mgmt_datafactory-9.3.0.tar.gz",
hash =
"sha256:f5fdd5cd416f0ed71dfedf05dc7677b8f0e52f3428fd5b17b04c9200dd8d36b3", size
= 494270, upload-time = "2026-03-11T07:52:35.206Z" }
+sdist = { url =
"https://files.pythonhosted.org/packages/56/f9/81397c6afd0fed926ffe3093d108d0392745bddc05c5dfc69880dfb70bb2/azure_mgmt_datafactory-10.0.0.tar.gz",
hash =
"sha256:c83eeb2b092c1f1e5bb2499ffbba6fd7809a1274739930f9db1e336273405587", size
= 717334, upload-time = "2026-07-08T09:40:00.573Z" }
wheels = [
- { url =
"https://files.pythonhosted.org/packages/33/35/ea6a784652119a7373a730d0eb87d5c0b7d153721d7e23c8e9b76547aca4/azure_mgmt_datafactory-9.3.0-py3-none-any.whl",
hash =
"sha256:fddb855a27e3f7b78328f184df146d71e433e1dfb9cc4923ea503c53813f0504", size
= 570055, upload-time = "2026-03-11T07:52:37.337Z" },
+ { url =
"https://files.pythonhosted.org/packages/bb/41/b5a74df49ed17aa27361fc55fd88a07bfe7c7a55bcb5ed210c0e0e712d57/azure_mgmt_datafactory-10.0.0-py3-none-any.whl",
hash =
"sha256:d0e93194c9e1cd820d2ed4f92813902af89cf4421c02e72a729ae32d87171198", size
= 601269, upload-time = "2026-07-08T09:40:02.559Z" },
]
[[package]]