Skip to content

Commit 535f787

Browse files
authored
fix(orchestrator):Add option to view input schema from workflows table (#609)
Signed-off-by: Lior Soffer <liorsoffer1@gmail.com>
1 parent 260900f commit 535f787

9 files changed

Lines changed: 224 additions & 36 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-orchestrator': patch
3+
---
4+
5+
Add option to view input schema from workflows table
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import React from 'react';
18+
import { useAsync } from 'react-use';
19+
import useObservable from 'react-use/esm/useObservable';
20+
21+
import {
22+
CodeSnippet,
23+
Progress,
24+
ResponseErrorPanel,
25+
} from '@backstage/core-components';
26+
import { appThemeApiRef, useApi } from '@backstage/core-plugin-api';
27+
28+
import { Box, Button, useTheme } from '@material-ui/core';
29+
30+
import { InputSchemaResponseDTO } from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';
31+
32+
import { orchestratorApiRef } from '../../api/api';
33+
import { FormattedWorkflowOverview } from '../../dataFormatters/WorkflowOverviewFormatter';
34+
import { InfoDialog } from '../InfoDialog';
35+
36+
const InputSchemaDialogContent = ({
37+
inputSchema,
38+
loading,
39+
error,
40+
}: {
41+
inputSchema: InputSchemaResponseDTO | undefined;
42+
loading: boolean;
43+
error: Error | undefined;
44+
}) => {
45+
const appThemeApi = useApi(appThemeApiRef);
46+
const activeThemeId = useObservable(
47+
appThemeApi.activeThemeId$(),
48+
appThemeApi.getActiveThemeId(),
49+
);
50+
const theme = useTheme();
51+
52+
if (loading) return <Progress />;
53+
if (error)
54+
return (
55+
<div style={{ width: '500px' }}>
56+
<ResponseErrorPanel error={error} />
57+
</div>
58+
);
59+
60+
return (
61+
<Box>
62+
{inputSchema?.inputSchema === undefined ? (
63+
'No input schema is defined for this workflow'
64+
) : (
65+
<CodeSnippet
66+
text={JSON.stringify(inputSchema, null, 2)}
67+
language="json"
68+
showLineNumbers
69+
showCopyCodeButton
70+
customStyle={{
71+
color:
72+
activeThemeId === 'dark'
73+
? theme.palette.grey[100]
74+
: theme.palette.grey[800],
75+
backgroundColor:
76+
activeThemeId === 'dark'
77+
? theme.palette.grey[900]
78+
: theme.palette.grey[100],
79+
padding: '25px 0',
80+
}}
81+
/>
82+
)}
83+
</Box>
84+
);
85+
};
86+
87+
export const InputSchemaDialog: React.FC<{
88+
rowData: FormattedWorkflowOverview;
89+
isInputSchemaDialogOpen: boolean;
90+
toggleInputSchemaDialog: () => void;
91+
}> = ({ rowData, isInputSchemaDialogOpen, toggleInputSchemaDialog }) => {
92+
const orchestratorApi = useApi(orchestratorApiRef);
93+
94+
const {
95+
value,
96+
loading,
97+
error: responseError,
98+
} = useAsync(async (): Promise<InputSchemaResponseDTO> => {
99+
const res = await orchestratorApi.getWorkflowDataInputSchema(rowData.id);
100+
101+
return res.data;
102+
}, [orchestratorApi, rowData]);
103+
104+
return (
105+
<InfoDialog
106+
title={`${rowData.name} input schema`}
107+
onClose={toggleInputSchemaDialog}
108+
open={isInputSchemaDialogOpen}
109+
dialogActions={
110+
<Button
111+
color="primary"
112+
variant="contained"
113+
onClick={toggleInputSchemaDialog}
114+
>
115+
Close
116+
</Button>
117+
}
118+
children={
119+
<InputSchemaDialogContent
120+
inputSchema={value}
121+
loading={loading}
122+
error={responseError}
123+
/>
124+
}
125+
wideDialog
126+
/>
127+
);
128+
};

workspaces/orchestrator/plugins/orchestrator/src/components/OrchestratorPage.tsx renamed to workspaces/orchestrator/plugins/orchestrator/src/components/OrchestratorPage/OrchestratorPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,12 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import React from 'react';
1718

1819
import { TabbedLayout } from '@backstage/core-components';
1920

20-
import { workflowInstancesRouteRef } from '../routes';
21-
import { BaseOrchestratorPage } from './BaseOrchestratorPage';
21+
import { workflowInstancesRouteRef } from '../../routes';
22+
import { BaseOrchestratorPage } from '../BaseOrchestratorPage';
2223
import { WorkflowRunsTabContent } from './WorkflowRunsTabContent';
2324
import { WorkflowsTabContent } from './WorkflowsTabContent';
2425

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowRunsTabContent.tsx renamed to workspaces/orchestrator/plugins/orchestrator/src/components/OrchestratorPage/WorkflowRunsTabContent.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ import {
4040
ProcessInstanceStatusDTO,
4141
} from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';
4242

43-
import { orchestratorApiRef } from '../api';
44-
import { DEFAULT_TABLE_PAGE_SIZE, VALUE_UNAVAILABLE } from '../constants';
45-
import usePolling from '../hooks/usePolling';
46-
import { workflowInstanceRouteRef, workflowRouteRef } from '../routes';
47-
import { Selector } from './Selector';
48-
import OverrideBackstageTable from './ui/OverrideBackstageTable';
49-
import { mapProcessInstanceToDetails } from './WorkflowInstancePageContent';
50-
import { WorkflowInstanceStatusIndicator } from './WorkflowInstanceStatusIndicator';
51-
import { WorkflowRunDetail } from './WorkflowRunDetail';
43+
import { orchestratorApiRef } from '../../api';
44+
import { DEFAULT_TABLE_PAGE_SIZE, VALUE_UNAVAILABLE } from '../../constants';
45+
import usePolling from '../../hooks/usePolling';
46+
import { workflowInstanceRouteRef, workflowRouteRef } from '../../routes';
47+
import { Selector } from '../Selector';
48+
import OverrideBackstageTable from '../ui/OverrideBackstageTable';
49+
import { mapProcessInstanceToDetails } from '../WorkflowInstancePageContent';
50+
import { WorkflowInstanceStatusIndicator } from '../WorkflowInstanceStatusIndicator';
51+
import { WorkflowRunDetail } from '../WorkflowRunDetail';
5252

5353
const makeSelectItemsFromProcessInstanceValues = () =>
5454
[

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowsTabContent.tsx renamed to workspaces/orchestrator/plugins/orchestrator/src/components/OrchestratorPage/WorkflowsTabContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import Grid from '@material-ui/core/Grid/Grid';
2727

2828
import { WorkflowOverviewDTO } from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';
2929

30-
import { orchestratorApiRef } from '../api';
31-
import usePolling from '../hooks/usePolling';
30+
import { orchestratorApiRef } from '../../api';
31+
import usePolling from '../../hooks/usePolling';
3232
import { WorkflowsTable } from './WorkflowsTable';
3333

3434
export const WorkflowsTabContent = () => {

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowsTable.tsx renamed to workspaces/orchestrator/plugins/orchestrator/src/components/OrchestratorPage/WorkflowsTable.tsx

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useRouteRef } from '@backstage/core-plugin-api';
2222
import { usePermission } from '@backstage/plugin-permission-react';
2323

2424
import { Box, makeStyles, Tooltip } from '@material-ui/core';
25+
import DeveloperModeOutlinedIcon from '@material-ui/icons/DeveloperModeOutlined';
2526
import FormatListBulleted from '@material-ui/icons/FormatListBulleted';
2627
import PlayArrow from '@material-ui/icons/PlayArrow';
2728
import TaskAltOutlinedIcon from '@mui/icons-material/TaskAltOutlined';
@@ -37,18 +38,19 @@ import {
3738
WorkflowOverviewDTO,
3839
} from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';
3940

40-
import { AVAILABLE, UNAVAILABLE, VALUE_UNAVAILABLE } from '../constants';
41+
import { AVAILABLE, UNAVAILABLE, VALUE_UNAVAILABLE } from '../../constants';
4142
import WorkflowOverviewFormatter, {
4243
FormattedWorkflowOverview,
43-
} from '../dataFormatters/WorkflowOverviewFormatter';
44-
import { usePermissionArray } from '../hooks/usePermissionArray';
44+
} from '../../dataFormatters/WorkflowOverviewFormatter';
45+
import { usePermissionArray } from '../../hooks/usePermissionArray';
4546
import {
4647
executeWorkflowRouteRef,
4748
workflowRouteRef,
4849
workflowRunsRouteRef,
49-
} from '../routes';
50-
import OverrideBackstageTable from './ui/OverrideBackstageTable';
51-
import { WorkflowInstanceStatusIndicator } from './WorkflowInstanceStatusIndicator';
50+
} from '../../routes';
51+
import OverrideBackstageTable from '../ui/OverrideBackstageTable';
52+
import { WorkflowInstanceStatusIndicator } from '../WorkflowInstanceStatusIndicator';
53+
import { InputSchemaDialog } from './InputSchemaDialog';
5254

5355
export interface WorkflowsTableProps {
5456
items: WorkflowOverviewDTO[];
@@ -127,6 +129,22 @@ export const WorkflowsTable = ({ items }: WorkflowsTableProps) => {
127129
const { allowed: permittedToUse } = usePermittedToUseBatch(items);
128130
const { allowed: permittedToView } = usePermittedToViewBatch(items);
129131

132+
const [isInputSchemaDialogOpen, setIsInputSchemaDialogOpen] = useState(false);
133+
const [dataForDialog, setDataForDialog] = useState<
134+
FormattedWorkflowOverview | undefined
135+
>(undefined);
136+
137+
const toggleInputSchemaDialog = React.useCallback(() => {
138+
setIsInputSchemaDialogOpen(prev => !prev);
139+
}, []);
140+
141+
const handleViewInputSchema = useCallback(
142+
(rowData: FormattedWorkflowOverview) => {
143+
setDataForDialog(rowData);
144+
toggleInputSchemaDialog();
145+
},
146+
[toggleInputSchemaDialog],
147+
);
130148
const initialState = useMemo(
131149
() => items.map(WorkflowOverviewFormatter.format),
132150
[items],
@@ -136,7 +154,7 @@ export const WorkflowsTable = ({ items }: WorkflowsTableProps) => {
136154
setData(initialState);
137155
}, [initialState]);
138156

139-
const handleView = useCallback(
157+
const handleViewVariables = useCallback(
140158
(rowData: FormattedWorkflowOverview) => {
141159
navigate(definitionRunsLink({ workflowId: rowData.id }));
142160
},
@@ -195,12 +213,24 @@ export const WorkflowsTable = ({ items }: WorkflowsTableProps) => {
195213
icon: FormatListBulleted,
196214
tooltip: 'View runs',
197215
disabled: !canViewWorkflow(rowData.id),
198-
onClick: () => handleView(rowData),
216+
onClick: () => handleViewVariables(rowData),
217+
}),
218+
rowData => ({
219+
icon: DeveloperModeOutlinedIcon,
220+
tooltip: 'View input schema',
221+
disabled: !canViewWorkflow(rowData.id),
222+
onClick: () => handleViewInputSchema(rowData),
199223
}),
200224
];
201225

202226
return actionItems;
203-
}, [canExecuteWorkflow, canViewWorkflow, handleExecute, handleView]);
227+
}, [
228+
canExecuteWorkflow,
229+
canViewWorkflow,
230+
handleExecute,
231+
handleViewVariables,
232+
handleViewInputSchema,
233+
]);
204234

205235
const columns = useMemo<TableColumn<FormattedWorkflowOverview>[]>(
206236
() => [
@@ -294,12 +324,21 @@ export const WorkflowsTable = ({ items }: WorkflowsTableProps) => {
294324
// TODO: use backend pagination only if the generic orchestratorWorkflowPermission is in place
295325
// use FE pagination otherwise (it means when specific permissions are used)
296326
return (
297-
<OverrideBackstageTable<FormattedWorkflowOverview>
298-
title="Workflows"
299-
options={options}
300-
columns={columns}
301-
data={data}
302-
actions={actions}
303-
/>
327+
<>
328+
{dataForDialog && (
329+
<InputSchemaDialog
330+
rowData={dataForDialog}
331+
isInputSchemaDialogOpen={isInputSchemaDialogOpen}
332+
toggleInputSchemaDialog={toggleInputSchemaDialog}
333+
/>
334+
)}
335+
<OverrideBackstageTable<FormattedWorkflowOverview>
336+
title="Workflows"
337+
options={options}
338+
columns={columns}
339+
data={data}
340+
actions={actions}
341+
/>
342+
</>
304343
);
305344
};

workspaces/orchestrator/plugins/orchestrator/src/components/Router.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import React from 'react';
1718
import { Route, Routes } from 'react-router-dom';
1819

@@ -22,7 +23,7 @@ import {
2223
workflowRouteRef,
2324
} from '../routes';
2425
import { ExecuteWorkflowPage } from './ExecuteWorkflowPage/ExecuteWorkflowPage';
25-
import { OrchestratorPage } from './OrchestratorPage';
26+
import { OrchestratorPage } from './OrchestratorPage/OrchestratorPage';
2627
import { WorkflowInstancePage } from './WorkflowInstancePage';
2728
import { WorkflowPage } from './WorkflowPage';
2829

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowInstancePageContent.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ import {
2727
import { appThemeApiRef, useApi } from '@backstage/core-plugin-api';
2828
import { usePermission } from '@backstage/plugin-permission-react';
2929

30-
import { Box, Button, Grid, makeStyles, Typography } from '@material-ui/core';
30+
import {
31+
Box,
32+
Button,
33+
Grid,
34+
makeStyles,
35+
Typography,
36+
useTheme,
37+
} from '@material-ui/core';
3138
import moment from 'moment';
3239

3340
import {
@@ -99,6 +106,7 @@ const VariablesDialogContent = ({
99106
appThemeApi.activeThemeId$(),
100107
appThemeApi.getActiveThemeId(),
101108
);
109+
const theme = useTheme();
102110

103111
return (
104112
<Box>
@@ -113,8 +121,14 @@ const VariablesDialogContent = ({
113121
showLineNumbers
114122
showCopyCodeButton
115123
customStyle={{
116-
color: activeThemeId === 'dark' ? '#abb2bf' : 'd3d3d3',
117-
backgroundColor: activeThemeId === 'dark' ? '#151515' : '#F0F0F0',
124+
color:
125+
activeThemeId === 'dark'
126+
? theme.palette.grey[100]
127+
: theme.palette.grey[800],
128+
backgroundColor:
129+
activeThemeId === 'dark'
130+
? theme.palette.grey[900]
131+
: theme.palette.grey[100],
118132
padding: '25px 0',
119133
}}
120134
/>

workspaces/orchestrator/plugins/orchestrator/src/components/WorkflowPage/WorkflowPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Grid } from '@material-ui/core';
2525
import { orchestratorApiRef } from '../../api';
2626
import { workflowRouteRef, workflowRunsRoutePath } from '../../routes';
2727
import { BaseOrchestratorPage } from '../BaseOrchestratorPage';
28-
import { WorkflowRunsTabContent } from '../WorkflowRunsTabContent';
28+
import { WorkflowRunsTabContent } from '../OrchestratorPage/WorkflowRunsTabContent';
2929
import { RunButton } from './RunButton';
3030
import { WorkflowDetailsTabContent } from './WorkflowDetailsTabContent';
3131

0 commit comments

Comments
 (0)