From 8644dcafff743238d23e60eeaa08b231711d3f9d Mon Sep 17 00:00:00 2001 From: Kevin Burnett <18027+burnettk@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:39:35 -0800 Subject: [PATCH 1/3] download working (#2230) Co-authored-by: burnettk --- .../src/components/ProcessModelFileList.tsx | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessModelFileList.tsx b/spiffworkflow-frontend/src/components/ProcessModelFileList.tsx index 1260a89ed..d351765d7 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelFileList.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelFileList.tsx @@ -15,6 +15,8 @@ import { PureAbility } from '@casl/ability'; import ButtonWithConfirmation from './ButtonWithConfirmation'; import ProcessModelTestRun from './ProcessModelTestRun'; import { ProcessFile } from '../interfaces'; +import HttpService from '../services/HttpService'; +import useAPIError from '../hooks/UseApiError'; interface ProcessModelFileListProps { processModel: any; @@ -35,6 +37,7 @@ export default function ProcessModelFileList({ onSetPrimaryFile, isTestCaseFile, }: ProcessModelFileListProps) { + const { addError, removeError } = useAPIError(); const profileModelFileEditUrl = (processModelFile: ProcessFile) => { if (processModel) { if (processModelFile.name.match(/\.(dmn|bpmn)$/)) { @@ -47,6 +50,40 @@ export default function ProcessModelFileList({ return null; }; + const handleProcessModelFileResult = (processModelFile: ProcessFile) => { + if ( + !('file_contents' in processModelFile) || + processModelFile.file_contents === undefined + ) { + addError({ + message: `Could not file file contents for file: ${processModelFile.name}`, + }); + return; + } + let contentType = 'application/xml'; + if (processModelFile.type === 'json') { + contentType = 'application/json'; + } + const element = document.createElement('a'); + const file = new Blob([processModelFile.file_contents], { + type: contentType, + }); + const downloadFileName = processModelFile.name; + element.href = URL.createObjectURL(file); + element.download = downloadFileName; + document.body.appendChild(element); + element.click(); + }; + + const downloadFile = (fileName: string) => { + removeError(); + const processModelPath = `process-models/${modifiedProcessModelId}`; + HttpService.makeCallToBackend({ + path: `/${processModelPath}/files/${fileName}`, + successCallback: handleProcessModelFileResult, + }); + }; + const renderButtonElements = ( processModelFile: ProcessFile, isPrimaryBpmnFile: boolean, @@ -80,12 +117,7 @@ export default function ProcessModelFileList({ iconDescription="Download File" hasIconOnly size="lg" - onClick={() => - window.open( - `/${targetUris.processModelFilePath}/${processModelFile.name}`, - '_blank', - ) - } + onClick={() => downloadFile(processModelFile.name)} /> , ); From bbbc157c22bdbc43ef49002f81a0d3f3d8ed6b0b Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Fri, 7 Feb 2025 13:54:20 -0500 Subject: [PATCH 2/3] =?UTF-8?q?Don't=20try=20to=20roll=20back=20the=20data?= =?UTF-8?q?base=20on=20a=20failed=20message=20delivery,=20we=20=E2=80=A6?= =?UTF-8?q?=20(#2231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't try to roll back the database on a failed message delivery, we must allow the error so we have an audit of what happened. Otherwise we have to kill all trace of it, which just creates a grand mystery. * pyl. * tweak message * more fixes. * clean up --------- Co-authored-by: burnettk --- .../services/message_service.py | 5 ++- .../message-start-with-error.bpmn | 45 +++++++++++++++++++ .../unit/test_message_service.py | 39 ++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 spiffworkflow-backend/tests/data/message-start-with-error/message-start-with-error.bpmn diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py index 7378abea3..755fadf37 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py @@ -5,6 +5,7 @@ from flask import g from SpiffWorkflow.bpmn import BpmnEvent # type: ignore from SpiffWorkflow.bpmn.specs.event_definitions.message import CorrelationProperty # type: ignore from SpiffWorkflow.bpmn.specs.mixins import StartEventMixin # type: ignore +from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore from SpiffWorkflow.spiff.specs.event_definitions import MessageEventDefinition # type: ignore from spiffworkflow_backend.background_processing.celery_tasks.process_instance_task_producer import ( @@ -148,7 +149,7 @@ class MessageService: return None except Exception as exception: - db.session.rollback() + # db.session.rollback() # don't try to roll this back. The message failed, and we need to know why. message_instance_send.status = "failed" message_instance_send.failure_cause = str(exception) db.session.add(message_instance_send) @@ -160,6 +161,8 @@ class MessageService: processor_receive.save() else: db.session.commit() + if isinstance(exception, SpiffWorkflowException): + exception.add_note("The process instance encountered an error and failed after starting.") raise exception @classmethod diff --git a/spiffworkflow-backend/tests/data/message-start-with-error/message-start-with-error.bpmn b/spiffworkflow-backend/tests/data/message-start-with-error/message-start-with-error.bpmn new file mode 100644 index 000000000..a32ee0752 --- /dev/null +++ b/spiffworkflow-backend/tests/data/message-start-with-error/message-start-with-error.bpmn @@ -0,0 +1,45 @@ + + + + + Flow_0eqkmd7 + + + + + + + Flow_0ur3sgf + + + + Flow_0eqkmd7 + Flow_0ur3sgf + x = 1 '123'asdfasdf;lkj #$!@#$$$#@#@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py index f9e72bf8f..f19073580 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py @@ -1,7 +1,9 @@ import time from flask import Flask +from flask import g from flask.testing import FlaskClient +from spiffworkflow_backend.helpers.spiff_enum import ProcessInstanceExecutionMode from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.message_triggerable_process_model import MessageTriggerableProcessModel @@ -12,6 +14,7 @@ from spiffworkflow_backend.services.message_service import MessageService from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService from spiffworkflow_backend.services.spec_file_service import SpecFileService +from spiffworkflow_backend.services.workflow_execution_service import WorkflowExecutionServiceError from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -94,6 +97,42 @@ class TestMessageService(BaseTest): for message_instance in message_instances: assert message_instance.correlation_keys == {"invoice": {"po_number": 1001, "customer_id": "Sartography"}} + def test_start_process_with_message_when_failure( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + """Assure we get a valid error when trying to start a process, and that process fails for some reason.""" + + # Load up the definition for the receiving process + # It has a message start event that should cause it to fire when a unique message comes through + # Fire up the first process + load_test_spec( + "test_group/message-start-with-error", + process_model_source_directory="message-start-with-error", + bpmn_file_name="message-start-with-error.bpmn", + ) + + # Now send in the message + user = self.find_or_create_user() + message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by( + message_name="test_bad_process" + ).first() + assert message_triggerable_process_model is not None + + MessageInstanceModel( + message_type="send", + name="test_bad_process", + payload={}, + user_id=user.id, + ) + g.user = user + try: + MessageService.run_process_model_from_message("test_bad_process", {}, ProcessInstanceExecutionMode.synchronous.value) + except WorkflowExecutionServiceError as e: + assert "The process instance encountered an error and failed after starting." in e.notes + def test_can_send_message_to_multiple_process_models( self, app: Flask, From b5f1b3f2c4200263cbcc9ff6c998142cf65f8363 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:16:00 +0000 Subject: [PATCH 3/3] Bump pip from 25.0 to 25.0.1 in /.github/workflows (#2232) Bumps [pip](https://github.com/pypa/pip) from 25.0 to 25.0.1. - [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/pip/compare/25.0...25.0.1) --- updated-dependencies: - dependency-name: pip dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 54fbc7411..1687c9f4f 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -1,2 +1,2 @@ -pip==25.0 +pip==25.0.1 poetry==2.0.1