Skip to content

Commit aa5474f

Browse files
committed
backport to v2, add unit tests
1 parent 2b4704b commit aa5474f

File tree

8 files changed

+151
-4
lines changed

8 files changed

+151
-4
lines changed

runtimes/v2/azure_functions_runtime/bindings/meta.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
CUSTOMER_PACKAGES_PATH,
1616
HTTP,
1717
HTTP_TRIGGER,
18+
SERVICE_BUS_CLIENT_NAME
1819
)
1920
from ..utils.helpers import set_sdk_version
2021

@@ -286,3 +287,39 @@ def get_deferred_raw_bindings(indexed_function, input_types):
286287
raw_bindings, bindings_logs = DEFERRED_BINDING_REGISTRY.get_raw_bindings(
287288
indexed_function, input_types)
288289
return raw_bindings, bindings_logs
290+
291+
292+
def get_settlement_client():
293+
return DEFERRED_BINDING_REGISTRY.get(SERVICE_BUS_CLIENT_NAME).get_client()
294+
295+
296+
def validate_settlement_param(params: dict,
297+
bound_params: dict,
298+
annotations: dict) -> str:
299+
"""
300+
Checks if the settlement client is enabled for a given function.
301+
302+
If there is more than one param that is not bound, return an empty string
303+
indicating no settlement client support. This is a bad app.
304+
305+
If there is only one unbound param, check if it's a type that is supported
306+
by the settlement client. If so, return the param_name, where param_name
307+
is the name of the param that is supported. If not, return an empty string
308+
indicating no settlement client support.
309+
310+
Note: If a param does not have a type annotation, it will be skipped and not
311+
considered for settlement client support.
312+
"""
313+
if len(set(params) - set(bound_params)) > 1:
314+
return ''
315+
316+
# There is only one unbound param, check the type
317+
settlement_param = next(iter(set(params) - set(bound_params)))
318+
try:
319+
param_type = annotations.get(settlement_param)
320+
# Check if the type is a supported type for the settlement client
321+
if DEFERRED_BINDING_REGISTRY.check_supported_grpc_client_type(param_type):
322+
return settlement_param
323+
except Exception:
324+
param_type = None
325+
return ''

runtimes/v2/azure_functions_runtime/functions.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from .bindings.meta import (has_implicit_output,
1212
check_deferred_bindings_enabled,
1313
check_output_type_annotation,
14-
check_input_type_annotation)
14+
check_input_type_annotation,
15+
validate_settlement_param)
1516
from .utils.constants import HTTP_TRIGGER
1617
from .utils.typing_inspect import is_generic_type, get_origin, get_args # type: ignore
1718

@@ -33,6 +34,7 @@ class FunctionInfo(typing.NamedTuple):
3334
has_return: bool
3435
is_http_func: bool
3536
deferred_bindings_enabled: bool
37+
settlement_client_arg: str
3638

3739
input_types: typing.Mapping[str, ParamTypeInfo]
3840
output_types: typing.Mapping[str, ParamTypeInfo]
@@ -138,7 +140,13 @@ def validate_function_params(params: dict, bound_params: dict,
138140
protos):
139141
logger.debug("Params: %s, BoundParams: %s, Annotations: %s, FuncName: %s",
140142
params, bound_params, annotations, func_name)
143+
settlement_client_arg = ''
141144
if set(params) - set(bound_params):
145+
# Check for settlement client support for the missing parameters
146+
settlement_client_arg = validate_settlement_param(
147+
params, bound_params, annotations)
148+
if settlement_client_arg != '':
149+
params.pop(settlement_client_arg)
142150
raise FunctionLoadError(
143151
func_name,
144152
'Function parameter mismatch — the following trigger/input bindings '
@@ -278,7 +286,8 @@ def validate_function_params(params: dict, bound_params: dict,
278286
output_types[param.name] = param_type_info
279287
else:
280288
input_types[param.name] = param_type_info
281-
return input_types, output_types, fx_deferred_bindings_enabled
289+
return (input_types, output_types, fx_deferred_bindings_enabled,
290+
settlement_client_arg)
282291

283292
@staticmethod
284293
def get_function_return_type(annotations: dict, has_explicit_return: bool,
@@ -330,6 +339,7 @@ def add_func_to_registry_and_return_funcinfo(
330339
has_explicit_return: bool,
331340
has_implicit_return: bool,
332341
deferred_bindings_enabled: bool,
342+
settlement_client_arg: str,
333343
input_types: typing.Dict[str, ParamTypeInfo],
334344
output_types: typing.Dict[str, ParamTypeInfo],
335345
return_type: str):
@@ -355,6 +365,7 @@ def add_func_to_registry_and_return_funcinfo(
355365
has_return=has_explicit_return or has_implicit_return,
356366
is_http_func=is_http_func,
357367
deferred_bindings_enabled=deferred_bindings_enabled,
368+
settlement_client_arg=settlement_client_arg,
358369
input_types=input_types,
359370
output_types=output_types,
360371
return_type=return_type,
@@ -412,7 +423,8 @@ def add_indexed_function(self, function, protos):
412423
func_name)
413424

414425
(input_types, output_types,
415-
deferred_bindings_enabled) = self.validate_function_params(
426+
deferred_bindings_enabled,
427+
settlement_client_arg) = self.validate_function_params(
416428
params,
417429
bound_params,
418430
annotations,
@@ -431,5 +443,6 @@ def add_indexed_function(self, function, protos):
431443
func, func_name, function_id, func_dir,
432444
requires_context, has_explicit_return,
433445
has_implicit_return, deferred_bindings_enabled,
446+
settlement_client_arg,
434447
input_types, output_types,
435448
return_type)

runtimes/v2/azure_functions_runtime/handle_event.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from .bindings.context import get_context
2424
from .bindings.meta import (from_incoming_proto,
25+
get_settlement_client,
2526
is_trigger_binding,
2627
load_binding_registry,
2728
to_outgoing_param_binding,
@@ -224,6 +225,9 @@ async def invocation_request(request):
224225
if fi.requires_context:
225226
args['context'] = fi_context
226227

228+
if fi.settlement_client_arg != '':
229+
args[fi.settlement_client_arg] = get_settlement_client()
230+
227231
if fi.output_types:
228232
for name in fi.output_types:
229233
args[name] = Out()

runtimes/v2/azure_functions_runtime/utils/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
MODULE_NOT_FOUND_TS_URL = "https://aka.ms/functions-modulenotfound"
1212
PYTHON_LANGUAGE_RUNTIME = "python"
1313
RETRY_POLICY = "retry_policy"
14+
SERVICE_BUS_CLIENT_NAME = "serviceBusClient"
1415
TRUE = "true"
1516
TRACEPARENT = "traceparent"
1617
TRACESTATE = "tracestate"

runtimes/v2/tests/unittests/test_deferred_bindings.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
ContainerClient,
1717
StorageStreamDownloader)
1818
from azurefunctions.extensions.bindings.eventhub import EventData, EventDataConverter
19+
from azurefunctions.extensions.bindings.servicebus import ServiceBusMessageActions
1920

2021
EVENTHUB_SAMPLE_CONTENT = b"\x00Sr\xc1\x8e\x08\xa3\x1bx-opt-sequence-number-epochT\xff\xa3\x15x-opt-sequence-numberU\x04\xa3\x0cx-opt-offset\x81\x00\x00\x00\x01\x00\x00\x010\xa3\x13x-opt-enqueued-time\x00\xa3\x1dcom.microsoft:datetime-offset\x81\x08\xddW\x05\xc3Q\xcf\x10\x00St\xc1I\x02\xa1\rDiagnostic-Id\xa1700-bdc3fde4889b4e907e0c9dcb46ff8d92-21f637af293ef13b-00\x00Su\xa0\x08message1" # noqa: E501
2122

@@ -98,3 +99,46 @@ async def test_check_deferred_bindings_enabled(self):
9899
ContainerClient, True), (True, True))
99100
self.assertEqual(meta.check_deferred_bindings_enabled(
100101
StorageStreamDownloader, True), (True, True))
102+
103+
async def test_valid_settlement_param():
104+
params = {'param1', 'param2', 'param3'}
105+
bound_params = {'param1', 'param2'}
106+
annotations = {
107+
'param1': func.InputStream,
108+
'param2': func.Out[str],
109+
'param3': ServiceBusMessageActions
110+
}
111+
112+
settlement_client_arg = meta.validate_settlement_param(
113+
params, bound_params, annotations)
114+
115+
assert settlement_client_arg == 'param3'
116+
117+
async def test_invalid_settlement_param():
118+
params = {'param1', 'param2', 'param3'}
119+
bound_params = {'param1', 'param2'}
120+
annotations = {
121+
'param1': func.InputStream,
122+
'param2': func.Out[str],
123+
'param3': str
124+
}
125+
126+
settlement_client_arg = meta.validate_settlement_param(
127+
params, bound_params, annotations)
128+
129+
assert settlement_client_arg == ''
130+
131+
async def test_invalid_settlement_param_multiple():
132+
params = {'param1', 'param2', 'param3', 'param4'}
133+
bound_params = {'param1', 'param2'}
134+
annotations = {
135+
'param1': func.InputStream,
136+
'param2': func.Out[str],
137+
'param3': ServiceBusMessageActions,
138+
'param4': str
139+
}
140+
141+
settlement_client_arg = meta.validate_settlement_param(
142+
params, bound_params, annotations)
143+
144+
assert settlement_client_arg == ''

workers/azure_functions_worker/bindings/meta.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
CUSTOMER_PACKAGES_PATH,
1111
HTTP,
1212
HTTP_TRIGGER,
13+
SERVICE_BUS_CLIENT_NAME
1314
)
1415
from ..http_v2 import HttpV2Registry
1516
from ..logging import logger
@@ -317,7 +318,7 @@ def get_deferred_raw_bindings(indexed_function, input_types):
317318

318319

319320
def get_settlement_client():
320-
return DEFERRED_BINDING_REGISTRY.get("serviceBusClient").get_client()
321+
return DEFERRED_BINDING_REGISTRY.get(SERVICE_BUS_CLIENT_NAME).get_client()
321322

322323

323324
def validate_settlement_param(params: dict,

workers/azure_functions_worker/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,6 @@
115115
'3.11': '2027-04',
116116
'3.12': '2028-04'
117117
}
118+
119+
# Service Bus Client Name
120+
SERVICE_BUS_CLIENT_NAME = "serviceBusClient"

workers/tests/extension_tests/deferred_bindings_tests/test_deferred_bindings.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
BlobClientConverter,
1818
ContainerClient,
1919
StorageStreamDownloader)
20+
from azurefunctions.extensions.bindings.servicebus import ServiceBusMessageActions
2021

2122
DEFERRED_BINDINGS_ENABLED_DIR = testutils.EXTENSION_TESTS_FOLDER / \
2223
'deferred_bindings_tests' / \
@@ -209,3 +210,46 @@ async def test_check_deferred_bindings_enabled(self):
209210
ContainerClient, True), (True, True))
210211
self.assertEqual(meta.check_deferred_bindings_enabled(
211212
StorageStreamDownloader, True), (True, True))
213+
214+
async def test_valid_settlement_param():
215+
params = {'param1', 'param2', 'param3'}
216+
bound_params = {'param1', 'param2'}
217+
annotations = {
218+
'param1': func.InputStream,
219+
'param2': func.Out[str],
220+
'param3': ServiceBusMessageActions
221+
}
222+
223+
settlement_client_arg = meta.validate_settlement_param(
224+
params, bound_params, annotations)
225+
226+
assert settlement_client_arg == 'param3'
227+
228+
async def test_invalid_settlement_param():
229+
params = {'param1', 'param2', 'param3'}
230+
bound_params = {'param1', 'param2'}
231+
annotations = {
232+
'param1': func.InputStream,
233+
'param2': func.Out[str],
234+
'param3': str
235+
}
236+
237+
settlement_client_arg = meta.validate_settlement_param(
238+
params, bound_params, annotations)
239+
240+
assert settlement_client_arg == ''
241+
242+
async def test_invalid_settlement_param_multiple():
243+
params = {'param1', 'param2', 'param3', 'param4'}
244+
bound_params = {'param1', 'param2'}
245+
annotations = {
246+
'param1': func.InputStream,
247+
'param2': func.Out[str],
248+
'param3': ServiceBusMessageActions,
249+
'param4': str
250+
}
251+
252+
settlement_client_arg = meta.validate_settlement_param(
253+
params, bound_params, annotations)
254+
255+
assert settlement_client_arg == ''

0 commit comments

Comments
 (0)