From 75729ba3df3a8e1eccbae237d96efa7f17f72b26 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 18 Oct 2022 16:41:13 -0400 Subject: [PATCH] Squashed 'spiffworkflow-frontend/' changes from 790d267b..bf2d2a22 bf2d2a22 added authentication table w/ burnettk d9d8f364 added an authentications route and updated error message to show sentry links w/ burnettk git-subtree-dir: spiffworkflow-frontend git-subtree-split: bf2d2a22430da355be261d864e3b507866deafdf --- src/App.tsx | 23 +++++++-- src/components/NavigationBar.tsx | 24 ---------- src/components/SubNavigation.tsx | 5 ++ src/interfaces.ts | 16 +++++++ src/routes/AdminRoutes.tsx | 4 +- src/routes/AuthenticationList.tsx | 64 ++++++++++++++++++++++++++ src/routes/ProcessInstanceList.tsx | 10 ++-- src/routes/ProcessModelEdit.tsx | 2 +- src/routes/ProcessModelEditDiagram.tsx | 2 +- src/routes/ProcessModelShow.tsx | 2 +- src/routes/TaskShow.tsx | 4 +- src/services/HttpService.ts | 4 +- 12 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 src/routes/AuthenticationList.tsx diff --git a/src/App.tsx b/src/App.tsx index 805548c0..0444f042 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,9 +10,12 @@ import TaskShow from './routes/TaskShow'; import ErrorBoundary from './components/ErrorBoundary'; import AdminRoutes from './routes/AdminRoutes'; import SubNavigation from './components/SubNavigation'; +import { ErrorForDisplay } from './interfaces'; export default function App() { - const [errorMessage, setErrorMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState( + null + ); const errorContextValueArray = useMemo( () => [errorMessage, setErrorMessage], @@ -20,10 +23,24 @@ export default function App() { ); let errorTag = null; - if (errorMessage !== '') { + if (errorMessage) { + let sentryLinkTag = null; + if (errorMessage.sentry_link) { + sentryLinkTag = ( + + { + ': Find details about this error here (it may take a moment to become available): ' + } + + {errorMessage.sentry_link} + + + ); + } errorTag = ( ); } diff --git a/src/components/NavigationBar.tsx b/src/components/NavigationBar.tsx index 85272091..9b070a55 100644 --- a/src/components/NavigationBar.tsx +++ b/src/components/NavigationBar.tsx @@ -1,34 +1,10 @@ import { Button, Navbar, Nav, Container } from 'react-bootstrap'; -// import { capitalizeFirstLetter } from '../helpers'; // @ts-expect-error TS(2307) FIXME: Cannot find module '../logo.svg' or its correspond... Remove this comment to see the full error message import logo from '../logo.svg'; import UserService from '../services/UserService'; // for ref: https://react-bootstrap.github.io/components/navbar/ export default function NavigationBar() { - // const navItems: string[] = []; - // if (UserService.hasRole(['admin'])) { - // navItems.push('/admin'); - // } - // navItems.push('/tasks'); - // - // const navElements = navItems.map((navItem) => { - // let className = ''; - // if (window.location.pathname.startsWith(navItem)) { - // className = 'active'; - // } - // const navItemWithoutSlash = navItem.replace(/\/*/, ''); - // const title = capitalizeFirstLetter(navItemWithoutSlash); - // return ( - // - // {title} - // - // ); - // }); const navElements = null; const handleLogout = () => { diff --git a/src/components/SubNavigation.tsx b/src/components/SubNavigation.tsx index 871d0173..6c5369aa 100644 --- a/src/components/SubNavigation.tsx +++ b/src/components/SubNavigation.tsx @@ -14,6 +14,8 @@ export default function SubNavigation() { newActiveKey = '/admin/process-instances'; } else if (location.pathname.match(/^\/admin\/secrets\b/)) { newActiveKey = '/admin/secrets'; + } else if (location.pathname.match(/^\/admin\/authentications\b/)) { + newActiveKey = '/admin/authentications'; } else if (location.pathname === '/') { newActiveKey = '/'; } else if (location.pathname.match(/^\/tasks\b/)) { @@ -40,6 +42,9 @@ export default function SubNavigation() { Secrets + + Authentications + ); } diff --git a/src/interfaces.ts b/src/interfaces.ts index a628940b..8e153c80 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -25,3 +25,19 @@ export interface ProcessModel { // tuple of display value and URL export type BreadcrumbItem = [displayValue: string, url?: string]; + +export interface ErrorForDisplay { + message: string; + sentry_link?: string; +} + +export interface AuthenticationParam { + id: string; + type: string; + required: boolean; +} + +export interface AuthenticationItem { + id: string; + parameters: AuthenticationParam[]; +} diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 264bc877..c3e39e16 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -23,13 +23,14 @@ import MessageInstanceList from './MessageInstanceList'; import SecretList from './SecretList'; import SecretNew from './SecretNew'; import SecretShow from './SecretShow'; +import AuthenticationList from './AuthenticationList'; export default function AdminRoutes() { const location = useLocation(); const setErrorMessage = (useContext as any)(ErrorContext)[1]; useEffect(() => { - setErrorMessage(''); + setErrorMessage(null); }, [location, setErrorMessage]); if (UserService.hasRole(['admin'])) { @@ -108,6 +109,7 @@ export default function AdminRoutes() { } /> } /> } /> + } /> ); } diff --git a/src/routes/AuthenticationList.tsx b/src/routes/AuthenticationList.tsx new file mode 100644 index 00000000..ab9789f1 --- /dev/null +++ b/src/routes/AuthenticationList.tsx @@ -0,0 +1,64 @@ +import { useContext, useEffect, useState } from 'react'; +import { Table } from 'react-bootstrap'; +import ErrorContext from '../contexts/ErrorContext'; +import { AuthenticationItem } from '../interfaces'; +import HttpService from '../services/HttpService'; + +export default function AuthenticationList() { + const setErrorMessage = (useContext as any)(ErrorContext)[1]; + + const [authenticationList, setAuthenticationList] = useState< + AuthenticationItem[] | null + >(null); + const [connectProxyBaseUrl, setConnectProxyBaseUrl] = useState( + null + ); + + useEffect(() => { + const processResult = (result: any) => { + setAuthenticationList(result.results); + setConnectProxyBaseUrl(result.connector_proxy_base_url); + }; + HttpService.makeCallToBackend({ + path: `/authentications`, + successCallback: processResult, + failureCallback: setErrorMessage, + }); + }, [setErrorMessage]); + + const buildTable = () => { + if (authenticationList) { + const rows = authenticationList.map((row) => { + return ( + + + + {row.id} + + + + ); + }); + return ( + + + + + + + {rows} +
ID
+ ); + } + return null; + }; + + if (authenticationList) { + return <>{buildTable()}; + } + + return
; +} diff --git a/src/routes/ProcessInstanceList.tsx b/src/routes/ProcessInstanceList.tsx index 7276bc24..a2e2aca2 100644 --- a/src/routes/ProcessInstanceList.tsx +++ b/src/routes/ProcessInstanceList.tsx @@ -176,19 +176,19 @@ export default function ProcessInstanceList() { let queryParamString = `per_page=${perPage}&page=${page}`; if (isTrueComparison(startFrom, '>', startTill)) { - setErrorMessage('startFrom cannot be after startTill'); + setErrorMessage({ message: 'startFrom cannot be after startTill' }); return; } if (isTrueComparison(endFrom, '>', endTill)) { - setErrorMessage('endFrom cannot be after endTill'); + setErrorMessage({ message: 'endFrom cannot be after endTill' }); return; } if (isTrueComparison(startFrom, '>', endFrom)) { - setErrorMessage('startFrom cannot be after endFrom'); + setErrorMessage({ message: 'startFrom cannot be after endFrom' }); return; } if (isTrueComparison(startTill, '>', endTill)) { - setErrorMessage('startTill cannot be after endTill'); + setErrorMessage({ message: 'startTill cannot be after endTill' }); return; } @@ -217,7 +217,7 @@ export default function ProcessInstanceList() { queryParamString += `&process_group_identifier=${currentProcessModel.process_group_id}&process_model_identifier=${currentProcessModel.id}`; } - setErrorMessage(''); + setErrorMessage(null); navigate(`/admin/process-instances?${queryParamString}`); }; diff --git a/src/routes/ProcessModelEdit.tsx b/src/routes/ProcessModelEdit.tsx index 85a91c96..4567d14a 100644 --- a/src/routes/ProcessModelEdit.tsx +++ b/src/routes/ProcessModelEdit.tsx @@ -49,7 +49,7 @@ export default function ProcessModelEdit() { }; const deleteProcessModel = () => { - setErrorMessage(''); + setErrorMessage(null); const processModelToUse = processModel as any; const processModelShowPath = `/process-models/${processModelToUse.process_group_id}/${processModelToUse.id}`; HttpService.makeCallToBackend({ diff --git a/src/routes/ProcessModelEditDiagram.tsx b/src/routes/ProcessModelEditDiagram.tsx index 2f394b8f..2382fce7 100644 --- a/src/routes/ProcessModelEditDiagram.tsx +++ b/src/routes/ProcessModelEditDiagram.tsx @@ -112,7 +112,7 @@ export default function ProcessModelEditDiagram() { }; const saveDiagram = (bpmnXML: any, fileName = params.file_name) => { - setErrorMessage(''); + setErrorMessage(null); setBpmnXmlForDiagramRendering(bpmnXML); let url = `/process-models/${params.process_group_id}/${params.process_model_id}/files`; diff --git a/src/routes/ProcessModelShow.tsx b/src/routes/ProcessModelShow.tsx index 93670fcb..63217d2d 100644 --- a/src/routes/ProcessModelShow.tsx +++ b/src/routes/ProcessModelShow.tsx @@ -82,7 +82,7 @@ export default function ProcessModelShow() { }, [params, reloadModel]); const processModelRun = (processInstance: any) => { - setErrorMessage(''); + setErrorMessage(null); HttpService.makeCallToBackend({ path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${processInstance.id}/run`, successCallback: setProcessInstanceResult, diff --git a/src/routes/TaskShow.tsx b/src/routes/TaskShow.tsx index 21954602..2f1dfd53 100644 --- a/src/routes/TaskShow.tsx +++ b/src/routes/TaskShow.tsx @@ -29,7 +29,7 @@ export default function TaskShow() { }, [params, setErrorMessage]); const processSubmitResult = (result: any) => { - setErrorMessage(''); + setErrorMessage(null); if (result.ok) { navigate(`/tasks`); } else if (result.process_instance_id) { @@ -40,7 +40,7 @@ export default function TaskShow() { }; const handleFormSubmit = (event: any) => { - setErrorMessage(''); + setErrorMessage(null); const dataToSubmit = event.formData; delete dataToSubmit.isManualTask; HttpService.makeCallToBackend({ diff --git a/src/services/HttpService.ts b/src/services/HttpService.ts index 075956bc..72a92a98 100644 --- a/src/services/HttpService.ts +++ b/src/services/HttpService.ts @@ -94,7 +94,7 @@ backendCallProps) => { message = result.message; } if (failureCallback) { - failureCallback(message); + failureCallback(result); } else { console.error(message); // eslint-disable-next-line no-alert @@ -105,7 +105,7 @@ backendCallProps) => { .catch((error) => { if (error.name !== 'UnauthenticatedError') { if (failureCallback) { - failureCallback(error.message); + failureCallback(error); } else { console.error(error.message); }