diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index fc21927a..e773b661 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -10,15 +10,15 @@ from typing import Union import flask.wrappers import jinja2 -from SpiffWorkflow.exceptions import WorkflowTaskException from flask import current_app from flask import g from flask import jsonify from flask import make_response from flask.wrappers import Response +from jinja2 import TemplateSyntaxError +from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState -from jinja2 import TemplateSyntaxError from sqlalchemy import and_ from sqlalchemy import asc from sqlalchemy import desc @@ -293,9 +293,11 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response task.properties["instructionsForEndUser"] = _render_jinja_template( task.properties["instructionsForEndUser"], spiff_task ) - except WorkflowTaskException as wfe: + except WorkflowTaskException as wfe: wfe.add_note("Failed to render instructions for end user.") - raise ApiError.from_workflow_exception("instructions_error", str(wfe), exp=wfe) + raise ApiError.from_workflow_exception( + "instructions_error", str(wfe), exp=wfe + ) from wfe return make_response(jsonify(task), 200) @@ -518,10 +520,13 @@ def _prepare_form_data( return _render_jinja_template(file_contents, spiff_task) except WorkflowTaskException as wfe: wfe.add_note(f"Error in Json Form File '{form_file}'") - api_error = ApiError.from_workflow_exception("instructions_error", str(wfe), exp=wfe) + api_error = ApiError.from_workflow_exception( + "instructions_error", str(wfe), exp=wfe + ) api_error.file_name = form_file raise api_error + def _render_jinja_template(unprocessed_template: str, spiff_task: SpiffTask) -> str: """Render_jinja_template.""" jinja_environment = jinja2.Environment( @@ -531,11 +536,17 @@ def _render_jinja_template(unprocessed_template: str, spiff_task: SpiffTask) -> template = jinja_environment.from_string(unprocessed_template) return template.render(**spiff_task.data) except jinja2.exceptions.TemplateError as template_error: - wfe = WorkflowTaskException(str(template_error), task=spiff_task, exception=template_error) + wfe = WorkflowTaskException( + str(template_error), task=spiff_task, exception=template_error + ) if isinstance(template_error, TemplateSyntaxError): wfe.line_number = template_error.lineno - wfe.error_line = template_error.source.split('\n')[template_error.lineno - 1] - wfe.add_note("Jinja2 template errors can happen when trying to displaying task data") + wfe.error_line = template_error.source.split("\n")[ + template_error.lineno - 1 + ] + wfe.add_note( + "Jinja2 template errors can happen when trying to displaying task data" + ) raise wfe from template_error @@ -582,14 +593,19 @@ def _update_form_schema_with_task_data_as_needed( if task_data_var not in task.data: wte = WorkflowTaskException( - f"Error building form. Attempting to create a selection list" - f" with options from variable '{task_data_var}' but it doesn't" - f" exist in the Task Data.", task=task) + ( + "Error building form. Attempting to create a" + " selection list with options from variable" + f" '{task_data_var}' but it doesn't exist in" + " the Task Data." + ), + task=task, + ) raise ( ApiError.from_workflow_exception( error_code="missing_task_data_var", message=str(wte), - exp=wte + exp=wte, ) ) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index a46d964b..76283be4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -873,7 +873,10 @@ class ProcessInstanceProcessor: f"Event of type {event_definition.event_type} sent to process instance" f" {self.process_instance_model.id}" ) - self.bpmn_process_instance.catch(event_definition) + try: + self.bpmn_process_instance.catch(event_definition) + except Exception as e: + print(e) self.do_engine_steps(save=True) def add_step(self, step: Union[dict, None] = None) -> None: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_for_good_errors.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_for_good_errors.py index 95525d06..eea6af3c 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_for_good_errors.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_for_good_errors.py @@ -1,25 +1,30 @@ """Test_various_bpmn_constructs.""" +from typing import Any + from flask.app import Flask from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + from spiffworkflow_backend import db from spiffworkflow_backend.models.human_task import HumanTaskModel -from tests.spiffworkflow_backend.helpers.base_test import BaseTest from spiffworkflow_backend.models.user import UserModel -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec class TestForGoodErrors(BaseTest): - """Assure when certain errors happen when rendering a jinaj2 error that it makes - some sense.""" + """Assure when certain errors happen when rendering a jinaj2 error that it makes some sense.""" - def get_next_user_task(self, process_instance_id: int, - client: FlaskClient, - with_super_admin_user: UserModel, - ): + def get_next_user_task( + self, + process_instance_id: int, + client: FlaskClient, + with_super_admin_user: UserModel, + ) -> Any: + """Returns the next available user task for a given process instance, if possible.""" human_tasks = ( db.session.query(HumanTaskModel) - .filter(HumanTaskModel.process_instance_id == process_instance_id) - .all() + .filter(HumanTaskModel.process_instance_id == process_instance_id) + .all() ) assert len(human_tasks) > 0, "No human tasks found for process." human_task = human_tasks[0] @@ -30,12 +35,13 @@ class TestForGoodErrors(BaseTest): return response def test_invalid_form( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: + """React json form schema with bad jinja syntax provides good error.""" process_model = load_test_spec( process_model_id="group/simple_form_with_error", process_model_source_directory="simple_form_with_error", @@ -53,13 +59,15 @@ class TestForGoodErrors(BaseTest): headers=self.logged_in_headers(with_super_admin_user), ) assert response.status_code == 200 - response = self.get_next_user_task(process_instance_id, client, with_super_admin_user) + response = self.get_next_user_task( + process_instance_id, client, with_super_admin_user + ) assert response.json is not None - assert response.json['error_type'] == 'TemplateSyntaxError' - assert response.json['line_number'] == 3 - assert response.json['file_name'] == 'simple_form.json' + assert response.json["error_type"] == "TemplateSyntaxError" + assert response.json["line_number"] == 3 + assert response.json["file_name"] == "simple_form.json" - def test_jinja2_error_message_for_end_user_instructions ( + def test_jinja2_error_message_for_end_user_instructions( self, app: Flask, client: FlaskClient, @@ -80,14 +88,16 @@ class TestForGoodErrors(BaseTest): f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model.id)}/{process_instance.id}/run", headers=self.logged_in_headers(with_super_admin_user), ) - response = self.get_next_user_task(process_instance.id, client, with_super_admin_user) + response = self.get_next_user_task( + process_instance.id, client, with_super_admin_user + ) assert response.status_code == 400 assert response.json is not None - assert response.json['error_type'] == 'TemplateSyntaxError' - assert response.json['line_number'] == 3 - assert response.json['error_line'] == '{{ x +=- 1}}' - assert response.json['file_name'] == 'instructions_error.bpmn' - assert 'instructions for end user' in response.json['message'] - assert 'Jinja2' in response.json['message'] - assert 'unexpected \'=\'' in response.json['message'] \ No newline at end of file + assert response.json["error_type"] == "TemplateSyntaxError" + assert response.json["line_number"] == 3 + assert response.json["error_line"] == "{{ x +=- 1}}" + assert response.json["file_name"] == "instructions_error.bpmn" + assert "instructions for end user" in response.json["message"] + assert "Jinja2" in response.json["message"] + assert "unexpected '='" in response.json["message"] diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 7213e31c..d4c46c64 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -2806,7 +2806,7 @@ class TestProcessApi(BaseTest): ) data = { - "dateTime": "timedelta(hours=1)", + "dateTime": "PT1H", "external": True, "internal": True, "label": "Event_0e4owa3", diff --git a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx index f6bf2b2e..411a9a53 100644 --- a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx +++ b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx @@ -59,11 +59,7 @@ export default function ErrorDisplay() { } errorTag = ( - (removeError())} - type="error" - > + removeError()} type="error"> {message}
{sentryLinkTag} diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceRun.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceRun.tsx index 1882ec59..37261d70 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceRun.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceRun.tsx @@ -115,11 +115,11 @@ export default function ProcessInstanceRun({ }; const processInstanceCreateAndRun = () => { - setErrorObject(null); + removeError(); HttpService.makeCallToBackend({ path: processInstanceCreatePath, successCallback: processModelRun, - failureCallback: setErrorObject, + failureCallback: addError, httpMethod: 'POST', }); }; diff --git a/spiffworkflow-frontend/src/contexts/APIErrorContext.tsx b/spiffworkflow-frontend/src/contexts/APIErrorContext.tsx index 98dcc7fc..bfd7d009 100644 --- a/spiffworkflow-frontend/src/contexts/APIErrorContext.tsx +++ b/spiffworkflow-frontend/src/contexts/APIErrorContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useState, useCallback } from 'react'; +import React, { createContext, useState } from 'react'; import { ErrorForDisplay } from '../interfaces'; type ErrorContextType = { @@ -28,7 +28,7 @@ export default function APIErrorProvider({ children }) { addError: (newError: ErrorForDisplay | null) => addError(newError), removeError: () => removeError(), }), - [error, addError, removeError] + [error] ); return ( diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index 628c0a89..b94e28bc 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -8,7 +8,6 @@ import { DEFAULT_PER_PAGE, DEFAULT_PAGE, } from './components/PaginationForTable'; -import { ErrorForDisplay } from './interfaces'; // https://www.30secondsofcode.org/js/s/slugify export const slugifyString = (str: any) => { diff --git a/spiffworkflow-frontend/src/routes/Configuration.tsx b/spiffworkflow-frontend/src/routes/Configuration.tsx index f9526799..6b378162 100644 --- a/spiffworkflow-frontend/src/routes/Configuration.tsx +++ b/spiffworkflow-frontend/src/routes/Configuration.tsx @@ -26,7 +26,7 @@ export default function Configuration() { const { ability } = usePermissionFetcher(permissionRequestData); useEffect(() => { - console.log("Configuration remove error") + console.log('Configuration remove error'); removeError(); let newSelectedTabIndex = 0; if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) { diff --git a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx index deae4e77..fb7c7044 100644 --- a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx @@ -3,7 +3,6 @@ import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; // @ts-ignore import { Tabs, TabList, Tab } from '@carbon/react'; import TaskShow from './TaskShow'; -import useAPIError from '../hooks/UseApiError'; import MyTasks from './MyTasks'; import GroupedTasks from './GroupedTasks'; import CompletedInstances from './CompletedInstances'; @@ -11,7 +10,6 @@ import CreateNewInstance from './CreateNewInstance'; export default function HomePageRoutes() { const location = useLocation(); - const { removeError } = useAPIError(); const [selectedTabIndex, setSelectedTabIndex] = useState(0); const navigate = useNavigate(); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 3ebf0f7e..c706007b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -717,7 +717,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { setSelectingEvent(false); initializeTaskDataToDisplay(taskToDisplay); setEventPayload('{}'); - console.log("cancel updating task") + console.log('cancel updating task'); removeError(); }; @@ -737,7 +737,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { if (!taskToDisplay) { return; } - console.log("saveTaskData") + console.log('saveTaskData'); removeError(); // taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute