Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Commit

Permalink
Workflow Workbench
Browse files Browse the repository at this point in the history
Other changes:
- Code formatting
- fetchWithContext allows text response (for /workflow retval)
- Style changes regarding small IconButton
- Introduce TS
- Dropdown coerces undefined to null (to prevent uncontrolled component warning)
  • Loading branch information
peterlau committed Apr 12, 2022
1 parent e38d917 commit 4b5317f
Show file tree
Hide file tree
Showing 31 changed files with 1,039 additions and 204 deletions.
6 changes: 6 additions & 0 deletions ui/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "react-app",
"rules": {
"import/no-anonymous-default-export": 0
}
}
7 changes: 1 addition & 6 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@
"eject": "react-scripts eject",
"prettier": "prettier --write ."
},
"eslintConfig": {
"extends": "react-app",
"rules": {
"import/no-anonymous-default-export": 0
}
},
"browserslist": {
"production": [
">0.2%",
Expand Down Expand Up @@ -86,6 +80,7 @@
"js-yaml": "3.13.1",
"prettier": "^2.2.1",
"sass": "^1.49.9",
"typescript": "^4.6.3",
"webdriver": "^5.0.0",
"webdriverio": "^5.0.0"
},
Expand Down
7 changes: 7 additions & 0 deletions ui/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Gantt from "./pages/kitchensink/Gantt";
import CustomRoutes from "./plugins/CustomRoutes";
import AppBarModules from "./plugins/AppBarModules";
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
import Workbench from "./pages/workbench/Workbench";

const useStyles = makeStyles((theme) => ({
root: {
Expand Down Expand Up @@ -68,6 +69,9 @@ export default function App() {
<Button component={NavLink} path="/taskQueue">
Task Queues
</Button>
<Button component={NavLink} path="/workbench">
Workbench
</Button>
<CustomAppBarButtons />

<div className={classes.toolbarRight}>
Expand Down Expand Up @@ -107,6 +111,9 @@ export default function App() {
<Route exact path="/taskQueue/:name?">
<TaskQueue />
</Route>
<Route exact path="/workbench">
<Workbench />
</Route>
<Route exact path="/kitchen">
<KitchenSink />
</Route>
Expand Down
10 changes: 9 additions & 1 deletion ui/src/components/Dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ export default function ({
style,
error,
helperText,
name,
value,
...props
}) {
return (
<FormControl style={style} className={className}>
{label && <InputLabel error={!!error}>{label}</InputLabel>}
<Autocomplete
renderInput={(params) => (
<Input {...params} error={!!error} helperText={helperText} />
<Input
{...params}
name={name}
error={!!error}
helperText={helperText}
/>
)}
value={value === undefined ? null : value} // convert undefined to null
{...props}
/>
</FormControl>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/ReactJson.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ const useStyles = makeStyles({
height: "100%",
display: "flex",
flexDirection: "column",
paddingTop: 15
paddingTop: 15,
},
editorWrapper: {
flex: 1,
marginLeft: 10,
position: "relative"
position: "relative",
},
label: {
marginTop: 5,
Expand Down
3 changes: 2 additions & 1 deletion ui/src/components/StatusBadge.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const colorMap = {
warning: "#fba404",
};

export default function StatusBadge({ status }) {
export default function StatusBadge({ status, ...props }) {
let color;
switch (status) {
case "RUNNING":
Expand All @@ -26,6 +26,7 @@ export default function StatusBadge({ status }) {

return (
<Chip
{...props}
style={color && { backgroundColor: color, color: "white" }}
label={status}
/>
Expand Down
19 changes: 19 additions & 0 deletions ui/src/components/formik/FormikDropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useField } from "formik";
import { Dropdown } from "..";

export default function (props) {
const [field, meta, helper] = useField(props);
const touchedError = meta.touched && meta.error;

return (
<>
<Dropdown
{...props}
{...field}
onChange={(e, value) => helper.setValue(value)}
error={touchedError}
helperText={touchedError}
/>
</>
);
}
27 changes: 23 additions & 4 deletions ui/src/components/formik/FormikJsonInput.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import React, { useRef } from "react";
import { useRef, useEffect } from "react";
import { useField } from "formik";
import Editor from "@monaco-editor/react";
import { makeStyles } from "@material-ui/styles";
import { FormHelperText, InputLabel } from "@material-ui/core";
import clsx from "clsx";

const useStyles = makeStyles({
wrapper: {
width: "100%",
},
monaco: {
padding: 10,
width: "100%",
borderColor: "rgba(128, 128, 128, 0.2)",
borderStyle: "solid",
borderWidth: 1,
Expand All @@ -25,7 +30,13 @@ const useStyles = makeStyles({
},
});

export default function ({ className, label, height, ...props }) {
export default function ({
className,
label,
height,
reinitialize = false,
...props
}) {
const classes = useStyles();
const [field, meta, helper] = useField(props);
const editorRef = useRef(null);
Expand All @@ -37,11 +48,18 @@ export default function ({ className, label, height, ...props }) {
});
}

useEffect(() => {
if (reinitialize && editorRef.current) {
editorRef.current.getModel().setValue(field.value);
}
}, [reinitialize, field.value]);

return (
<div className={className}>
<InputLabel variant="outlined" error={meta.touched && meta.error}>
<div className={clsx([classes.wrapper, className])}>
<InputLabel variant="outlined" error={meta.touched && !!meta.error}>
{label}
</InputLabel>

<Editor
className={classes.monaco}
height={height || 90}
Expand Down Expand Up @@ -70,6 +88,7 @@ export default function ({ className, label, height, ...props }) {
overviewRulerBorder: false,
}}
/>

{meta.touched && meta.error ? (
<FormHelperText variant="outlined" error>
{meta.error}
Expand Down
35 changes: 35 additions & 0 deletions ui/src/components/formik/FormikVersionDropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useFormikContext } from "formik";
import { useWorkflowNamesAndVersions } from "../../data/workflow";
import FormikDropdown from "./FormikDropdown";
import { useEffect } from "react";

export default function FormikVersionDropdown(props) {
const { name } = props;
const { data: namesAndVersions } = useWorkflowNamesAndVersions();
const {
setFieldValue,
values: { workflowName, workflowVersion },
} = useFormikContext();

useEffect(() => {
if (workflowVersion && namesAndVersions.has(workflowName)) {
const found = namesAndVersions
.get(workflowName)
.find((row) => row.version.toString() === workflowVersion);
if (!found) {
console.log(
`Version ${workflowVersion} not found for new workflowName. Clearing dropdown.`
);
setFieldValue(name, null, false);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [namesAndVersions, workflowName, workflowVersion]);

const versions =
workflowName && namesAndVersions.has(workflowName)
? namesAndVersions.get(workflowName).map((row) => "" + row.version)
: [];

return <FormikDropdown options={versions} {...props} />;
}
1 change: 1 addition & 0 deletions ui/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export { default as ReactJson } from "./ReactJson";
// Misc
export { default as LinearProgress } from "./LinearProgress";
export { default as Pill } from "./Pill";
export { default as StatusBadge } from "./StatusBadge";
33 changes: 28 additions & 5 deletions ui/src/data/common.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,43 @@
import _ from "lodash";
import { useQuery, useMutation } from "react-query";
import { useQuery, useQueries, useMutation } from "react-query";
import { useFetchContext, fetchWithContext } from "../plugins/fetch";
import assert from "assert";

export function useFetchParallel(paths, reactQueryOptions) {
const fetchContext = useFetchContext();

return useQueries(
paths.map((path) => {
assert(_.isArray(path));
return {
queryKey: [fetchContext.stack, ...path],
queryFn: () => fetchWithContext(`/${path.join("/")}`, fetchContext),
enabled:
fetchContext.ready && _.get(reactQueryOptions, "enabled", true),
keepPreviousData: true,
...reactQueryOptions,
};
})
);
}

export function useFetch(path, reactQueryOptions, defaultResponse) {
const fetchContext = useFetchContext();
const key = _.isArray(path)
? [fetchContext.stack, ...path]
: [fetchContext.stack, path];
const pathStr = _.isArray(path) ? `/${path.join("/")}` : path;
return useQuery(
[fetchContext.stack, path],
key,
() => {
if (path) {
return fetchWithContext(path, fetchContext);
if (pathStr) {
return fetchWithContext(pathStr, fetchContext);
} else {
return Promise.resolve(defaultResponse);
}
},
{
enabled: fetchContext.ready,
enabled: fetchContext.ready && _.get(reactQueryOptions, "enabled", true),
keepPreviousData: true,
...reactQueryOptions,
}
Expand Down
62 changes: 56 additions & 6 deletions ui/src/data/workflow.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useMemo } from "react";
import { useQuery, useMutation } from "react-query";
import qs from "qs";
import { useQuery, useMutation, useQueryClient } from "react-query";
import { useFetchContext, fetchWithContext } from "../plugins/fetch";
import { useFetch } from "./common";
import { useFetch, useFetchParallel } from "./common";
import { useEnv } from "../plugins/env";
import qs from "qs";

const STALE_TIME_WORKFLOW_DEFS = 600000; // 10 mins
const STALE_TIME_SEARCH = 60000; // 1 min
Expand Down Expand Up @@ -36,16 +37,43 @@ export function useWorkflowSearch(searchObj) {
}

export function useWorkflow(workflowId) {
return useFetch(`/workflow/${workflowId}`);
return useFetch(`/workflow/${workflowId}`, { enabled: !!workflowId });
}

export function useWorkflows(workflowIds, reactQueryOptions) {
return useFetchParallel(
workflowIds.map((workflowId) => ["workflow", workflowId]),
reactQueryOptions
);
}

export function useInvalidateWorkflows() {
const { stack } = useEnv();
const client = useQueryClient();

return function (workflowIds) {
console.log("invalidating workflow Ids", workflowIds);
client.invalidateQueries({
predicate: (query) =>
query.queryKey[0] === stack &&
query.queryKey[1] === "workflow" &&
workflowIds.includes(query.queryKey[2]),
});
};
}

export function useWorkflowDef(workflowName, version, defaultWorkflow) {
export function useWorkflowDef(
workflowName,
version,
defaultWorkflow,
reactQueryOptions = {}
) {
let path;
if (workflowName) {
path = `/metadata/workflow/${workflowName}`;
if (version) path += `?version=${version}`;
}
return useFetch(path, {}, defaultWorkflow);
return useFetch(path, reactQueryOptions, defaultWorkflow);
}

export function useWorkflowDefs() {
Expand Down Expand Up @@ -135,3 +163,25 @@ export function useWorkflowNamesAndVersions() {

return { ...rest, data: newData };
}

export function useStartWorkflow(callbacks) {
const path = "/workflow";
const fetchContext = useFetchContext();

return useMutation(
({ body }) =>
fetchWithContext(
path,
fetchContext,
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
},
false
),
callbacks
);
}
4 changes: 2 additions & 2 deletions ui/src/pages/definition/EventHandler.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const useStyles = makeStyles({
paper: {
flex: 1,
margin: 30,
paddingTop: 10
}
paddingTop: 10,
},
});

export default function EventHandlerDefinition() {
Expand Down
Loading

0 comments on commit 4b5317f

Please sign in to comment.