2020from collections .abc import Sequence
2121from typing import TYPE_CHECKING , Any
2222
23+ from airflow .configuration import conf
2324from airflow .providers .common .compat .sdk import AirflowException , BaseOperator , BaseOperatorLink
2425from airflow .providers .microsoft .azure .hooks .powerbi import PowerBIHook
2526from airflow .providers .microsoft .azure .triggers .powerbi import (
@@ -61,10 +62,12 @@ class PowerBIDatasetRefreshOperator(BaseOperator):
6162 :param dataset_id: The dataset id.
6263 :param group_id: The workspace id.
6364 :param conn_id: Airflow Connection ID that contains the connection information for the Power BI account used for authentication.
64- :param timeout: Time in seconds to wait for a dataset to reach a terminal status for asynchronous waits. Used only if ``wait_for_termination `` is True.
65+ :param timeout: Time in seconds to wait for a dataset to reach a terminal status for asynchronous waits. Used only if ``wait_for_completion `` is True.
6566 :param check_interval: Number of seconds to wait before rechecking the
6667 refresh status.
6768 :param request_body: Additional arguments to pass to the request body, as described in https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/refresh-dataset-in-group#request-body.
69+ :param wait_for_completion: If True, wait for the dataset refresh to complete. If False, trigger the refresh and return immediately without waiting.
70+ :param deferrable: This parameter is deprecated and no longer has any effect. The operator now always uses deferrable execution when ``wait_for_completion=True``.
6871 """
6972
7073 template_fields : Sequence [str ] = (
@@ -86,13 +89,19 @@ def __init__(
8689 api_version : APIVersion | str | None = None ,
8790 check_interval : int = 60 ,
8891 request_body : dict [str , Any ] | None = None ,
92+ wait_for_completion : bool = True ,
93+ deferrable : bool = conf .getboolean ("operators" , "default_deferrable" , fallback = False ),
8994 ** kwargs ,
9095 ) -> None :
9196 super ().__init__ (** kwargs )
97+ if "deferrable" in kwargs or deferrable is not True :
98+ self .log .warning (
99+ "The PowerBIDatasetRefreshOperator now always uses deferrable execution when wait_for_completion=True."
100+ )
92101 self .hook = PowerBIHook (conn_id = conn_id , proxies = proxies , api_version = api_version , timeout = timeout )
93102 self .dataset_id = dataset_id
94103 self .group_id = group_id
95- self .wait_for_termination = True
104+ self .wait_for_completion = wait_for_completion
96105 self .conn_id = conn_id
97106 self .timeout = timeout
98107 self .check_interval = check_interval
@@ -108,63 +117,76 @@ def api_version(self) -> str | None:
108117
109118 def execute (self , context : Context ):
110119 """Refresh the Power BI Dataset."""
111- if self .wait_for_termination :
112- self .defer (
113- trigger = PowerBITrigger (
114- conn_id = self .conn_id ,
115- group_id = self .group_id ,
116- dataset_id = self .dataset_id ,
117- timeout = self .timeout ,
118- proxies = self .proxies ,
119- api_version = self .api_version ,
120- check_interval = self .check_interval ,
121- wait_for_termination = self .wait_for_termination ,
122- request_body = self .request_body ,
123- ),
124- method_name = self .get_refresh_status .__name__ ,
120+ if not self .wait_for_completion :
121+ # Fire and forget - synchronous execution, no deferral
122+ hook = PowerBIHook (
123+ conn_id = self .conn_id , proxies = self .proxies , api_version = self .api_version , timeout = self .timeout
125124 )
126125
127- def get_refresh_status ( self , context : Context , event : dict [ str , str ] | None = None ):
128- """Push the refresh Id to XCom then runs the Trigger to wait for refresh completion."""
129- if event :
130- if event [ "status" ] == "error" :
131- raise AirflowException ( event [ "message" ] )
126+ dataset_refresh_id = hook . trigger_dataset_refresh (
127+ dataset_id = self . dataset_id ,
128+ group_id = self . group_id ,
129+ request_body = self . request_body ,
130+ )
132131
133- dataset_refresh_id = event ["dataset_refresh_id" ]
132+ if dataset_refresh_id :
133+ self .log .info ("Triggered dataset refresh %s (fire-and-forget)" , dataset_refresh_id )
134+ context ["ti" ].xcom_push (
135+ key = f"{ self .task_id } .powerbi_dataset_refresh_id" ,
136+ value = dataset_refresh_id ,
137+ )
138+ else :
139+ raise AirflowException ("Failed to trigger dataset refresh" )
140+ return
141+
142+ # Wait for termination - use deferrable trigger
143+ self .defer (
144+ trigger = PowerBITrigger (
145+ conn_id = self .conn_id ,
146+ group_id = self .group_id ,
147+ dataset_id = self .dataset_id ,
148+ timeout = self .timeout ,
149+ proxies = self .proxies ,
150+ api_version = self .api_version ,
151+ check_interval = self .check_interval ,
152+ wait_for_termination = self .wait_for_completion ,
153+ request_body = self .request_body ,
154+ ),
155+ method_name = self .execute_complete .__name__ ,
156+ )
157+
158+ def execute_complete (self , context : Context , event : dict [str , str ]) -> None :
159+ """
160+ Handle trigger completion and push results to XCom or raise an exception.
161+
162+ :param context: Airflow context dictionary
163+ :param event: Event dict from trigger with status and dataset_refresh_id
164+ """
165+ if not event :
166+ return
167+
168+ # Success - push both ID and status to XCom
169+ dataset_refresh_id = event .get ("dataset_refresh_id" )
170+ dataset_refresh_status = event .get ("dataset_refresh_status" )
134171
135172 if dataset_refresh_id :
136173 context ["ti" ].xcom_push (
137- key = f"{ self .task_id } .powerbi_dataset_refresh_Id " ,
174+ key = f"{ self .task_id } .powerbi_dataset_refresh_id " ,
138175 value = dataset_refresh_id ,
139176 )
140- self .defer (
141- trigger = PowerBITrigger (
142- conn_id = self .conn_id ,
143- group_id = self .group_id ,
144- dataset_id = self .dataset_id ,
145- dataset_refresh_id = dataset_refresh_id ,
146- timeout = self .timeout ,
147- proxies = self .proxies ,
148- api_version = self .api_version ,
149- check_interval = self .check_interval ,
150- wait_for_termination = self .wait_for_termination ,
151- ),
152- method_name = self .execute_complete .__name__ ,
153- )
154-
155- def execute_complete (self , context : Context , event : dict [str , str ]) -> Any :
156- """
157- Return immediately - callback for when the trigger fires.
158177
159- Relies on trigger to throw an exception, otherwise it assumes execution was successful.
160- """
161- if event :
178+ if dataset_refresh_status :
162179 context ["ti" ].xcom_push (
163180 key = f"{ self .task_id } .powerbi_dataset_refresh_status" ,
164- value = event [ " dataset_refresh_status" ] ,
181+ value = dataset_refresh_status ,
165182 )
166- if event ["status" ] == "error" :
167- raise AirflowException (event ["message" ])
183+
184+ if event ["status" ] == "error" :
185+ raise AirflowException (event ["message" ])
186+
187+ self .log .info (
188+ "Dataset refresh %s completed with status: %s" , dataset_refresh_id , dataset_refresh_status
189+ )
168190
169191
170192class PowerBIWorkspaceListOperator (BaseOperator ):
0 commit comments