Fix dmn and user tasks (#1996)

* updated bpmn-js items

* updated dmn-js items as well

* fixed some linting

* some initial changes to make our dmn and user task handlers work better with react w/ burnettk

* removed unnecessary useEffects and existing form can be edited

* json schema form can set data properly from examples w/ burnettk

* some cleanup w/ burnettk

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2024-07-25 15:45:48 -04:00 committed by GitHub
parent 30b55a2e63
commit 77f272e6eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 117 additions and 146 deletions

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState, useRef } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import Editor from '@monaco-editor/react';
// eslint-disable-next-line import/no-extraneous-dependencies
import merge from 'lodash/merge';
@ -15,7 +15,7 @@ import {
Button,
Loading,
} from '@carbon/react';
import { useDebounce } from 'use-debounce';
import { useDebouncedCallback } from 'use-debounce';
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
import HttpService from '../../services/HttpService';
import ExamplesTable from './ExamplesTable';
@ -72,11 +72,8 @@ export default function ReactFormBuilder({
useState<boolean>(false);
const [strSchema, setStrSchema] = useState<string>('');
const [debouncedStrSchema] = useDebounce(strSchema, 500);
const [strUI, setStrUI] = useState<string>('');
const [debouncedStrUI] = useDebounce(strUI, 500);
const [strFormData, setStrFormData] = useState<string>('');
const [debouncedFormData] = useDebounce(strFormData, 500);
const [postJsonSchema, setPostJsonSchema] = useState<object>({});
const [postJsonUI, setPostJsonUI] = useState<object>({});
@ -106,102 +103,6 @@ export default function ReactFormBuilder({
dataEditorRef.current = editor;
}
const saveFile = useCallback(
(file: File, create: boolean = false, callback: Function | null = null) => {
if ((create && !canCreateFiles) || (!create && !canUpdateFiles)) {
return;
}
let httpMethod = 'PUT';
let url = `/process-models/${processModelId}/files`;
if (create && canCreateFiles) {
httpMethod = 'POST';
} else if (canUpdateFiles) {
url += `/${file.name}`;
}
const submission = new FormData();
submission.append('file', file);
submission.append('fileName', file.name);
HttpService.makeCallToBackend({
path: url,
successCallback: () => {
if (callback) {
callback();
}
},
failureCallback: (e: any) => {
setErrorMessage(`Failed to save file: '${fileName}'. ${e.message}`);
},
httpMethod,
postBody: submission,
});
},
[processModelId, fileName, canUpdateFiles, canCreateFiles],
);
const hasValidName = (identifierToCheck: string) => {
return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/);
};
const createFiles = (base: string) => {
if (hasValidName(base)) {
// meaning it switched from invalid to valid
if (filenameBaseInvalid) {
setFilenameBaseInvalid(false);
}
} else {
setFilenameBaseInvalid(true);
return;
}
saveFile(new File(['{}'], base + SCHEMA_EXTENSION), true, () => {
saveFile(new File(['{}'], base + UI_EXTENSION), true, () => {
saveFile(new File(['{}'], base + DATA_EXTENSION), true, () => {
setBaseFileName(base);
onFileNameSet(base + SCHEMA_EXTENSION);
});
});
});
};
const isReady = () => {
// Use a ready flag so that we still allow people to completely delete
// the schema, ui or data if they want to clear it out.
if (ready) {
return true;
}
if (
debouncedStrSchema !== '' &&
debouncedStrUI !== '' &&
debouncedFormData !== ''
) {
setReady(true);
return true;
}
return false;
};
// Auto save schema changes
useEffect(() => {
if (baseFileName !== '' && ready) {
saveFile(new File([debouncedStrSchema], baseFileName + SCHEMA_EXTENSION));
}
}, [debouncedStrSchema, baseFileName, saveFile, ready]);
// Auto save ui changes
useEffect(() => {
if (baseFileName !== '' && ready) {
saveFile(new File([debouncedStrUI], baseFileName + UI_EXTENSION));
}
}, [debouncedStrUI, baseFileName, saveFile, ready]);
// Auto save example data changes
useEffect(() => {
if (baseFileName !== '' && ready) {
saveFile(new File([debouncedFormData], baseFileName + DATA_EXTENSION));
}
}, [debouncedFormData, baseFileName, saveFile, ready]);
useEffect(() => {
/**
* we need to run the schema and ui through a backend call before rendering the form,
@ -212,28 +113,24 @@ export default function ReactFormBuilder({
let ui = {};
let data = {};
if (
debouncedFormData === '' ||
debouncedStrSchema === '' ||
debouncedStrUI === ''
) {
if (strSchema === '' || strUI === '' || strFormData === '') {
return;
}
try {
schema = JSON.parse(debouncedStrSchema);
schema = JSON.parse(strSchema);
} catch (e) {
setErrorMessage('Please check the Json Schema for errors.');
return;
}
try {
ui = JSON.parse(debouncedStrUI);
ui = JSON.parse(strUI);
} catch (e) {
setErrorMessage('Please check the UI Settings for errors.');
return;
}
try {
data = JSON.parse(debouncedFormData);
data = JSON.parse(strFormData);
} catch (e) {
setErrorMessage('Please check the Data View for errors.');
return;
@ -261,7 +158,97 @@ export default function ReactFormBuilder({
task_data: data,
},
});
}, [debouncedStrSchema, debouncedStrUI, debouncedFormData, canCreateFiles]);
}, [strSchema, strUI, strFormData, canCreateFiles]);
const saveFile = (
file: File,
create: boolean = false,
callback: Function | null = null,
) => {
if ((create && !canCreateFiles) || (!create && !canUpdateFiles)) {
return;
}
let httpMethod = 'PUT';
let url = `/process-models/${processModelId}/files`;
if (create && canCreateFiles) {
httpMethod = 'POST';
} else if (canUpdateFiles) {
url += `/${file.name}`;
}
const submission = new FormData();
submission.append('file', file);
submission.append('fileName', file.name);
HttpService.makeCallToBackend({
path: url,
successCallback: () => {
if (callback) {
callback();
}
},
failureCallback: (e: any) => {
setErrorMessage(`Failed to save file: '${fileName}'. ${e.message}`);
},
httpMethod,
postBody: submission,
});
};
const hasValidName = (identifierToCheck: string) => {
return identifierToCheck.match(/^[a-z0-9][0-9a-z-]+[a-z0-9]$/);
};
const createFiles = (base: string) => {
if (hasValidName(base)) {
// meaning it switched from invalid to valid
if (filenameBaseInvalid) {
setFilenameBaseInvalid(false);
}
} else {
setFilenameBaseInvalid(true);
return;
}
saveFile(new File(['{}'], base + SCHEMA_EXTENSION), true, () => {
saveFile(new File(['{}'], base + UI_EXTENSION), true, () => {
saveFile(new File(['{}'], base + DATA_EXTENSION), true, () => {
setBaseFileName(base);
onFileNameSet(base + SCHEMA_EXTENSION);
setStrSchema('{}');
setStrUI('{}');
setStrFormData('{}');
});
});
});
};
const isReady = () => {
// Use a ready flag so that we still allow people to completely delete
// the schema, ui or data if they want to clear it out.
if (ready) {
return true;
}
if (strSchema !== '' && strUI !== '' && strFormData !== '') {
setReady(true);
return true;
}
return false;
};
// if we share a debounce and update all str states at once
// then only one will get fired so split them out like this.
const updateStrFileDebounce = useDebouncedCallback((newContent: string) => {
saveFile(new File([newContent], baseFileName + SCHEMA_EXTENSION));
}, 500);
const updateStrUIFileDebounce = useDebouncedCallback((newContent: string) => {
saveFile(new File([newContent], baseFileName + UI_EXTENSION));
}, 500);
const updateFormDataFileDebounce = useDebouncedCallback(
(newContent: string) => {
saveFile(new File([newContent], baseFileName + DATA_EXTENSION));
},
500,
);
const handleTabChange = (evt: any) => {
setSelectedIndex(evt.selectedIndex);
@ -475,7 +462,10 @@ export default function ReactFormBuilder({
width="auto"
defaultLanguage="json"
defaultValue={strSchema}
onChange={(value) => setStrSchema(value || '')}
onChange={(value) => {
updateStrFileDebounce(value || '');
setStrSchema(value || '');
}}
onMount={handleSchemaEditorDidMount}
options={{ readOnly: !canUpdateFiles }}
/>
@ -497,7 +487,10 @@ export default function ReactFormBuilder({
width="auto"
defaultLanguage="json"
defaultValue={strUI}
onChange={(value) => setStrUI(value || '')}
onChange={(value) => {
updateStrUIFileDebounce(value || '');
setStrUI(value || '');
}}
onMount={handleUiEditorDidMount}
options={{ readOnly: !canUpdateFiles }}
/>
@ -515,7 +508,10 @@ export default function ReactFormBuilder({
width="auto"
defaultLanguage="json"
defaultValue={strFormData}
onChange={(value: any) => updateDataFromStr(value || '')}
onChange={(value) => {
updateFormDataFileDebounce(value || '');
updateDataFromStr(value || '');
}}
onMount={handleDataEditorDidMount}
options={{ readOnly: !canUpdateFiles }}
/>

View File

@ -56,7 +56,6 @@ import {
import ProcessSearch from '../components/ProcessSearch';
import { Notification } from '../components/Notification';
import ActiveUsers from '../components/ActiveUsers';
import { useFocusedTabStatus } from '../hooks/useFocusedTabStatus';
import useScriptAssistEnabled from '../hooks/useScriptAssistEnabled';
import useProcessScriptAssistMessage from '../hooks/useProcessScriptAssistQuery';
import SpiffTooltip from '../components/SpiffTooltip';
@ -66,7 +65,6 @@ import { usePermissionFetcher } from '../hooks/PermissionService';
export default function ProcessModelEditDiagram() {
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
const isFocused = useFocusedTabStatus();
const handleShowFileNameEditor = () => setShowFileNameEditor(true);
const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
const [diagramHasChanges, setDiagramHasChanges] = useState<boolean>(false);
@ -74,7 +72,7 @@ export default function ProcessModelEditDiagram() {
const [scriptText, setScriptText] = useState<string>('');
const [scriptType, setScriptType] = useState<string>('');
const [fileEventBus, setFileEventBus] = useState<any>(null);
const [jsonScehmaFileName, setJsonScehmaFileName] = useState<string>('');
const [jsonSchemaFileName, setJsonSchemaFileName] = useState<string>('');
const [showJsonSchemaEditor, setShowJsonSchemaEditor] = useState(false);
const [scriptEventBus, setScriptEventBus] = useState<any>(null);
@ -435,7 +433,6 @@ export default function ProcessModelEditDiagram() {
const onJsonSchemaFilesRequested = useCallback(
(event: any) => {
setFileEventBus(event.eventBus);
const re = /.*[-.]schema.json/;
if (processModel) {
const jsonFiles = processModel.files.filter((f) => f.name.match(re));
@ -452,7 +449,6 @@ export default function ProcessModelEditDiagram() {
const onDmnFilesRequested = useCallback(
(event: any) => {
setFileEventBus(event.eventBus);
if (processModel) {
const dmnFiles = processModel.files.filter((f) => f.type === 'dmn');
const options: any[] = [];
@ -494,27 +490,6 @@ export default function ProcessModelEditDiagram() {
[ability, targetUris.messageModelListPath],
);
useEffect(() => {
const updateDiagramFiles = (pm: ProcessModel) => {
setProcessModel(pm);
const re = /.*[-.]schema.json/;
const jsonFiles = pm.files.filter((f) => f.name.match(re));
const options = jsonFiles.map((f) => {
return { label: f.name, value: f.name };
});
fileEventBus.fire('spiff.json_schema_files.returned', { options });
};
if (isFocused && fileEventBus) {
// Request the process model again, and manually fire off the
// commands to update the file lists for json and dmn files.
HttpService.makeCallToBackend({
path: `/${processModelPath}?include_file_references=true`,
successCallback: updateDiagramFiles,
});
}
}, [isFocused, fileEventBus, processModelPath]);
const getScriptUnitTestElements = (element: any) => {
const { extensionElements } = element.businessObject;
if (extensionElements && extensionElements.values.length > 0) {
@ -1258,15 +1233,15 @@ export default function ProcessModelEditDiagram() {
const onLaunchJsonSchemaEditor = useCallback(
(_element: any, fileName: string, eventBus: any) => {
setFileEventBus(eventBus);
setJsonScehmaFileName(fileName);
setJsonSchemaFileName(fileName);
setShowJsonSchemaEditor(true);
},
[],
);
const handleJsonScehmaEditorClose = () => {
const handleJsonSchemaEditorClose = () => {
fileEventBus.fire('spiff.jsonSchema.update', {
value: jsonScehmaFileName,
value: jsonSchemaFileName,
});
setShowJsonSchemaEditor(false);
};
@ -1280,14 +1255,14 @@ export default function ProcessModelEditDiagram() {
open={showJsonSchemaEditor}
modalHeading="Edit JSON Schema"
primaryButtonText="Close"
onRequestSubmit={handleJsonScehmaEditorClose}
onRequestClose={handleJsonScehmaEditorClose}
onRequestSubmit={handleJsonSchemaEditorClose}
onRequestClose={handleJsonSchemaEditorClose}
size="lg"
>
<ReactFormBuilder
processModelId={params.process_model_id || ''}
fileName={jsonScehmaFileName}
onFileNameSet={setJsonScehmaFileName}
fileName={jsonSchemaFileName}
onFileNameSet={setJsonSchemaFileName}
canUpdateFiles={ability.can(
'POST',
targetUris.processModelFileCreatePath,