@@ -72,6 +72,21 @@ class TryNumberPlugin(AirflowPlugin):
7272 operator_extra_links = [TryNumberLink ()]
7373
7474
75+ class XComModelLink (BaseOperatorLink ):
76+ name = "XCom Model"
77+ operators = [CustomOperator ]
78+
79+ def get_link (self , operator , * , ti_key ):
80+ from airflow .providers .common .compat .sdk import XComModel
81+
82+ return XComModel .get_value (key = "plugin_link" , ti_key = ti_key ) or ""
83+
84+
85+ class XComModelPlugin (AirflowPlugin ):
86+ name = "xcom_model_plugin"
87+ operator_extra_links = [XComModelLink ()]
88+
89+
7590@pytest .mark .mock_plugin_manager (plugins = [])
7691class TestGetExtraLinks :
7792 dag_id = "TEST_DAG_ID"
@@ -265,6 +280,23 @@ def test_should_respond_200_support_plugins(self, test_client):
265280 ).model_dump ()
266281 )
267282
283+ @pytest .mark .mock_plugin_manager (plugins = [XComModelPlugin ])
284+ def test_plugin_link_can_read_xcom_with_xcommodel (self , test_client ):
285+ XCom .set (
286+ key = "plugin_link" ,
287+ value = "https://example.com/plugin-link" ,
288+ task_id = self .task_single_link ,
289+ dag_id = self .dag_id ,
290+ run_id = self .dag_run_id ,
291+ )
292+
293+ response = test_client .get (
294+ f"/dags/{ self .dag_id } /dagRuns/{ self .dag_run_id } /taskInstances/{ self .task_single_link } /links" ,
295+ )
296+
297+ assert response .status_code == 200
298+ assert response .json ()["extra_links" ]["XCom Model" ] == "https://example.com/plugin-link"
299+
268300 def test_should_respond_200_mapped_task_instance (self , test_client , session ):
269301 for map_index , value in enumerate (["TEST_LINK_VALUE_1" , "TEST_LINK_VALUE_2" ]):
270302 XCom .set (
0 commit comments