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:
parent
a38ecc2e60
commit
b2fb0dd79f
|
@ -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,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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/)) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue