diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ab9f68e5..31d0e85f 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -634,8 +634,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } newUser := users[0] - - if !strings.Contains(newUser.Orgs[0], currentOrg.Id) { + if !shuffle.ArrayContains(newUser.Orgs, currentOrg.Id) { newUser.Orgs = append(newUser.Orgs, currentOrg.Id) } @@ -643,13 +642,18 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { newUser.ActiveOrg = currentOrg } - err = shuffle.SetUser(ctx, &newUser, false) + err = shuffle.SetUser(ctx, &newUser, true) if err != nil { log.Printf("[WARNING] Failed updating the user %s: %s", data.Username, err) resp.WriteHeader(400) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) return } + + resp.WriteHeader(200) + resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) + log.Printf("[INFO] %s Successfully re-added to org %s (%s)", data.Username, currentOrg.Name, currentOrg.Id) + return } else { log.Printf("[WARNING] Failed registering user: %s", err) resp.WriteHeader(401) diff --git a/frontend/package.json b/frontend/package.json index b832a893..186c50c7 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,54 +1,64 @@ { "name": "shuffler", "homepage": "https://shuffler.io", - "version": "1.4.0", + "version": "1.3.3", "private": true, "dependencies": { + "@babel/plugin-proposal-class-properties": "^7.18.6", "@codemirror/commands": "^6.2.4", - "@emotion/is-prop-valid": "^1.1.1", + "@codemirror/lang-python": "^6.1.3", "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", + "@emotion/styled": "^11.6.0", "@lezer/highlight": "^1.1.3", + "@metamask/detect-provider": "^1.2.0", "@mui/icons-material": "^5.14.0", "@mui/material": "^5.14.0", "@mui/styles": "^5.14.0", "@mui/x-data-grid": "^5.17.11", "@mui/x-date-pickers": "^6.11.1", + "@types/algoliasearch": "^3.34.11", + "@uiw/codemirror-theme-basic": "^4.21.20", "@uiw/codemirror-theme-vscode": "^4.21.20", "@uiw/codemirror-themes": "^4.21.9", - "@uiw/react-codemirror": "^4.21.9", - "@use-it/interval": "^1.0.0", - "algoliasearch": "^4.13.1", - "calculate-size": "^1.1.1", - "class-transformer": "^0.4.0", + "@uiw/react-codemirror": "^4.21.21", + "@use-it/interval": "^0.1.3", + "algoliasearch": "^4.8.3", + "class-transformer": "^0.2.0", + "codemirror": "^6.0.1", + "cpx": "^1.5.0", "create-react-app": "^5.0.1", - "cytoscape": "^3.15.1", - "cytoscape-edgehandles": "^3.6.0", - "cytoscape-node-html-label": "^1.1.5", - "d3": "^7.1.1", - "dayjs": "^1.11.9", + "cytoscape": "^3.23.0", + "cytoscape-clipboard": "^2.2.1", + "cytoscape-cxtmenu": "^3.4.0", + "cytoscape-edgehandles": "^3.5.1", + "cytoscape-grid-guide": "~2.3.3", + "cytoscape-node-html-label": "^1.2.2", + "cytoscape-panzoom": "^2.5.3", + "cytoscape-undo-redo": "^1.3.3", + "d3": "~4.10.0", + "dayjs": "^1.11.10", "dotenv": "^6.1.0", - "downshift": "^3.3.5", + "downshift": "^3.4.8", + "express": "^4.17.1", + "express-useragent": "^1.0.15", "github-markdown-css": "^3.0.1", + "i18next": "^22.3.0", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-chained-backend": "^4.2.0", + "i18next-http-backend": "^2.1.1", + "i18next-localstorage-backend": "^4.1.0", + "i18next-xhr-backend": "^3.2.2", "import": "0.0.6", "interweave": "^11.2.0", - "jss": "^10.10.0", - "jss-camel-case": "^6.1.0", - "jss-default-unit": "^8.0.2", - "jss-global": "^3.0.0", - "jss-nested": "^6.0.1", - "jss-props-sort": "^6.0.0", - "jss-vendor-prefixer": "^8.0.1", + "is-plain-obj": "^4.1.0", + "json-bigint": "^1.0.0", + "match-sorter": "^6.3.1", "md5-file": "^4.0.0", "mdbreact": "^4.21.1", - "mime": "^3.0.0", - "moment": "^2.29.1", + "moment": "~2.29.4", "mui-chips-input": "^2.1.3", "mui-nested-menu": "^3.2.1", - "process": "^0.11.10", "react": "^18.2.0", - "react-alert": "^7.0.3", - "react-alert-template-basic": "^1.0.0", "react-alice-carousel": "^2.6.4", "react-avatar-editor": "^11.1.0", "react-beforeunload": "^2.2.1", @@ -57,31 +67,40 @@ "react-cytoscapejs": "^2.0.0", "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", - "react-draggable": "^3.3.2", + "react-draggable": "^4.4.5", "react-driftjs": "^1.2.2", "react-dropzone": "^14.2.3", "react-ga4": "^2.0.0", "react-hotkeys": "^2.0.0", + "react-i18next": "^13.1.2", "react-instantsearch-dom": "^6.28.0", "react-json-pretty": "^2.2.0", "react-json-view": "^1.21.3", + "react-json-view-ssr": "^1.19.1", "react-markdown": "^8.0.7", "react-markdown-github": "^3.3.1", "react-powerhooks": "^0.0.7", "react-router": "^6.14.1", "react-router-dom": "^6.14.1", "react-scripts": "^5.0.1", + "react-social-icons": "^5.15.0", + "react-stripe-elements": "^6.1.2", "react-toastify": "^9.1.3", - "reaviz": "^14.9.4", + "reaviz": "^14.9.7", "remark-gfm": "^3.0.1", + "remark-html": "^16.0.1", + "remark-images": "^4.0.0", + "remark-rehype": "^11.0.0", + "rsuite": "^5.23.0", "search-insights": "^2.2.1", - "shellwords": "^0.1.1", + "shellwords": "^1.0.1", "simplebar": "^4.2.3", - "styled-components": "^4.4.0", - "webpack": "^5.88.2", - "yaml": "^1.7.2", + "styled-components": "^4.4.1", + "sync-fetch": "^0.3.0", + "terser-webpack-plugin": "^4.2.3", + "yaml": "^1.10.0", "yamljs": "^0.3.0", - "zone.js": "^0.13.1" + "zone.js": "~0.8.26" }, "scripts": { "start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start", @@ -92,12 +111,7 @@ "lint_file": "eslint 'src/views/AngularWorkflow.jsx'" }, "eslintConfig": { - "extends": "react-app", - "rules": { - "jsx-a11y/img-redundant-alt": "off", - "no-redeclare": "off", - "no-loop-func": "off" - } + "extends": "react-app" }, "browserslist": [ ">0.2%", @@ -106,13 +120,19 @@ "not op_mini all" ], "devDependencies": { - "@babel/core": "^7.15.8", - "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "babel-eslint": "^10.1.0", - "prettier": "2.4.1", + "@babel/cli": "^7.22.10", + "@babel/core": "^7.22.10", + "@babel/plugin-proposal-export-default-from": "^7.22.5", + "@babel/preset-flow": "^7.22.5", + "@babel/preset-react": "^7.22.5", + "babel-loader": "^8.0.5", + "babel-plugin-css-modules-transform": "^1.6.2", + "babel-plugin-react-css-modules": "^5.2.6", + "babel-plugin-transform-imports": "^2.0.0", + "babel-preset-env": "^1.7.0", + "babel-preset-es2015": "^6.24.1", "promise-window": "^1.2.1", - "react-16": "npm:react@16.13.1", - "react-dom-16": "npm:react-dom@16.13.1", - "react-error-overlay": "6.0.9" + "react-hot-loader": "^4.13.0", + "webpack-cli": "^5.1.4" } } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 4e4fc9c9..eccd320f 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -37,7 +37,6 @@ import UpdateAuthentication from "./views/UpdateAuthentication.jsx"; import FrameworkWrapper from "./views/FrameworkWrapper.jsx"; import ScrollToTop from "./components/ScrollToTop"; import AlertTemplate from "./components/AlertTemplate"; -import { useAlert, positions, Provider } from "react-alert"; import { isMobile } from "react-device-detect"; import RuntimeDebugger from "./components/RuntimeDebugger.jsx" @@ -160,10 +159,6 @@ const App = (message, props) => { // Dumb for content load (per now), but good for making the site not suddenly reload parts (ajax thingies) - const options = { - timeout: 9000, - position: positions.BOTTOM_LEFT, - }; const handleFirstInteraction = (event) => { console.log("First interaction: ", event) @@ -566,9 +561,7 @@ const App = (message, props) => { - - {includedData} - + {includedData} { const deleteCache = (orgId, key) => { toast("Attempting to delete Cache"); - fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, { - method: "DELETE", + + // method: "DELETE", + const method = "POST" + //const url = `${globalUrl}/api/v1/orgs/${orgId}/cache/${key}` + const url = `${globalUrl}/api/v1/orgs/${orgId}/delete_cache` + const parsed = { + "org_id": orgId, + "key": key, + } + + fetch(url, { + method: method, headers: { Accept: "application/json", }, + body: JSON.stringify(parsed), credentials: "include", }) .then((response) => { diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index 189a1cf6..81e9fd3d 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -32,6 +32,8 @@ import { import { FixName } from "../views/Apps.jsx"; import aa from 'search-insights' +import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; + // Handles workflow updates on first open to highlight the issues of the workflow // Variables // Action (exists, missing fields) @@ -60,6 +62,8 @@ const ConfigureWorkflow = (props) => { setAuthenticationType, setAuthenticationModalOpen, setConfigureWorkflowModalOpen, + + setConfigurationFinished, } = props; const [requiredActions, setRequiredActions] = React.useState([]); @@ -70,9 +74,19 @@ const ConfigureWorkflow = (props) => { const [firstLoad, setFirstLoad] = React.useState(""); const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false); const [loopRunning, setLoopRunning] = useState(false) - const [checkStarted, setCheckStarted] = React.useState(false); + useEffect(() => { + console.log("Required actions: ", requiredActions) + if (requiredActions.length === 0) { + console.log("No more actions? Set parent to done?") + + if (setConfigurationFinished !== undefined) { + setConfigurationFinished(true) + } + } + }, [requiredActions]) + const stop = () => { setLoopRunning(false) } @@ -81,6 +95,7 @@ const ConfigureWorkflow = (props) => { setLoopRunning(true) } + useEffect(() => { if (loopRunning) { const intervalId = setInterval(() => { @@ -159,7 +174,6 @@ const ConfigureWorkflow = (props) => { return response.json(); }) .then((responseJson) => { - console.log("ACTION: ", responseJson); if ( responseJson.actions !== undefined && responseJson.actions !== null @@ -215,6 +229,9 @@ const ConfigureWorkflow = (props) => { // without version match const newappname = action.app_name.toLowerCase().replaceAll(" ", "_") const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) + + + if (app === undefined || app === null) { const subapp = apps.find(app => app.name === action.app_name) @@ -229,47 +246,51 @@ const ConfigureWorkflow = (props) => { "required": true, }) } else { + if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) { - // Check if configuration is filled or not + // Check if configuration is filled or not var filled = true; for (let [key,keyval] in Object.entries(action.parameters)) { if (action.parameters[key].configuration) { - //console.log("Found config: ", action.parameters[key]) - if ( - action.parameters[key].value === null || - action.parameters[key].value.length === 0 - ) { + if (action.parameters[key].value === null || action.parameters[key].value.length === 0) { filled = false; break; } } } + if (app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app") { + filled = false + + action.auth_type = "oauth2" + } + newaction.steps.push({ "title": "Authenticate app", "type": "authenticate", "required": true, + "auth_type": app.authentication.type, }) if (!filled) { newaction.must_authenticate = true; newaction.action_ids.push(action.id); } - } else if (action.authentication_id !== "" && app.authentication.required === true) { - console.log("Should verify authentication ID ", action.authentication_id) + + } else if (action.authentication_id !== undefined && action.authentication_id !== null && action.authentication_id !== "" && app.authentication.required === true) { + console.log("FIXME: Should verify authentication ID ", action.authentication_id) } newaction.app = app; } - if ( - action.errors !== undefined && - action.errors !== null && - action.errors.length > 0 - ) { + + if (newaction.errors !== undefined && newaction.errors !== null && newaction.errors.length > 0) { //console.log("Node has errors!: ", action.errors) } + //console.log(newaction.app_name,"AUTH: ", newaction.must_authenticate, " ACTIVATE: ", newaction.must_activate) + if (newaction.must_authenticate) { var authenticationOptions = []; for (let [key,keyval] in Object.entries(appAuthentication)) { @@ -395,29 +416,29 @@ const ConfigureWorkflow = (props) => { setRequiredActions(newactions); } - if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) { - var newactions = [] - for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) { - var newaction = requiredActions[actionkey]; - const app = newaction.app; + if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) { + var newactions = [] + for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) { + var newaction = requiredActions[actionkey]; + const app = newaction.app; - for (let [key,keyval] in Object.entries(appAuthentication)) { - const auth = appAuthentication[key]; + for (let [key,keyval] in Object.entries(appAuthentication)) { + const auth = appAuthentication[key]; - // Does this account for all the different ones of the same? - if (auth.app.name === app.name && auth.active === true) { - newaction.auth_done = true; - break; + // Does this account for all the different ones of the same? + if (auth.app.name === app.name && auth.active === true) { + newaction.auth_done = true; + break; + } } + + newactions.push(newaction); } - newactions.push(newaction); - } - - setRequiredActions(newactions); - setPreviousAuth(appAuthentication); - // Set auth done to true - //"auth_done": false + setRequiredActions(newactions); + setPreviousAuth(appAuthentication); + // Set auth done to true + //"auth_done": false } const TriggerSection = (props) => { @@ -610,14 +631,24 @@ const ConfigureWorkflow = (props) => { const [authFields, setAuthFields] = useState([]) const [sensitiveFields, setSensitiveFields] = useState([]) + //console.log("ACTION", action) + + useEffect(() => { + if (finalized === true) { + setOpened(false) + setFilled(true) + } + }, [finalized]) + if (authFields.length === 0 && opened === true) { // Loop through fields of the action + var newfields = [] - const params = action.action.parameters - var sensitiveIndexes = [] var index = 0 + var sensitiveIndexes = [] + const params = action.action.parameters for (let key in params) { const param = params[key] @@ -732,9 +763,34 @@ const ConfigureWorkflow = (props) => { : null} + {opened ?
- {authFields.map((field, index) => { + + {action.app.authentication.type === "oauth2-app" || action.app.authentication.type === "oauth2" || action.auth_type === "oauth2" ? +
+ +
+ : + authFields.map((field, index) => { + console.log("THESE FIELDS?: ", field) + var parsedName = field.key // Remove _basic at the end if it exists if (parsedName.toLowerCase().endsWith("_basic")) { @@ -807,25 +863,28 @@ const ConfigureWorkflow = (props) => {
) })} - + {action.app.authentication.type !== "oauth2-app" && action.app.authentication.type !== "oauth2" ? + + : null} : null} @@ -1237,19 +1296,22 @@ const ConfigureWorkflow = (props) => { return (
-
+
{setConfigureWorkflowModalOpen !== undefined ? - {workflow.name} + + {workflow.name} + : null } - - Please configure the following apps for automatic startup of automation: - {requiredActions.length > 0 ? ( + + Please configure the following steps to help us complete your workflow. This can also be done later. + + {setConfigureWorkflowModalOpen !== undefined ? Required Actions @@ -1258,6 +1320,11 @@ const ConfigureWorkflow = (props) => { {requiredActions.map((data, index) => { + + // AppWrapper = Default in a workflow, only shows with steps + // AppSection = + // AppSectionSelfcontained = default for template generator + return (
{data.steps !== undefined && data.steps !== null && data.show_steps === true && setConfigureWorkflowModalOpen !== undefined ? diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index e9891240..277b1a40 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -615,7 +615,7 @@ const Header = (props) => { - Version: 1.3.2 + Version: 1.3.3 diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index d9b58330..c76a6b59 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -97,6 +97,8 @@ const AuthenticationOauth2 = (props) => { autoAuth, authButtonOnly, isLoggedIn, + + setFinalized, } = props; let navigate = useNavigate(); @@ -489,20 +491,27 @@ const AuthenticationOauth2 = (props) => { var open = true; const timer = setInterval(() => { if (newwin.closed) { - console.log("Closing?") - + console.log("Closing?") setButtonClicked(false); clearInterval(timer); //alert('"Secure Payment" window closed!'); - // - if (getAppAuthentication !== undefined) { - getAppAuthentication(true, true, true); - } + if (getAppAuthentication !== undefined) { + getAppAuthentication(true, true, true); + } + + // This is more a guess than anything + // Should be handled in getAppAuthentication() + // in the parent component to make it accurate, + // seeing as we don't know what the parent component + // wants to happen + if (setFinalized !== undefined) { + setFinalized(true) + } } else { - console.log("Not closed") - } + console.log("Not closed") + } }, 1000); //do { // setTimeout(() => { diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 66c159a0..aef01467 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -3373,14 +3373,16 @@ const ParsedAction = (props) => { variant="outlined" color="secondary" /> - : null} - - {data.label} + : null} + {data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ? + + : null} + {data.label} ); })} diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 39b1a26c..c1926c72 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -47,6 +47,7 @@ import ReactJson from "react-json-view"; import PaperComponent from "../components/PaperComponent.jsx"; import CodeMirror from '@uiw/react-codemirror'; +import { python } from '@codemirror/lang-python'; //import 'codemirror/keymap/sublime'; //import 'codemirror/addon/selection/mark-selection.js' //import 'codemirror/theme/gruvbox-dark.css'; @@ -212,6 +213,80 @@ const CodeEditor = (props) => { expectedOutput(localcodedata) }, []) + useEffect(() => { + expectedOutput(localcodedata) + }, [availableVariables]) + + var to_be_copied = ""; + const HandleJsonCopy = (base, copy, base_node_name) => { + if (typeof copy.name === "string") { + copy.name = copy.name.replaceAll(" ", "_"); + } + + //lol + if (typeof base === 'object' || typeof base === 'dict') { + base = JSON.stringify(base) + } + + if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") { + base_node_name = "exec" + } + + console.log("COPY: ", base_node_name, copy); + + //var newitem = JSON.parse(base); + var newitem = validateJson(base).result + + + to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); + for (let copykey in copy.namespace) { + if (copy.namespace[copykey].includes("Results for")) { + continue; + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.namespace[copykey]]; + if (!isNaN(copy.namespace[copykey])) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.namespace[copykey]; + } + } + } + + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.name]; + if (!isNaN(copy.name)) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.name; + } + } + + to_be_copied.replaceAll(" ", "_"); + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } + + navigator.clipboard.writeText(to_be_copied); + copyText.select(); + copyText.setSelectionRange(0, 99999); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + console.log("COPYING!"); + toast("Copied JSON path to clipboard.") + } else { + console.log("Couldn't find element ", elementName); + } + } + const aiSubmit = (value, inputAction) => { if (value === undefined || value === "") { console.log("No value input!") @@ -938,10 +1013,10 @@ const CodeEditor = (props) => { style: { zIndex: 12501, color: "white", - minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200, - maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200, - minHeight: isMobile ? "100%" : 720, - maxHeight: isMobile ? "100%" : 720, + minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100, + maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100, + minHeight: isMobile ? "100%" : 620, + maxHeight: isMobile ? "100%" : 620, border: theme.palette.defaultBorder, padding: isMobile ? "25px 10px 25px 10px" : 25, }, @@ -951,8 +1026,8 @@ const CodeEditor = (props) => { style={{ zIndex: 5000, position: "absolute", - top: 14, - right: 18, + top: 6, + right: 6, color: "grey", }} onClick={() => { @@ -1425,22 +1500,26 @@ const CodeEditor = (props) => { borderRadius: theme.palette.borderRadius, position: "relative", paddingTop: 0, - minHeight: 548, - overflow: "hidden", + // minHeight: 548, + // overflow: "hidden", }}> { console.log("CURSOR: ", value.getCursor()) @@ -1463,8 +1542,9 @@ const CodeEditor = (props) => { }} options={{ mode: validation === true ? "json" : "python", - lineWrapping: linewrap, + lineWrapping: true, theme: vscodeDark, + lineNumbers: true, }} />
@@ -1604,7 +1684,7 @@ const CodeEditor = (props) => { color="primary" style={{ border: `1px solid ${theme.palette.primary.main}`, - marginLeft: 200, + marginLeft: 175, maxHeight: 35, minWidth: 70, }} @@ -1633,8 +1713,8 @@ const CodeEditor = (props) => { borderRadius: 5, border: `2px solid ${theme.palette.inputColor}`, padding: 10, - maxHeight: 500, - minheight: 500, + maxHeight: 450, + minheight: 450, overflow: "auto", }} collapsed={false} @@ -1643,7 +1723,12 @@ const CodeEditor = (props) => { }} displayDataTypes={false} onSelect={(select) => { - //HandleJsonCopy(validate.result, select, "exec"); + var basename = "exec" + if (selectedAction !== undefined && selectedAction !== null && Object.keys(selectedAction).length !== 0) { + basename = selectedAction.label.toLowerCase().replaceAll(" ", "_") + } + + HandleJsonCopy(expOutput, select, basename) }} name={"JSON autocompletion"} /> @@ -1659,10 +1744,10 @@ const CodeEditor = (props) => { marginTop: -2, border: `2px solid ${theme.palette.inputColor}`, borderRadius: theme.palette.borderRadius, - maxHeight: 500, - minHeight: 500, - minWidth: 580, - maxWidth: 580, + maxHeight: 450, + minHeight: 450, + minWidth: 480, + maxWidth: 480, overflow: "auto", whiteSpace: "pre-wrap", }} @@ -1689,7 +1774,7 @@ const CodeEditor = (props) => { }} displayDataTypes={false} onSelect={(select) => { - //HandleJsonCopy(validate.result, select, "exec"); + //HandleJsonCopy(executionResult.result, select, "exec"); }} name={"Test result"} /> @@ -1725,7 +1810,7 @@ const CodeEditor = (props) => {
-
+
- : null} + : null*/} ) @@ -537,6 +649,8 @@ const WorkflowTemplatePopup = (props) => { : ""}
+ +
) } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 0a2220cc..969a30c4 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1408,13 +1408,20 @@ const AngularWorkflow = (defaultprops) => { const streamUrl = "https://stream.shuffler.io" const url = `${streamUrl}/api/v1/workflows/${props.match.params.key}/stream` + var parsedbody = body + try { + parsedbody = JSON.stringify(body) + } catch (e) { + console.log("Error parsing body for stream: ", e) + } + fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, - body: JSON.stringify(body), + body: parsedbody, credentials: "include", }) .then((response) => { @@ -2630,7 +2637,6 @@ const AngularWorkflow = (defaultprops) => { const appendChunks = (result) => { var chunk = decoder.decode(result.value || new Uint8Array, {stream: !result.done}); - console.log("Got chunk: ", chunk) if (chunk === undefined || chunk === null) { console.log("Chunk is undefined or null") @@ -2777,8 +2783,11 @@ const AngularWorkflow = (defaultprops) => { if (response.status >= 500) { toast("Something went wrong while loading the workflow. Please reload.") } else { - toast("You don't access to this workflow or loading failed.") - window.location.pathname = "/workflows"; + toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..") + + setTimeout(() => { + window.location.pathname = "/workflows"; + }, 2000); } } diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 33184cc0..71625903 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -916,11 +916,12 @@ const AppCreator = (defaultprops) => { var categoryindex = -1; // Stupid way of finding a category/grouping for (let splitkey in pathsplit) { - if (pathsplit[splitkey].includes("_shuffle_replace_")) { - const regex = /_shuffle_replace_\d/i; - //console.log("NEW: ", - pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "") - } + if (pathsplit[splitkey].includes("_shuffle_replace_")) { + //const regex = /_shuffle_replace_\d/i; + const regex = /_shuffle_replace_\d+/i + //console.log("NEW: ", + pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "") + } if ( pathsplit[splitkey].length > 0 && @@ -2016,9 +2017,10 @@ const AppCreator = (defaultprops) => { var pathjoin = item.url+"_"+item.method.toLowerCase() if (handledPaths.includes(pathjoin)) { - // Max 100 of same lol - for (let i = 0; i < 100; i++) { - item.url = item.url+"_shuffle_replace_"+i + // Max 1000 of same. Will it be ok for graphql longterm? + const baseurl = item.url + for (let i = 0; i < 1000; i++) { + item.url = baseurl+"_shuffle_replace_"+i pathjoin = item.url+"_"+item.method.toLowerCase() if (handledPaths.includes(pathjoin)) { @@ -2596,9 +2598,16 @@ const AppCreator = (defaultprops) => { }) .then((responseJson) => { if (!responseJson.success) { + if (responseJson.extra !== undefined && responseJson.extra !== null) { + toast("Failed building: " + responseJson.extra); + } + if (responseJson.reason !== undefined) { setErrorCode(responseJson.reason); - toast("Failed to verify: " + responseJson.reason); + + if (responseJson.extra === undefined && responseJson.extra === null) { + toast("Failed to verify: " + responseJson.reason); + } } } else { toast("Successfully uploaded openapi"); @@ -3401,13 +3410,13 @@ const AppCreator = (defaultprops) => { }} label={parsedChip} onClick={() => { - if (chipRequired) { - currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1) - } else { - currentAction["required_bodyfields"].push(chipData) - } + if (chipRequired) { + currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1) + } else { + currentAction["required_bodyfields"].push(chipData) + } - setCurrentAction(currentAction); + setCurrentAction(currentAction); setChipRequired(!chipRequired); }} /> @@ -3729,27 +3738,6 @@ const AppCreator = (defaultprops) => { onClose={() => { console.log("Closing modal"); - // Old: This had some issue with arrays - //setUrlPath(""); - //setCurrentAction({ - // name: "", - // description: "", - // url: "", - // file_field: "", - // headers: "", - // paths: [], - // queries: [], - // body: "", - // errors: [], - // method: actionNonBodyRequest[0], - // action_label: "No Label", - // required_bodyfields: [], - //}); - //setCurrentActionMethod(apikeySelection[0]); - //setUrlPathQueries([]); - //setActionsModalOpen(false); - //setFileUploadEnabled(false); - console.log(currentAction); const errors = getActionErrors(); addActionToView(errors); @@ -3789,7 +3777,8 @@ const AppCreator = (defaultprops) => { variant="outlined" defaultValue={currentAction["name"]} onChange={(e) => { - setActionField("name", e.target.value); + var trimmed = e.target.value.trim(); + setActionField("name", trimmed) }} onBlur={(e) => { // Fix basic issues in frontend. Python functions run a-zA-Z0-9_ @@ -3798,6 +3787,18 @@ const AppCreator = (defaultprops) => { if (found !== null) { setActionField("name", found.join("")); } + + // Look through all actions and see if there is one with the same name + if (currentAction.url === "" && actions !== undefined && actions !== null && actions.length > 0) { + for (var i = 0; i < actions.length; i++) { + if (actions[i].name.toLowerCase() === e.target.value.toLowerCase()) { + toast("Action with name " + e.target.value + " already exists. If you keep this, it will be overwritten.") + break + } + } + + } + }} key={currentAction} InputProps={{ @@ -4283,6 +4284,7 @@ const AppCreator = (defaultprops) => { > Submit + {/* + */}
@@ -4501,7 +4504,7 @@ const AppCreator = (defaultprops) => { style={{ borderRadius: 0, position: "absolute", top: 70, }} variant={actions.length === 0 ? "contained" : "outlined"} onClick={(e) => { - e.preventDefault(); + e.preventDefault(); setCurrentActionMethod(actionNonBodyRequest[0]); setCurrentAction({ @@ -4515,8 +4518,8 @@ const AppCreator = (defaultprops) => { body: "", errors: [], method: actionNonBodyRequest[0], - action_label: "No Label", - required_bodyfields: [], + action_label: "No Label", + required_bodyfields: [], }); setActionsModalOpen(true); }} @@ -4533,20 +4536,20 @@ const AppCreator = (defaultprops) => { //console.log("Actions: ", filteredActions) if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) { - return null - } + return null + } - return ( + return (
{filteredActions.slice(0, actionAmount).map((data, index) => { //console.log("Found action: ", data) - return ( - - ) - })} -
- ) - } + return ( + + ) + })} + + ) + } diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index ef7f3bad..37bcec0a 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -523,6 +523,12 @@ const Docs = (defaultprops) => { ); } + if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { + if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { + extraInfo = "" + } + } + return ( { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 73f02cc2..001fdd89 100755 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -2199,15 +2199,15 @@ const Workflows = (props) => { // Can create and set workflows const setNewWorkflow = ( - name, - description, - tags, - defaultReturnValue, - editingWorkflow, - redirect, - currentUsecases, - inputblogpost, - inputstatus, + name, + description, + tags, + defaultReturnValue, + editingWorkflow, + redirect, + currentUsecases, + inputblogpost, + inputstatus, ) => { var method = "POST"; var extraData = ""; @@ -2292,7 +2292,7 @@ const Workflows = (props) => { }) .catch((error) => { toast(error.toString()); - setSubmitLoading(false) + setSubmitLoading(false) setModalOpen(false); setSubmitLoading(false); });