Skip to content

Commit

Permalink
feat(ui): allow importing external workflows and copying flow nodes i…
Browse files Browse the repository at this point in the history
…n next generation `New Workflow` (chaos-mesh#3368)

* feat(ui): import external workflows

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* refactor: rewrite flowToWorkflow

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* fix: isolate flow nodes and real templates

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* feat: allow copying flow nodes

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* chore: update CHANGELOG

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* fix: mutate state

https://lodash.com/docs/4.17.15#merge
Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* chore: add marker-end arrow

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* chore: remove network.target.podsPreviewHelper

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* fix: build

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* fix: support copy/delete group nodes

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>

* fix: replace default entry when entry exists

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>
Signed-off-by: STRRL <im@strrl.dev>
  • Loading branch information
g1eny0ung authored and STRRL committed Sep 13, 2022
1 parent eaf9e49 commit 3bb655f
Show file tree
Hide file tree
Showing 31 changed files with 1,181 additions and 508 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ For more information and how-to, see [RFC: Keep A Changelog](https://github.com/
- Add helm annotations for Artifact Hub [#3355](https://github.com/chaos-mesh/chaos-mesh/pull/3355)
- Add implementation of blockchaos in chaos-daemon [#2907](https://github.com/chaos-mesh/chaos-mesh/pull/2907)
- Bump chaos-tproxy to v0.5.1 [#3412](https://github.com/chaos-mesh/chaos-mesh/pull/3412)
- Allow importing external workflows and copying flow nodes in next generation `New Workflow` [#3368](https://github.com/chaos-mesh/chaos-mesh/pull/3368)

### Changed

Expand Down
4 changes: 4 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ package-lock.json

# eslint
.eslintcache

# yarn
.yarn
.yarnrc
3 changes: 2 additions & 1 deletion ui/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
"js-yaml": "^4.0.0",
"lodash": "^4.17.21",
"luxon": "^1.25.0",
"re-resizable": "^6.9.9",
"react": "^17.0.1",
"react-ace": "^9.2.1",
"react-dnd": "^15.1.1",
"react-dnd-html5-backend": "^15.1.2",
"react-dom": "^17.0.1",
"react-flow-renderer": "^10.1.2",
"react-flow-renderer": "^10.3.0",
"react-helmet": "^6.1.0",
"react-intl": "^5.22.0",
"react-redux": "^7.2.2",
Expand Down
2 changes: 1 addition & 1 deletion ui/app/src/components/AutoForm/Info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default function Info({ belong, kind, action }: InfoProps) {
name="name"
label={<T id="common.name" />}
helperText={
getIn(errors, 'name') && getIn(touched, 'name') ? getIn(errors, 'name') : <T id="newE.basic.nameHelper" />
getIn(errors, 'name') && getIn(touched, 'name') ? getIn(errors, 'name') : <T id="newW.node.nameHelper" />
}
error={getIn(errors, 'name') && getIn(touched, 'name')}
/>
Expand Down
9 changes: 8 additions & 1 deletion ui/app/src/components/AutoForm/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ export const scopeInitialValues = {
value: undefined,
}

export const scheduleInitialValues = {
export interface Schedule {
schedule: string
historyLimit?: number
concurrencyPolicy?: 'Forbid' | 'Allow'
startingDeadlineSeconds?: number
}

export const scheduleInitialValues: Schedule = {
schedule: '',
historyLimit: 1,
concurrencyPolicy: 'Forbid',
Expand Down
72 changes: 39 additions & 33 deletions ui/app/src/components/AutoForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Box, Divider, FormHelperText, MenuItem, Typography } from '@mui/materia
import { eval as expEval, parse } from 'expression-eval'
import { Form, Formik, getIn } from 'formik'
import type { FormikConfig, FormikErrors, FormikTouched, FormikValues } from 'formik'
import _ from 'lodash'
import { useEffect, useState } from 'react'

import Checkbox from '@ui/mui-extends/esm/Checkbox'
Expand All @@ -26,6 +27,7 @@ import Space from '@ui/mui-extends/esm/Space'
import { useStoreSelector } from 'store'

import { AutocompleteField, SelectField, Submit, TextField, TextTextField } from 'components/FormField'
import { SpecialTemplateType } from 'components/NewWorkflowNext/utils/convert'
import Scope from 'components/Scope'
import { T } from 'components/T'

Expand Down Expand Up @@ -62,12 +64,17 @@ export interface AtomFormData {
}

const AutoForm: React.FC<AutoFormProps> = ({ belong = Belong.Experiment, id, kind, act: action, formikProps }) => {
const kindAction = concatKindAction(kind, action)
const [initialValues, setInitialValues] = useState<Record<string, any>>({
id,
kind,
action,
...(kind === 'NetworkChaos' && { target: scopeInitialValues }),
...(kind !== 'PhysicalMachineChaos' && kind !== 'Suspend' && scopeInitialValues),
...(kind !== 'PhysicalMachineChaos' &&
kind !== SpecialTemplateType.Suspend &&
kind !== SpecialTemplateType.Serial &&
kind !== SpecialTemplateType.Parallel &&
scopeInitialValues),
...(belong === Belong.Workflow && { ...workflowNodeInfoInitialValues, templateType: kind }),
})
const [form, setForm] = useState<AtomFormData[]>([])
Expand All @@ -89,11 +96,12 @@ const AutoForm: React.FC<AutoFormProps> = ({ belong = Belong.Experiment, id, kin
}

async function loadData() {
if (kind === 'Suspend') {
setInitialValues((oldValues) => ({
...oldValues,
...formikProps.initialValues,
}))
if (
kind === SpecialTemplateType.Suspend ||
kind === SpecialTemplateType.Serial ||
kind === SpecialTemplateType.Parallel
) {
setInitialValues((oldValues) => _.merge({}, oldValues, formikProps.initialValues))

return
}
Expand All @@ -116,10 +124,7 @@ const AutoForm: React.FC<AutoFormProps> = ({ belong = Belong.Experiment, id, kin
})
: data

setInitialValues((oldValues) => ({
...oldValues,
...(formikProps.initialValues || formToRecords(form)),
}))
setInitialValues((oldValues) => _.merge({}, oldValues, formikProps.initialValues || formToRecords(form)))
setForm(form)
}

Expand Down Expand Up @@ -246,7 +251,7 @@ const AutoForm: React.FC<AutoFormProps> = ({ belong = Belong.Experiment, id, kin
<Form>
<Space>
<Typography variant="h6" fontWeight="bold">
{concatKindAction(kind, action)}
{kindAction}
</Typography>
{action && (
<SelectField
Expand All @@ -271,34 +276,35 @@ const AutoForm: React.FC<AutoFormProps> = ({ belong = Belong.Experiment, id, kin
scope="target.selector"
modeScope="target"
podsPreviewTitle={<T id="newE.target.network.target.podsPreview" />}
podsPreviewDesc={<T id="newE.target.network.target.podsPreviewHelper" />}
/>
</Space>
</>
)}
{kind !== 'Suspend' && (
<>
<Divider />
<Typography variant="h6" fontWeight="bold">
<T id="newE.steps.scope" />
</Typography>
{kind !== 'PhysicalMachineChaos' && <Scope kind={kind} namespaces={namespaces} />}
<Divider />
<Box>
{kind !== SpecialTemplateType.Suspend &&
kind !== SpecialTemplateType.Serial &&
kind !== SpecialTemplateType.Parallel && (
<>
<Divider />
<Typography variant="h6" fontWeight="bold">
Schedule
<T id="newE.steps.scope" />
</Typography>
<Checkbox
label="Scheduled"
helperText="Check the box to convert the Experiment into a Schedule."
checked={scheduled}
onChange={switchToSchedule}
/>
</Box>
{scheduled && <Schedule />}
<Divider />
</>
)}
{kind !== 'PhysicalMachineChaos' && <Scope kind={kind} namespaces={namespaces} />}
<Divider />
<Box>
<Typography variant="h6" fontWeight="bold">
Schedule
</Typography>
<Checkbox
label="Scheduled"
helperText={`Check the box to convert ${kindAction} into a Schedule.`}
checked={scheduled}
onChange={switchToSchedule}
/>
</Box>
{scheduled && <Schedule />}
<Divider />
</>
)}
<Typography variant="h6" fontWeight="bold">
Info
</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ const TargetGenerated: React.FC<TargetGeneratedProps> = ({ env, kind, data, vali
scope="target.selector"
modeScope="target"
podsPreviewTitle={i18n('newE.target.network.target.podsPreview')}
podsPreviewDesc={i18n('newE.target.network.target.podsPreviewHelper')}
/>
)}
</MoreOptions>
Expand Down
6 changes: 3 additions & 3 deletions ui/app/src/components/NewWorkflowNext/AdjustableEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { getBezierPath, getEdgeCenter } from 'react-flow-renderer'
import type { EdgeProps } from 'react-flow-renderer'

import CustomTooltip from './CustomTooltip'
import FlowTooltip from './FlowTooltip'

const foreignObjectSize = 24

Expand Down Expand Up @@ -56,9 +56,9 @@ export default function SuspendEdge({
x={edgeCenterX - foreignObjectSize / 2}
y={edgeCenterY - foreignObjectSize / 2}
>
<CustomTooltip arrow placement="top" {...data.tooltipProps}>
<FlowTooltip arrow placement="top" {...data.tooltipProps}>
<div style={{ width: '100%', height: '100%' }} />
</CustomTooltip>
</FlowTooltip>
</foreignObject>
</>
)
Expand Down
13 changes: 7 additions & 6 deletions ui/app/src/components/NewWorkflowNext/BareNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,28 @@
* limitations under the License.
*
*/
import CircleOutlinedIcon from '@mui/icons-material/CircleOutlined'
import { Button } from '@mui/material'
import { Box, Button } from '@mui/material'
import type { ButtonProps } from '@mui/material'
import { forwardRef } from 'react'

import { iconByKind } from 'lib/byKind'

export type BareNodeProps = ButtonProps & {
kind?: string
kind: string
}

export default forwardRef<HTMLSpanElement, BareNodeProps>(({ kind, sx, ...rest }: BareNodeProps, ref) => (
export default forwardRef<HTMLSpanElement, BareNodeProps>(({ kind, sx, children, name, ...rest }, ref) => (
<Button
ref={ref}
component="span"
variant="outlined"
color="secondary"
size="small"
startIcon={kind ? iconByKind(kind) : <CircleOutlinedIcon />}
startIcon={iconByKind(kind)}
disableFocusRipple
sx={{ justifyContent: 'flex-start', width: 200, ...sx }}
sx={{ alignItems: 'center', width: 200, ...sx }}
children={<Box flex={1}>{children}</Box>}
title={name}
{...rest}
/>
))
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ import { useDrag } from 'react-dnd'

import BareNode from '../BareNode'
import type { BareNodeProps } from '../BareNode'
import { SpecialTemplateType } from '../utils/convert'
import { ElementTypes } from './types'

interface DraggableBareNodeProps extends BareNodeProps {
elementType: ElementTypes
elementType: ElementTypes | SpecialTemplateType
act?: string

/**
Expand Down
15 changes: 15 additions & 0 deletions ui/app/src/components/NewWorkflowNext/Elements/FunctionalNodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,27 @@
*/
import Space from '@ui/mui-extends/esm/Space'

import { SpecialTemplateType } from '../utils/convert'
import DraggableBareNode from './DraggableBareNode'
import { ElementTypes, ElementsProps } from './types'

export default function FunctionalNodes({ onElementClick }: ElementsProps) {
return (
<Space>
<DraggableBareNode
elementType={SpecialTemplateType.Serial}
kind={SpecialTemplateType.Serial}
onNodeClick={onElementClick}
>
Serial
</DraggableBareNode>
<DraggableBareNode
elementType={SpecialTemplateType.Parallel}
kind={SpecialTemplateType.Parallel}
onNodeClick={onElementClick}
>
Parallel
</DraggableBareNode>
<DraggableBareNode elementType={ElementTypes.Suspend} kind="Suspend" onNodeClick={onElementClick}>
Suspend
</DraggableBareNode>
Expand Down
33 changes: 12 additions & 21 deletions ui/app/src/components/NewWorkflowNext/FlowNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,26 @@
* limitations under the License.
*
*/
import { styled } from '@mui/material'
import type { TooltipProps } from '@mui/material'
import { Handle, Position } from 'react-flow-renderer'
import type { Node } from 'react-flow-renderer'
import { memo } from 'react'
import { Position } from 'react-flow-renderer'
import type { NodeProps } from 'react-flow-renderer'

import BareNode from './BareNode'
import type { BareNodeProps } from './BareNode'
import CustomTooltip from './CustomTooltip'
import StyledHandle from './StyleHandle'

const StyledHandle = styled(Handle)(({ theme }) => ({
width: '8px !important',
height: '8px !important',
background: `${theme.palette.background.default} !important`,
borderColor: `${theme.palette.outline.main} !important`,
zIndex: 1,
}))
export type FlowNodeProps = NodeProps<BareNodeProps & { finished: true; name: string }>

export type FlowNodeProps = Node<BareNodeProps & { origin?: boolean; tooltipProps: TooltipProps }>

export default function FlowNode({ data }: FlowNodeProps) {
const { origin, tooltipProps, ...rest } = data
function FlowNode({ data, isConnectable }: FlowNodeProps) {
const { finished, ...rest } = data // Exclude `finished` from the data.

return (
<>
{!origin && <StyledHandle type="target" position={Position.Left} />}
<CustomTooltip arrow placement="top" {...tooltipProps}>
<BareNode {...rest} />
</CustomTooltip>
<StyledHandle type="source" position={Position.Right} />
{isConnectable && <StyledHandle type="target" position={Position.Left} />}
<BareNode {...rest} />
{isConnectable && <StyledHandle type="source" position={Position.Right} />}
</>
)
}

export default memo(FlowNode)
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { Tooltip, styled, tooltipClasses } from '@mui/material'
import type { TooltipProps } from '@mui/material'

const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
const FlowTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.arrow}`]: {
Expand All @@ -29,4 +29,4 @@ const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
},
}))

export default CustomTooltip
export default FlowTooltip
Loading

0 comments on commit 3bb655f

Please sign in to comment.