apache-airflow-providers-microsoft-azure 10.0.0rc1__py3-none-any.whl → 10.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.
@@ -0,0 +1,292 @@
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 copy import deepcopy
21
+ from typing import (
22
+ TYPE_CHECKING,
23
+ Any,
24
+ Callable,
25
+ Sequence,
26
+ )
27
+
28
+ from airflow.exceptions import AirflowException, TaskDeferred
29
+ from airflow.models import BaseOperator
30
+ from airflow.providers.microsoft.azure.hooks.msgraph import KiotaRequestAdapterHook
31
+ from airflow.providers.microsoft.azure.triggers.msgraph import (
32
+ MSGraphTrigger,
33
+ ResponseSerializer,
34
+ )
35
+ from airflow.utils.xcom import XCOM_RETURN_KEY
36
+
37
+ if TYPE_CHECKING:
38
+ from io import BytesIO
39
+
40
+ from kiota_abstractions.request_adapter import ResponseType
41
+ from kiota_abstractions.request_information import QueryParams
42
+ from kiota_abstractions.response_handler import NativeResponseType
43
+ from kiota_abstractions.serialization import ParsableFactory
44
+ from msgraph_core import APIVersion
45
+
46
+ from airflow.utils.context import Context
47
+
48
+
49
+ class MSGraphAsyncOperator(BaseOperator):
50
+ """
51
+ A Microsoft Graph API operator which allows you to execute REST call to the Microsoft Graph API.
52
+
53
+ https://learn.microsoft.com/en-us/graph/use-the-api
54
+
55
+ .. seealso::
56
+ For more information on how to use this operator, take a look at the guide:
57
+ :ref:`howto/operator:MSGraphAsyncOperator`
58
+
59
+ :param url: The url being executed on the Microsoft Graph API (templated).
60
+ :param response_type: The expected return type of the response as a string. Possible value are: `bytes`,
61
+ `str`, `int`, `float`, `bool` and `datetime` (default is None).
62
+ :param response_handler: Function to convert the native HTTPX response returned by the hook (default is
63
+ lambda response, error_map: response.json()). The default expression will convert the native response
64
+ to JSON. If response_type parameter is specified, then the response_handler will be ignored.
65
+ :param method: The HTTP method being used to do the REST call (default is GET).
66
+ :param conn_id: The HTTP Connection ID to run the operator against (templated).
67
+ :param key: The key that will be used to store `XCom's` ("return_value" is default).
68
+ :param timeout: The HTTP timeout being used by the `KiotaRequestAdapter` (default is None).
69
+ When no timeout is specified or set to None then there is no HTTP timeout on each request.
70
+ :param proxies: A dict defining the HTTP proxies to be used (default is None).
71
+ :param api_version: The API version of the Microsoft Graph API to be used (default is v1).
72
+ You can pass an enum named APIVersion which has 2 possible members v1 and beta,
73
+ or you can pass a string as `v1.0` or `beta`.
74
+ :param result_processor: Function to further process the response from MS Graph API
75
+ (default is lambda: context, response: response). When the response returned by the
76
+ `KiotaRequestAdapterHook` are bytes, then those will be base64 encoded into a string.
77
+ :param serializer: Class which handles response serialization (default is ResponseSerializer).
78
+ Bytes will be base64 encoded into a string, so it can be stored as an XCom.
79
+ """
80
+
81
+ template_fields: Sequence[str] = (
82
+ "url",
83
+ "response_type",
84
+ "path_parameters",
85
+ "url_template",
86
+ "query_parameters",
87
+ "headers",
88
+ "data",
89
+ "conn_id",
90
+ )
91
+
92
+ def __init__(
93
+ self,
94
+ *,
95
+ url: str,
96
+ response_type: ResponseType | None = None,
97
+ response_handler: Callable[
98
+ [NativeResponseType, dict[str, ParsableFactory | None] | None], Any
99
+ ] = lambda response, error_map: response.json(),
100
+ path_parameters: dict[str, Any] | None = None,
101
+ url_template: str | None = None,
102
+ method: str = "GET",
103
+ query_parameters: dict[str, QueryParams] | None = None,
104
+ headers: dict[str, str] | None = None,
105
+ data: dict[str, Any] | str | BytesIO | None = None,
106
+ conn_id: str = KiotaRequestAdapterHook.default_conn_name,
107
+ key: str = XCOM_RETURN_KEY,
108
+ timeout: float | None = None,
109
+ proxies: dict | None = None,
110
+ api_version: APIVersion | None = None,
111
+ pagination_function: Callable[[MSGraphAsyncOperator, dict], tuple[str, dict]] | None = None,
112
+ result_processor: Callable[[Context, Any], Any] = lambda context, result: result,
113
+ serializer: type[ResponseSerializer] = ResponseSerializer,
114
+ **kwargs: Any,
115
+ ):
116
+ super().__init__(**kwargs)
117
+ self.url = url
118
+ self.response_type = response_type
119
+ self.response_handler = response_handler
120
+ self.path_parameters = path_parameters
121
+ self.url_template = url_template
122
+ self.method = method
123
+ self.query_parameters = query_parameters
124
+ self.headers = headers
125
+ self.data = data
126
+ self.conn_id = conn_id
127
+ self.key = key
128
+ self.timeout = timeout
129
+ self.proxies = proxies
130
+ self.api_version = api_version
131
+ self.pagination_function = pagination_function or self.paginate
132
+ self.result_processor = result_processor
133
+ self.serializer: ResponseSerializer = serializer()
134
+ self.results: list[Any] | None = None
135
+
136
+ def execute(self, context: Context) -> None:
137
+ self.log.info("Executing url '%s' as '%s'", self.url, self.method)
138
+ self.defer(
139
+ trigger=MSGraphTrigger(
140
+ url=self.url,
141
+ response_type=self.response_type,
142
+ path_parameters=self.path_parameters,
143
+ url_template=self.url_template,
144
+ method=self.method,
145
+ query_parameters=self.query_parameters,
146
+ headers=self.headers,
147
+ data=self.data,
148
+ conn_id=self.conn_id,
149
+ timeout=self.timeout,
150
+ proxies=self.proxies,
151
+ api_version=self.api_version,
152
+ serializer=type(self.serializer),
153
+ ),
154
+ method_name=self.execute_complete.__name__,
155
+ )
156
+
157
+ def execute_complete(
158
+ self,
159
+ context: Context,
160
+ event: dict[Any, Any] | None = None,
161
+ ) -> Any:
162
+ """
163
+ Execute callback when MSGraphTrigger finishes execution.
164
+
165
+ This method gets executed automatically when MSGraphTrigger completes its execution.
166
+ """
167
+ self.log.debug("context: %s", context)
168
+
169
+ if event:
170
+ self.log.info("%s completed with %s: %s", self.task_id, event.get("status"), event)
171
+
172
+ if event.get("status") == "failure":
173
+ raise AirflowException(event.get("message"))
174
+
175
+ response = event.get("response")
176
+
177
+ self.log.info("response: %s", response)
178
+
179
+ if response:
180
+ response = self.serializer.deserialize(response)
181
+
182
+ self.log.debug("deserialize response: %s", response)
183
+
184
+ result = self.result_processor(context, response)
185
+
186
+ self.log.debug("processed response: %s", result)
187
+
188
+ event["response"] = result
189
+
190
+ try:
191
+ self.trigger_next_link(response, method_name=self.pull_execute_complete.__name__)
192
+ except TaskDeferred as exception:
193
+ self.append_result(
194
+ result=result,
195
+ append_result_as_list_if_absent=True,
196
+ )
197
+ self.push_xcom(context=context, value=self.results)
198
+ raise exception
199
+
200
+ self.append_result(result=result)
201
+ self.log.debug("results: %s", self.results)
202
+
203
+ return self.results
204
+ return None
205
+
206
+ def append_result(
207
+ self,
208
+ result: Any,
209
+ append_result_as_list_if_absent: bool = False,
210
+ ):
211
+ self.log.debug("value: %s", result)
212
+
213
+ if isinstance(self.results, list):
214
+ if isinstance(result, list):
215
+ self.results.extend(result)
216
+ else:
217
+ self.results.append(result)
218
+ else:
219
+ if append_result_as_list_if_absent:
220
+ if isinstance(result, list):
221
+ self.results = result
222
+ else:
223
+ self.results = [result]
224
+ else:
225
+ self.results = result
226
+
227
+ def push_xcom(self, context: Context, value) -> None:
228
+ self.log.debug("do_xcom_push: %s", self.do_xcom_push)
229
+ if self.do_xcom_push:
230
+ self.log.info("Pushing XCom with key '%s': %s", self.key, value)
231
+ self.xcom_push(context=context, key=self.key, value=value)
232
+
233
+ def pull_execute_complete(self, context: Context, event: dict[Any, Any] | None = None) -> Any:
234
+ self.results = list(
235
+ self.xcom_pull(
236
+ context=context,
237
+ task_ids=self.task_id,
238
+ dag_id=self.dag_id,
239
+ key=self.key,
240
+ )
241
+ or []
242
+ )
243
+ self.log.info(
244
+ "Pulled XCom with task_id '%s' and dag_id '%s' and key '%s': %s",
245
+ self.task_id,
246
+ self.dag_id,
247
+ self.key,
248
+ self.results,
249
+ )
250
+ return self.execute_complete(context, event)
251
+
252
+ @staticmethod
253
+ def paginate(operator: MSGraphAsyncOperator, response: dict) -> tuple[Any, dict[str, Any] | None]:
254
+ odata_count = response.get("@odata.count")
255
+ if odata_count and operator.query_parameters:
256
+ query_parameters = deepcopy(operator.query_parameters)
257
+ top = query_parameters.get("$top")
258
+ odata_count = response.get("@odata.count")
259
+
260
+ if top and odata_count:
261
+ if len(response.get("value", [])) == top:
262
+ skip = (
263
+ sum(map(lambda result: len(result["value"]), operator.results)) + top
264
+ if operator.results
265
+ else top
266
+ )
267
+ query_parameters["$skip"] = skip
268
+ return operator.url, query_parameters
269
+ return response.get("@odata.nextLink"), operator.query_parameters
270
+
271
+ def trigger_next_link(self, response, method_name="execute_complete") -> None:
272
+ if isinstance(response, dict):
273
+ url, query_parameters = self.pagination_function(self, response)
274
+
275
+ self.log.debug("url: %s", url)
276
+ self.log.debug("query_parameters: %s", query_parameters)
277
+
278
+ if url:
279
+ self.defer(
280
+ trigger=MSGraphTrigger(
281
+ url=url,
282
+ query_parameters=query_parameters,
283
+ response_type=self.response_type,
284
+ response_handler=self.response_handler,
285
+ conn_id=self.conn_id,
286
+ timeout=self.timeout,
287
+ proxies=self.proxies,
288
+ api_version=self.api_version,
289
+ serializer=type(self.serializer),
290
+ ),
291
+ method_name=method_name,
292
+ )
@@ -0,0 +1,163 @@
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
+ import asyncio
21
+ import json
22
+ from typing import TYPE_CHECKING, Any, Callable, Sequence
23
+
24
+ from airflow.providers.microsoft.azure.hooks.msgraph import KiotaRequestAdapterHook
25
+ from airflow.providers.microsoft.azure.triggers.msgraph import MSGraphTrigger, ResponseSerializer
26
+ from airflow.sensors.base import BaseSensorOperator, PokeReturnValue
27
+
28
+ if TYPE_CHECKING:
29
+ from io import BytesIO
30
+
31
+ from kiota_abstractions.request_information import QueryParams
32
+ from kiota_abstractions.response_handler import NativeResponseType
33
+ from kiota_abstractions.serialization import ParsableFactory
34
+ from kiota_http.httpx_request_adapter import ResponseType
35
+ from msgraph_core import APIVersion
36
+
37
+ from airflow.triggers.base import TriggerEvent
38
+ from airflow.utils.context import Context
39
+
40
+
41
+ def default_event_processor(context: Context, event: TriggerEvent) -> bool:
42
+ if event.payload["status"] == "success":
43
+ return json.loads(event.payload["response"])["status"] == "Succeeded"
44
+ return False
45
+
46
+
47
+ class MSGraphSensor(BaseSensorOperator):
48
+ """
49
+ A Microsoft Graph API sensor which allows you to poll an async REST call to the Microsoft Graph API.
50
+
51
+ :param url: The url being executed on the Microsoft Graph API (templated).
52
+ :param response_type: The expected return type of the response as a string. Possible value are: `bytes`,
53
+ `str`, `int`, `float`, `bool` and `datetime` (default is None).
54
+ :param response_handler: Function to convert the native HTTPX response returned by the hook (default is
55
+ lambda response, error_map: response.json()). The default expression will convert the native response
56
+ to JSON. If response_type parameter is specified, then the response_handler will be ignored.
57
+ :param method: The HTTP method being used to do the REST call (default is GET).
58
+ :param conn_id: The HTTP Connection ID to run the operator against (templated).
59
+ :param proxies: A dict defining the HTTP proxies to be used (default is None).
60
+ :param api_version: The API version of the Microsoft Graph API to be used (default is v1).
61
+ You can pass an enum named APIVersion which has 2 possible members v1 and beta,
62
+ or you can pass a string as `v1.0` or `beta`.
63
+ :param event_processor: Function which checks the response from MS Graph API (default is the
64
+ `default_event_processor` method) and returns a boolean. When the result is True, the sensor
65
+ will stop poking, otherwise it will continue until it's True or times out.
66
+ :param result_processor: Function to further process the response from MS Graph API
67
+ (default is lambda: context, response: response). When the response returned by the
68
+ `KiotaRequestAdapterHook` are bytes, then those will be base64 encoded into a string.
69
+ :param serializer: Class which handles response serialization (default is ResponseSerializer).
70
+ Bytes will be base64 encoded into a string, so it can be stored as an XCom.
71
+ """
72
+
73
+ template_fields: Sequence[str] = (
74
+ "url",
75
+ "response_type",
76
+ "path_parameters",
77
+ "url_template",
78
+ "query_parameters",
79
+ "headers",
80
+ "data",
81
+ "conn_id",
82
+ )
83
+
84
+ def __init__(
85
+ self,
86
+ url: str,
87
+ response_type: ResponseType | None = None,
88
+ response_handler: Callable[
89
+ [NativeResponseType, dict[str, ParsableFactory | None] | None], Any
90
+ ] = lambda response, error_map: response.json(),
91
+ path_parameters: dict[str, Any] | None = None,
92
+ url_template: str | None = None,
93
+ method: str = "GET",
94
+ query_parameters: dict[str, QueryParams] | None = None,
95
+ headers: dict[str, str] | None = None,
96
+ data: dict[str, Any] | str | BytesIO | None = None,
97
+ conn_id: str = KiotaRequestAdapterHook.default_conn_name,
98
+ proxies: dict | None = None,
99
+ api_version: APIVersion | None = None,
100
+ event_processor: Callable[[Context, TriggerEvent], bool] = default_event_processor,
101
+ result_processor: Callable[[Context, Any], Any] = lambda context, result: result,
102
+ serializer: type[ResponseSerializer] = ResponseSerializer,
103
+ **kwargs,
104
+ ):
105
+ super().__init__(**kwargs)
106
+ self.url = url
107
+ self.response_type = response_type
108
+ self.response_handler = response_handler
109
+ self.path_parameters = path_parameters
110
+ self.url_template = url_template
111
+ self.method = method
112
+ self.query_parameters = query_parameters
113
+ self.headers = headers
114
+ self.data = data
115
+ self.conn_id = conn_id
116
+ self.proxies = proxies
117
+ self.api_version = api_version
118
+ self.event_processor = event_processor
119
+ self.result_processor = result_processor
120
+ self.serializer = serializer()
121
+
122
+ @property
123
+ def trigger(self):
124
+ return MSGraphTrigger(
125
+ url=self.url,
126
+ response_type=self.response_type,
127
+ response_handler=self.response_handler,
128
+ path_parameters=self.path_parameters,
129
+ url_template=self.url_template,
130
+ method=self.method,
131
+ query_parameters=self.query_parameters,
132
+ headers=self.headers,
133
+ data=self.data,
134
+ conn_id=self.conn_id,
135
+ timeout=self.timeout,
136
+ proxies=self.proxies,
137
+ api_version=self.api_version,
138
+ serializer=type(self.serializer),
139
+ )
140
+
141
+ async def async_poke(self, context: Context) -> bool | PokeReturnValue:
142
+ self.log.info("Sensor triggered")
143
+
144
+ async for event in self.trigger.run():
145
+ self.log.debug("event: %s", event)
146
+
147
+ is_done = self.event_processor(context, event)
148
+
149
+ self.log.debug("is_done: %s", is_done)
150
+
151
+ response = self.serializer.deserialize(event.payload["response"])
152
+
153
+ self.log.debug("deserialize event: %s", response)
154
+
155
+ result = self.result_processor(context, response)
156
+
157
+ self.log.debug("result: %s", result)
158
+
159
+ return PokeReturnValue(is_done=is_done, xcom_value=result)
160
+ return PokeReturnValue(is_done=True)
161
+
162
+ def poke(self, context) -> bool | PokeReturnValue:
163
+ return asyncio.run(self.async_poke(context))
@@ -0,0 +1,242 @@
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
+ import json
21
+ import locale
22
+ from base64 import b64encode
23
+ from contextlib import suppress
24
+ from datetime import datetime
25
+ from json import JSONDecodeError
26
+ from typing import (
27
+ TYPE_CHECKING,
28
+ Any,
29
+ AsyncIterator,
30
+ Callable,
31
+ Sequence,
32
+ )
33
+ from uuid import UUID
34
+
35
+ import pendulum
36
+
37
+ from airflow.providers.microsoft.azure.hooks.msgraph import KiotaRequestAdapterHook
38
+ from airflow.triggers.base import BaseTrigger, TriggerEvent
39
+ from airflow.utils.module_loading import import_string
40
+
41
+ if TYPE_CHECKING:
42
+ from io import BytesIO
43
+
44
+ from kiota_abstractions.request_adapter import RequestAdapter
45
+ from kiota_abstractions.request_information import QueryParams
46
+ from kiota_abstractions.response_handler import NativeResponseType
47
+ from kiota_abstractions.serialization import ParsableFactory
48
+ from kiota_http.httpx_request_adapter import ResponseType
49
+ from msgraph_core import APIVersion
50
+
51
+
52
+ class ResponseSerializer:
53
+ """ResponseSerializer serializes the response as a string."""
54
+
55
+ def __init__(self, encoding: str | None = None):
56
+ self.encoding = encoding or locale.getpreferredencoding()
57
+
58
+ def serialize(self, response) -> str | None:
59
+ def convert(value) -> str | None:
60
+ if value is not None:
61
+ if isinstance(value, UUID):
62
+ return str(value)
63
+ if isinstance(value, datetime):
64
+ return value.isoformat()
65
+ if isinstance(value, pendulum.DateTime):
66
+ return value.to_iso8601_string() # Adjust the format as needed
67
+ raise TypeError(f"Object of type {type(value)} is not JSON serializable!")
68
+ return None
69
+
70
+ if response is not None:
71
+ if isinstance(response, bytes):
72
+ return b64encode(response).decode(self.encoding)
73
+ with suppress(JSONDecodeError):
74
+ return json.dumps(response, default=convert)
75
+ return response
76
+ return None
77
+
78
+ def deserialize(self, response) -> Any:
79
+ if isinstance(response, str):
80
+ with suppress(JSONDecodeError):
81
+ response = json.loads(response)
82
+ return response
83
+
84
+
85
+ class MSGraphTrigger(BaseTrigger):
86
+ """
87
+ A Microsoft Graph API trigger which allows you to execute an async REST call to the Microsoft Graph API.
88
+
89
+ :param url: The url being executed on the Microsoft Graph API (templated).
90
+ :param response_type: The expected return type of the response as a string. Possible value are: `bytes`,
91
+ `str`, `int`, `float`, `bool` and `datetime` (default is None).
92
+ :param response_handler: Function to convert the native HTTPX response returned by the hook (default is
93
+ lambda response, error_map: response.json()). The default expression will convert the native response
94
+ to JSON. If response_type parameter is specified, then the response_handler will be ignored.
95
+ :param method: The HTTP method being used to do the REST call (default is GET).
96
+ :param conn_id: The HTTP Connection ID to run the operator against (templated).
97
+ :param timeout: The HTTP timeout being used by the `KiotaRequestAdapter` (default is None).
98
+ When no timeout is specified or set to None then there is no HTTP timeout on each request.
99
+ :param proxies: A dict defining the HTTP proxies to be used (default is None).
100
+ :param api_version: The API version of the Microsoft Graph API to be used (default is v1).
101
+ You can pass an enum named APIVersion which has 2 possible members v1 and beta,
102
+ or you can pass a string as `v1.0` or `beta`.
103
+ :param serializer: Class which handles response serialization (default is ResponseSerializer).
104
+ Bytes will be base64 encoded into a string, so it can be stored as an XCom.
105
+ """
106
+
107
+ template_fields: Sequence[str] = (
108
+ "url",
109
+ "response_type",
110
+ "path_parameters",
111
+ "url_template",
112
+ "query_parameters",
113
+ "headers",
114
+ "data",
115
+ "conn_id",
116
+ )
117
+
118
+ def __init__(
119
+ self,
120
+ url: str,
121
+ response_type: ResponseType | None = None,
122
+ response_handler: Callable[
123
+ [NativeResponseType, dict[str, ParsableFactory | None] | None], Any
124
+ ] = lambda response, error_map: response.json(),
125
+ path_parameters: dict[str, Any] | None = None,
126
+ url_template: str | None = None,
127
+ method: str = "GET",
128
+ query_parameters: dict[str, QueryParams] | None = None,
129
+ headers: dict[str, str] | None = None,
130
+ data: dict[str, Any] | str | BytesIO | None = None,
131
+ conn_id: str = KiotaRequestAdapterHook.default_conn_name,
132
+ timeout: float | None = None,
133
+ proxies: dict | None = None,
134
+ api_version: APIVersion | None = None,
135
+ serializer: type[ResponseSerializer] = ResponseSerializer,
136
+ ):
137
+ super().__init__()
138
+ self.hook = KiotaRequestAdapterHook(
139
+ conn_id=conn_id,
140
+ timeout=timeout,
141
+ proxies=proxies,
142
+ api_version=api_version,
143
+ )
144
+ self.url = url
145
+ self.response_type = response_type
146
+ self.response_handler = response_handler
147
+ self.path_parameters = path_parameters
148
+ self.url_template = url_template
149
+ self.method = method
150
+ self.query_parameters = query_parameters
151
+ self.headers = headers
152
+ self.data = data
153
+ self.serializer: ResponseSerializer = self.resolve_type(serializer, default=ResponseSerializer)()
154
+
155
+ @classmethod
156
+ def resolve_type(cls, value: str | type, default) -> type:
157
+ if isinstance(value, str):
158
+ with suppress(ImportError):
159
+ return import_string(value)
160
+ return default
161
+ return value or default
162
+
163
+ def serialize(self) -> tuple[str, dict[str, Any]]:
164
+ """Serialize the HttpTrigger arguments and classpath."""
165
+ api_version = self.api_version.value if self.api_version else None
166
+ return (
167
+ f"{self.__class__.__module__}.{self.__class__.__name__}",
168
+ {
169
+ "conn_id": self.conn_id,
170
+ "timeout": self.timeout,
171
+ "proxies": self.proxies,
172
+ "api_version": api_version,
173
+ "serializer": f"{self.serializer.__class__.__module__}.{self.serializer.__class__.__name__}",
174
+ "url": self.url,
175
+ "path_parameters": self.path_parameters,
176
+ "url_template": self.url_template,
177
+ "method": self.method,
178
+ "query_parameters": self.query_parameters,
179
+ "headers": self.headers,
180
+ "data": self.data,
181
+ "response_type": self.response_type,
182
+ },
183
+ )
184
+
185
+ def get_conn(self) -> RequestAdapter:
186
+ return self.hook.get_conn()
187
+
188
+ @property
189
+ def conn_id(self) -> str:
190
+ return self.hook.conn_id
191
+
192
+ @property
193
+ def timeout(self) -> float | None:
194
+ return self.hook.timeout
195
+
196
+ @property
197
+ def proxies(self) -> dict | None:
198
+ return self.hook.proxies
199
+
200
+ @property
201
+ def api_version(self) -> APIVersion:
202
+ return self.hook.api_version
203
+
204
+ async def run(self) -> AsyncIterator[TriggerEvent]:
205
+ """Make a series of asynchronous HTTP calls via a KiotaRequestAdapterHook."""
206
+ try:
207
+ response = await self.hook.run(
208
+ url=self.url,
209
+ response_type=self.response_type,
210
+ response_handler=self.response_handler,
211
+ path_parameters=self.path_parameters,
212
+ method=self.method,
213
+ query_parameters=self.query_parameters,
214
+ headers=self.headers,
215
+ data=self.data,
216
+ )
217
+
218
+ self.log.debug("response: %s", response)
219
+
220
+ if response:
221
+ response_type = type(response)
222
+
223
+ self.log.debug("response type: %s", response_type)
224
+
225
+ yield TriggerEvent(
226
+ {
227
+ "status": "success",
228
+ "type": f"{response_type.__module__}.{response_type.__name__}",
229
+ "response": self.serializer.serialize(response),
230
+ }
231
+ )
232
+ else:
233
+ yield TriggerEvent(
234
+ {
235
+ "status": "success",
236
+ "type": None,
237
+ "response": None,
238
+ }
239
+ )
240
+ except Exception as e:
241
+ self.log.exception("An error occurred: %s", e)
242
+ yield TriggerEvent({"status": "failure", "message": str(e)})