diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py index 7711c36f9..eaf67f6c9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py @@ -18,13 +18,13 @@ def setup_database_uri(app: Flask) -> None: if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None: database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}" if app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "sqlite": - app.config["SQLALCHEMY_DATABASE_URI"] = ( - f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3" - ) + app.config[ + "SQLALCHEMY_DATABASE_URI" + ] = f"sqlite:///{app.instance_path}/db_{app.config['ENV_IDENTIFIER']}.sqlite3" elif app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_TYPE") == "postgres": - app.config["SQLALCHEMY_DATABASE_URI"] = ( - f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}" - ) + app.config[ + "SQLALCHEMY_DATABASE_URI" + ] = f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}" else: # use pswd to trick flake8 with hardcoded passwords db_pswd = app.config.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index 922ae456b..dd8130829 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -127,9 +127,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): def serialized_with_metadata(self) -> dict[str, Any]: process_instance_attributes = self.serialized process_instance_attributes["process_metadata"] = self.process_metadata - process_instance_attributes["process_model_with_diagram_identifier"] = ( - self.process_model_with_diagram_identifier - ) + process_instance_attributes[ + "process_model_with_diagram_identifier" + ] = self.process_model_with_diagram_identifier return process_instance_attributes @property diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index f79e141e3..c836456a9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -5,6 +5,7 @@ import uuid from sys import exc_info from typing import Any from typing import Dict +from typing import Generator from typing import Optional from typing import TypedDict from typing import Union @@ -353,7 +354,7 @@ def task_show(process_instance_id: int, task_guid: str = "next") -> flask.wrappe return make_response(jsonify(task), 200) -def _render_instructions_for_end_user(spiff_task: SpiffTask, task: Task): +def _render_instructions_for_end_user(spiff_task: SpiffTask, task: Task) -> str: """Assure any instructions for end user are processed for jinja syntax.""" if task.properties and "instructionsForEndUser" in task.properties: if task.properties["instructionsForEndUser"]: @@ -391,7 +392,7 @@ def process_data_show( ) -def _interstitial_stream(process_instance_id: int): +def _interstitial_stream(process_instance_id: int) -> Generator[str, str, None]: process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) reported_ids = [] # bit of an issue with end tasks showing as getting completed twice. @@ -415,9 +416,8 @@ def _interstitial_stream(process_instance_id: int): processor.save() # Fixme - maybe find a way not to do this on every method? -def interstitial(process_instance_id: int): - """A Server Side Events Stream for watching the execution of engine tasks in a - process instance.""" +def interstitial(process_instance_id: int) -> Response: + """A Server Side Events Stream for watching the execution of engine tasks.""" return Response(stream_with_context(_interstitial_stream(process_instance_id)), mimetype="text/event-stream") 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 28b3fe934..37ce900f4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -433,9 +433,9 @@ class ProcessInstanceProcessor: tld.process_instance_id = process_instance_model.id # we want this to be the fully qualified path to the process model including all group subcomponents - current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = ( - f"{process_instance_model.process_model_identifier}" - ) + current_app.config[ + "THREAD_LOCAL_DATA" + ].process_model_identifier = f"{process_instance_model.process_model_identifier}" self.process_instance_model = process_instance_model self.process_model_service = ProcessModelService() @@ -595,9 +595,9 @@ class ProcessInstanceProcessor: bpmn_subprocess_definition.bpmn_identifier ] = bpmn_process_definition_dict spiff_bpmn_process_dict["subprocess_specs"][bpmn_subprocess_definition.bpmn_identifier]["task_specs"] = {} - bpmn_subprocess_definition_bpmn_identifiers[bpmn_subprocess_definition.id] = ( - bpmn_subprocess_definition.bpmn_identifier - ) + bpmn_subprocess_definition_bpmn_identifiers[ + bpmn_subprocess_definition.id + ] = bpmn_subprocess_definition.bpmn_identifier task_definitions = TaskDefinitionModel.query.filter( TaskDefinitionModel.bpmn_process_definition_id.in_( # type: ignore diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 487619b84..0d641adb1 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -267,8 +267,7 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): class RunUntilUserTaskOrMessageExecutionStrategy(ExecutionStrategy): - """When you want to run tasks until you hit something to report to the end user, or - until there are no other engine steps to complete.""" + """When you want to run tasks until you hit something to report to the end user.""" def get_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> List[SpiffTask]: return list( 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 7398d0f43..c4de5914e 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1654,7 +1654,7 @@ class TestProcessApi(BaseTest): # a list. It tests all of our code. No reason to test Flasks SSE support. results = list(_interstitial_stream(process_instance_id)) # strip the "data:" prefix and convert remaining string to dict. - json_results = list(map(lambda x: json.loads(x[5:]), results)) + json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore # There should be 2 results back - # the first script task should not be returned (it contains no end user instructions) # The second script task should produce rendered jinja text @@ -1675,10 +1675,10 @@ class TestProcessApi(BaseTest): # we should now be on a task that does not belong to the original user, and the interstitial page should know this. results = list(_interstitial_stream(process_instance_id)) - json_results = list(map(lambda x: json.loads(x[5:]), results)) + json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(results) == 1 assert json_results[0]["state"] == "READY" - assert json_results[0]["can_complete"] == False + assert json_results[0]["can_complete"] is False assert json_results[0]["title"] == "Please Approve" assert json_results[0]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane" @@ -1692,7 +1692,7 @@ class TestProcessApi(BaseTest): list(_interstitial_stream(process_instance_id)) list(_interstitial_stream(process_instance_id)) results = list(_interstitial_stream(process_instance_id)) - json_results = list(map(lambda x: json.loads(x[5:]), results)) + json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(json_results) == 1 assert json_results[0]["state"] == "COMPLETED" assert json_results[0]["properties"]["instructionsForEndUser"] == "I am the end task" diff --git a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx index 3b367b57b..7c400d412 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx @@ -17,6 +17,7 @@ export default function ProcessInterstitial() { const [status, setStatus] = useState('running'); const params = useParams(); const navigate = useNavigate(); + const userTasks = ['User Task', 'Manual Task']; useEffect(() => { fetchEventSource( @@ -35,7 +36,8 @@ export default function ProcessInterstitial() { }, } ); - }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // it is critical to only run this once. useEffect(() => { // Added this seperate use effect so that the timer interval will be cleared if @@ -43,7 +45,7 @@ export default function ProcessInterstitial() { if ( lastTask && lastTask.can_complete && - ['User Task', 'Manual Task'].includes(lastTask.type) + userTasks.includes(lastTask.type) ) { const timerId = setInterval(() => { navigate(`/tasks/${lastTask.process_instance_id}/${lastTask.id}`); @@ -57,14 +59,9 @@ export default function ProcessInterstitial() { if (status !== 'running') { setStatus(lastTask.state); } - if ( - !lastTask.can_complete && - ['User Task', 'Manual Task'].includes(lastTask.type) - ) { + if (!lastTask.can_complete && userTasks.includes(lastTask.type)) { setStatus('LOCKED'); } - console.log(`Status is : ${status}}`); - console.log('last task is : ', lastTask); switch (status) { case 'running': return ( @@ -87,10 +84,7 @@ export default function ProcessInterstitial() { }; const userMessage = (myTask: ProcessInstanceTask) => { - if ( - !myTask.can_complete && - ['User Task', 'Manual Task'].includes(myTask.type) - ) { + if (!myTask.can_complete && userTasks.includes(myTask.type)) { return
This next task must be completed by a different person.
; } return ( diff --git a/spiffworkflow-frontend/src/routes/ProcessRoutes.tsx b/spiffworkflow-frontend/src/routes/ProcessRoutes.tsx index 539797896..c4ad03524 100644 --- a/spiffworkflow-frontend/src/routes/ProcessRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessRoutes.tsx @@ -1,12 +1,5 @@ -import { useEffect, useState } from 'react'; -import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; +import { Route, Routes } from 'react-router-dom'; // @ts-ignore -import { Tabs, TabList, Tab } from '@carbon/react'; -import TaskShow from './TaskShow'; -import MyTasks from './MyTasks'; -import GroupedTasks from './GroupedTasks'; -import CompletedInstances from './CompletedInstances'; -import CreateNewInstance from './CreateNewInstance'; import ProcessInterstitial from './ProcessInterstitial'; export default function ProcessRoutes() { diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 3986242f8..56ea9bc22 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -13,7 +13,6 @@ import { ButtonSet, } from '@carbon/react'; -import MDEditor from '@uiw/react-md-editor'; // eslint-disable-next-line import/no-named-as-default import Form from '../themes/carbon'; import HttpService from '../services/HttpService'; @@ -90,13 +89,6 @@ function TypeAheadWidget({ ); } -class UnexpectedHumanTaskType extends Error { - constructor(message: string) { - super(message); - this.name = 'UnexpectedHumanTaskType'; - } -} - enum FormSubmitType { Default, Draft, @@ -108,16 +100,11 @@ export default function TaskShow() { const params = useParams(); const navigate = useNavigate(); const [disabled, setDisabled] = useState(false); - const [refreshSeconds, setRefreshSeconds] = useState(0); - // save current form data so that we can avoid validations in certain situations const [currentFormObject, setCurrentFormObject] = useState({}); const { addError, removeError } = useAPIError(); - // eslint-disable-next-line sonarjs/no-duplicate-string - const supportedHumanTaskTypes = ['User Task', 'Manual Task']; - const navigateToInterstitial = (myTask: ProcessInstanceTask) => { navigate( `/process/${modifyProcessIdentifierForPathParam( @@ -361,12 +348,6 @@ export default function TaskShow() { Save as draft ); - } else { - return ( -

- Page will refresh in {refreshSeconds} seconds. -

- ); } reactFragmentToHideSubmitButton = (