added better error message for failed tasks w/ burnettk

This commit is contained in:
jasquat 2022-12-28 16:29:17 -05:00
parent daf85f0efa
commit fb20d89459
13 changed files with 75 additions and 54 deletions

View File

@ -17,13 +17,11 @@ import UserService from './services/UserService';
import { Notification } from './components/Notification'; import { Notification } from './components/Notification';
export default function App() { export default function App() {
const [errorMessage, setErrorMessage] = useState<ErrorForDisplay | null>( const [errorObject, setErrorObject] = useState<ErrorForDisplay | null>(null);
null
);
const errorContextValueArray = useMemo( const errorContextValueArray = useMemo(
() => [errorMessage, setErrorMessage], () => [errorObject, setErrorObject],
[errorMessage] [errorObject]
); );
if (!UserService.isLoggedIn()) { if (!UserService.isLoggedIn()) {
@ -34,27 +32,46 @@ export default function App() {
const ability = defineAbility(() => {}); const ability = defineAbility(() => {});
let errorTag = null; let errorTag = null;
if (errorMessage) { if (errorObject) {
let sentryLinkTag = null; let sentryLinkTag = null;
if (errorMessage.sentry_link) { if (errorObject.sentry_link) {
sentryLinkTag = ( sentryLinkTag = (
<span> <span>
{ {
': Find details about this error here (it may take a moment to become available): ' ': Find details about this error here (it may take a moment to become available): '
} }
<a href={errorMessage.sentry_link} target="_blank" rel="noreferrer"> <a href={errorObject.sentry_link} target="_blank" rel="noreferrer">
{errorMessage.sentry_link} {errorObject.sentry_link}
</a> </a>
</span> </span>
); );
} }
let message = <div>{errorObject.message}</div>;
let title = 'Error:';
if ('task_name' in errorObject) {
title = `Error in python script:`;
message = (
<>
<br />
<div>
Task: {errorObject.task_name} ({errorObject.task_id})
</div>
<div>File name: {errorObject.file_name}</div>
<div>Line number in script task: {errorObject.line_number}</div>
<br />
<div>{errorObject.message}</div>
</>
);
}
errorTag = ( errorTag = (
<Notification <Notification
title="Error:" title={title}
onClose={() => setErrorMessage(null)} onClose={() => setErrorObject(null)}
type="error" type="error"
> >
{errorMessage.message} {message}
{sentryLinkTag} {sentryLinkTag}
</Notification> </Notification>
); );

View File

@ -21,9 +21,9 @@ export function Notification({
onClose, onClose,
type = 'success', type = 'success',
}: OwnProps) { }: OwnProps) {
let iconComponent = <Checkmark />; let iconComponent = <Checkmark className="notification-icon" />;
if (type === 'error') { if (type === 'error') {
iconComponent = <Error />; iconComponent = <Error className="notification-icon" />;
} }
return ( return (
<div <div

View File

@ -132,7 +132,7 @@ export default function ProcessInstanceListTable({
const [endFromTimeInvalid, setEndFromTimeInvalid] = useState<boolean>(false); const [endFromTimeInvalid, setEndFromTimeInvalid] = useState<boolean>(false);
const [endToTimeInvalid, setEndToTimeInvalid] = useState<boolean>(false); const [endToTimeInvalid, setEndToTimeInvalid] = useState<boolean>(false);
const [errorMessage, setErrorMessage] = (useContext as any)(ErrorContext); const [errorObject, setErrorObject] = (useContext as any)(ErrorContext);
const processInstancePathPrefix = const processInstancePathPrefix =
variant === 'all' variant === 'all'
@ -469,7 +469,7 @@ export default function ProcessInstanceListTable({
} }
if (message !== '') { if (message !== '') {
valid = false; valid = false;
setErrorMessageSafely(message, errorMessage, setErrorMessage); setErrorMessageSafely(message, errorObject, setErrorObject);
} }
} }
@ -527,7 +527,7 @@ export default function ProcessInstanceListTable({
queryParamString += `&report_id=${processInstanceReportSelection.id}`; queryParamString += `&report_id=${processInstanceReportSelection.id}`;
} }
setErrorMessage(null); setErrorObject(null);
setProcessInstanceReportJustSaved(null); setProcessInstanceReportJustSaved(null);
navigate(`${processInstancePathPrefix}?${queryParamString}`); navigate(`${processInstancePathPrefix}?${queryParamString}`);
}; };
@ -626,7 +626,7 @@ export default function ProcessInstanceListTable({
queryParamString = `?report_id=${selectedReport.id}`; queryParamString = `?report_id=${selectedReport.id}`;
} }
setErrorMessage(null); setErrorObject(null);
setProcessInstanceReportJustSaved(mode || null); setProcessInstanceReportJustSaved(mode || null);
navigate(`${processInstancePathPrefix}${queryParamString}`); navigate(`${processInstancePathPrefix}${queryParamString}`);
}; };

View File

@ -78,7 +78,7 @@ export default function ProcessInstanceRun({
checkPermissions = true, checkPermissions = true,
}: OwnProps) { }: OwnProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const modifiedProcessModelId = modifyProcessIdentifierForPathParam( const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
processModel.id processModel.id
); );
@ -105,12 +105,12 @@ export default function ProcessInstanceRun({
}; };
const processModelRun = (processInstance: any) => { const processModelRun = (processInstance: any) => {
setErrorMessage(null); setErrorObject(null);
storeRecentProcessModelInLocalStorage(processModel); storeRecentProcessModelInLocalStorage(processModel);
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/process-instances/${modifiedProcessModelId}/${processInstance.id}/run`, path: `/process-instances/${modifiedProcessModelId}/${processInstance.id}/run`,
successCallback: onProcessInstanceRun, successCallback: onProcessInstanceRun,
failureCallback: setErrorMessage, failureCallback: setErrorObject,
httpMethod: 'POST', httpMethod: 'POST',
}); });
}; };

View File

@ -158,6 +158,10 @@ export type HotCrumbItem = HotCrumbItemArray | HotCrumbItemObject;
export interface ErrorForDisplay { export interface ErrorForDisplay {
message: string; message: string;
sentry_link?: string; sentry_link?: string;
task_name?: string;
task_id?: string;
line_number?: number;
file_name?: string;
} }
export interface AuthenticationParam { export interface AuthenticationParam {

View File

@ -25,11 +25,11 @@ import JsonSchemaFormBuilder from './JsonSchemaFormBuilder';
export default function AdminRoutes() { export default function AdminRoutes() {
const location = useLocation(); const location = useLocation();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
useEffect(() => { useEffect(() => {
setErrorMessage(null); setErrorObject(null);
}, [location, setErrorMessage]); }, [location, setErrorObject]);
if (UserService.hasRole(['admin'])) { if (UserService.hasRole(['admin'])) {
return ( return (

View File

@ -7,7 +7,7 @@ import HttpService from '../services/HttpService';
import UserService from '../services/UserService'; import UserService from '../services/UserService';
export default function AuthenticationList() { export default function AuthenticationList() {
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const [authenticationList, setAuthenticationList] = useState< const [authenticationList, setAuthenticationList] = useState<
AuthenticationItem[] | null AuthenticationItem[] | null
@ -26,9 +26,9 @@ export default function AuthenticationList() {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/authentications`, path: `/authentications`,
successCallback: processResult, successCallback: processResult,
failureCallback: setErrorMessage, failureCallback: setErrorObject,
}); });
}, [setErrorMessage]); }, [setErrorObject]);
const buildTable = () => { const buildTable = () => {
if (authenticationList) { if (authenticationList) {

View File

@ -14,7 +14,7 @@ import { usePermissionFetcher } from '../hooks/PermissionService';
export default function Configuration() { export default function Configuration() {
const location = useLocation(); const location = useLocation();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0); const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0);
const navigate = useNavigate(); const navigate = useNavigate();
@ -26,13 +26,13 @@ export default function Configuration() {
const { ability } = usePermissionFetcher(permissionRequestData); const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => { useEffect(() => {
setErrorMessage(null); setErrorObject(null);
let newSelectedTabIndex = 0; let newSelectedTabIndex = 0;
if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) { if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) {
newSelectedTabIndex = 1; newSelectedTabIndex = 1;
} }
setSelectedTabIndex(newSelectedTabIndex); setSelectedTabIndex(newSelectedTabIndex);
}, [location, setErrorMessage]); }, [location, setErrorObject]);
return ( return (
<> <>

View File

@ -11,12 +11,12 @@ import CreateNewInstance from './CreateNewInstance';
export default function HomePageRoutes() { export default function HomePageRoutes() {
const location = useLocation(); const location = useLocation();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0); const [selectedTabIndex, setSelectedTabIndex] = useState<number>(0);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
setErrorMessage(null); setErrorObject(null);
let newSelectedTabIndex = 0; let newSelectedTabIndex = 0;
if (location.pathname.match(/^\/tasks\/completed-instances\b/)) { if (location.pathname.match(/^\/tasks\/completed-instances\b/)) {
newSelectedTabIndex = 1; newSelectedTabIndex = 1;
@ -24,7 +24,7 @@ export default function HomePageRoutes() {
newSelectedTabIndex = 2; newSelectedTabIndex = 2;
} }
setSelectedTabIndex(newSelectedTabIndex); setSelectedTabIndex(newSelectedTabIndex);
}, [location, setErrorMessage]); }, [location, setErrorObject]);
const renderTabs = () => { const renderTabs = () => {
if (location.pathname.match(/^\/tasks\/\d+\/\b/)) { if (location.pathname.match(/^\/tasks\/\d+\/\b/)) {

View File

@ -67,7 +67,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
useState<ProcessData | null>(null); useState<ProcessData | null>(null);
const [editingTaskData, setEditingTaskData] = useState<boolean>(false); const [editingTaskData, setEditingTaskData] = useState<boolean>(false);
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const unModifiedProcessModelId = unModifyProcessIdentifierForPathParam( const unModifiedProcessModelId = unModifyProcessIdentifierForPathParam(
`${params.process_model_id}` `${params.process_model_id}`
@ -511,7 +511,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const cancelEditingTaskData = () => { const cancelEditingTaskData = () => {
setEditingTaskData(false); setEditingTaskData(false);
initializeTaskDataToDisplay(taskToDisplay); initializeTaskDataToDisplay(taskToDisplay);
setErrorMessage(null); setErrorObject(null);
}; };
const taskDataStringToObject = (dataString: string) => { const taskDataStringToObject = (dataString: string) => {
@ -527,7 +527,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
}; };
const saveTaskDataFailure = (result: any) => { const saveTaskDataFailure = (result: any) => {
setErrorMessage({ message: result.message }); setErrorObject({ message: result.message });
}; };
const saveTaskData = () => { const saveTaskData = () => {
@ -535,7 +535,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
return; return;
} }
setErrorMessage(null); setErrorObject(null);
// taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute // taskToUse is copy of taskToDisplay, with taskDataToDisplay in data attribute
const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay }; const taskToUse: any = { ...taskToDisplay, data: taskDataToDisplay };

View File

@ -98,7 +98,7 @@ export default function ProcessModelEditDiagram() {
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const [processModelFile, setProcessModelFile] = useState<ProcessFile | null>( const [processModelFile, setProcessModelFile] = useState<ProcessFile | null>(
null null
); );
@ -172,7 +172,7 @@ export default function ProcessModelEditDiagram() {
const saveDiagram = (bpmnXML: any, fileName = params.file_name) => { const saveDiagram = (bpmnXML: any, fileName = params.file_name) => {
setDisplaySaveFileMessage(false); setDisplaySaveFileMessage(false);
setErrorMessage(null); setErrorObject(null);
setBpmnXmlForDiagramRendering(bpmnXML); setBpmnXmlForDiagramRendering(bpmnXML);
let url = `/process-models/${modifiedProcessModelId}/files`; let url = `/process-models/${modifiedProcessModelId}/files`;
@ -198,7 +198,7 @@ export default function ProcessModelEditDiagram() {
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: url, path: url,
successCallback: navigateToProcessModelFile, successCallback: navigateToProcessModelFile,
failureCallback: setErrorMessage, failureCallback: setErrorObject,
httpMethod, httpMethod,
postBody: formData, postBody: formData,
}); });
@ -510,17 +510,17 @@ export default function ProcessModelEditDiagram() {
const unitTestFailureElement = () => { const unitTestFailureElement = () => {
if (scriptUnitTestResult && scriptUnitTestResult.result === false) { if (scriptUnitTestResult && scriptUnitTestResult.result === false) {
let errorMessage = ''; let errorObject = '';
if (scriptUnitTestResult.context) { if (scriptUnitTestResult.context) {
errorMessage = 'Unexpected result. Please see the comparison below.'; errorObject = 'Unexpected result. Please see the comparison below.';
} else if (scriptUnitTestResult.line_number) { } else if (scriptUnitTestResult.line_number) {
errorMessage = `Error encountered running the script. Please check the code around line ${scriptUnitTestResult.line_number}`; errorObject = `Error encountered running the script. Please check the code around line ${scriptUnitTestResult.line_number}`;
} else { } else {
errorMessage = `Error encountered running the script. ${JSON.stringify( errorObject = `Error encountered running the script. ${JSON.stringify(
scriptUnitTestResult.error scriptUnitTestResult.error
)}`; )}`;
} }
let errorStringElement = <span>{errorMessage}</span>; let errorStringElement = <span>{errorObject}</span>;
let errorContextElement = null; let errorContextElement = null;

View File

@ -52,7 +52,7 @@ import { Notification } from '../components/Notification';
export default function ProcessModelShow() { export default function ProcessModelShow() {
const params = useParams(); const params = useParams();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const [processModel, setProcessModel] = useState<ProcessModel | null>(null); const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
const [processInstance, setProcessInstance] = const [processInstance, setProcessInstance] =
@ -148,7 +148,7 @@ export default function ProcessModelShow() {
!('file_contents' in processModelFile) || !('file_contents' in processModelFile) ||
processModelFile.file_contents === undefined processModelFile.file_contents === undefined
) { ) {
setErrorMessage({ setErrorObject({
message: `Could not file file contents for file: ${processModelFile.name}`, message: `Could not file file contents for file: ${processModelFile.name}`,
}); });
return; return;
@ -169,7 +169,7 @@ export default function ProcessModelShow() {
}; };
const downloadFile = (fileName: string) => { const downloadFile = (fileName: string) => {
setErrorMessage(null); setErrorObject(null);
const processModelPath = `process-models/${modifiedProcessModelId}`; const processModelPath = `process-models/${modifiedProcessModelId}`;
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/${processModelPath}/files/${fileName}`, path: `/${processModelPath}/files/${fileName}`,

View File

@ -36,7 +36,7 @@ export default function TaskShow() {
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const setErrorMessage = (useContext as any)(ErrorContext)[1]; const setErrorObject = (useContext as any)(ErrorContext)[1];
const { targetUris } = useUriListForPermissions(); const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = { const permissionRequestData: PermissionsToCheck = {
@ -64,30 +64,30 @@ export default function TaskShow() {
path: `/tasks/${params.process_instance_id}/${params.task_id}`, path: `/tasks/${params.process_instance_id}/${params.task_id}`,
successCallback: processResult, successCallback: processResult,
// This causes the page to continuously reload // This causes the page to continuously reload
// failureCallback: setErrorMessage, // failureCallback: setErrorObject,
}); });
} }
}, [params, permissionsLoaded, ability, targetUris]); }, [params, permissionsLoaded, ability, targetUris]);
const processSubmitResult = (result: any) => { const processSubmitResult = (result: any) => {
setErrorMessage(null); setErrorObject(null);
if (result.ok) { if (result.ok) {
navigate(`/tasks`); navigate(`/tasks`);
} else if (result.process_instance_id) { } else if (result.process_instance_id) {
navigate(`/tasks/${result.process_instance_id}/${result.id}`); navigate(`/tasks/${result.process_instance_id}/${result.id}`);
} else { } else {
setErrorMessage(`Received unexpected error: ${result.message}`); setErrorObject(`Received unexpected error: ${result.message}`);
} }
}; };
const handleFormSubmit = (event: any) => { const handleFormSubmit = (event: any) => {
setErrorMessage(null); setErrorObject(null);
const dataToSubmit = event.formData; const dataToSubmit = event.formData;
delete dataToSubmit.isManualTask; delete dataToSubmit.isManualTask;
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/tasks/${params.process_instance_id}/${params.task_id}`, path: `/tasks/${params.process_instance_id}/${params.task_id}`,
successCallback: processSubmitResult, successCallback: processSubmitResult,
failureCallback: setErrorMessage, failureCallback: setErrorObject,
httpMethod: 'PUT', httpMethod: 'PUT',
postBody: dataToSubmit, postBody: dataToSubmit,
}); });