This is tough. We can't provide much feedback through HQ to the frontend unless we return a 200 I believe. But I understand your desire to have the 500 in your SDK backend logs to easily figure out which requests failed. We can look and see if there's a better mechanism in the HQ side for this, but at the moment I think your pattern is the preferred one.
In extension.py, there are two types of methods that can be made (generally speaking): Q2TectonForm-rending methods such as the default method and Ajax methods that are to be used to get some data. In the latter type of method, we have been handling exceptions that we do not have a clean way to resolve by returning an InternalServerError object. An example of this is shown below:
@ajax
async def example_ajax_function(self) -> TectonResponse:
try:
# Do some stuff that may raise an exception
except Exception as ex:
exception_message = traceback.format_exc()
self.logger.error(ex)
self.logger.error(exception_message)
return InternalServerError("An internal server error occurred.")
else:
return Success()
The above example neatly returns a 500 HTTP response to the calling JavaScript and logs the exception. From these return values, we can handle a 500 response gracefully on the frontend.
In the former type of methods in extension.py that I mentioned, Q2TectonForm-rendering methods, we cannot find a clear way to handle exceptions. If some exception occurs, we have been logging the exception and sending an error template to the user to view. The template contains a modal that notifies the user that an error has occurred, and this modal has a button that will navigate the user back to a previous page or other extension. We have been doing this with a kind of exception-handling decorator. An example of this is below:
def _render_error_template(
self,
title="Error",
message: Optional[str] = None,
action_label="Close",
route="default",
is_internal_route=True,
) -> Q2TectonForm:
default_message = (
"We ran into an error processing your request. "
"Please try again or contact our Contact Center."
)
return self.get_tecton_form(
"Error",
modal=modals.TectonModal(
modal_type=modals.ModalType.ERROR,
title=title,
message=default_message if message is None else message,
modal_actions=[
modals.ModalAction(
action_label,
route if is_internal_route is False else "",
route if is_internal_route is True else "",
)
],
show_close=False,
is_blocking=True,
),
hide_submit_button=True,
)
def _template_error_handler(func: Callable[[], Q2TectonForm]) -> Q2TectonForm:
async def inner_function(*args, **kwargs):
try:
return await func(args[0])
except Exception as ex:
exception_message = traceback.format_exc()
args[0].logger.error(ex)
args[0].logger.error(exception_message)
if func.__name__ == "default":
return args[0]._render_error_template(
route="landingPage", is_internal_route=False
)
else:
return args[0]._render_error_template()
return inner_function
@_template_error_handler
async def default(self) -> Q2TectonForm:
# Do some stuff here. If an unhandled exceptions occurs, it will
# be caught by _template_error_handler and our error template
# will be passed to the client.
template = jinja.jinja_generate(
# Contains data to get the desired Jinja template
)
html = self.get_tecton_form(
# Contains data to generate Tecton form
)
return html
This way of catching exceptions gives us the ability to render an error message to our users if something goes wrong instead of having an infinitely loading screen or some other offputting behavior. There are a few problems with this, though, such as this method not actually throwing a 500 response when something goes wrong.
What is the proper way for SDK users to handle exceptions in extension.py ? Is there any kind of exception middleware inside the SDK that can be used?
Thank you,
Ethan