Skip to content

Commit

Permalink
Fd 674 wm (#1540)
Browse files Browse the repository at this point in the history
* add additional properties into workflow import and refactor workflow input from grid into vstack

* remove bad URLs used when redirecting

* create new function in shared to omit deeply nested objects

* add info comment about test framework limitations

* handle sent json in inputParams

* support multiple properties to be omitted in omitDeep function

* add possibility to export workflow definition

* generate graphql types
  • Loading branch information
MarcoMruz authored and soson committed May 13, 2024
1 parent 30b9efa commit ec0a007
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 53 deletions.
99 changes: 95 additions & 4 deletions packages/frinx-workflow-ui/src/__generated__/graphql.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { VoidFunctionComponent } from 'react';
import {
Button,
ButtonGroup,
Modal,
ModalBody,
ModalCloseButton,
Expand All @@ -14,23 +15,36 @@ import { ClientWorkflowWithTasks, Editor, removeGraphqlSpecsFromWorkflow } from
type ModalProps = {
isOpen: boolean;
onClose: () => void;
onExportClick: () => void;
workflow?: ClientWorkflowWithTasks;
};

const DefinitionModal: VoidFunctionComponent<ModalProps> = ({ isOpen, onClose, workflow }) => {
const DefinitionModal: VoidFunctionComponent<ModalProps> = ({ isOpen, onClose, workflow, onExportClick }) => {
return (
<Modal size="3xl" scrollBehavior="inside" isOpen={isOpen} onClose={onClose} isCentered>
<ModalOverlay />
<ModalCloseButton />
<ModalContent>
<ModalHeader>{workflow?.name}</ModalHeader>
<ModalBody>
<Editor defaultValue={JSON.stringify(removeGraphqlSpecsFromWorkflow(workflow), null, 2)} width="100%" />
<Editor
defaultValue={JSON.stringify(removeGraphqlSpecsFromWorkflow(workflow), null, 2)}
width="100%"
options={{
copyWithSyntaxHighlighting: false,
selectionHighlight: false,
}}
/>
</ModalBody>
<ModalFooter>
<Button colorScheme="gray" onClick={onClose}>
Close
</Button>
<ButtonGroup>
<Button colorScheme="gray" onClick={onExportClick}>
Export definition
</Button>
<Button colorScheme="gray" onClick={onClose}>
Close
</Button>
</ButtonGroup>
</ModalFooter>
</ModalContent>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const WorkflowDiagram = ({ meta, result }: Props) => {
}

const tasks = jsonParse<Task[]>(meta.tasksJson) || [];
const taskMap = new Map(unwrap(result.tasks).map((t) => [t.id, t]));
const taskMap = new Map(unwrap(result.tasks).map((t) => [t.referenceTaskName, t]));
const elements: { nodes: Node<NodeData>[]; edges: Edge[] } = getLayoutedElements(
getElementsFromWorkflow(tasks.map(convertWorkflowTaskToExtendedTask), true),
'TB',
Expand Down Expand Up @@ -96,7 +96,7 @@ const WorkflowDiagram = ({ meta, result }: Props) => {
fitView
onNodeClick={(_e, node) => {
if (node.data?.isSubWorkflow) {
navigate(`../executed/${node.data?.subWorkflowId}`);
navigate(`../${node.data?.subWorkflowId}`);
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,34 @@ const WorkflowListHeader: VoidFunctionComponent<Props> = ({ onImportSuccess }) =
const json = readFiles
.map((data) => JSON.parse(data))
.map((data) => {
const { tasks, name, description, labels, outputParameters, restartable, timeoutSeconds, version } = data;
const {
tasks,
labels,
name,
description,
outputParameters,
restartable,
timeoutSeconds,
version,
inputParameters,
ownerApp,
createdBy,
updatedBy,
} = data;
return {
tasks: JSON.stringify(tasks),
name,
description: { description, labels: labels || [] },
outputParameters,
inputParameters: inputParameters || [],
restartable,
timeoutSeconds,
version,
ownerApp: ownerApp || '',
createTime: Date.now(),
updateTime: Date.now(),
createdBy: createdBy || '',
updatedBy: updatedBy || '',
};
});
const response = await Promise.all(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { VoidFunctionComponent, useState, useEffect } from 'react';
import { IconButton, Button, SimpleGrid, Box, Stack, Textarea, Text, Icon, useDisclosure } from '@chakra-ui/react';
import { IconButton, Button, SimpleGrid, Box, Stack, Text, Icon, useDisclosure } from '@chakra-ui/react';
import FeatherIcon from 'feather-icons-react';
import unescapeJs from 'unescape-js';
import { Editor } from '@frinx/shared';
import ExternalStorageModal from './external-storage-modal';

type Props = {
Expand All @@ -14,20 +14,6 @@ type Props = {
externalOutputPayloadStoragePath?: string | null;
};

const getJSON = (data: Record<string, unknown> | unknown, isEscaped: boolean) => {
return isEscaped
? JSON.stringify(data, null, 2)
.replace(/\\n/g, '\\n')
.replace(/\\'/g, "\\'")
.replace(/\\"/g, '\\"')
.replace(/\\&/g, '\\&')
.replace(/\\r/g, '\\r')
.replace(/\\t/g, '\\t')
.replace(/\\b/g, '\\b')
.replace(/\\f/g, '\\f')
: unescapeJs(JSON.stringify(data, null, 2));
};

const InputOutputTab: VoidFunctionComponent<Props> = ({
isEscaped,
input,
Expand Down Expand Up @@ -74,6 +60,7 @@ const InputOutputTab: VoidFunctionComponent<Props> = ({
onClick={() => copyToClipBoard(input)}
/>
<Button size="sm" onClick={() => onEscapeChange(!isEscaped)}>
Textarea,
{isEscaped ? 'Unescape' : 'Escape'}
</Button>
{externalInputPayloadStoragePath && (
Expand All @@ -87,7 +74,7 @@ const InputOutputTab: VoidFunctionComponent<Props> = ({
</Button>
)}
</Stack>
<Textarea value={getJSON(input, isEscaped)} isReadOnly id="workflowInput" variant="filled" minH={500} />
<Editor value={JSON.stringify(input, null, 2)} options={{ readOnly: true, lineNumbers: 'off' }} />
</Box>
<Box>
<Stack direction="row" spacing={2} align="center" mb={2}>
Expand Down Expand Up @@ -115,7 +102,7 @@ const InputOutputTab: VoidFunctionComponent<Props> = ({
</Button>
)}
</Stack>
<Textarea value={getJSON(output, isEscaped)} isReadOnly id="workflowOutput" variant="filled" minH={500} />
<Editor value={JSON.stringify(output, null, 2)} options={{ readOnly: true, lineNumbers: 'off' }} />
</Box>
</SimpleGrid>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { FC } from 'react';
import { Box, Stack, IconButton, Button, Text, Textarea, Icon } from '@chakra-ui/react';
import { Box, Stack, IconButton, Button, Text, Icon } from '@chakra-ui/react';
import FeatherIcon from 'feather-icons-react';
import { omit } from 'lodash';
import { unescapedJSON } from '../../../helpers/utils.helpers';
import { Editor, omitDeep } from '@frinx/shared';
import { ExecutedWorkflowDetailQuery } from '../../../__generated__/graphql';

type Props = {
Expand All @@ -29,12 +29,9 @@ const WorkflowJsonTab: FC<Props> = ({ isEscaped, result, copyToClipBoard, onEsca
{isEscaped ? 'Unescape' : 'Escape'}
</Button>
</Stack>
<Textarea
value={unescapedJSON(isEscaped, omit(result, ['__typename']))}
isReadOnly
id="json"
variant="filled"
minH={500}
<Editor
value={JSON.stringify(omitDeep(result, ['__typename']), null, 2)}
options={{ readOnly: true, lineNumbers: 'off', minimap: { enabled: false } }}
/>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
useToast,
VStack,
} from '@chakra-ui/react';
import { ClientWorkflow, useNotifications } from '@frinx/shared';
import { ClientWorkflow, jsonParse, useNotifications } from '@frinx/shared';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { gql, useMutation, useQuery, useSubscription } from 'urql';
import {
Expand Down Expand Up @@ -147,6 +147,7 @@ const EXECUTED_WORKFLOW_SUBSCRIPTION = gql`
status
taskType
subWorkflowId
referenceTaskName
}
}
}
Expand Down Expand Up @@ -593,7 +594,7 @@ const ExecutedWorkflowDetail: FC<Props> = ({ onExecutedOperation }) => {
<EditRerunTab
onRerunClick={handleOnRerunClick}
workflowDefinition={clientWorkflow}
workflowInput={executedWorkflow.input != null ? JSON.parse(executedWorkflow.input) : {}}
workflowInput={jsonParse(executedWorkflow.input) ?? {}}
/>
)}
</TabPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
CreateScheduleMutationVariables,
ExecuteWorkflowByNameMutation,
ExecuteWorkflowByNameMutationVariables,
ExportWorkflowDefinitionMutation,
ExportWorkflowDefinitionMutationVariables,
} from '../../__generated__/graphql';

type Props = {
Expand Down Expand Up @@ -54,6 +56,14 @@ const EXECUTE_WORKFLOW_MUTATION = gql`
}
`;

const EXPORT_WORKFLOW_MUTATION = gql`
mutation ExportWorkflowDefinition($name: String!, $version: Int) {
conductor {
exportWorkflowDefinition(name: $name, version: $version)
}
}
`;

const WorkflowDefinitionsModals: VoidFunctionComponent<Props> = ({
workflows,
activeWorkflow,
Expand All @@ -73,6 +83,10 @@ const WorkflowDefinitionsModals: VoidFunctionComponent<Props> = ({
EXECUTE_WORKFLOW_MUTATION,
);

const [, exportWorkflow] = useMutation<ExportWorkflowDefinitionMutation, ExportWorkflowDefinitionMutationVariables>(
EXPORT_WORKFLOW_MUTATION,
);

const { addToastNotification } = useNotifications();

const handleWorkflowSchedule = (scheduledWf: CreateScheduleInput) => {
Expand Down Expand Up @@ -161,13 +175,58 @@ const WorkflowDefinitionsModals: VoidFunctionComponent<Props> = ({
});
};

const handleOnExportClick = () => {
if (activeWorkflow == null) {
addToastNotification({
content: 'We cannot export undefined workflow',
type: 'error',
});

return;
}

exportWorkflow({
name: activeWorkflow.name,
version: activeWorkflow.version,
})
.then((res) => {
if (!res.error) {
const workflowDefinition = res.data?.conductor.exportWorkflowDefinition;

const file = new Blob([JSON.stringify(workflowDefinition, null, 2)], { type: 'application/json' });
const a = document.createElement('a');
const url = URL.createObjectURL(file);
a.href = url;
a.download = `${activeWorkflow.name}.json`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}

if (res.error) {
addToastNotification({ content: res.error.message, type: 'error' });
}
})
.catch(() => {
addToastNotification({ content: 'We have a problem to export selected workflow', type: 'error' });
});
};

if (activeWorkflow == null) {
return null;
}

return (
<>
<DefinitionModal workflow={activeWorkflow} isOpen={definitionModal.isOpen} onClose={definitionModal.onClose} />
<DefinitionModal
workflow={activeWorkflow}
isOpen={definitionModal.isOpen}
onClose={definitionModal.onClose}
onExportClick={handleOnExportClick}
/>
<DependencyModal
workflow={activeWorkflow}
onClose={dependencyModal.onClose}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ const WorkflowFormInput: VoidFunctionComponent<Props> = ({
/>
)}

{isJson && <Editor value={values[inputParameterKey]} onChange={(e) => onChange(inputParameterKey, e ?? '')} />}
{isJson && (
<Editor
value={JSON.stringify(values[inputParameterKey], null, 2)}
onChange={(e) => onChange(inputParameterKey, e ?? '')}
/>
)}

{isList && (
<FormControl>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { VoidFunctionComponent } from 'react';
import { Grid, GridItem } from '@chakra-ui/react';
import { VStack } from '@chakra-ui/react';
import WorkflowFormInput from './workflow-form-input';
import { InputParameter } from '../../helpers/workflow.helpers';

Expand All @@ -17,18 +17,17 @@ const WorkflowInputsForm: VoidFunctionComponent<Props> = ({
onChange,
}) => {
return (
<Grid templateColumns="repeat(2, 1fr)" gap={5}>
<VStack spacing={4}>
{inputParameterKeys.map((inputParameterKey) => (
<GridItem key={inputParameterKey}>
<WorkflowFormInput
inputParameterKey={inputParameterKey}
onChange={onChange}
values={values}
parsedInputParameters={parsedInputParameters}
/>
</GridItem>
<WorkflowFormInput
key={inputParameterKey}
inputParameterKey={inputParameterKey}
onChange={onChange}
values={values}
parsedInputParameters={parsedInputParameters}
/>
))}
</Grid>
</VStack>
);
};

Expand Down
Loading

0 comments on commit ec0a007

Please sign in to comment.