run_pyl had various recommendations that I find a bit of a pain in the butt, but that I did anyway.

This commit is contained in:
Dan 2023-01-25 16:43:28 -05:00
parent a38ecc2e60
commit b2fb0dd79f
11 changed files with 79 additions and 57 deletions

View File

@ -10,15 +10,15 @@ from typing import Union
import flask.wrappers import flask.wrappers
import jinja2 import jinja2
from SpiffWorkflow.exceptions import WorkflowTaskException
from flask import current_app from flask import current_app
from flask import g from flask import g
from flask import jsonify from flask import jsonify
from flask import make_response from flask import make_response
from flask.wrappers import 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 Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskState from SpiffWorkflow.task import TaskState
from jinja2 import TemplateSyntaxError
from sqlalchemy import and_ from sqlalchemy import and_
from sqlalchemy import asc from sqlalchemy import asc
from sqlalchemy import desc 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"] = _render_jinja_template(
task.properties["instructionsForEndUser"], spiff_task task.properties["instructionsForEndUser"], spiff_task
) )
except WorkflowTaskException as wfe: except WorkflowTaskException as wfe:
wfe.add_note("Failed to render instructions for end user.") 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) return make_response(jsonify(task), 200)
@ -518,10 +520,13 @@ def _prepare_form_data(
return _render_jinja_template(file_contents, spiff_task) return _render_jinja_template(file_contents, spiff_task)
except WorkflowTaskException as wfe: except WorkflowTaskException as wfe:
wfe.add_note(f"Error in Json Form File '{form_file}'") 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 api_error.file_name = form_file
raise api_error raise api_error
def _render_jinja_template(unprocessed_template: str, spiff_task: SpiffTask) -> str: def _render_jinja_template(unprocessed_template: str, spiff_task: SpiffTask) -> str:
"""Render_jinja_template.""" """Render_jinja_template."""
jinja_environment = jinja2.Environment( 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) template = jinja_environment.from_string(unprocessed_template)
return template.render(**spiff_task.data) return template.render(**spiff_task.data)
except jinja2.exceptions.TemplateError as template_error: 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): if isinstance(template_error, TemplateSyntaxError):
wfe.line_number = template_error.lineno wfe.line_number = template_error.lineno
wfe.error_line = template_error.source.split('\n')[template_error.lineno - 1] wfe.error_line = template_error.source.split("\n")[
wfe.add_note("Jinja2 template errors can happen when trying to displaying task data") template_error.lineno - 1
]
wfe.add_note(
"Jinja2 template errors can happen when trying to displaying task data"
)
raise wfe from template_error 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: if task_data_var not in task.data:
wte = WorkflowTaskException( wte = WorkflowTaskException(
f"Error building form. Attempting to create a selection list" (
f" with options from variable '{task_data_var}' but it doesn't" "Error building form. Attempting to create a"
f" exist in the Task Data.", task=task) " selection list with options from variable"
f" '{task_data_var}' but it doesn't exist in"
" the Task Data."
),
task=task,
)
raise ( raise (
ApiError.from_workflow_exception( ApiError.from_workflow_exception(
error_code="missing_task_data_var", error_code="missing_task_data_var",
message=str(wte), message=str(wte),
exp=wte exp=wte,
) )
) )

View File

@ -873,7 +873,10 @@ class ProcessInstanceProcessor:
f"Event of type {event_definition.event_type} sent to process instance" f"Event of type {event_definition.event_type} sent to process instance"
f" {self.process_instance_model.id}" 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) self.do_engine_steps(save=True)
def add_step(self, step: Union[dict, None] = None) -> None: def add_step(self, step: Union[dict, None] = None) -> None:

View File

@ -1,25 +1,30 @@
"""Test_various_bpmn_constructs.""" """Test_various_bpmn_constructs."""
from typing import Any
from flask.app import Flask from flask.app import Flask
from flask.testing import FlaskClient 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 import db
from spiffworkflow_backend.models.human_task import HumanTaskModel 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 spiffworkflow_backend.models.user import UserModel
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
class TestForGoodErrors(BaseTest): class TestForGoodErrors(BaseTest):
"""Assure when certain errors happen when rendering a jinaj2 error that it makes """Assure when certain errors happen when rendering a jinaj2 error that it makes some sense."""
some sense."""
def get_next_user_task(self, process_instance_id: int, def get_next_user_task(
client: FlaskClient, self,
with_super_admin_user: UserModel, 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 = ( human_tasks = (
db.session.query(HumanTaskModel) db.session.query(HumanTaskModel)
.filter(HumanTaskModel.process_instance_id == process_instance_id) .filter(HumanTaskModel.process_instance_id == process_instance_id)
.all() .all()
) )
assert len(human_tasks) > 0, "No human tasks found for process." assert len(human_tasks) > 0, "No human tasks found for process."
human_task = human_tasks[0] human_task = human_tasks[0]
@ -30,12 +35,13 @@ class TestForGoodErrors(BaseTest):
return response return response
def test_invalid_form( def test_invalid_form(
self, self,
app: Flask, app: Flask,
client: FlaskClient, client: FlaskClient,
with_db_and_bpmn_file_cleanup: None, with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel, with_super_admin_user: UserModel,
) -> None: ) -> None:
"""React json form schema with bad jinja syntax provides good error."""
process_model = load_test_spec( process_model = load_test_spec(
process_model_id="group/simple_form_with_error", process_model_id="group/simple_form_with_error",
process_model_source_directory="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), headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.status_code == 200 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 is not None
assert response.json['error_type'] == 'TemplateSyntaxError' assert response.json["error_type"] == "TemplateSyntaxError"
assert response.json['line_number'] == 3 assert response.json["line_number"] == 3
assert response.json['file_name'] == 'simple_form.json' 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, self,
app: Flask, app: Flask,
client: FlaskClient, 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", 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), 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.status_code == 400
assert response.json is not None assert response.json is not None
assert response.json['error_type'] == 'TemplateSyntaxError' assert response.json["error_type"] == "TemplateSyntaxError"
assert response.json['line_number'] == 3 assert response.json["line_number"] == 3
assert response.json['error_line'] == '{{ x +=- 1}}' assert response.json["error_line"] == "{{ x +=- 1}}"
assert response.json['file_name'] == 'instructions_error.bpmn' assert response.json["file_name"] == "instructions_error.bpmn"
assert 'instructions for end user' in response.json['message'] assert "instructions for end user" in response.json["message"]
assert 'Jinja2' in response.json['message'] assert "Jinja2" in response.json["message"]
assert 'unexpected \'=\'' in response.json['message'] assert "unexpected '='" in response.json["message"]

View File

@ -2806,7 +2806,7 @@ class TestProcessApi(BaseTest):
) )
data = { data = {
"dateTime": "timedelta(hours=1)", "dateTime": "PT1H",
"external": True, "external": True,
"internal": True, "internal": True,
"label": "Event_0e4owa3", "label": "Event_0e4owa3",

View File

@ -59,11 +59,7 @@ export default function ErrorDisplay() {
} }
errorTag = ( errorTag = (
<Notification <Notification title={title} onClose={() => removeError()} type="error">
title={title}
onClose={() => (removeError())}
type="error"
>
{message} {message}
<br /> <br />
{sentryLinkTag} {sentryLinkTag}

View File

@ -115,11 +115,11 @@ export default function ProcessInstanceRun({
}; };
const processInstanceCreateAndRun = () => { const processInstanceCreateAndRun = () => {
setErrorObject(null); removeError();
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: processInstanceCreatePath, path: processInstanceCreatePath,
successCallback: processModelRun, successCallback: processModelRun,
failureCallback: setErrorObject, failureCallback: addError,
httpMethod: 'POST', httpMethod: 'POST',
}); });
}; };

View File

@ -1,4 +1,4 @@
import React, { createContext, useState, useCallback } from 'react'; import React, { createContext, useState } from 'react';
import { ErrorForDisplay } from '../interfaces'; import { ErrorForDisplay } from '../interfaces';
type ErrorContextType = { type ErrorContextType = {
@ -28,7 +28,7 @@ export default function APIErrorProvider({ children }) {
addError: (newError: ErrorForDisplay | null) => addError(newError), addError: (newError: ErrorForDisplay | null) => addError(newError),
removeError: () => removeError(), removeError: () => removeError(),
}), }),
[error, addError, removeError] [error]
); );
return ( return (

View File

@ -8,7 +8,6 @@ import {
DEFAULT_PER_PAGE, DEFAULT_PER_PAGE,
DEFAULT_PAGE, DEFAULT_PAGE,
} from './components/PaginationForTable'; } from './components/PaginationForTable';
import { ErrorForDisplay } from './interfaces';
// https://www.30secondsofcode.org/js/s/slugify // https://www.30secondsofcode.org/js/s/slugify
export const slugifyString = (str: any) => { export const slugifyString = (str: any) => {

View File

@ -26,7 +26,7 @@ export default function Configuration() {
const { ability } = usePermissionFetcher(permissionRequestData); const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => { useEffect(() => {
console.log("Configuration remove error") console.log('Configuration remove error');
removeError(); removeError();
let newSelectedTabIndex = 0; let newSelectedTabIndex = 0;
if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) { if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) {

View File

@ -3,7 +3,6 @@ import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
// @ts-ignore // @ts-ignore
import { Tabs, TabList, Tab } from '@carbon/react'; import { Tabs, TabList, Tab } from '@carbon/react';
import TaskShow from './TaskShow'; import TaskShow from './TaskShow';
import useAPIError from '../hooks/UseApiError';
import MyTasks from './MyTasks'; import MyTasks from './MyTasks';
import GroupedTasks from './GroupedTasks'; import GroupedTasks from './GroupedTasks';
import CompletedInstances from './CompletedInstances'; import CompletedInstances from './CompletedInstances';
@ -11,7 +10,6 @@ import CreateNewInstance from './CreateNewInstance';
export default function HomePageRoutes() { export default function HomePageRoutes() {
const location = useLocation(); const location = useLocation();
const { removeError } = useAPIError();
const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0); const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0);
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -717,7 +717,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
setSelectingEvent(false); setSelectingEvent(false);
initializeTaskDataToDisplay(taskToDisplay); initializeTaskDataToDisplay(taskToDisplay);
setEventPayload('{}'); setEventPayload('{}');
console.log("cancel updating task") console.log('cancel updating task');
removeError(); removeError();
}; };
@ -737,7 +737,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
if (!taskToDisplay) { if (!taskToDisplay) {
return; return;
} }
console.log("saveTaskData") console.log('saveTaskData');
removeError(); removeError();
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute // taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute