src/dfcx_scrapi/builders/fulfillments.py (153 lines of code) (raw):

"""A set of builder methods to create CX proto resource objects""" # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from typing import Any, Dict, List from google.cloud.dialogflowcx_v3beta1.types import Fulfillment, ResponseMessage from dfcx_scrapi.builders.builders_common import BuildersCommon from dfcx_scrapi.builders.response_messages import ResponseMessageBuilder # logging config logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) class FulfillmentBuilder(BuildersCommon): """Base Class for CX Fulfillment builder.""" # TODO: ConditionalCases: def add_conditional_case(self) -> Fulfillment: _proto_type = Fulfillment _proto_type_str = "Fulfillment" def __str__(self) -> str: """String representation of the proto_obj.""" try: self._check_proto_obj_attr_exist() except ValueError: return "" basic_info_str = self._show_basic_info() resp_msgs_str = self._show_response_messages() params_str = self._show_parameters() return ( f"Fulfillment Basic Information:\n{'-'*20}\n{basic_info_str}" f"\n\n\nFulfillment ResponseMessages:\n{'-'*20}\n{resp_msgs_str}" f"\n\n\nFulfillment Parameters:\n{'-'*20}\n{params_str}" ) def _show_basic_info(self) -> str: """String representation for the basic information of proto_obj.""" self._check_proto_obj_attr_exist() partial_resp = self.proto_obj.return_partial_responses return ( f"webhook: {self.proto_obj.webhook}" f"\ntag: {self.proto_obj.tag}" f"\nreturn_partial_responses: {partial_resp}" ) def _show_parameters(self) -> str: """String representation for the parameters presets of proto_obj.""" self._check_proto_obj_attr_exist() return "\n".join([ f"{param.parameter}: {param.value if param.value else 'null'}" for param in self.proto_obj.set_parameter_actions ]) def _show_response_messages(self) -> str: """String representation of response messages in proto_obj.""" self._check_proto_obj_attr_exist() return "\n".join([ f"ResponseMessage {i+1}:\n{str(ResponseMessageBuilder(msg))}" for i, msg in enumerate(self.proto_obj.messages) ]) def show_fulfillment(self, mode: str = "whole"): """Show the proto_obj information. Args: mode (str): Specifies what part of the fulfillment to show. Options: ['basic', 'parameters', 'messages' or 'response messages', 'whole'] """ self._check_proto_obj_attr_exist() if mode == "basic": print(self._show_basic_info()) elif mode == "parameters": print(self._show_parameters()) elif mode in ["messages", "response messages"]: print(self._show_response_messages()) elif mode == "whole": print(self) else: raise ValueError( "mode should be in" "['basic', 'parameters'," " 'messages' or 'response messages', 'whole']" ) def create_new_proto_obj( self, webhook: str = None, tag: str = None, return_partial_responses: bool = False, overwrite: bool = False ) -> Fulfillment: """Create a new Fulfillment. Args: webhook (str): The webhook to call. Format: ``projects/<Project ID>/locations/<Location ID>/agents /<Agent ID>/webhooks/<Webhook ID>``. tag (str): The tag is typically used by the webhook service to identify which fulfillment is being called, but it could be used for other purposes. This field is required if ``webhook`` is specified. return_partial_responses (bool): Whether Dialogflow should return currently queued fulfillment response messages in streaming APIs. If a webhook is specified, it happens before Dialogflow invokes webhook. overwrite (bool) Overwrite the new proto_obj if proto_obj already contains a Fulfillment. Returns: A Fulfillment object stored in proto_obj. """ # Types error checking if (return_partial_responses and not isinstance(return_partial_responses, bool)): raise ValueError( "return_partial_responses should be bool." ) if ((webhook and not isinstance(webhook, str)) or (tag and not isinstance(tag, str))): raise ValueError( "webhook and tag should be string." ) # webhook with tag presence check if webhook and not tag: raise ValueError( "tag is required when webhook is specified." ) # `overwrite` parameter error checking if self.proto_obj is not None and not overwrite: raise UserWarning( "proto_obj already contains a Fulfillment." " If you wish to overwrite it, pass overwrite as True." ) # Create the fulfillment if overwrite or not self.proto_obj: self.proto_obj = Fulfillment( webhook=webhook, return_partial_responses=return_partial_responses, tag=tag ) return self.proto_obj def add_response_message( self, response_message: ResponseMessage ) -> Fulfillment: """Add a rich message response to present to the user. Args: response_message (ResponseMessage): The ResponseMessage to add to the Fulfillment. Refer to `builders.response_message.ResponseMessageBuilder` to build one. Returns: A Fulfillment object stored in proto_obj """ self._check_proto_obj_attr_exist() if not isinstance(response_message, ResponseMessage): raise ValueError( "`response_message` type should be ResponseMessage." ) self.proto_obj.messages.append(response_message) return self.proto_obj def add_parameter_presets( self, parameter_map: Dict[str, Any] ) -> Fulfillment: """Set parameter values before executing the webhook. Args: parameter_map (Dict[str, Any]): A dictionary that represents parameters as keys and the parameter values as it's values. A `None` value clears the parameter. Returns: A Fulfillment object stored in proto_obj """ self._check_proto_obj_attr_exist() # Type error checking if isinstance(parameter_map, dict): if not all(isinstance(k, str) for k in parameter_map.keys()): raise ValueError( "Only strings are allowed as" " dictionary keys in `parameter_map`." ) for param, val in parameter_map.items(): self.proto_obj.set_parameter_actions.append( Fulfillment.SetParameterAction(parameter=param, value=val) ) return self.proto_obj else: raise ValueError( "`parameter_map` should be a dictionary." ) def remove_parameter_presets( self, parameters: List[str] ) -> Fulfillment: """Remove parameter values from the fulfillment. Args: parameters (List[str]): A list of parameters that should be removed from the fulfillment. Only strings are allowed. Returns: A Fulfillment object stored in proto_obj """ self._check_proto_obj_attr_exist() # Type error checking if isinstance(parameters, list): if not all(isinstance(p, str) for p in parameters): raise ValueError( "Only strings are allowed as in parameters." ) new_params = [] for param in self.proto_obj.set_parameter_actions: if param.parameter in parameters: continue new_params.append(param) self.proto_obj.set_parameter_actions = new_params return self.proto_obj else: raise ValueError( "parameter_map should be a list of strings." ) def has_webhook(self) -> bool: """Check whether the Fulfillment in proto_obj uses a Webhook. Returns: True if proto_obj uses a Webhook and False otherwise """ try: self._check_proto_obj_attr_exist() except ValueError: return False return bool(self.proto_obj.webhook)