diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index 9e075600..3e9f54eb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -75,6 +75,10 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): ) # type: ignore message_instances = relationship("MessageInstanceModel", cascade="delete") # type: ignore message_correlations = relationship("MessageCorrelationModel", cascade="delete") # type: ignore + process_metadata = relationship( + "ProcessInstanceMetadataModel", + cascade="delete", + ) # type: ignore bpmn_json: str | None = deferred(db.Column(db.JSON)) # type: ignore start_in_seconds: int | None = db.Column(db.Integer) @@ -111,6 +115,11 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): "process_initiator_username": self.process_initiator.username, } + def serialized_with_metadata(self) -> dict[str, Any]: + process_instance_attributes = self.serialized + process_instance_attributes["process_metadata"] = self.process_metadata + return process_instance_attributes + @property def serialized_flat(self) -> dict: """Return object in serializeable format with data merged together with top-level attributes. diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 679c2fa7..27ccc8ab 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -694,7 +694,8 @@ def _get_process_instance( ) process_instance.bpmn_xml_file_contents = bpmn_xml_file_contents - return make_response(jsonify(process_instance), 200) + process_instance_as_dict = process_instance.serialized_with_metadata() + return make_response(jsonify(process_instance_as_dict), 200) def _find_process_instance_for_me_or_raise( diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index f2d2d0a6..990d3bbd 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -66,6 +66,12 @@ export interface ProcessFile { file_contents?: string; } +export interface ProcessInstanceMetadata { + id: number; + key: string; + value: string; +} + export interface ProcessInstance { id: number; process_model_identifier: string; @@ -80,6 +86,7 @@ export interface ProcessInstance { updated_at_in_seconds: number; bpmn_version_control_identifier: string; bpmn_version_control_type: string; + process_metadata?: ProcessInstanceMetadata[]; } export interface MessageCorrelationProperties { diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 279322a6..41168bdf 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -44,6 +44,7 @@ import { PermissionsToCheck, ProcessData, ProcessInstance, + ProcessInstanceMetadata, ProcessInstanceTask, } from '../interfaces'; import { usePermissionFetcher } from '../hooks/PermissionService'; @@ -74,6 +75,8 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { const [eventTextEditorEnabled, setEventTextEditorEnabled] = useState(false); const [displayDetails, setDisplayDetails] = useState(false); + const [showProcessInstanceMetadata, setShowProcessInstanceMetadata] = + useState(false); const setErrorObject = (useContext as any)(ErrorContext)[1]; @@ -446,6 +449,19 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { Messages + {processInstance.process_metadata && + processInstance.process_metadata.length > 0 ? ( + + ) : null} @@ -903,6 +919,41 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ); }; + const processInstanceMetadataArea = () => { + if ( + !processInstance || + (processInstance.process_metadata && + processInstance.process_metadata.length < 1) + ) { + return null; + } + const metadataComponents: any[] = []; + (processInstance.process_metadata || []).forEach( + (processInstanceMetadata: ProcessInstanceMetadata) => { + metadataComponents.push( + + + {processInstanceMetadata.key} + + + {processInstanceMetadata.value} + + + ); + } + ); + return ( + setShowProcessInstanceMetadata(false)} + > + {metadataComponents} + + ); + }; + const taskUpdateDisplayArea = () => { const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; const candidateEvents: any = getEvents(taskToUse); @@ -1034,6 +1085,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
{taskUpdateDisplayArea()} {processDataDisplayArea()} + {processInstanceMetadataArea()} {stepsElement()}