From 802e6fb8b6df52e4eec8d68519c8cdc54bab3f80 Mon Sep 17 00:00:00 2001
From: Aditya <60684641+0x0elliot@users.noreply.github.com>
Date: Thu, 16 Nov 2023 16:55:11 +0530
Subject: [PATCH] fix(debug-frontend): syncing cloud debug frontend
---
.gitignore | 1 +
frontend/src/components/RuntimeDebugger.jsx | 491 ++++++++++++++++++++
2 files changed, 492 insertions(+)
create mode 100644 frontend/src/components/RuntimeDebugger.jsx
diff --git a/.gitignore b/.gitignore
index 3ad770b6..29491a78 100755
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ backend/onprem/app_sdk/apps
*.exe
*debug*
+!**/RuntimeDebugger.jsx
shuffle-database/batch_metrics_enabled.conf
shuffle-database/logging_enabled.conf
diff --git a/frontend/src/components/RuntimeDebugger.jsx b/frontend/src/components/RuntimeDebugger.jsx
new file mode 100644
index 00000000..fa149570
--- /dev/null
+++ b/frontend/src/components/RuntimeDebugger.jsx
@@ -0,0 +1,491 @@
+import React, { useState, useEffect } from 'react';
+
+import {
+ TextField,
+ Link,
+ Button,
+ CircularProgress,
+ Select,
+ MenuList,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Autocomplete,
+ Tooltip,
+ Typography,
+} from '@mui/material';
+
+import { toast } from "react-toastify"
+import { makeStyles } from "@mui/styles";
+import theme from '../theme.jsx';
+import dayjs from 'dayjs';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
+import Pagination from '@mui/material/Pagination';
+import {
+ DatePicker,
+ DateTimePicker,
+ LocalizationProvider,
+} from '@mui/x-date-pickers'
+
+import {
+ OpenInNew as OpenInNewIcon,
+} from '@mui/icons-material';
+
+import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
+
+const useStyles = makeStyles({
+ notchedOutline: {
+ borderColor: "#f85a3e !important",
+ },
+});
+
+const RuntimeDebugger = (props) => {
+ const { userdata, globalUrl, } = props
+
+ const classes = useStyles();
+
+ //const [workflowId, setWorkflowId] = useState("");
+ //const [status, setStatus] = useState("FINISHED");
+ //const [endTime, setEndTime] = useState(dayjs().subtract(0, 'day'))
+ //const [startTime, setStartTime] = useState(dayjs().subtract(30, 'day'))
+
+ const [workflowId, setWorkflowId] = useState("")
+ const [status, setStatus] = useState("")
+ const [endTime, setEndTime] = useState("")
+ const [startTime, setStartTime] = useState("")
+
+ const [workflow, setWorkflow] = useState({})
+ const [searchLoading, setSearchLoading] = useState(false)
+ const [rowCursor, setCursor] = useState("")
+ const [rowsPerPage, setRowsPerPage] = useState(10)
+ const [resultRows, setResultRows] = useState([])
+ const [workflows, setWorkflows] = useState([
+ {"id": "", "name": "All Workflows",}
+ ])
+ /*
+ [
+ {id: "1", execution_id: "1", status: "FINISHED", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
+ {id: "2", execution_id: "2", status: "WAITING", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
+ {id: "3", execution_id: "3", status: "EXECUTING",startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
+ {id: "4", execution_id: "4", status: "ABORTED", startTimestamp: "2021-10-01 12:00:00", endTimestamp: "2021-10-01 12:00:00", workflow: {"id": "1234", "name": "what",}},
+ ]);
+ */
+
+ const submitSearch = (workflowId, status, startTime, endTime, cursor, limit) => {
+
+ setResultRows([])
+ setSearchLoading(true)
+ const fetchData = {
+ workflow_id: workflowId,
+ cursor: cursor,
+ limit: limit,
+
+ status: status.toUpperCase(),
+ start_time: startTime,
+ end_time: endTime,
+ }
+
+ fetch(`${globalUrl}/api/v1/workflows/search`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "include",
+ body: JSON.stringify(fetchData),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ setSearchLoading(false)
+ if (data.success) {
+ if (data.runs !== undefined && data.runs !== null && data.runs.length > 0) {
+ for (let key in data.runs) {
+ data.runs[key].id = data.runs[key].execution_id
+
+ // make started_at field into ISO string
+ //data.runs[key].endTimestamp = data.runs[key].ended_at.toISOString().slice(0, 19).replace('T', ' ')
+ const startTimestamp = new Date(data.runs[key].started_at*1000)
+ data.runs[key].startTimestamp = startTimestamp.toISOString().slice(0, 19).replace('T', ' ')
+ const endTimestamp = new Date(data.runs[key].completed_at*1000)
+ data.runs[key].endTimestamp = endTimestamp.toISOString().slice(0, 19).replace('T', ' ')
+ }
+
+
+ // Add 20 empty rows to the end of the resultRows array
+ // This is to make sure that the scrollbar is always visible
+ setResultRows(data.runs)
+ }
+ } else {
+ console.error("Search error: ", data.reason)
+ }
+ })
+ .catch((error) => {
+ setSearchLoading(false)
+ console.error("Error:", error);
+ })
+ }
+
+ const getAvailableWorkflows = () => {
+ fetch(globalUrl + "/api/v1/workflows", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ credentials: "include",
+ })
+ .then((response) => {
+ if (response.status !== 200) {
+ console.log("Status not 200 for workflows :O!");
+ return;
+ }
+ return response.json();
+ })
+ .then((responseJson) => {
+ if (responseJson !== undefined) {
+ var foundWorkflows = [{"id": "", "name": "All Workflows",}]
+ foundWorkflows.push(...responseJson)
+ setWorkflows(foundWorkflows)
+ }
+ })
+ .catch((error) => {
+ console.log("Error getting workflows: " + error);
+ })
+ }
+
+ useEffect(() => {
+ getAvailableWorkflows()
+
+ // Find workflow_id in url query
+ const urlParams = new URLSearchParams(window.location.search);
+ const workflowId = urlParams.get('workflow_id');
+ if (workflowId !== undefined && workflowId !== null && workflowId !== "" && workflowId.length === 36) {
+ setWorkflowId(workflowId)
+
+ // find the workflow in workflows and select it
+ for (let key in workflows) {
+ if (workflows[key].id === workflowId) {
+ setWorkflow(workflows[key])
+ break
+ }
+ }
+ }
+
+ const foundStatus = urlParams.get('status');
+ if (foundStatus !== undefined && foundStatus !== null && foundStatus !== "") {
+ setStatus(foundStatus)
+ }
+ }, [])
+
+ const columns: GridColDef[] = [
+ {
+ field: 'status',
+ headerName: 'Status',
+ width: 150,
+ renderCell: (params) => (
+ {
+ setStatus(params.row.status)
+ }}>
+ {params.row.status}
+
+ ),
+ },
+ {
+ field: 'workflow.name',
+ headerName: 'Workflow Name',
+ width: 250,
+ renderCell: (params) => (
+ {
+ setWorkflowId(params.row.workflow.id)
+
+ for (let key in workflows) {
+ if (workflows[key].id === params.row.workflow.id) {
+ setWorkflow(workflows[key])
+ break
+ }
+ }
+ }}>
+ {params.row.workflow.name}
+
+ ),
+ },
+ {
+ field: 'workflow results',
+ headerName: 'Results',
+ width: 75,
+ renderCell: (params) => {
+ //console.log("PARAMS: ", params)
+ var extraItems = 0
+ if (params.row.workflow.triggers !== null) {
+ for (let key in params.row.workflow.triggers) {
+ if (params.row.workflow.triggers[key].app_name === "User Input" || params.row.workflow.triggers[key].app_name === "Shuffle Workflow") {
+ extraItems += 1
+ }
+ }
+ }
+
+ const actionLength = params.row.workflow.actions === null ? 0 : params.row.workflow.actions.length+extraItems
+ const resultLength = params.row.results === null ? 0 : params.row.results.length
+
+ //console.log("actionLength: ", actionLength, " resultLength: ", resultLength)
+ return (
+ {
+ }}>
+ {resultLength} / {actionLength}
+
+ )
+ },
+ },
+ { field: 'startTimestamp', headerName: 'Start time', width: 160, },
+ { field: 'endTimestamp', headerName: 'End time', width: 160, },
+ {
+ field: 'id',
+ headerName: 'Explore',
+ width: 65,
+ renderCell: (params) => (
+
+