Skip to content

Commit

Permalink
Merge pull request #51 from osstotalsoft/metadata-history
Browse files Browse the repository at this point in the history
Metadata history
  • Loading branch information
niemenX authored Jan 12, 2024
2 parents 8762c03 + 894dc5d commit d375ce7
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 13 deletions.
19 changes: 16 additions & 3 deletions gql-bff/src/features/workflow/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ const workflowResolvers = {
else return filterResourcesByTenant(allWorkflows, tenant?.id);
},

exportWorkflows: async (_parent, { workflowList, allHandlers }, context, _info) => {
exportWorkflows: async (
_parent,
{ workflowList, allHandlers },
context,
_info
) => {
const { dataSources, tenant } = context;
const { code } = tenant ?? { code: "" };

Expand Down Expand Up @@ -214,8 +219,16 @@ const workflowResolvers = {
);

if (
JSON.stringify({ ...initialWorkflow, updateTime: undefined }) ==
JSON.stringify({ ...result, updateTime: undefined })
JSON.stringify({
...initialWorkflow,
updateTime: undefined,
createTime: undefined,
}) ==
JSON.stringify({
...result,
updateTime: undefined,
createTime: undefined,
})
) {
console.log(
"Workflows has no changes and will not be added to history!"
Expand Down
57 changes: 57 additions & 0 deletions gql-bff/src/features/workflowHistory/resolvers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { getWorkflowHistory } = require("./datasources/workflowHistoryEs");
const isMultiTenant = JSON.parse(process.env.IS_MULTITENANT);

const workflowHistoryResolvers = {
Query: {
Expand All @@ -10,6 +11,62 @@ const workflowHistoryResolvers = {
) => {
return await getWorkflowHistory(workflowName, version);
},

allWorkflowHistory: async (_parent, _args, context, _info) => {
const { dataSources, tenant } = context;
const allWorkflows = await dataSources.workflowApi.getWorkflowList();
const flows = isMultiTenant
? filterResourcesByTenant(allWorkflows, tenant?.id)
: allWorkflows;

const history = {};
for (const flow of flows) {
const flowHistory = await getWorkflowHistory(flow.name, flow.version);

if (flowHistory.length <= 1) continue;
const latest = flowHistory.sort((a, b) =>
a.timeStamp > b.timeStamp ? -1 : 1
)[1];
if (
JSON.stringify({
...latest.definition,
updateTime: undefined,
createTime: undefined,
}) ===
JSON.stringify({
...flow,
updateTime: undefined,
createTime: undefined,
})
)
continue;

history[`${flow.name}_${flow.version}`] = {
current: flow,
latest: flowHistory.sort((a, b) =>
a.timeStamp > b.timeStamp ? -1 : 1
)[1],
};
}
return history;
},
allExecutionHistory: async (_parent, _args, context, _info) => {
const { dataSources, tenant } = context;
const allWorkflows = await dataSources.workflowApi.getWorkflowList();
const flows = isMultiTenant
? filterResourcesByTenant(allWorkflows, tenant?.id)
: allWorkflows;

const history = {};
for (const flow of flows) {
const flowHistory = await dataSources?.executionApi?.getExecutionList({size: 1000, sort:
"startTime:DESC", start: 0, freeText: `(workflowType: ${flow.name} AND version: ${flow.version})`});

history[`${flow.name}/${flow.version}`] = flowHistory.totalHits;
}

return history;
},
},
WorkflowHistory: {
definition: (parent, _args, _context, _info) => {
Expand Down
2 changes: 2 additions & 0 deletions gql-bff/src/features/workflowHistory/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ type WorkflowHistoryList {

extend type Query {
workflowHistory(workflowName: String!, version: Int!): WorkflowHistoryList
allWorkflowHistory: JSON
allExecutionHistory: JSON
}
8 changes: 7 additions & 1 deletion react-ui/public/static/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"SystemTasks": "System_Tasks",
"EventHandlers": "Event handlers",
"Configurations": "Configurations",
"Schedule": "Schedule"
"Schedule": "Schedule",
"History": "History",
"ExecutionHistory": "Execution history"
},
"Tooltips": {
"Logout": "Logout",
Expand Down Expand Up @@ -539,5 +541,9 @@
"MissingAsyncHandlers": "Missing async handlers",
"WorkflowName": "Workflow name",
"TaskReferenceName": "Task reference name"
},
"History": {
"Flow":"Flow name/version",
"ExecutionCount": "Execution count"
}
}
8 changes: 7 additions & 1 deletion react-ui/public/static/locales/fr/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"Executions": "Exécutions",
"SystemTasks": "System_Tasks",
"EventHandlers": "Gestionnaires d'événements",
"Configurations": "Configurations"
"Configurations": "Configurations",
"History": "History",
"ExecutionHistory": "Execution history"
},
"Tooltips": {
"Logout": "Déconnexion",
Expand Down Expand Up @@ -495,5 +497,9 @@
"False": "Faux",
"Beautify": "Formater le code"
}
},
"History": {
"Flow":"Flow name/version",
"ExecutionCount": "Execution count"
}
}
8 changes: 7 additions & 1 deletion react-ui/public/static/locales/ro/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"Executions": "Executii",
"SystemTasks": "System_Tasks",
"EventHandlers": "Event handlers",
"Configurations": "Configurari"
"Configurations": "Configurari",
"History": "Istoric",
"ExecutionHistory": "Istoric executii"
},
"Tooltips": {
"Logout": "Iesire",
Expand Down Expand Up @@ -509,5 +511,9 @@
"Import": "Importa workflows",
"ImportButton": "Importa workflows",
"Placeholder": "Valoare"
},
"History": {
"Flow":"Nume workflow/versiune",
"ExecutionCount": "Numar de executii"
}
}
7 changes: 5 additions & 2 deletions react-ui/src/constants/menuConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import AssignmentIcon from '@mui/icons-material/Assignment'
import PlayCircleFilledIcon from '@mui/icons-material/PlayCircleFilled'
import NotificationsIcon from '@mui/icons-material/Notifications'
import MenuIcon from '@mui/icons-material/Menu'

import HistoryIcon from '@mui/icons-material/History';
import SummarizeIcon from '@mui/icons-material/Summarize';

const menuItems = [
{ icon: <AccountTreeIcon />, text: 'NavBar.Workflows', path: '/workflows', name: 'Workflows' },
Expand All @@ -17,7 +18,9 @@ const menuItems = [
{ icon: <NotificationsIcon />, text: 'NavBar.EventHandlers', path: '/eventHandlers', name: 'EventHandlers' },
{ icon: <AssignmentIcon />, text: 'NavBar.Tasks', path: '/tasks', name: 'Tasks' }
]
}
},
{ icon: <HistoryIcon />, text: 'NavBar.History', path: '/history', name: 'History' },
{ icon: <SummarizeIcon />, text: 'NavBar.ExecutionHistory', path: '/execution-history', name: 'ExecutionHistory' },
]

export default menuItems
2 changes: 1 addition & 1 deletion react-ui/src/features/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export const sortingDirection = {
DESC: 'DESC'
}

export const fieldsToBeRemoved = ['__typename', 'historyId', 'readOnly', 'startHandlers']
export const fieldsToBeRemoved = ['__typename', 'historyId', 'readOnly', 'startHandlers', 'updateTime', 'createTime']
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { Typography } from '@totalsoft/rocket-ui'
import sortobject from 'deep-sort-object'
import { fieldsToBeRemoved } from 'features/common/constants'

const CompareDefinition = ({ definition, currentDefinition }) => {
const CompareDefinition = ({ definition, currentDefinition , showDates}) => {
const { t } = useTranslation()

const old = sortobject({ ...omitDeep(definition, fieldsToBeRemoved) })
const current = sortobject(omitDeep(currentDefinition, fieldsToBeRemoved))

return (
<ReactDiffViewer
styles={{ diffContainer: { wordBreak: 'break-all' } }}
oldValue={JSON.stringify(old, null, 2)}
newValue={JSON.stringify(current, null, 2)}
extraLinesSurroundingDiff={0}
Expand All @@ -25,12 +26,12 @@ const CompareDefinition = ({ definition, currentDefinition }) => {
compareMethod={DiffMethod.WORDS}
leftTitle={
<Typography variant='h5' align='center'>
{t('WorkflowHistory.Dialog.Compare.OldDefinition')}
{t('WorkflowHistory.Dialog.Compare.OldDefinition')} {showDates && `(${new Date(definition?.updateTime).toLocaleString()})`}
</Typography>
}
rightTitle={
<Typography variant='h5' align='center'>
{t('WorkflowHistory.Dialog.Compare.CurrentDefinition')}
{t('WorkflowHistory.Dialog.Compare.CurrentDefinition')} {showDates && `(${new Date(currentDefinition?.updateTime).toLocaleString()})`}
</Typography>
}
/>
Expand All @@ -39,7 +40,8 @@ const CompareDefinition = ({ definition, currentDefinition }) => {

CompareDefinition.propTypes = {
definition: PropTypes.object.isRequired,
currentDefinition: PropTypes.object
currentDefinition: PropTypes.object,
showDates: PropTypes.bool
}

export default CompareDefinition
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react'
import { useQueryWithErrorHandling } from 'hooks/errorHandling'
import { EXECUTION_HISTORY_QUERY } from '../queries/HistoryQuery'
import { FakeText } from '@totalsoft/rocket-ui'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'
import Paper from '@mui/material/Paper'
import { useTranslation } from 'react-i18next'

const ExecutionHistoryContainer = () => {
const { t } = useTranslation()
const { loading, data } = useQueryWithErrorHandling(EXECUTION_HISTORY_QUERY, { variables: {} })

const flows = data?.allExecutionHistory
? Object.keys(data?.allExecutionHistory).map(a => ({ name: a, value: data?.allExecutionHistory[a] }))
: []

if (loading) return <FakeText lines={8} />

return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size='small' aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell>{t("History.Flow")}</TableCell>
<TableCell align='right'>{t("History.ExecutionCount")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{flows.map((flow, index) => (
<TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell component='th' scope='row'>
{flow.name}
</TableCell>
<TableCell align='right'>{flow.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}

export default ExecutionHistoryContainer
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { useCallback, useState } from 'react'
import { useQueryWithErrorHandling } from 'hooks/errorHandling'
import { HISTORY_QUERY } from '../queries/HistoryQuery'
import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import ListItemText from '@mui/material/ListItemText'
import ListItemButton from '@mui/material/ListItemButton'
import Grid from '@mui/material/Grid'
import { FakeText } from '@totalsoft/rocket-ui'
import CompareDefinition from 'features/workflow/edit/components/workflowHistory/modals/CompareDefinition'

const HistoryContainer = () => {
const [flow, setFlow] = useState(null)
const { loading, data } = useQueryWithErrorHandling(HISTORY_QUERY, { variables: {} })

const flows = data?.allWorkflowHistory || []

const onViewClick = useCallback(
event => {
const name = event.currentTarget.id
setFlow(data?.allWorkflowHistory[name])
},
[setFlow, data?.allWorkflowHistory]
)

if (loading) return <FakeText lines={8} />

return (
<>
<Grid container>
<Grid item xs={2} style={{ overflowY: 'scroll', maxHeight: 'calc(100vh - 150px)' }}>
<List>
{Object.keys(flows).map((flow, index) => (
<ListItem key={index} disablePadding>
<ListItemButton id={flow} name={flow} onClick={onViewClick}>
<ListItemText style={{ wordBreak: 'break-all' }} primary={flows[flow].current.name} />
</ListItemButton>
</ListItem>
))}
</List>
</Grid>
<Grid item xs={10} style={{ overflowY: 'scroll', maxHeight: 'calc(100vh - 150px)' }}>
{flow && <CompareDefinition definition={flow?.latest?.definition} currentDefinition={flow?.current} showDates={true} />}
</Grid>
</Grid>
</>
)
}

export default HistoryContainer
13 changes: 13 additions & 0 deletions react-ui/src/features/workflow/history/queries/HistoryQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { gql } from '@apollo/client'

export const HISTORY_QUERY = gql`
query allWorkflowHistory {
allWorkflowHistory
}
`

export const EXECUTION_HISTORY_QUERY = gql`
query allExecutionHistory {
allExecutionHistory
}
`
5 changes: 5 additions & 0 deletions react-ui/src/routes/AppRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import EventHandlerContainer from 'features/eventHandler/edit/components/EventHa
import WorkflowContainer from 'features/workflow/edit/components/WorkflowContainer'
import ScheduleListContainer from 'features/schedule/list/components/ScheduleListContainer'
import ScheduleContainer from 'features/schedule/edit/components/ScheduleContainer'
import HistoryContainer from 'features/workflow/history/components/HistoryContainer'
import ExecutionHistoryContainer from 'features/workflow/history/components/ExecutionHistoryContainer'

export default function AppRoutes() {
return (
Expand All @@ -38,6 +40,9 @@ export default function AppRoutes() {
<Route exact path='/schedule' element={<CustomRoute isPrivate={true} component={ScheduleListContainer} />} />
<Route exact path='/schedule/:new' element={<CustomRoute isPrivate={true} component={ScheduleContainer} />} />
<Route exact path='/schedule/:name' element={<CustomRoute isPrivate={true} component={ScheduleContainer} />} />

<Route exact path='/history' element={<CustomRoute isPrivate={true} component={HistoryContainer} />} />
<Route exact path='/execution-history' element={<CustomRoute isPrivate={true} component={ExecutionHistoryContainer} />} />
<Route
isPrivate={true}
exact
Expand Down

0 comments on commit d375ce7

Please sign in to comment.