From c8555d7c73c46396c361c62687801f3663f66b7b Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 1 Jun 2023 16:59:04 +0530 Subject: [PATCH 01/26] Adding daprServiceInvocation trigger Signed-off-by: MD Ashique --- azure/functions/decorators/constants.py | 1 + azure/functions/decorators/dapr.py | 22 +++++++++++ azure/functions/decorators/function_app.py | 46 ++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 azure/functions/decorators/dapr.py diff --git a/azure/functions/decorators/constants.py b/azure/functions/decorators/constants.py index fde60864..68b0b6fd 100644 --- a/azure/functions/decorators/constants.py +++ b/azure/functions/decorators/constants.py @@ -19,3 +19,4 @@ EVENT_GRID_TRIGGER = "eventGridTrigger" EVENT_GRID = "eventGrid" TABLE = "table" +DAPR_SERVICE_INVOCATION_TRIGGER='daprServiceInvocationTrigger' diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py new file mode 100644 index 00000000..a31eaf99 --- /dev/null +++ b/azure/functions/decorators/dapr.py @@ -0,0 +1,22 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from typing import Optional + +from azure.functions.decorators.constants import DAPR_SERVICE_INVOCATION_TRIGGER +from azure.functions.decorators.core import Trigger, DataType, OutputBinding, \ + Cardinality + + +class DaprServiceInvocationTrigger(Trigger): + + @staticmethod + def get_binding_name() -> str: + return DAPR_SERVICE_INVOCATION_TRIGGER + + def __init__(self, + name: str, + method_name: str, + data_type: Optional[DataType] = None, + **kwargs): + self.method_name = method_name + super().__init__(name=name, data_type=data_type) \ No newline at end of file diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py index 327c6756..9b367ba6 100644 --- a/azure/functions/decorators/function_app.py +++ b/azure/functions/decorators/function_app.py @@ -32,6 +32,7 @@ from .warmup import WarmUpTrigger from .._http_asgi import AsgiMiddleware from .._http_wsgi import WsgiMiddleware, Context +from azure.functions.decorators.dapr import DaprServiceInvocationTrigger class Function(object): @@ -2100,3 +2101,48 @@ def _add_http_app(self, route="/{*route}") def http_app_func(req: HttpRequest, context: Context): return wsgi_middleware.handle(req, context) + + + def dapr_service_invocation_trigger(self, + arg_name: str, + method_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_service_invocation_trigger decorator adds + :class:`DaprServiceInvocationTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprServiceInvocationTrigger + in the function.json which enables function to be triggered when new + message(s) are sent to the event hub. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param method_name: The name of the service method to be invoked by Dapr. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprServiceInvocationTrigger( + name=arg_name, + method_name=method_name, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap \ No newline at end of file From c1d41e776b8b07e21fc3926fb8fe08452d18f0a5 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 1 Jun 2023 17:12:23 +0530 Subject: [PATCH 02/26] Adding daprServiceInvocation trigger Signed-off-by: MD Ashique --- azure/functions/decorators/function_app.py | 84 +++++++++++----------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py index 9b367ba6..bfefb4d3 100644 --- a/azure/functions/decorators/function_app.py +++ b/azure/functions/decorators/function_app.py @@ -2103,46 +2103,46 @@ def http_app_func(req: HttpRequest, context: Context): return wsgi_middleware.handle(req, context) - def dapr_service_invocation_trigger(self, - arg_name: str, - method_name: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: - """The dapr_service_invocation_trigger decorator adds - :class:`DaprServiceInvocationTrigger` - to the :class:`FunctionBuilder` object - for building :class:`Function` object used in worker function - indexing model. This is equivalent to defining DaprServiceInvocationTrigger - in the function.json which enables function to be triggered when new - message(s) are sent to the event hub. - All optional fields will be given default value by function host when - they are parsed by function host. - - Ref: https://aka.ms/azure-function-binding-event-hubs - - :param arg_name: The name of the variable that represents - :param method_name: The name of the service method to be invoked by Dapr. - :param data_type: Defines how Functions runtime should treat the - parameter value. - :param kwargs: Keyword arguments for specifying additional binding - fields to include in the binding json. - - :return: Decorator function. - """ - - @self._configure_function_builder - def wrap(fb): - def decorator(): - fb.add_trigger( - trigger=DaprServiceInvocationTrigger( - name=arg_name, - method_name=method_name, - data_type=parse_singular_param_to_enum(data_type, - DataType), - **kwargs)) - return fb - - return decorator() +def dapr_service_invocation_trigger(self, + arg_name: str, + method_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_service_invocation_trigger decorator adds + :class:`DaprServiceInvocationTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprServiceInvocationTrigger + in the function.json which enables function to be triggered when new + message(s) are sent to the event hub. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param method_name: The name of the service method to be invoked by Dapr. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ - return wrap \ No newline at end of file + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprServiceInvocationTrigger( + name=arg_name, + method_name=method_name, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap \ No newline at end of file From a8a8e6babcd06cbea80cd3099f487deb93e610df Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 1 Jun 2023 18:02:05 +0530 Subject: [PATCH 03/26] moving dapr service invocation trigger to TriggerApi class Signed-off-by: MD Ashique --- azure/functions/decorators/function_app.py | 91 +++++++++++----------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py index bfefb4d3..d7c372ad 100644 --- a/azure/functions/decorators/function_app.py +++ b/azure/functions/decorators/function_app.py @@ -1102,6 +1102,50 @@ def decorator(): return decorator() return wrap + + def dapr_service_invocation_trigger(self, + arg_name: str, + method_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_service_invocation_trigger decorator adds + :class:`DaprServiceInvocationTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprServiceInvocationTrigger + in the function.json which enables function to be triggered when new + message(s) are sent to the event hub. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param method_name: The name of the service method to be invoked by Dapr. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprServiceInvocationTrigger( + name=arg_name, + method_name=method_name, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap class BindingApi(DecoratorApi, ABC): @@ -2100,49 +2144,4 @@ def _add_http_app(self, auth_level=self.auth_level, route="/{*route}") def http_app_func(req: HttpRequest, context: Context): - return wsgi_middleware.handle(req, context) - - -def dapr_service_invocation_trigger(self, - arg_name: str, - method_name: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: - """The dapr_service_invocation_trigger decorator adds - :class:`DaprServiceInvocationTrigger` - to the :class:`FunctionBuilder` object - for building :class:`Function` object used in worker function - indexing model. This is equivalent to defining DaprServiceInvocationTrigger - in the function.json which enables function to be triggered when new - message(s) are sent to the event hub. - All optional fields will be given default value by function host when - they are parsed by function host. - - Ref: https://aka.ms/azure-function-binding-event-hubs - - :param arg_name: The name of the variable that represents - :param method_name: The name of the service method to be invoked by Dapr. - :param data_type: Defines how Functions runtime should treat the - parameter value. - :param kwargs: Keyword arguments for specifying additional binding - fields to include in the binding json. - - :return: Decorator function. - """ - - @self._configure_function_builder - def wrap(fb): - def decorator(): - fb.add_trigger( - trigger=DaprServiceInvocationTrigger( - name=arg_name, - method_name=method_name, - data_type=parse_singular_param_to_enum(data_type, - DataType), - **kwargs)) - return fb - - return decorator() - - return wrap \ No newline at end of file + return wsgi_middleware.handle(req, context) \ No newline at end of file From 32474b5a72ff63730635f4446d0d4c61b5477ac9 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 1 Jun 2023 18:16:15 +0530 Subject: [PATCH 04/26] Adding test_dapr_service_invocation_default_args Signed-off-by: MD Ashique --- azure/functions/decorators/constants.py | 2 +- tests/decorators/test_decorators.py | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/azure/functions/decorators/constants.py b/azure/functions/decorators/constants.py index 68b0b6fd..36b54887 100644 --- a/azure/functions/decorators/constants.py +++ b/azure/functions/decorators/constants.py @@ -19,4 +19,4 @@ EVENT_GRID_TRIGGER = "eventGridTrigger" EVENT_GRID = "eventGrid" TABLE = "table" -DAPR_SERVICE_INVOCATION_TRIGGER='daprServiceInvocationTrigger' +DAPR_SERVICE_INVOCATION_TRIGGER="daprServiceInvocationTrigger" diff --git a/tests/decorators/test_decorators.py b/tests/decorators/test_decorators.py index 146921af..d3b5433a 100644 --- a/tests/decorators/test_decorators.py +++ b/tests/decorators/test_decorators.py @@ -5,7 +5,8 @@ from azure.functions.decorators.constants import TIMER_TRIGGER, HTTP_TRIGGER, \ HTTP_OUTPUT, QUEUE, QUEUE_TRIGGER, SERVICE_BUS, SERVICE_BUS_TRIGGER, \ EVENT_HUB, EVENT_HUB_TRIGGER, COSMOS_DB, COSMOS_DB_TRIGGER, BLOB, \ - BLOB_TRIGGER, EVENT_GRID_TRIGGER, EVENT_GRID, TABLE, WARMUP_TRIGGER + BLOB_TRIGGER, EVENT_GRID_TRIGGER, EVENT_GRID, TABLE, WARMUP_TRIGGER, \ + DAPR_SERVICE_INVOCATION_TRIGGER from azure.functions.decorators.core import DataType, AuthLevel, \ BindingDirection, AccessRights, Cardinality from azure.functions.decorators.function_app import FunctionApp @@ -2093,3 +2094,25 @@ def _test_function_metadata_order(self, app): new_metadata_payload = str(func) self.assertEqual(new_metadata_payload, last_metadata_payload) last_metadata_payload = new_metadata_payload + + def test_dapr_service_invocation_default_args(self): + app = self.func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy_method_name") + + def dummy(): + pass + + func = self._get_user_function(app) + + assert_json(self, func, {"scriptFile": "function_app.py", + "bindings": [ + { + "direction": BindingDirection.IN, + "type": DAPR_SERVICE_INVOCATION_TRIGGER, + "name": "req", + "methodName": "dummy_method_name" + } + ] + }) From ee5d433a5677c155e9cc8ac267a21b78af00617a Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Wed, 7 Jun 2023 18:30:46 +0530 Subject: [PATCH 05/26] moving dapr related configs to its own file Signed-off-by: MD Ashique --- azure/functions/__init__.py | 4 +- azure/functions/decorators/__init__.py | 4 +- azure/functions/decorators/constants.py | 2 + azure/functions/decorators/dapr.py | 34 +++- .../functions/decorators/dapr_function_app.py | 160 ++++++++++++++++++ azure/functions/decorators/function_app.py | 46 ----- tests/decorators/test_dapr.py | 89 ++++++++++ 7 files changed, 290 insertions(+), 49 deletions(-) create mode 100644 azure/functions/decorators/dapr_function_app.py create mode 100644 tests/decorators/test_dapr.py diff --git a/azure/functions/__init__.py b/azure/functions/__init__.py index 8d7fb215..bc2d5549 100644 --- a/azure/functions/__init__.py +++ b/azure/functions/__init__.py @@ -23,6 +23,7 @@ from ._queue import QueueMessage from ._servicebus import ServiceBusMessage from ._sql import SqlRow, SqlRowList +from .decorators.dapr_function_app import DaprFunctionApp # Import binding implementations to register them from . import blob # NoQA @@ -93,7 +94,8 @@ 'AuthLevel', 'Cardinality', 'AccessRights', - 'HttpMethod' + 'HttpMethod', + 'DaprFunctionApp' ) __version__ = '1.14.1b3' diff --git a/azure/functions/decorators/__init__.py b/azure/functions/decorators/__init__.py index 6dbe5d47..ee25cc76 100644 --- a/azure/functions/decorators/__init__.py +++ b/azure/functions/decorators/__init__.py @@ -5,6 +5,7 @@ AuthLevel, Blueprint, ExternalHttpFunctionApp, AsgiFunctionApp, \ WsgiFunctionApp, FunctionRegister, TriggerApi, BindingApi from .http import HttpMethod +from .dapr_function_app import DaprFunctionApp __all__ = [ 'FunctionApp', @@ -21,5 +22,6 @@ 'AuthLevel', 'Cardinality', 'AccessRights', - 'HttpMethod' + 'HttpMethod', + 'DaprFunctionApp' ] diff --git a/azure/functions/decorators/constants.py b/azure/functions/decorators/constants.py index 36b54887..fe9ab611 100644 --- a/azure/functions/decorators/constants.py +++ b/azure/functions/decorators/constants.py @@ -20,3 +20,5 @@ EVENT_GRID = "eventGrid" TABLE = "table" DAPR_SERVICE_INVOCATION_TRIGGER="daprServiceInvocationTrigger" +DAPR_BINDING_TRIGGER="daprBindingTrigger" +DAPR_TOPIC_TRIGGER="daprTopicTrigger" diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index a31eaf99..364e2866 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -2,7 +2,7 @@ # Licensed under the MIT License. from typing import Optional -from azure.functions.decorators.constants import DAPR_SERVICE_INVOCATION_TRIGGER +from azure.functions.decorators.constants import DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_TOPIC_TRIGGER from azure.functions.decorators.core import Trigger, DataType, OutputBinding, \ Cardinality @@ -19,4 +19,36 @@ def __init__(self, data_type: Optional[DataType] = None, **kwargs): self.method_name = method_name + super().__init__(name=name, data_type=data_type) + +class DaprBindingTrigger(Trigger): + + @staticmethod + def get_binding_name() -> str: + return DAPR_BINDING_TRIGGER + + def __init__(self, + name: str, + binding_name: str, + data_type: Optional[DataType] = None, + **kwargs): + self.binding_name = binding_name + super().__init__(name=name, data_type=data_type) + +class DaprTopicTrigger(Trigger): + + @staticmethod + def get_binding_name() -> str: + return DAPR_TOPIC_TRIGGER + + def __init__(self, + name: str, + pub_sub_name: str, + topic: str, + route: str, + data_type: Optional[DataType] = None, + **kwargs): + self.pub_sub_name = pub_sub_name + self.topic = topic + self.route = route super().__init__(name=name, data_type=data_type) \ No newline at end of file diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py new file mode 100644 index 00000000..94c3424d --- /dev/null +++ b/azure/functions/decorators/dapr_function_app.py @@ -0,0 +1,160 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from abc import ABC +from typing import Any, Callable, Optional, Union +from azure.functions.decorators.core import DataType, AuthLevel +from azure.functions.decorators.utils import parse_singular_param_to_enum +from azure.functions.decorators.function_app import DecoratorApi, FunctionRegister +from azure.functions.decorators.dapr import DaprBindingTrigger, DaprServiceInvocationTrigger, DaprTopicTrigger + +class DaprTriggerApi(DecoratorApi, ABC): + + def dapr_service_invocation_trigger(self, + arg_name: str, + method_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_service_invocation_trigger decorator adds + :class:`DaprServiceInvocationTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprServiceInvocationTrigger + in the function.json which enables function to be triggered when new + service invocation occurs through Dapr. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param method_name: The name of the service method to be invoked by Dapr. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprServiceInvocationTrigger( + name=arg_name, + method_name=method_name, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_binding_trigger(self, + arg_name: str, + binding_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_binding_trigger decorator adds + :class:`DaprBindingTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprBindingTrigger + in the function.json which enables function to be triggered when new + message(s) are sent to the event hub. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param binding_name: The name of the binding to be invoked by Dapr. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprBindingTrigger( + name=arg_name, + binding_name=binding_name, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_topic_trigger(self, + arg_name: str, + pub_sub_name: str, + topic: str, + route: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: + """The dapr_topic_trigger decorator adds + :class:`DaprTopicTrigger` + to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprTopicTrigger + in the function.json which enables function to be triggered when new + message(s) are sent to the Dapr pubsub. + All optional fields will be given default value by function host when + they are parsed by function host. + + Ref: https://aka.ms/azure-function-binding-event-hubs + + :param arg_name: The name of the variable that represents + :param pub_sub_name: The name of the pubsub. + :param topic: The name of the topic. + :param route: The name of the route. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_trigger( + trigger=DaprTopicTrigger( + name=arg_name, + pub_sub_name=pub_sub_name, + topic=topic, + route=route, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + +class DaprFunctionApp(FunctionRegister, DaprTriggerApi): + + def __init__(self, + http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION): + """Constructor of :class:`DaprFunctionApp` object. + + :param http_auth_level: Determines what keys, if any, need to be + present + on the request in order to invoke the function. + """ + super().__init__(auth_level=http_auth_level) \ No newline at end of file diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py index d7c372ad..f7902469 100644 --- a/azure/functions/decorators/function_app.py +++ b/azure/functions/decorators/function_app.py @@ -32,7 +32,6 @@ from .warmup import WarmUpTrigger from .._http_asgi import AsgiMiddleware from .._http_wsgi import WsgiMiddleware, Context -from azure.functions.decorators.dapr import DaprServiceInvocationTrigger class Function(object): @@ -1102,51 +1101,6 @@ def decorator(): return decorator() return wrap - - def dapr_service_invocation_trigger(self, - arg_name: str, - method_name: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: - """The dapr_service_invocation_trigger decorator adds - :class:`DaprServiceInvocationTrigger` - to the :class:`FunctionBuilder` object - for building :class:`Function` object used in worker function - indexing model. This is equivalent to defining DaprServiceInvocationTrigger - in the function.json which enables function to be triggered when new - message(s) are sent to the event hub. - All optional fields will be given default value by function host when - they are parsed by function host. - - Ref: https://aka.ms/azure-function-binding-event-hubs - - :param arg_name: The name of the variable that represents - :param method_name: The name of the service method to be invoked by Dapr. - :param data_type: Defines how Functions runtime should treat the - parameter value. - :param kwargs: Keyword arguments for specifying additional binding - fields to include in the binding json. - - :return: Decorator function. - """ - - @self._configure_function_builder - def wrap(fb): - def decorator(): - fb.add_trigger( - trigger=DaprServiceInvocationTrigger( - name=arg_name, - method_name=method_name, - data_type=parse_singular_param_to_enum(data_type, - DataType), - **kwargs)) - return fb - - return decorator() - - return wrap - class BindingApi(DecoratorApi, ABC): """Interface to extend for using existing binding decorator functions.""" diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py new file mode 100644 index 00000000..e33558a7 --- /dev/null +++ b/tests/decorators/test_dapr.py @@ -0,0 +1,89 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import json +import unittest + +from azure.functions.decorators.core import BindingDirection +from azure.functions.decorators.dapr_function_app import DaprFunctionApp +from azure.functions.decorators.constants import DAPR_BINDING_TRIGGER, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_TOPIC_TRIGGER +from tests.decorators.testutils import assert_json + + +class TestDapr(unittest.TestCase): + def setUp(self): + self.dapr_func_app = DaprFunctionApp() + + def _get_user_function(self, app): + funcs = app.get_functions() + self.assertEqual(len(funcs), 1) + return funcs[0] + + def test_dapr_service_invocation_trigger_default_args(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy_method_name") + + def dummy(): + pass + + func = self._get_user_function(app) + + assert_json(self, func, {"scriptFile": "function_app.py", + "bindings": [ + { + "direction": BindingDirection.IN, + "type": DAPR_SERVICE_INVOCATION_TRIGGER, + "name": "req", + "methodName": "dummy_method_name" + } + ] + }) + + def test_dapr_binding_trigger_default_args(self): + app = self.dapr_func_app + + @app.dapr_binding_trigger(arg_name="req", + binding_name="dummy_binding_name") + + def dummy(): + pass + + func = self._get_user_function(app) + + assert_json(self, func, {"scriptFile": "function_app.py", + "bindings": [ + { + "direction": BindingDirection.IN, + "type": DAPR_BINDING_TRIGGER, + "name": "req", + "bindingName": "dummy_binding_name" + } + ] + }) + + def test_dapr_topic_trigger_default_args(self): + app = self.dapr_func_app + + @app.dapr_topic_trigger(arg_name="req", + pub_sub_name="dummy_pub_sub_name", + topic="dummy_topic", + route="/dummy_route") + + def dummy(): + pass + + func = self._get_user_function(app) + + assert_json(self, func, {"scriptFile": "function_app.py", + "bindings": [ + { + "direction": BindingDirection.IN, + "type": DAPR_TOPIC_TRIGGER, + "name": "req", + "pubSubName": "dummy_pub_sub_name", + "topic":"dummy_topic", + "route":"/dummy_route" + } + ] + }) \ No newline at end of file From 3b1021293d3dae1cafbdeeefb93ac211b4d84587 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 8 Jun 2023 19:32:26 +0530 Subject: [PATCH 06/26] Adding bindings for Dapr extension Signed-off-by: MD Ashique --- azure/functions/decorators/constants.py | 6 + azure/functions/decorators/dapr.py | 110 ++++- .../functions/decorators/dapr_function_app.py | 376 ++++++++++++++++-- 3 files changed, 467 insertions(+), 25 deletions(-) diff --git a/azure/functions/decorators/constants.py b/azure/functions/decorators/constants.py index fe9ab611..595a7679 100644 --- a/azure/functions/decorators/constants.py +++ b/azure/functions/decorators/constants.py @@ -22,3 +22,9 @@ DAPR_SERVICE_INVOCATION_TRIGGER="daprServiceInvocationTrigger" DAPR_BINDING_TRIGGER="daprBindingTrigger" DAPR_TOPIC_TRIGGER="daprTopicTrigger" +DAPR_STATE = "daprState" +DAPR_SECRET = "daprSecret" +DAPR_PUBLISH = "daprPublish" +DAPR_INVOKE = "daprInvoke" +DAPR_PUBLISH = "daprPublish" +DAPR_BINDING = "daprBinding" \ No newline at end of file diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index 364e2866..907be07a 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -2,8 +2,8 @@ # Licensed under the MIT License. from typing import Optional -from azure.functions.decorators.constants import DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_TOPIC_TRIGGER -from azure.functions.decorators.core import Trigger, DataType, OutputBinding, \ +from azure.functions.decorators.constants import DAPR_BINDING, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER +from azure.functions.decorators.core import InputBinding, Trigger, DataType, OutputBinding, \ Cardinality @@ -51,4 +51,110 @@ def __init__(self, self.pub_sub_name = pub_sub_name self.topic = topic self.route = route + super().__init__(name=name, data_type=data_type) + +class DaprStateInput(InputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_STATE + + def __init__(self, + name: str, + state_store: str, + key: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.state_store = state_store + self.key = key + self.dapr_address = dapr_address + super().__init__(name=name, data_type=data_type) + +class DaprSecretInput(InputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_SECRET + + def __init__(self, + name: str, + secretS_store_name: str, + key: str, + metadata: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.secretS_store_name = secretS_store_name + self.key = key + self.metadata = metadata + self.dapr_address = dapr_address + super().__init__(name=name, data_type=data_type) + +class DaprStateOutput(OutputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_STATE + + def __init__(self, + name: str, + state_store: str, + key: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.state_store = state_store + self.key = key + self.dapr_address = dapr_address + super().__init__(name=name, data_type=data_type) + +class DaprInvokeOutput(OutputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_INVOKE + + def __init__(self, + name: str, + app_id: str, + method_name: str, + http_verb: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.app_id = app_id + self.method_name = method_name + self.http_verb = http_verb + self.dapr_address = dapr_address + super().__init__(name=name, data_type=data_type) + +class DaprPublishOutput(OutputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_PUBLISH + + def __init__(self, + name: str, + pub_sub_name: str, + topic: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.pub_sub_name = pub_sub_name + self.topic = topic + self.dapr_address = dapr_address + super().__init__(name=name, data_type=data_type) + +class DaprBindingOutput(OutputBinding): + @staticmethod + def get_binding_name() -> str: + return DAPR_BINDING + + def __init__(self, + name: str, + binding_name: str, + operation: str, + dapr_address: Optional[str], + data_type: Optional[DataType] = None, + **kwargs): + self.binding_name = binding_name + self.operation = operation + self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) \ No newline at end of file diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 94c3424d..d92fbb42 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -5,16 +5,16 @@ from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum from azure.functions.decorators.function_app import DecoratorApi, FunctionRegister -from azure.functions.decorators.dapr import DaprBindingTrigger, DaprServiceInvocationTrigger, DaprTopicTrigger +from azure.functions.decorators.dapr import DaprBindingOutput, DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger class DaprTriggerApi(DecoratorApi, ABC): def dapr_service_invocation_trigger(self, - arg_name: str, - method_name: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: + arg_name: str, + method_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: """The dapr_service_invocation_trigger decorator adds :class:`DaprServiceInvocationTrigger` to the :class:`FunctionBuilder` object @@ -25,10 +25,12 @@ def dapr_service_invocation_trigger(self, All optional fields will be given default value by function host when they are parsed by function host. - Ref: https://aka.ms/azure-function-binding-event-hubs + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-trigger-dapr :param arg_name: The name of the variable that represents - :param method_name: The name of the service method to be invoked by Dapr. + :param method_name: The name of the method on a remote Dapr App. + If not specified, the name of the function is used as the method name. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -64,15 +66,16 @@ def dapr_binding_trigger(self, to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprBindingTrigger - in the function.json which enables function to be triggered when new - message(s) are sent to the event hub. + in the function.json which enables function to be triggered on Dapr input binding. All optional fields will be given default value by function host when they are parsed by function host. - Ref: https://aka.ms/azure-function-binding-event-hubs + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-trigger-dapr :param arg_name: The name of the variable that represents - :param binding_name: The name of the binding to be invoked by Dapr. + :param binding_name: The name of the Dapr trigger. + If not specified, the name of the function is used as the trigger name. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -98,13 +101,13 @@ def decorator(): return wrap def dapr_topic_trigger(self, - arg_name: str, - pub_sub_name: str, - topic: str, - route: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: + arg_name: str, + pub_sub_name: str, + topic: str, + route: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: """The dapr_topic_trigger decorator adds :class:`DaprTopicTrigger` to the :class:`FunctionBuilder` object @@ -115,12 +118,13 @@ def dapr_topic_trigger(self, All optional fields will be given default value by function host when they are parsed by function host. - Ref: https://aka.ms/azure-function-binding-event-hubs + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-trigger-dapr :param arg_name: The name of the variable that represents - :param pub_sub_name: The name of the pubsub. - :param topic: The name of the topic. - :param route: The name of the route. + :param pub_sub_name: The pub/sub name. + :param topic: The topic. If unspecified the function name will be used. + :param route: The route for the trigger. If unspecified the topic name will be used. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -147,6 +151,332 @@ def decorator(): return wrap +class DaprBindingApi(DecoratorApi, ABC): + + def dapr_state_input(self, + arg_name: str, + state_store: str, + key: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_state_input decorator adds + :class:`DaprStateInput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprStateInput + in the function.json which enables function to read state from underlying state store component. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param state_store: State store containing the state. + :param key: The name of the key. + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprStateInput( + name=arg_name, + state_store=state_store, + key=key, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_secret_input(self, + arg_name: str, + secret_store_name: str, + key: str, + metadata: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_secret_input decorator adds + :class:`DaprSecretInput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. This is equivalent to defining DaprSecretInput + in the function.json which enables function to read secret from underlying secret store component. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param secret_store_name: The name of the secret store to get the secret from. + :param key: The key identifying the name of the secret to get. + :param metadata: An array of metadata properties in the form "key1=value1&key2=value2". + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value. + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json. + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprSecretInput( + name=arg_name, + secret_store_name=secret_store_name, + key=key, + metadata=metadata, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_state_output(self, + arg_name: str, + state_store: str, + key: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_state_output decorator adds + :class:`DaprStateOutput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. + This is equivalent to defining DaprStateOutput + in the function.json which enables function to write to the dapr state store. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + output object in function code. + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param state_store: State store containing the state for keys. + :param key: The name of the key. + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprStateOutput( + name=arg_name, + state_store=state_store, + key=key, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_invoke_output(self, + arg_name: str, + app_id: str, + method_name: str, + http_verb: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_invoke_output decorator adds + :class:`DaprInvokeOutput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. + This is equivalent to defining DaprInvokeOutput + in the function.json which enables function to invoke another Dapr App. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + output object in function code. + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param app_id: The dapr app name to invoke. + :param method_name: The method name of the app to invoke. + :param http_verb: The http verb of the app to invoke. + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprInvokeOutput( + name=arg_name, + app_id=app_id, + method_name=method_name, + http_verb=http_verb, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_publish_output(self, + arg_name: str, + pub_sub_name: str, + topic: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_publish_output decorator adds + :class:`DaprPublishOutput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. + This is equivalent to defining DaprPublishOutput + in the function.json which enables function to publish topic. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + output object in function code. + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param pub_sub_name: The pub/sub name to publish to. + :param topic: The name of the topic to publish to. + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprPublishOutput( + name=arg_name, + pub_sub_name=pub_sub_name, + topic=topic, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + + def dapr_binding_output(self, + arg_name: str, + binding_name: str, + operation: str, + dapr_address: Optional[str], + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs) \ + -> Callable[..., Any]: + """The dapr_binding_output decorator adds + :class:`DaprBindingOutput` to the :class:`FunctionBuilder` object + for building :class:`Function` object used in worker function + indexing model. + This is equivalent to defining DaprBindingOutput + in the function.json which enables function to send a value to a Dapr output binding. + All optional fields will be given default value by function host when + they are parsed by function host. + + TODO: need to add ref for documentation + Ref: https://aka.ms/azure-function-binding-dapr + + :param arg_name: The name of the variable that represents DaprState + output object in function code. + :param arg_name: The name of the variable that represents DaprState + input object in function code. + :param binding_name: The configured name of the binding. + :param operation: The configured operation. + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param data_type: Defines how Functions runtime should treat the + parameter value + :param kwargs: Keyword arguments for specifying additional binding + fields to include in the binding json + + :return: Decorator function. + """ + + @self._configure_function_builder + def wrap(fb): + def decorator(): + fb.add_binding( + binding=DaprBindingOutput( + name=arg_name, + binding_name=binding_name, + operation=operation, + dapr_address=dapr_address, + data_type=parse_singular_param_to_enum(data_type, + DataType), + **kwargs)) + return fb + + return decorator() + + return wrap + class DaprFunctionApp(FunctionRegister, DaprTriggerApi): def __init__(self, From 1c385a12b07db81651c302b1ec92c3ef21452858 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 8 Jun 2023 20:47:58 +0530 Subject: [PATCH 07/26] Adding test for dapr bindings Signed-off-by: MD Ashique --- azure/functions/decorators/dapr.py | 4 +- .../functions/decorators/dapr_function_app.py | 20 ++- out.txt | 0 tests/decorators/test_dapr.py | 158 +++++++++++++++++- 4 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 out.txt diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index 907be07a..ee9566ae 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -77,13 +77,13 @@ def get_binding_name() -> str: def __init__(self, name: str, - secretS_store_name: str, + secret_store_name: str, key: str, metadata: str, dapr_address: Optional[str], data_type: Optional[DataType] = None, **kwargs): - self.secretS_store_name = secretS_store_name + self.secret_store_name = secret_store_name self.key = key self.metadata = metadata self.dapr_address = dapr_address diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index d92fbb42..1c2adbf1 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -157,7 +157,8 @@ def dapr_state_input(self, arg_name: str, state_store: str, key: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -209,7 +210,8 @@ def dapr_secret_input(self, secret_store_name: str, key: str, metadata: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -262,7 +264,8 @@ def dapr_state_output(self, arg_name: str, state_store: str, key: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -317,7 +320,8 @@ def dapr_invoke_output(self, app_id: str, method_name: str, http_verb: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -373,7 +377,8 @@ def dapr_publish_output(self, arg_name: str, pub_sub_name: str, topic: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -427,7 +432,8 @@ def dapr_binding_output(self, arg_name: str, binding_name: str, operation: str, - dapr_address: Optional[str], + dapr_address: Optional[ + Union[DataType, str]] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -477,7 +483,7 @@ def decorator(): return wrap -class DaprFunctionApp(FunctionRegister, DaprTriggerApi): +class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi): def __init__(self, http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION): diff --git a/out.txt b/out.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index e33558a7..f541aeba 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -5,7 +5,7 @@ from azure.functions.decorators.core import BindingDirection from azure.functions.decorators.dapr_function_app import DaprFunctionApp -from azure.functions.decorators.constants import DAPR_BINDING_TRIGGER, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_TOPIC_TRIGGER +from azure.functions.decorators.constants import DAPR_BINDING, DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER from tests.decorators.testutils import assert_json @@ -86,4 +86,158 @@ def dummy(): "route":"/dummy_route" } ] - }) \ No newline at end of file + }) + + def test_dapr_state_input_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_state_input(arg_name="in", + state_store="dummy_state_store", + key="dummy_key") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.IN, + "type": DAPR_STATE, + "name": "in", + "stateStore": "dummy_state_store", + "key": "dummy_key" + }) + + def test_dapr_secret_input_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_secret_input(arg_name="in", + secret_store_name="dummy_secret_store_name", + key="dummy_key", + metadata="dummy_metadata") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.IN, + "type": DAPR_SECRET, + "name": "in", + "secretStoreName": "dummy_secret_store_name", + "key": "dummy_key", + "metadata": "dummy_metadata" + }) + + def test_dapr_state_output_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_state_output(arg_name="out", + state_store="dummy_state_store", + key="dummy_key") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.OUT, + "type": DAPR_STATE, + "name": "out", + "stateStore": "dummy_state_store", + "key": "dummy_key" + }) + + def test_dapr_invoke_output_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_invoke_output(arg_name="out", + app_id="dummy_app_id", + method_name="dummy_method_name", + http_verb="dummy_http_verb") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.OUT, + "type": DAPR_INVOKE, + "name": "out", + "appId": "dummy_app_id", + "methodName": "dummy_method_name", + "httpVerb": "dummy_http_verb" + }) + + def test_dapr_publish_output_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_publish_output(arg_name="out", + pub_sub_name="dummy_pub_sub_name", + topic="dummy_topic") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.OUT, + "type": DAPR_PUBLISH, + "name": "out", + "pubSubName": "dummy_pub_sub_name", + "topic": "dummy_topic" + }) + + def test_dapr_binding_output_binding(self): + app = self.dapr_func_app + + @app.dapr_service_invocation_trigger(arg_name="req", + method_name="dummy") + @app.dapr_binding_output(arg_name="out", + binding_name="dummy_binding_name", + operation="dummy_operation") + def dummy(): + pass + + func = self._get_user_function(app) + + self.assertEqual(len(func.get_bindings()), 2) + + output = func.get_bindings()[0] + + self.assertEqual(output.get_dict_repr(), { + "direction": BindingDirection.OUT, + "type": DAPR_BINDING, + "name": "out", + "bindingName": "dummy_binding_name", + "operation": "dummy_operation" + }) \ No newline at end of file From 9a30bb9e587a41c5cccb92b139d47a663a24f0fe Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 8 Jun 2023 20:50:37 +0530 Subject: [PATCH 08/26] Adding test for dapr bindings Signed-off-by: MD Ashique --- out.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 out.txt diff --git a/out.txt b/out.txt deleted file mode 100644 index e69de29b..00000000 From 563993efa4e0b8975814bd8c109a9ec2be405654 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Tue, 13 Jun 2023 16:35:21 +0530 Subject: [PATCH 09/26] Using TriggerApi and BindingApi instead of DecoratorApi Signed-off-by: MD Ashique --- azure/functions/decorators/dapr_function_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 1c2adbf1..b24552ee 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -4,10 +4,10 @@ from typing import Any, Callable, Optional, Union from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum -from azure.functions.decorators.function_app import DecoratorApi, FunctionRegister +from azure.functions.decorators.function_app import BindingApi, FunctionRegister, TriggerApi from azure.functions.decorators.dapr import DaprBindingOutput, DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger -class DaprTriggerApi(DecoratorApi, ABC): +class DaprTriggerApi(TriggerApi, ABC): def dapr_service_invocation_trigger(self, arg_name: str, @@ -151,7 +151,7 @@ def decorator(): return wrap -class DaprBindingApi(DecoratorApi, ABC): +class DaprBindingApi(BindingApi, ABC): def dapr_state_input(self, arg_name: str, From 66a2ac2126e69700fe877d7b13e52b0642581164 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Thu, 15 Jun 2023 19:09:23 +0530 Subject: [PATCH 10/26] Making route optional param Signed-off-by: MD Ashique --- azure/functions/decorators/dapr.py | 2 +- azure/functions/decorators/dapr_function_app.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index ee9566ae..f87bc980 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -45,7 +45,7 @@ def __init__(self, name: str, pub_sub_name: str, topic: str, - route: str, + route: Optional[str] = None, data_type: Optional[DataType] = None, **kwargs): self.pub_sub_name = pub_sub_name diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index b24552ee..296b7729 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -104,7 +104,7 @@ def dapr_topic_trigger(self, arg_name: str, pub_sub_name: str, topic: str, - route: str, + route: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs: Any) -> Callable[..., Any]: From 2dfbd7b8005de2806bfd19a4679e02feb4840559 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Mon, 19 Jun 2023 16:37:31 +0530 Subject: [PATCH 11/26] Adding aka.ms links for triggers and bindings Signed-off-by: MD Ashique --- .../functions/decorators/dapr_function_app.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 296b7729..abaf53f5 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -25,8 +25,7 @@ def dapr_service_invocation_trigger(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-trigger-dapr + Ref: https://aka.ms/azure-function-dapr-trigger-service-invocation :param arg_name: The name of the variable that represents :param method_name: The name of the method on a remote Dapr App. @@ -70,8 +69,7 @@ def dapr_binding_trigger(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-trigger-dapr + Ref: https://aka.ms/azure-function-dapr-trigger-binding :param arg_name: The name of the variable that represents :param binding_name: The name of the Dapr trigger. @@ -118,8 +116,7 @@ def dapr_topic_trigger(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-trigger-dapr + Ref: https://aka.ms/azure-function-dapr-trigger-topic :param arg_name: The name of the variable that represents :param pub_sub_name: The pub/sub name. @@ -171,8 +168,7 @@ def dapr_state_input(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-state-input-binding :param arg_name: The name of the variable that represents DaprState input object in function code. @@ -224,8 +220,7 @@ def dapr_secret_input(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-secret-input-binding :param arg_name: The name of the variable that represents DaprState input object in function code. @@ -279,8 +274,7 @@ def dapr_state_output(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-state-output-binding :param arg_name: The name of the variable that represents DaprState output object in function code. @@ -335,8 +329,7 @@ def dapr_invoke_output(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-invoke-output-binding :param arg_name: The name of the variable that represents DaprState output object in function code. @@ -392,8 +385,7 @@ def dapr_publish_output(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-publish-output-binding :param arg_name: The name of the variable that represents DaprState output object in function code. @@ -447,8 +439,7 @@ def dapr_binding_output(self, All optional fields will be given default value by function host when they are parsed by function host. - TODO: need to add ref for documentation - Ref: https://aka.ms/azure-function-binding-dapr + Ref: https://aka.ms/azure-function-dapr-binding-output-binding :param arg_name: The name of the variable that represents DaprState output object in function code. From b0cd7ccdd38f1b820495afcd6a823aa827ab1562 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Wed, 21 Jun 2023 18:25:31 +0530 Subject: [PATCH 12/26] assigning topic to route when route is empty Signed-off-by: MD Ashique --- azure/functions/decorators/dapr_function_app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index abaf53f5..ce11ad16 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -129,6 +129,8 @@ def dapr_topic_trigger(self, :return: Decorator function. """ + if not route : + route = topic @self._configure_function_builder def wrap(fb): From 222a199e43b917af0af92deec31e9f51986ecfa4 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Wed, 21 Jun 2023 18:28:54 +0530 Subject: [PATCH 13/26] assigning topic to route when route is empty Signed-off-by: MD Ashique --- azure/functions/decorators/dapr_function_app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index ce11ad16..0a4d7aae 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -129,6 +129,9 @@ def dapr_topic_trigger(self, :return: Decorator function. """ + #TODO: This check is temporary, it should be removed once route issue is fixed at python worker. + #Currently, python worker treats route as HttpTrigger attribute and expects value for route. + #route could be nil for dapr topic trigger. if not route : route = topic From 26b58bc995f0747d415e2c4223a6f7d0d89c1b08 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 13:23:10 +0530 Subject: [PATCH 14/26] Fixed dapr_address param type Signed-off-by: MD Ashique --- .../functions/decorators/dapr_function_app.py | 18 +++++-------- tests/decorators/test_decorators.py | 27 ++----------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 0a4d7aae..48c4e37a 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -159,8 +159,7 @@ def dapr_state_input(self, arg_name: str, state_store: str, key: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -211,8 +210,7 @@ def dapr_secret_input(self, secret_store_name: str, key: str, metadata: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -264,8 +262,7 @@ def dapr_state_output(self, arg_name: str, state_store: str, key: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -319,8 +316,7 @@ def dapr_invoke_output(self, app_id: str, method_name: str, http_verb: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -375,8 +371,7 @@ def dapr_publish_output(self, arg_name: str, pub_sub_name: str, topic: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ @@ -429,8 +424,7 @@ def dapr_binding_output(self, arg_name: str, binding_name: str, operation: str, - dapr_address: Optional[ - Union[DataType, str]] = None, + dapr_address: Optional[str] = None, data_type: Optional[ Union[DataType, str]] = None, **kwargs) \ diff --git a/tests/decorators/test_decorators.py b/tests/decorators/test_decorators.py index d3b5433a..c381617b 100644 --- a/tests/decorators/test_decorators.py +++ b/tests/decorators/test_decorators.py @@ -5,8 +5,7 @@ from azure.functions.decorators.constants import TIMER_TRIGGER, HTTP_TRIGGER, \ HTTP_OUTPUT, QUEUE, QUEUE_TRIGGER, SERVICE_BUS, SERVICE_BUS_TRIGGER, \ EVENT_HUB, EVENT_HUB_TRIGGER, COSMOS_DB, COSMOS_DB_TRIGGER, BLOB, \ - BLOB_TRIGGER, EVENT_GRID_TRIGGER, EVENT_GRID, TABLE, WARMUP_TRIGGER, \ - DAPR_SERVICE_INVOCATION_TRIGGER + BLOB_TRIGGER, EVENT_GRID_TRIGGER, EVENT_GRID, TABLE, WARMUP_TRIGGER from azure.functions.decorators.core import DataType, AuthLevel, \ BindingDirection, AccessRights, Cardinality from azure.functions.decorators.function_app import FunctionApp @@ -2093,26 +2092,4 @@ def _test_function_metadata_order(self, app): for _ in range(3): new_metadata_payload = str(func) self.assertEqual(new_metadata_payload, last_metadata_payload) - last_metadata_payload = new_metadata_payload - - def test_dapr_service_invocation_default_args(self): - app = self.func_app - - @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy_method_name") - - def dummy(): - pass - - func = self._get_user_function(app) - - assert_json(self, func, {"scriptFile": "function_app.py", - "bindings": [ - { - "direction": BindingDirection.IN, - "type": DAPR_SERVICE_INVOCATION_TRIGGER, - "name": "req", - "methodName": "dummy_method_name" - } - ] - }) + last_metadata_payload = new_metadata_payload \ No newline at end of file From b4cbac28d00731059d5a214d21166e2178ac3cb0 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 13:36:46 +0530 Subject: [PATCH 15/26] Fixed dapr_address param type Signed-off-by: MD Ashique --- tests/decorators/test_dapr.py | 7 +++---- tests/decorators/test_decorators.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index f541aeba..63c83ac5 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -1,14 +1,13 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import json import unittest from azure.functions.decorators.core import BindingDirection from azure.functions.decorators.dapr_function_app import DaprFunctionApp -from azure.functions.decorators.constants import DAPR_BINDING, DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER +from azure.functions.decorators.constants import DAPR_BINDING, DAPR_BINDING_TRIGGER, \ + DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER from tests.decorators.testutils import assert_json - class TestDapr(unittest.TestCase): def setUp(self): self.dapr_func_app = DaprFunctionApp() @@ -22,7 +21,7 @@ def test_dapr_service_invocation_trigger_default_args(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy_method_name") + method_name="dummy_method_name") def dummy(): pass diff --git a/tests/decorators/test_decorators.py b/tests/decorators/test_decorators.py index c381617b..146921af 100644 --- a/tests/decorators/test_decorators.py +++ b/tests/decorators/test_decorators.py @@ -2092,4 +2092,4 @@ def _test_function_metadata_order(self, app): for _ in range(3): new_metadata_payload = str(func) self.assertEqual(new_metadata_payload, last_metadata_payload) - last_metadata_payload = new_metadata_payload \ No newline at end of file + last_metadata_payload = new_metadata_payload From e4e3351b2880f7f90aa59ed4e53f1c1e3447e645 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:01:07 +0530 Subject: [PATCH 16/26] fixed indentations Signed-off-by: MD Ashique --- azure/functions/decorators/constants.py | 8 +- azure/functions/decorators/dapr.py | 18 ++- .../functions/decorators/dapr_function_app.py | 104 ++++++++++-------- tests/decorators/test_dapr.py | 52 ++++----- 4 files changed, 101 insertions(+), 81 deletions(-) diff --git a/azure/functions/decorators/constants.py b/azure/functions/decorators/constants.py index 595a7679..c622812e 100644 --- a/azure/functions/decorators/constants.py +++ b/azure/functions/decorators/constants.py @@ -19,12 +19,12 @@ EVENT_GRID_TRIGGER = "eventGridTrigger" EVENT_GRID = "eventGrid" TABLE = "table" -DAPR_SERVICE_INVOCATION_TRIGGER="daprServiceInvocationTrigger" -DAPR_BINDING_TRIGGER="daprBindingTrigger" -DAPR_TOPIC_TRIGGER="daprTopicTrigger" +DAPR_SERVICE_INVOCATION_TRIGGER = "daprServiceInvocationTrigger" +DAPR_BINDING_TRIGGER = "daprBindingTrigger" +DAPR_TOPIC_TRIGGER = "daprTopicTrigger" DAPR_STATE = "daprState" DAPR_SECRET = "daprSecret" DAPR_PUBLISH = "daprPublish" DAPR_INVOKE = "daprInvoke" DAPR_PUBLISH = "daprPublish" -DAPR_BINDING = "daprBinding" \ No newline at end of file +DAPR_BINDING = "daprBinding" diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index f87bc980..c71ae28c 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -2,9 +2,11 @@ # Licensed under the MIT License. from typing import Optional -from azure.functions.decorators.constants import DAPR_BINDING, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER -from azure.functions.decorators.core import InputBinding, Trigger, DataType, OutputBinding, \ - Cardinality +from azure.functions.decorators.constants import DAPR_BINDING, DAPR_INVOKE, DAPR_PUBLISH, \ + DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_STATE, \ + DAPR_TOPIC_TRIGGER +from azure.functions.decorators.core import InputBinding, Trigger, DataType, \ + OutputBinding class DaprServiceInvocationTrigger(Trigger): @@ -21,6 +23,7 @@ def __init__(self, self.method_name = method_name super().__init__(name=name, data_type=data_type) + class DaprBindingTrigger(Trigger): @staticmethod @@ -35,6 +38,7 @@ def __init__(self, self.binding_name = binding_name super().__init__(name=name, data_type=data_type) + class DaprTopicTrigger(Trigger): @staticmethod @@ -53,6 +57,7 @@ def __init__(self, self.route = route super().__init__(name=name, data_type=data_type) + class DaprStateInput(InputBinding): @staticmethod def get_binding_name() -> str: @@ -70,6 +75,7 @@ def __init__(self, self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) + class DaprSecretInput(InputBinding): @staticmethod def get_binding_name() -> str: @@ -89,6 +95,7 @@ def __init__(self, self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) + class DaprStateOutput(OutputBinding): @staticmethod def get_binding_name() -> str: @@ -106,6 +113,7 @@ def __init__(self, self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) + class DaprInvokeOutput(OutputBinding): @staticmethod def get_binding_name() -> str: @@ -125,6 +133,7 @@ def __init__(self, self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) + class DaprPublishOutput(OutputBinding): @staticmethod def get_binding_name() -> str: @@ -142,6 +151,7 @@ def __init__(self, self.dapr_address = dapr_address super().__init__(name=name, data_type=data_type) + class DaprBindingOutput(OutputBinding): @staticmethod def get_binding_name() -> str: @@ -157,4 +167,4 @@ def __init__(self, self.binding_name = binding_name self.operation = operation self.dapr_address = dapr_address - super().__init__(name=name, data_type=data_type) \ No newline at end of file + super().__init__(name=name, data_type=data_type) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 48c4e37a..14bfe36e 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -55,11 +55,11 @@ def decorator(): return wrap def dapr_binding_trigger(self, - arg_name: str, - binding_name: str, - data_type: Optional[ - Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: + arg_name: str, + binding_name: str, + data_type: Optional[ + Union[DataType, str]] = None, + **kwargs: Any) -> Callable[..., Any]: """The dapr_binding_trigger decorator adds :class:`DaprBindingTrigger` to the :class:`FunctionBuilder` object @@ -129,9 +129,10 @@ def dapr_topic_trigger(self, :return: Decorator function. """ - #TODO: This check is temporary, it should be removed once route issue is fixed at python worker. - #Currently, python worker treats route as HttpTrigger attribute and expects value for route. - #route could be nil for dapr topic trigger. + #TODO: This is a temporary check, it should be removed once route issue is + # fixed at python worker. + # Currently, python worker treats route as HttpTrigger attribute and + # expects value for route. Route could be nil for dapr topic trigger. if not route : route = topic @@ -168,7 +169,8 @@ def dapr_state_input(self, :class:`DaprStateInput` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprStateInput - in the function.json which enables function to read state from underlying state store component. + in the function.json which enables function to read state from underlying + state store component. All optional fields will be given default value by function host when they are parsed by function host. @@ -178,7 +180,8 @@ def dapr_state_input(self, input object in function code. :param state_store: State store containing the state. :param key: The name of the key. - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -204,16 +207,16 @@ def decorator(): return decorator() return wrap - + def dapr_secret_input(self, - arg_name: str, - secret_store_name: str, - key: str, - metadata: str, - dapr_address: Optional[str] = None, - data_type: Optional[ + arg_name: str, + secret_store_name: str, + key: str, + metadata: str, + dapr_address: Optional[str] = None, + data_type: Optional[ Union[DataType, str]] = None, - **kwargs) \ + **kwargs) \ -> Callable[..., Any]: """The dapr_secret_input decorator adds :class:`DaprSecretInput` to the :class:`FunctionBuilder` object @@ -230,7 +233,8 @@ def dapr_secret_input(self, :param secret_store_name: The name of the secret store to get the secret from. :param key: The key identifying the name of the secret to get. :param metadata: An array of metadata properties in the form "key1=value1&key2=value2". - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -257,7 +261,7 @@ def decorator(): return decorator() return wrap - + def dapr_state_output(self, arg_name: str, state_store: str, @@ -284,7 +288,8 @@ def dapr_state_output(self, input object in function code. :param state_store: State store containing the state for keys. :param key: The name of the key. - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value :param kwargs: Keyword arguments for specifying additional binding @@ -312,14 +317,14 @@ def decorator(): return wrap def dapr_invoke_output(self, - arg_name: str, - app_id: str, - method_name: str, - http_verb: str, - dapr_address: Optional[str] = None, - data_type: Optional[ + arg_name: str, + app_id: str, + method_name: str, + http_verb: str, + dapr_address: Optional[str] = None, + data_type: Optional[ Union[DataType, str]] = None, - **kwargs) \ + **kwargs) \ -> Callable[..., Any]: """The dapr_invoke_output decorator adds :class:`DaprInvokeOutput` to the :class:`FunctionBuilder` object @@ -339,7 +344,8 @@ def dapr_invoke_output(self, :param app_id: The dapr app name to invoke. :param method_name: The method name of the app to invoke. :param http_verb: The http verb of the app to invoke. - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value :param kwargs: Keyword arguments for specifying additional binding @@ -366,15 +372,15 @@ def decorator(): return decorator() return wrap - + def dapr_publish_output(self, - arg_name: str, - pub_sub_name: str, - topic: str, - dapr_address: Optional[str] = None, - data_type: Optional[ + arg_name: str, + pub_sub_name: str, + topic: str, + dapr_address: Optional[str] = None, + data_type: Optional[ Union[DataType, str]] = None, - **kwargs) \ + **kwargs) \ -> Callable[..., Any]: """The dapr_publish_output decorator adds :class:`DaprPublishOutput` to the :class:`FunctionBuilder` object @@ -393,7 +399,8 @@ def dapr_publish_output(self, input object in function code. :param pub_sub_name: The pub/sub name to publish to. :param topic: The name of the topic to publish to. - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + ßit will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value :param kwargs: Keyword arguments for specifying additional binding @@ -419,22 +426,23 @@ def decorator(): return decorator() return wrap - + def dapr_binding_output(self, - arg_name: str, - binding_name: str, - operation: str, - dapr_address: Optional[str] = None, - data_type: Optional[ + arg_name: str, + binding_name: str, + operation: str, + dapr_address: Optional[str] = None, + data_type: Optional[ Union[DataType, str]] = None, - **kwargs) \ + **kwargs) \ -> Callable[..., Any]: """The dapr_binding_output decorator adds :class:`DaprBindingOutput` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprBindingOutput - in the function.json which enables function to send a value to a Dapr output binding. + in the function.json which enables function to send a value to + a Dapr output binding. All optional fields will be given default value by function host when they are parsed by function host. @@ -446,7 +454,8 @@ def dapr_binding_output(self, input object in function code. :param binding_name: The configured name of the binding. :param operation: The configured operation. - :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default it will be set + to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value :param kwargs: Keyword arguments for specifying additional binding @@ -473,6 +482,7 @@ def decorator(): return wrap + class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi): def __init__(self, @@ -483,4 +493,4 @@ def __init__(self, present on the request in order to invoke the function. """ - super().__init__(auth_level=http_auth_level) \ No newline at end of file + super().__init__(auth_level=http_auth_level) diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index 63c83ac5..fe03efaa 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -43,7 +43,7 @@ def test_dapr_binding_trigger_default_args(self): app = self.dapr_func_app @app.dapr_binding_trigger(arg_name="req", - binding_name="dummy_binding_name") + binding_name="dummy_binding_name") def dummy(): pass @@ -91,10 +91,10 @@ def test_dapr_state_input_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_state_input(arg_name="in", - state_store="dummy_state_store", - key="dummy_key") + state_store="dummy_state_store", + key="dummy_key") def dummy(): pass @@ -116,11 +116,11 @@ def test_dapr_secret_input_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_secret_input(arg_name="in", - secret_store_name="dummy_secret_store_name", - key="dummy_key", - metadata="dummy_metadata") + secret_store_name="dummy_secret_store_name", + key="dummy_key", + metadata="dummy_metadata") def dummy(): pass @@ -129,7 +129,7 @@ def dummy(): self.assertEqual(len(func.get_bindings()), 2) output = func.get_bindings()[0] - + self.assertEqual(output.get_dict_repr(), { "direction": BindingDirection.IN, "type": DAPR_SECRET, @@ -143,10 +143,10 @@ def test_dapr_state_output_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_state_output(arg_name="out", - state_store="dummy_state_store", - key="dummy_key") + state_store="dummy_state_store", + key="dummy_key") def dummy(): pass @@ -155,7 +155,7 @@ def dummy(): self.assertEqual(len(func.get_bindings()), 2) output = func.get_bindings()[0] - + self.assertEqual(output.get_dict_repr(), { "direction": BindingDirection.OUT, "type": DAPR_STATE, @@ -168,11 +168,11 @@ def test_dapr_invoke_output_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_invoke_output(arg_name="out", - app_id="dummy_app_id", - method_name="dummy_method_name", - http_verb="dummy_http_verb") + app_id="dummy_app_id", + method_name="dummy_method_name", + http_verb="dummy_http_verb") def dummy(): pass @@ -181,7 +181,7 @@ def dummy(): self.assertEqual(len(func.get_bindings()), 2) output = func.get_bindings()[0] - + self.assertEqual(output.get_dict_repr(), { "direction": BindingDirection.OUT, "type": DAPR_INVOKE, @@ -195,10 +195,10 @@ def test_dapr_publish_output_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_publish_output(arg_name="out", - pub_sub_name="dummy_pub_sub_name", - topic="dummy_topic") + pub_sub_name="dummy_pub_sub_name", + topic="dummy_topic") def dummy(): pass @@ -220,10 +220,10 @@ def test_dapr_binding_output_binding(self): app = self.dapr_func_app @app.dapr_service_invocation_trigger(arg_name="req", - method_name="dummy") + method_name="dummy") @app.dapr_binding_output(arg_name="out", - binding_name="dummy_binding_name", - operation="dummy_operation") + binding_name="dummy_binding_name", + operation="dummy_operation") def dummy(): pass @@ -232,11 +232,11 @@ def dummy(): self.assertEqual(len(func.get_bindings()), 2) output = func.get_bindings()[0] - + self.assertEqual(output.get_dict_repr(), { "direction": BindingDirection.OUT, "type": DAPR_BINDING, "name": "out", "bindingName": "dummy_binding_name", "operation": "dummy_operation" - }) \ No newline at end of file + }) From bb7c21e923d1fdca8dd970f26eab576d72da4f10 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:25:05 +0530 Subject: [PATCH 17/26] fixed indentations Signed-off-by: MD Ashique --- azure/functions/decorators/dapr.py | 6 +- .../functions/decorators/dapr_function_app.py | 83 +++++++++++-------- azure/functions/decorators/function_app.py | 3 +- tests/decorators/test_dapr.py | 28 +++---- 4 files changed, 66 insertions(+), 54 deletions(-) diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr.py index c71ae28c..ff62965d 100644 --- a/azure/functions/decorators/dapr.py +++ b/azure/functions/decorators/dapr.py @@ -2,9 +2,9 @@ # Licensed under the MIT License. from typing import Optional -from azure.functions.decorators.constants import DAPR_BINDING, DAPR_INVOKE, DAPR_PUBLISH, \ - DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_BINDING_TRIGGER, DAPR_STATE, \ - DAPR_TOPIC_TRIGGER +from azure.functions.decorators.constants import DAPR_BINDING, DAPR_INVOKE, \ + DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, \ + DAPR_BINDING_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER from azure.functions.decorators.core import InputBinding, Trigger, DataType, \ OutputBinding diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 14bfe36e..7bbb9554 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -4,8 +4,11 @@ from typing import Any, Callable, Optional, Union from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum -from azure.functions.decorators.function_app import BindingApi, FunctionRegister, TriggerApi -from azure.functions.decorators.dapr import DaprBindingOutput, DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger +from azure.functions.decorators.function_app import BindingApi, FunctionRegister, \ + TriggerApi +from azure.functions.decorators.dapr import DaprBindingOutput, DaprBindingTrigger, \ + DaprInvokeOutput, DaprPublishOutput, DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger + class DaprTriggerApi(TriggerApi, ABC): @@ -19,7 +22,8 @@ def dapr_service_invocation_trigger(self, :class:`DaprServiceInvocationTrigger` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function - indexing model. This is equivalent to defining DaprServiceInvocationTrigger + indexing model. This is equivalent to defining + DaprServiceInvocationTrigger in the function.json which enables function to be triggered when new service invocation occurs through Dapr. All optional fields will be given default value by function host when @@ -28,7 +32,7 @@ def dapr_service_invocation_trigger(self, Ref: https://aka.ms/azure-function-dapr-trigger-service-invocation :param arg_name: The name of the variable that represents - :param method_name: The name of the method on a remote Dapr App. + :param method_name: The name of the method on a remote Dapr App. If not specified, the name of the function is used as the method name. :param data_type: Defines how Functions runtime should treat the parameter value. @@ -46,7 +50,7 @@ def decorator(): name=arg_name, method_name=method_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -55,17 +59,18 @@ def decorator(): return wrap def dapr_binding_trigger(self, - arg_name: str, - binding_name: str, - data_type: Optional[ + arg_name: str, + binding_name: str, + data_type: Optional[ Union[DataType, str]] = None, - **kwargs: Any) -> Callable[..., Any]: + **kwargs: Any) -> Callable[..., Any]: """The dapr_binding_trigger decorator adds :class:`DaprBindingTrigger` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprBindingTrigger - in the function.json which enables function to be triggered on Dapr input binding. + in the function.json which enables function to be triggered + on Dapr input binding. All optional fields will be given default value by function host when they are parsed by function host. @@ -90,7 +95,7 @@ def decorator(): name=arg_name, binding_name=binding_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -121,7 +126,8 @@ def dapr_topic_trigger(self, :param arg_name: The name of the variable that represents :param pub_sub_name: The pub/sub name. :param topic: The topic. If unspecified the function name will be used. - :param route: The route for the trigger. If unspecified the topic name will be used. + :param route: The route for the trigger. If unspecified + the topic name will be used. :param data_type: Defines how Functions runtime should treat the parameter value. :param kwargs: Keyword arguments for specifying additional binding @@ -129,11 +135,11 @@ def dapr_topic_trigger(self, :return: Decorator function. """ - #TODO: This is a temporary check, it should be removed once route issue is - # fixed at python worker. - # Currently, python worker treats route as HttpTrigger attribute and + # TODO: This is a temporary check, it should be removed once route + # issue is fixed at python worker. + # Currently, python worker treats route as HttpTrigger attribute and # expects value for route. Route could be nil for dapr topic trigger. - if not route : + if not route : route = topic @self._configure_function_builder @@ -146,7 +152,7 @@ def decorator(): topic=topic, route=route, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -154,6 +160,7 @@ def decorator(): return wrap + class DaprBindingApi(BindingApi, ABC): def dapr_state_input(self, @@ -169,8 +176,8 @@ def dapr_state_input(self, :class:`DaprStateInput` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprStateInput - in the function.json which enables function to read state from underlying - state store component. + in the function.json which enables function to read state from + underlying state store component. All optional fields will be given default value by function host when they are parsed by function host. @@ -180,7 +187,7 @@ def dapr_state_input(self, input object in function code. :param state_store: State store containing the state. :param key: The name of the key. - :param dapr_address: Dapr address, it is optional field, by default + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value. @@ -215,14 +222,15 @@ def dapr_secret_input(self, metadata: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_secret_input decorator adds :class:`DaprSecretInput` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprSecretInput - in the function.json which enables function to read secret from underlying secret store component. + in the function.json which enables function to read secret from + underlying secret store component. All optional fields will be given default value by function host when they are parsed by function host. @@ -230,10 +238,12 @@ def dapr_secret_input(self, :param arg_name: The name of the variable that represents DaprState input object in function code. - :param secret_store_name: The name of the secret store to get the secret from. + :param secret_store_name: The name of the secret store to + get the secret from. :param key: The key identifying the name of the secret to get. - :param metadata: An array of metadata properties in the form "key1=value1&key2=value2". - :param dapr_address: Dapr address, it is optional field, by default + :param metadata: An array of metadata properties in the form + "key1=value1&key2=value2". + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value. @@ -268,7 +278,7 @@ def dapr_state_output(self, key: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_state_output decorator adds @@ -276,7 +286,8 @@ def dapr_state_output(self, for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprStateOutput - in the function.json which enables function to write to the dapr state store. + in the function.json which enables function to write to the dapr + state store. All optional fields will be given default value by function host when they are parsed by function host. @@ -288,7 +299,7 @@ def dapr_state_output(self, input object in function code. :param state_store: State store containing the state for keys. :param key: The name of the key. - :param dapr_address: Dapr address, it is optional field, by default + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value @@ -323,7 +334,7 @@ def dapr_invoke_output(self, http_verb: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_invoke_output decorator adds @@ -344,7 +355,7 @@ def dapr_invoke_output(self, :param app_id: The dapr app name to invoke. :param method_name: The method name of the app to invoke. :param http_verb: The http verb of the app to invoke. - :param dapr_address: Dapr address, it is optional field, by default + :param dapr_address: Dapr address, it is optional field, by default it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value @@ -379,7 +390,7 @@ def dapr_publish_output(self, topic: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_publish_output decorator adds @@ -399,7 +410,7 @@ def dapr_publish_output(self, input object in function code. :param pub_sub_name: The pub/sub name to publish to. :param topic: The name of the topic to publish to. - :param dapr_address: Dapr address, it is optional field, by default + :param dapr_address: Dapr address, it is optional field, by default ßit will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value @@ -433,7 +444,7 @@ def dapr_binding_output(self, operation: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_binding_output decorator adds @@ -441,7 +452,7 @@ def dapr_binding_output(self, for building :class:`Function` object used in worker function indexing model. This is equivalent to defining DaprBindingOutput - in the function.json which enables function to send a value to + in the function.json which enables function to send a value to a Dapr output binding. All optional fields will be given default value by function host when they are parsed by function host. @@ -454,8 +465,8 @@ def dapr_binding_output(self, input object in function code. :param binding_name: The configured name of the binding. :param operation: The configured operation. - :param dapr_address: Dapr address, it is optional field, by default it will be set - to http://localhost:{daprHttpPort}. + :param dapr_address: Dapr address, it is optional field, by default + it will be set to http://localhost:{daprHttpPort}. :param data_type: Defines how Functions runtime should treat the parameter value :param kwargs: Keyword arguments for specifying additional binding diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py index f7902469..327c6756 100644 --- a/azure/functions/decorators/function_app.py +++ b/azure/functions/decorators/function_app.py @@ -1102,6 +1102,7 @@ def decorator(): return wrap + class BindingApi(DecoratorApi, ABC): """Interface to extend for using existing binding decorator functions.""" @@ -2098,4 +2099,4 @@ def _add_http_app(self, auth_level=self.auth_level, route="/{*route}") def http_app_func(req: HttpRequest, context: Context): - return wsgi_middleware.handle(req, context) \ No newline at end of file + return wsgi_middleware.handle(req, context) diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index fe03efaa..92c67fc2 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -4,10 +4,12 @@ from azure.functions.decorators.core import BindingDirection from azure.functions.decorators.dapr_function_app import DaprFunctionApp -from azure.functions.decorators.constants import DAPR_BINDING, DAPR_BINDING_TRIGGER, \ - DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER +from azure.functions.decorators.constants import DAPR_BINDING, \ + DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, \ + DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER from tests.decorators.testutils import assert_json + class TestDapr(unittest.TestCase): def setUp(self): self.dapr_func_app = DaprFunctionApp() @@ -22,7 +24,6 @@ def test_dapr_service_invocation_trigger_default_args(self): @app.dapr_service_invocation_trigger(arg_name="req", method_name="dummy_method_name") - def dummy(): pass @@ -30,12 +31,13 @@ def dummy(): assert_json(self, func, {"scriptFile": "function_app.py", "bindings": [ - { - "direction": BindingDirection.IN, - "type": DAPR_SERVICE_INVOCATION_TRIGGER, - "name": "req", - "methodName": "dummy_method_name" - } + { + "direction": BindingDirection.IN, + "type": + DAPR_SERVICE_INVOCATION_TRIGGER, + "name": "req", + "methodName": "dummy_method_name" + } ] }) @@ -44,7 +46,6 @@ def test_dapr_binding_trigger_default_args(self): @app.dapr_binding_trigger(arg_name="req", binding_name="dummy_binding_name") - def dummy(): pass @@ -68,7 +69,6 @@ def test_dapr_topic_trigger_default_args(self): pub_sub_name="dummy_pub_sub_name", topic="dummy_topic", route="/dummy_route") - def dummy(): pass @@ -81,8 +81,8 @@ def dummy(): "type": DAPR_TOPIC_TRIGGER, "name": "req", "pubSubName": "dummy_pub_sub_name", - "topic":"dummy_topic", - "route":"/dummy_route" + "topic": "dummy_topic", + "route": "/dummy_route" } ] }) @@ -207,7 +207,7 @@ def dummy(): self.assertEqual(len(func.get_bindings()), 2) output = func.get_bindings()[0] - + self.assertEqual(output.get_dict_repr(), { "direction": BindingDirection.OUT, "type": DAPR_PUBLISH, From 5f182d5267143aa09f5bb37a5620db855e1c906f Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:33:27 +0530 Subject: [PATCH 18/26] fixed indentations Signed-off-by: MD Ashique --- .../functions/decorators/dapr_function_app.py | 29 ++++++++++--------- tests/decorators/test_dapr.py | 4 +-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 7bbb9554..0fcb3e0c 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -4,10 +4,11 @@ from typing import Any, Callable, Optional, Union from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum -from azure.functions.decorators.function_app import BindingApi, FunctionRegister, \ - TriggerApi -from azure.functions.decorators.dapr import DaprBindingOutput, DaprBindingTrigger, \ - DaprInvokeOutput, DaprPublishOutput, DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger +from azure.functions.decorators.function_app import BindingApi, \ + FunctionRegister, TriggerApi +from azure.functions.decorators.dapr import DaprBindingOutput, \ + DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, \ + DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger class DaprTriggerApi(TriggerApi, ABC): @@ -22,7 +23,7 @@ def dapr_service_invocation_trigger(self, :class:`DaprServiceInvocationTrigger` to the :class:`FunctionBuilder` object for building :class:`Function` object used in worker function - indexing model. This is equivalent to defining + indexing model. This is equivalent to defining DaprServiceInvocationTrigger in the function.json which enables function to be triggered when new service invocation occurs through Dapr. @@ -50,7 +51,7 @@ def decorator(): name=arg_name, method_name=method_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -62,7 +63,7 @@ def dapr_binding_trigger(self, arg_name: str, binding_name: str, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs: Any) -> Callable[..., Any]: """The dapr_binding_trigger decorator adds :class:`DaprBindingTrigger` @@ -95,7 +96,7 @@ def decorator(): name=arg_name, binding_name=binding_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -139,7 +140,7 @@ def dapr_topic_trigger(self, # issue is fixed at python worker. # Currently, python worker treats route as HttpTrigger attribute and # expects value for route. Route could be nil for dapr topic trigger. - if not route : + if not route: route = topic @self._configure_function_builder @@ -162,7 +163,7 @@ def decorator(): class DaprBindingApi(BindingApi, ABC): - + def dapr_state_input(self, arg_name: str, state_store: str, @@ -207,7 +208,7 @@ def decorator(): key=key, dapr_address=dapr_address, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -222,7 +223,7 @@ def dapr_secret_input(self, metadata: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_secret_input decorator adds @@ -319,7 +320,7 @@ def decorator(): key=key, dapr_address=dapr_address, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -334,7 +335,7 @@ def dapr_invoke_output(self, http_verb: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_invoke_output decorator adds diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index 92c67fc2..843d4e72 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -31,13 +31,13 @@ def dummy(): assert_json(self, func, {"scriptFile": "function_app.py", "bindings": [ - { + { "direction": BindingDirection.IN, "type": DAPR_SERVICE_INVOCATION_TRIGGER, "name": "req", "methodName": "dummy_method_name" - } + } ] }) From 2ee768dac33ce00c87167066a886a2645d20e5ba Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:44:19 +0530 Subject: [PATCH 19/26] fixed indentations Signed-off-by: MD Ashique --- .../functions/decorators/dapr_function_app.py | 21 ++++++++++--------- tests/decorators/test_dapr.py | 10 ++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index 0fcb3e0c..ee9c14f1 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -8,7 +8,8 @@ FunctionRegister, TriggerApi from azure.functions.decorators.dapr import DaprBindingOutput, \ DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, \ - DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, DaprTopicTrigger + DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, \ + DaprTopicTrigger class DaprTriggerApi(TriggerApi, ABC): @@ -51,7 +52,7 @@ def decorator(): name=arg_name, method_name=method_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -63,7 +64,7 @@ def dapr_binding_trigger(self, arg_name: str, binding_name: str, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs: Any) -> Callable[..., Any]: """The dapr_binding_trigger decorator adds :class:`DaprBindingTrigger` @@ -96,7 +97,7 @@ def decorator(): name=arg_name, binding_name=binding_name, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -153,7 +154,7 @@ def decorator(): topic=topic, route=route, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -208,7 +209,7 @@ def decorator(): key=key, dapr_address=dapr_address, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -223,7 +224,7 @@ def dapr_secret_input(self, metadata: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_secret_input decorator adds @@ -279,7 +280,7 @@ def dapr_state_output(self, key: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_state_output decorator adds @@ -320,7 +321,7 @@ def decorator(): key=key, dapr_address=dapr_address, data_type=parse_singular_param_to_enum(data_type, - DataType), + DataType), **kwargs)) return fb @@ -335,7 +336,7 @@ def dapr_invoke_output(self, http_verb: str, dapr_address: Optional[str] = None, data_type: Optional[ - Union[DataType, str]] = None, + Union[DataType, str]] = None, **kwargs) \ -> Callable[..., Any]: """The dapr_invoke_output decorator adds diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index 843d4e72..c2f70ca6 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -32,11 +32,11 @@ def dummy(): assert_json(self, func, {"scriptFile": "function_app.py", "bindings": [ { - "direction": BindingDirection.IN, - "type": - DAPR_SERVICE_INVOCATION_TRIGGER, - "name": "req", - "methodName": "dummy_method_name" + "direction": BindingDirection.IN, + "type": \ + DAPR_SERVICE_INVOCATION_TRIGGER, + "name": "req", + "methodName": "dummy_method_name" } ] }) From 0630c98bc8c90a31108db00a17b24c5ecd690def Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:49:56 +0530 Subject: [PATCH 20/26] fixed indentations Signed-off-by: MD Ashique --- .../functions/decorators/dapr_function_app.py | 4 +-- tests/decorators/test_dapr.py | 26 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr_function_app.py index ee9c14f1..cf19f4c9 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr_function_app.py @@ -8,8 +8,8 @@ FunctionRegister, TriggerApi from azure.functions.decorators.dapr import DaprBindingOutput, \ DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, \ - DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, DaprStateOutput, \ - DaprTopicTrigger + DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, \ + DaprStateOutput, DaprTopicTrigger class DaprTriggerApi(TriggerApi, ABC): diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index c2f70ca6..583624fb 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -29,17 +29,21 @@ def dummy(): func = self._get_user_function(app) - assert_json(self, func, {"scriptFile": "function_app.py", - "bindings": [ - { - "direction": BindingDirection.IN, - "type": \ - DAPR_SERVICE_INVOCATION_TRIGGER, - "name": "req", - "methodName": "dummy_method_name" - } - ] - }) + assert_json + ( + self, func, + { + "scriptFile": "function_app.py", + "bindings": [ + { + "direction": BindingDirection.IN, + "type": DAPR_SERVICE_INVOCATION_TRIGGER, + "name": "req", + "methodName": "dummy_method_name" + } + ] + } + ) def test_dapr_binding_trigger_default_args(self): app = self.dapr_func_app From 109e30f6bb9cb6530c5fa5cd167a28c99d876417 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 23 Jun 2023 14:52:13 +0530 Subject: [PATCH 21/26] fixed indentations Signed-off-by: MD Ashique --- tests/decorators/test_dapr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index 583624fb..eb7a959c 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -31,7 +31,7 @@ def dummy(): assert_json ( - self, func, + self, func, { "scriptFile": "function_app.py", "bindings": [ From 1cf068e0167198135f3cba0bb8ff2e313cad6430 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Mon, 17 Jul 2023 18:23:34 +0530 Subject: [PATCH 22/26] Fixed review comments Signed-off-by: MD Ashique --- azure/functions/__init__.py | 2 +- azure/functions/decorators/__init__.py | 2 +- azure/functions/decorators/{ => dapr}/dapr.py | 0 azure/functions/decorators/{ => dapr}/dapr_function_app.py | 3 +++ tests/decorators/test_dapr.py | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) rename azure/functions/decorators/{ => dapr}/dapr.py (100%) rename azure/functions/decorators/{ => dapr}/dapr_function_app.py (99%) diff --git a/azure/functions/__init__.py b/azure/functions/__init__.py index dccaf2f5..ece46095 100644 --- a/azure/functions/__init__.py +++ b/azure/functions/__init__.py @@ -12,6 +12,7 @@ AsgiFunctionApp, WsgiFunctionApp, ExternalHttpFunctionApp) from ._durable_functions import OrchestrationContext, EntityContext +from .decorators.dapr.dapr_function_app import DaprFunctionApp from .decorators.function_app import (FunctionRegister, TriggerApi, BindingApi) from .extension import (ExtensionMeta, FunctionExtensionException, @@ -23,7 +24,6 @@ from ._queue import QueueMessage from ._servicebus import ServiceBusMessage from ._sql import SqlRow, SqlRowList -from .decorators.dapr_function_app import DaprFunctionApp # Import binding implementations to register them from . import blob # NoQA diff --git a/azure/functions/decorators/__init__.py b/azure/functions/decorators/__init__.py index ee25cc76..a4ead06a 100644 --- a/azure/functions/decorators/__init__.py +++ b/azure/functions/decorators/__init__.py @@ -5,7 +5,7 @@ AuthLevel, Blueprint, ExternalHttpFunctionApp, AsgiFunctionApp, \ WsgiFunctionApp, FunctionRegister, TriggerApi, BindingApi from .http import HttpMethod -from .dapr_function_app import DaprFunctionApp +from .dapr.dapr_function_app import DaprFunctionApp __all__ = [ 'FunctionApp', diff --git a/azure/functions/decorators/dapr.py b/azure/functions/decorators/dapr/dapr.py similarity index 100% rename from azure/functions/decorators/dapr.py rename to azure/functions/decorators/dapr/dapr.py diff --git a/azure/functions/decorators/dapr_function_app.py b/azure/functions/decorators/dapr/dapr_function_app.py similarity index 99% rename from azure/functions/decorators/dapr_function_app.py rename to azure/functions/decorators/dapr/dapr_function_app.py index cf19f4c9..57598858 100644 --- a/azure/functions/decorators/dapr_function_app.py +++ b/azure/functions/decorators/dapr/dapr_function_app.py @@ -497,7 +497,10 @@ def decorator(): class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi): + """DaprFunctionApp object used for Dapr bindings and triggers. + Ref: https://aka.ms/dapr-bindings-triggers + """ def __init__(self, http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION): """Constructor of :class:`DaprFunctionApp` object. diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index eb7a959c..e48235c7 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -3,7 +3,7 @@ import unittest from azure.functions.decorators.core import BindingDirection -from azure.functions.decorators.dapr_function_app import DaprFunctionApp +from azure.functions.decorators.dapr.dapr_function_app import DaprFunctionApp from azure.functions.decorators.constants import DAPR_BINDING, \ DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, \ DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER From a7ec74d6c308fc7834db4486e4ea35b83534b172 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Tue, 18 Jul 2023 00:04:16 +0530 Subject: [PATCH 23/26] adding SettingsApi Signed-off-by: MD Ashique --- azure/functions/decorators/dapr/dapr_function_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure/functions/decorators/dapr/dapr_function_app.py b/azure/functions/decorators/dapr/dapr_function_app.py index 57598858..40393ded 100644 --- a/azure/functions/decorators/dapr/dapr_function_app.py +++ b/azure/functions/decorators/dapr/dapr_function_app.py @@ -5,8 +5,8 @@ from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum from azure.functions.decorators.function_app import BindingApi, \ - FunctionRegister, TriggerApi -from azure.functions.decorators.dapr import DaprBindingOutput, \ + FunctionRegister, SettingsApi, TriggerApi +from azure.functions.decorators.dapr.dapr import DaprBindingOutput, \ DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, \ DaprSecretInput, DaprServiceInvocationTrigger, DaprStateInput, \ DaprStateOutput, DaprTopicTrigger @@ -496,7 +496,7 @@ def decorator(): return wrap -class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi): +class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi, SettingsApi): """DaprFunctionApp object used for Dapr bindings and triggers. Ref: https://aka.ms/dapr-bindings-triggers From b7828b3821381e5b4f862f29a1a353c0c02c3c21 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Tue, 18 Jul 2023 10:45:19 +0530 Subject: [PATCH 24/26] line break Signed-off-by: MD Ashique --- azure/functions/decorators/dapr/dapr_function_app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure/functions/decorators/dapr/dapr_function_app.py b/azure/functions/decorators/dapr/dapr_function_app.py index 40393ded..2bc97a50 100644 --- a/azure/functions/decorators/dapr/dapr_function_app.py +++ b/azure/functions/decorators/dapr/dapr_function_app.py @@ -496,7 +496,8 @@ def decorator(): return wrap -class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi, SettingsApi): +class DaprFunctionApp(FunctionRegister, DaprTriggerApi, + DaprBindingApi, SettingsApi): """DaprFunctionApp object used for Dapr bindings and triggers. Ref: https://aka.ms/dapr-bindings-triggers From 058d9fd8dff4c0bcc30209c8b256f0772c84cd48 Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Wed, 19 Jul 2023 22:01:25 +0530 Subject: [PATCH 25/26] Adding DaprBluePrint Signed-off-by: MD Ashique --- azure/functions/__init__.py | 5 +-- azure/functions/decorators/__init__.py | 5 +-- .../decorators/dapr/dapr_function_app.py | 9 +++++- tests/decorators/test_dapr.py | 32 +++++++++++++++++-- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/azure/functions/__init__.py b/azure/functions/__init__.py index e69ed793..6604a64a 100644 --- a/azure/functions/__init__.py +++ b/azure/functions/__init__.py @@ -12,7 +12,7 @@ AsgiFunctionApp, WsgiFunctionApp, ExternalHttpFunctionApp) from ._durable_functions import OrchestrationContext, EntityContext -from .decorators.dapr.dapr_function_app import DaprFunctionApp +from .decorators.dapr.dapr_function_app import DaprFunctionApp, DaprBlueprint from .decorators.function_app import (FunctionRegister, TriggerApi, BindingApi, SettingsApi) from .extension import (ExtensionMeta, FunctionExtensionException, @@ -96,7 +96,8 @@ 'Cardinality', 'AccessRights', 'HttpMethod', - 'DaprFunctionApp' + 'DaprFunctionApp', + 'DaprBlueprint' ) __version__ = '1.15.1b1' diff --git a/azure/functions/decorators/__init__.py b/azure/functions/decorators/__init__.py index 9807eafb..83b81eb4 100644 --- a/azure/functions/decorators/__init__.py +++ b/azure/functions/decorators/__init__.py @@ -5,7 +5,7 @@ AuthLevel, Blueprint, ExternalHttpFunctionApp, AsgiFunctionApp, \ WsgiFunctionApp, FunctionRegister, TriggerApi, BindingApi, SettingsApi from .http import HttpMethod -from .dapr.dapr_function_app import DaprFunctionApp +from .dapr.dapr_function_app import DaprFunctionApp, DaprBlueprint __all__ = [ 'FunctionApp', @@ -24,5 +24,6 @@ 'Cardinality', 'AccessRights', 'HttpMethod', - 'DaprFunctionApp' + 'DaprFunctionApp', + 'DaprBlueprint' ] diff --git a/azure/functions/decorators/dapr/dapr_function_app.py b/azure/functions/decorators/dapr/dapr_function_app.py index 2bc97a50..1ec86969 100644 --- a/azure/functions/decorators/dapr/dapr_function_app.py +++ b/azure/functions/decorators/dapr/dapr_function_app.py @@ -4,7 +4,7 @@ from typing import Any, Callable, Optional, Union from azure.functions.decorators.core import DataType, AuthLevel from azure.functions.decorators.utils import parse_singular_param_to_enum -from azure.functions.decorators.function_app import BindingApi, \ +from azure.functions.decorators.function_app import BindingApi, Blueprint, \ FunctionRegister, SettingsApi, TriggerApi from azure.functions.decorators.dapr.dapr import DaprBindingOutput, \ DaprBindingTrigger, DaprInvokeOutput, DaprPublishOutput, \ @@ -495,6 +495,13 @@ def decorator(): return wrap +class DaprBlueprint(Blueprint, DaprTriggerApi, DaprBindingApi): + """Functions container class where all the functions + loaded in it can be registered in :class:`FunctionRegister` subclasses + but itself can not be indexed directly. The class contains all existing + supported trigger and binding decorator functions. + """ + pass class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi, SettingsApi): diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index e48235c7..94a6451e 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -2,8 +2,8 @@ # Licensed under the MIT License. import unittest -from azure.functions.decorators.core import BindingDirection -from azure.functions.decorators.dapr.dapr_function_app import DaprFunctionApp +from azure.functions.decorators.core import SCRIPT_FILE_NAME, AuthLevel, BindingDirection +from azure.functions.decorators.dapr.dapr_function_app import DaprBlueprint, DaprFunctionApp from azure.functions.decorators.constants import DAPR_BINDING, \ DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, \ DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER @@ -244,3 +244,31 @@ def dummy(): "bindingName": "dummy_binding_name", "operation": "dummy_operation" }) + + def test_register_dapr_blueprint(self): + bp = DaprBlueprint() + + @bp.schedule(arg_name="name", schedule="10****") + def hello(name: str): + return "hello" + + app = DaprFunctionApp() + app.register_blueprint(bp) + + self.assertEqual(len(app.get_functions()), 1) + self.assertEqual(app.auth_level, AuthLevel.FUNCTION) + self.assertEqual(app.app_script_file, SCRIPT_FILE_NAME) + + def test_register_dapr_blueprint_app_auth_level(self): + bp = DaprBlueprint() + + @bp.route("name") + def hello(name: str): + return "hello" + + app = DaprFunctionApp(http_auth_level=AuthLevel.ANONYMOUS) + app.register_blueprint(bp) + + self.assertEqual(len(app.get_functions()), 1) + self.assertEqual(app.get_functions()[0].get_trigger().auth_level, + AuthLevel.ANONYMOUS) From 86da23d00e2e8dd0cd56c88b175d61814f35a8ef Mon Sep 17 00:00:00 2001 From: MD Ashique Date: Fri, 21 Jul 2023 13:50:46 +0530 Subject: [PATCH 26/26] fixed spacing issue Signed-off-by: MD Ashique --- azure/functions/decorators/dapr/dapr_function_app.py | 6 ++++-- tests/decorators/test_dapr.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/azure/functions/decorators/dapr/dapr_function_app.py b/azure/functions/decorators/dapr/dapr_function_app.py index 1ec86969..65a2189c 100644 --- a/azure/functions/decorators/dapr/dapr_function_app.py +++ b/azure/functions/decorators/dapr/dapr_function_app.py @@ -495,15 +495,17 @@ def decorator(): return wrap + class DaprBlueprint(Blueprint, DaprTriggerApi, DaprBindingApi): """Functions container class where all the functions loaded in it can be registered in :class:`FunctionRegister` subclasses but itself can not be indexed directly. The class contains all existing supported trigger and binding decorator functions. """ - pass + pass + -class DaprFunctionApp(FunctionRegister, DaprTriggerApi, +class DaprFunctionApp(FunctionRegister, DaprTriggerApi, DaprBindingApi, SettingsApi): """DaprFunctionApp object used for Dapr bindings and triggers. diff --git a/tests/decorators/test_dapr.py b/tests/decorators/test_dapr.py index 94a6451e..e4cd14b7 100644 --- a/tests/decorators/test_dapr.py +++ b/tests/decorators/test_dapr.py @@ -2,8 +2,10 @@ # Licensed under the MIT License. import unittest -from azure.functions.decorators.core import SCRIPT_FILE_NAME, AuthLevel, BindingDirection -from azure.functions.decorators.dapr.dapr_function_app import DaprBlueprint, DaprFunctionApp +from azure.functions.decorators.core import SCRIPT_FILE_NAME, AuthLevel, \ + BindingDirection +from azure.functions.decorators.dapr.dapr_function_app import DaprBlueprint, \ + DaprFunctionApp from azure.functions.decorators.constants import DAPR_BINDING, \ DAPR_BINDING_TRIGGER, DAPR_INVOKE, DAPR_PUBLISH, DAPR_SECRET, \ DAPR_SERVICE_INVOCATION_TRIGGER, DAPR_STATE, DAPR_TOPIC_TRIGGER