Fix that dreadful unknown "KeyError" exception that was cropping up.

Adding a bit of detail to the spiffworkflow exceptions when a duplicate process model is found.
Disable the submit button on tasks after you click submit (avoid the double click and give users a better experience)
This commit is contained in:
Dan 2023-02-03 17:01:03 -05:00
parent cd7c109a00
commit d010c2bce6
5 changed files with 31 additions and 13 deletions

View File

@ -254,9 +254,9 @@ class BpmnParser(object):
def create_parser(self, node, filename=None, lane=None): def create_parser(self, node, filename=None, lane=None):
parser = self.PROCESS_PARSER_CLASS(self, node, self.namespaces, filename=filename, lane=lane) parser = self.PROCESS_PARSER_CLASS(self, node, self.namespaces, filename=filename, lane=lane)
if parser.get_id() in self.process_parsers: if parser.get_id() in self.process_parsers:
raise ValidationException('Duplicate process ID', node=node, file_name=filename) raise ValidationException(f'Duplicate process ID: {parser.get_id()}', node=node, file_name=filename)
if parser.get_name() in self.process_parsers_by_name: if parser.get_name() in self.process_parsers_by_name:
raise ValidationException('Duplicate process name', node=node, file_name=filename) raise ValidationException(f'Duplicate process name: {parser.get_name()}', node=node, file_name=filename)
self.process_parsers[parser.get_id()] = parser self.process_parsers[parser.get_id()] = parser
self.process_parsers_by_name[parser.get_name()] = parser self.process_parsers_by_name[parser.get_name()] = parser

View File

@ -242,6 +242,10 @@ def handle_exception(exception: Exception) -> flask.wrappers.Response:
api_exception = None api_exception = None
if isinstance(exception, ApiError): if isinstance(exception, ApiError):
api_exception = exception api_exception = exception
elif isinstance(exception, SpiffWorkflowException):
api_exception = ApiError.from_workflow_exception(
"unexpected_workflow_exception", "Unexpected Workflow Error", exception
)
else: else:
api_exception = ApiError( api_exception = ApiError(
error_code=error_code, error_code=error_code,

View File

@ -275,7 +275,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response
) from exception ) from exception
if task.data: if task.data:
_update_form_schema_with_task_data_as_needed(form_dict, task) _update_form_schema_with_task_data_as_needed(form_dict, task, spiff_task)
if form_contents: if form_contents:
task.form_schema = form_dict task.form_schema = form_dict
@ -588,7 +588,9 @@ def _get_spiff_task_from_process_instance(
# originally from: https://bitcoden.com/answers/python-nested-dictionary-update-value-where-any-nested-key-matches # originally from: https://bitcoden.com/answers/python-nested-dictionary-update-value-where-any-nested-key-matches
def _update_form_schema_with_task_data_as_needed(in_dict: dict, task: Task) -> None: def _update_form_schema_with_task_data_as_needed(
in_dict: dict, task: Task, spiff_task: SpiffTask
) -> None:
"""Update_nested.""" """Update_nested."""
if task.data is None: if task.data is None:
return None return None
@ -615,7 +617,7 @@ def _update_form_schema_with_task_data_as_needed(in_dict: dict, task: Task) -> N
f" '{task_data_var}' but it doesn't exist in" f" '{task_data_var}' but it doesn't exist in"
" the Task Data." " the Task Data."
), ),
task=task, task=spiff_task,
) )
raise ( raise (
ApiError.from_workflow_exception( ApiError.from_workflow_exception(
@ -648,11 +650,11 @@ def _update_form_schema_with_task_data_as_needed(in_dict: dict, task: Task) -> N
in_dict[k] = options_for_react_json_schema_form in_dict[k] = options_for_react_json_schema_form
elif isinstance(value, dict): elif isinstance(value, dict):
_update_form_schema_with_task_data_as_needed(value, task) _update_form_schema_with_task_data_as_needed(value, task, spiff_task)
elif isinstance(value, list): elif isinstance(value, list):
for o in value: for o in value:
if isinstance(o, dict): if isinstance(o, dict):
_update_form_schema_with_task_data_as_needed(o, task) _update_form_schema_with_task_data_as_needed(o, task, spiff_task)
def _get_potential_owner_usernames(assigned_user: AliasedClass) -> Any: def _get_potential_owner_usernames(assigned_user: AliasedClass) -> Any:

View File

@ -499,7 +499,10 @@ class ProcessModelService(FileSystemService):
if name is None: if name is None:
raise ApiError( raise ApiError(
error_code="missing_name_of_process_model", error_code="missing_name_of_process_model",
message="Missing name of process model. It should be given", message=(
"Missing name of process model. Path not found:"
f" {json_file_path}"
),
) )
process_model_info = ProcessModelInfo( process_model_info = ProcessModelInfo(

View File

@ -41,7 +41,10 @@ export default function TaskShow() {
// instead of passing the process model identifier in through the params // instead of passing the process model identifier in through the params
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: url, path: url,
successCallback: setUserTasks, successCallback: (tasks: any) => {
setDisabled(false);
setUserTasks(tasks);
},
onUnauthorized: () => {}, onUnauthorized: () => {},
failureCallback: (error: any) => { failureCallback: (error: any) => {
addError(error); addError(error);
@ -59,7 +62,6 @@ export default function TaskShow() {
const processSubmitResult = (result: any) => { const processSubmitResult = (result: any) => {
removeError(); removeError();
setDisabled(false);
if (result.ok) { if (result.ok) {
navigate(`/tasks`); navigate(`/tasks`);
} else if (result.process_instance_id) { } else if (result.process_instance_id) {
@ -212,10 +214,16 @@ export default function TaskShow() {
reactFragmentToHideSubmitButton = <div />; reactFragmentToHideSubmitButton = <div />;
} }
if (task.type === 'Manual Task' && task.state === 'READY') { if (task.state === 'READY') {
let buttonText = 'Submit';
if (task.type === 'Manual Task') {
buttonText = 'Continue';
}
reactFragmentToHideSubmitButton = ( reactFragmentToHideSubmitButton = (
<div> <div>
<Button type="submit">Continue</Button> <Button type="submit" disabled={disabled}>
{buttonText}
</Button>
</div> </div>
); );
} }
@ -228,6 +236,7 @@ export default function TaskShow() {
<Grid fullWidth condensed> <Grid fullWidth condensed>
<Column sm={4} md={5} lg={8}> <Column sm={4} md={5} lg={8}>
<Form <Form
disabled={disabled}
formData={taskData} formData={taskData}
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
schema={jsonSchema} schema={jsonSchema}