diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index 78ea7b2d3..bea97f058 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -354,7 +354,7 @@ def process_list() -> Any: This includes processes that are not the primary process - helpful for finding possible call activities. """ - references = SpecReferenceCache.query.filter_by(type="process") + references = SpecReferenceCache.query.filter_by(type="process").all() return SpecReferenceSchema(many=True).dump(references) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py index 3563e2b3a..412c4b82a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/data_setup_service.py @@ -27,9 +27,9 @@ class DataSetupService: current_app.logger.debug("DataSetupService.save_all_process_models() start") failing_process_models = [] process_models = ProcessModelService().get_process_models() + SpecFileService.clear_caches() for process_model in process_models: current_app.logger.debug(f"Process Model: {process_model.display_name}") - SpecFileService.clear_caches() try: refs = SpecFileService.get_references_for_process(process_model) for ref in refs: diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index 0e242476d..28d3c8eba 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -7574,7 +7574,7 @@ }, "node_modules/bpmn-js-spiffworkflow": { "version": "0.0.8", - "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#c90359945c98034c76a65fcbe8709f8ddeaf949a", + "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#e92f48da7cb4416310af71bb1699caaca87324cd", "license": "MIT", "dependencies": { "inherits": "^2.0.4", @@ -36529,7 +36529,7 @@ } }, "bpmn-js-spiffworkflow": { - "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#c90359945c98034c76a65fcbe8709f8ddeaf949a", + "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#e92f48da7cb4416310af71bb1699caaca87324cd", "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main", "requires": { "inherits": "^2.0.4", diff --git a/spiffworkflow-frontend/src/components/ProcessSearch.tsx b/spiffworkflow-frontend/src/components/ProcessSearch.tsx index 7637760a5..75ee69b24 100644 --- a/spiffworkflow-frontend/src/components/ProcessSearch.tsx +++ b/spiffworkflow-frontend/src/components/ProcessSearch.tsx @@ -17,14 +17,17 @@ export default function ProcessSearch({ processes, selectedItem, onChange, - titleText = 'Process model', + titleText = 'Process Search', height = '50px', }: OwnProps) { const shouldFilter = (options: any) => { const process: ProcessReference = options.item; const { inputValue } = options; - return `${process.identifier} (${process.display_name})`.includes( - inputValue + return ( + inputValue === null || + `${process.display_name} (${process.identifier})` + .toLowerCase() + .includes(inputValue.toLowerCase()) ); }; return ( @@ -36,8 +39,8 @@ export default function ProcessSearch({ items={processes} itemToString={(process: ProcessReference) => { if (process) { - return `${process.identifier} (${truncateString( - process.display_name, + return `${process.display_name} (${truncateString( + process.identifier, 20 )})`; } diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index 1f765157d..956ff6c62 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -80,6 +80,7 @@ type OwnProps = { onServiceTasksRequested?: (..._args: any[]) => any; onJsonFilesRequested?: (..._args: any[]) => any; onDmnFilesRequested?: (..._args: any[]) => any; + onSearchProcessModels?: (..._args: any[]) => any; url?: string; }; @@ -103,6 +104,7 @@ export default function ReactDiagramEditor({ onServiceTasksRequested, onJsonFilesRequested, onDmnFilesRequested, + onSearchProcessModels, url, }: OwnProps) { const [diagramXMLString, setDiagramXMLString] = useState(''); @@ -303,6 +305,12 @@ export default function ReactDiagramEditor({ diagramModeler.on('spiff.json_files.requested', (event: any) => { handleServiceTasksRequested(event); }); + + diagramModeler.on('spiff.callactivity.search', (event: any) => { + if (onSearchProcessModels) { + onSearchProcessModels(event.value, event.eventBus, event.element); + } + }); }, [ diagramModelerState, diagramType, @@ -315,6 +323,7 @@ export default function ReactDiagramEditor({ onServiceTasksRequested, onJsonFilesRequested, onDmnFilesRequested, + onSearchProcessModels, ]); useEffect(() => { diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 9eee17a4b..d1a12c2f7 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -17,10 +17,18 @@ export interface ProcessGroup { description?: string | null; } -export interface ProcessFileReference { +export interface ProcessReference { id: string; // The unique id of the process or decision table. - name: string; // The process or decision table name. + name: string; // The process or decision Display name. + identifier: string; + display_name: string; + process_group_id: string; + process_model_id: string; type: string; // either "decision" or "process" + file_name: string; + has_lanes: boolean; + is_executable: boolean; + is_primary: boolean; } export interface ProcessFile { @@ -28,7 +36,7 @@ export interface ProcessFile { last_modified: string; name: string; process_model_id: string; - references: ProcessFileReference[]; + references: ProcessReference[]; size: number; type: string; file_contents?: string; @@ -71,6 +79,10 @@ export interface CarbonComboBoxSelection { selectedItem: ProcessModel; } +export interface CarbonComboBoxProcessSelection { + selectedItem: ProcessReference; +} + export interface PermissionsToCheck { [key: string]: string[]; } diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 06644e2e7..173b8e7a0 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -18,7 +18,13 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; import { makeid, modifyProcessModelPath } from '../helpers'; -import { ProcessFile, ProcessModel } from '../interfaces'; +import { + CarbonComboBoxProcessSelection, + ProcessFile, + ProcessModel, + ProcessReference, +} from '../interfaces'; +import ProcessSearch from '../components/ProcessSearch'; export default function ProcessModelEditDiagram() { const [showFileNameEditor, setShowFileNameEditor] = useState(false); @@ -36,6 +42,10 @@ export default function ProcessModelEditDiagram() { const [markdownText, setMarkdownText] = useState(''); const [markdownEventBus, setMarkdownEventBus] = useState(null); const [showMarkdownEditor, setShowMarkdownEditor] = useState(false); + const [showProcessSearch, setShowProcessSearch] = useState(false); + const [processSearchEventBus, setProcessSearchEventBus] = useState(null); + const [processSearchElement, setProcessSearchElement] = useState(null); + const [processes, setProcesses] = useState([]); const handleShowMarkdownEditor = () => setShowMarkdownEditor(true); @@ -90,6 +100,23 @@ export default function ProcessModelEditDiagram() { const processModelPath = `process-models/${modifiedProcessModelId}`; + useEffect(() => { + // Grab all available process models in case we need to search for them. + // Taken from the Process Group List + const processResults = (result: any) => { + const selectionArray = result.map((item: any) => { + const label = `${item.display_name} (${item.identifier})`; + Object.assign(item, { label }); + return item; + }); + setProcesses(selectionArray); + }; + HttpService.makeCallToBackend({ + path: `/processes`, + successCallback: processResults, + }); + }, [processModel]); + useEffect(() => { const processResult = (result: ProcessModel) => { setProcessModel(result); @@ -278,7 +305,7 @@ export default function ProcessModelEditDiagram() { const options: any[] = []; dmnFiles.forEach((file) => { file.references.forEach((ref) => { - options.push({ label: ref.name, value: ref.id }); + options.push({ label: ref.display_name, value: ref.identifier }); }); }); event.eventBus.fire('spiff.dmn_files.returned', { options }); @@ -656,6 +683,45 @@ export default function ProcessModelEditDiagram() { ); }; + const onSearchProcessModels = ( + processId: string, + eventBus: any, + element: any + ) => { + setProcessSearchEventBus(eventBus); + setProcessSearchElement(element); + setShowProcessSearch(true); + }; + const processSearchOnClose = (selection: CarbonComboBoxProcessSelection) => { + const selectedProcessModel = selection.selectedItem; + if (selectedProcessModel) { + processSearchEventBus.fire('spiff.callactivity.update', { + element: processSearchElement, + value: selectedProcessModel.identifier, + }); + } + setShowProcessSearch(false); + }; + + const processModelSelector = () => { + return ( + + + + ); + }; + const findFileNameForReferenceId = ( id: string, type: string @@ -676,19 +742,18 @@ export default function ProcessModelEditDiagram() { return matchFile; }; - /** - * fixme: Not currently in use. This would only work for bpmn files within the process model. Which is right for DMN and json, but not right here. Need to merge in work on the nested process groups before tackling this. - * @param processId - */ - const onLaunchBpmnEditor = (processId: string) => { - const file = findFileNameForReferenceId(processId, 'bpmn'); - if (file) { + const processRef = processes.find((p) => { + return p.identifier === processId; + }); + if (processRef) { const path = generatePath( - '/admin/process-models/:process_model_id/files/:file_name', + '/admin/process-models/:process_model_path/files/:file_name', { - process_model_id: params.process_model_id, - file_name: file.name, + process_model_path: modifyProcessModelPath( + processRef.process_model_id + ), + file_name: processRef.file_name, } ); window.open(path); @@ -763,12 +828,17 @@ export default function ProcessModelEditDiagram() { onJsonFilesRequested={onJsonFilesRequested} onLaunchDmnEditor={onLaunchDmnEditor} onDmnFilesRequested={onDmnFilesRequested} + onSearchProcessModels={onSearchProcessModels} /> ); }; // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it - if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { + if ( + (bpmnXmlForDiagramRendering || !params.file_name) && + processModel && + processes.length > 0 + ) { const processModelFileName = processModelFile ? processModelFile.name : ''; return ( <> @@ -790,7 +860,7 @@ export default function ProcessModelEditDiagram() { {newFileNameBox()} {scriptEditor()} {markdownEditor()} - + {processModelSelector()}
);