Made it so wait for results does what it should for loops on cloud

This commit is contained in:
Frikky
2024-01-25 14:56:52 +01:00
parent 7a6970e3d6
commit 7a09037604
14 changed files with 550 additions and 227 deletions
+7 -3
View File
@@ -634,8 +634,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
} }
newUser := users[0] newUser := users[0]
if !shuffle.ArrayContains(newUser.Orgs, currentOrg.Id) {
if !strings.Contains(newUser.Orgs[0], currentOrg.Id) {
newUser.Orgs = append(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 newUser.ActiveOrg = currentOrg
} }
err = shuffle.SetUser(ctx, &newUser, false) err = shuffle.SetUser(ctx, &newUser, true)
if err != nil { if err != nil {
log.Printf("[WARNING] Failed updating the user %s: %s", data.Username, err) log.Printf("[WARNING] Failed updating the user %s: %s", data.Username, err)
resp.WriteHeader(400) resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return 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 { } else {
log.Printf("[WARNING] Failed registering user: %s", err) log.Printf("[WARNING] Failed registering user: %s", err)
resp.WriteHeader(401) resp.WriteHeader(401)
+66 -46
View File
@@ -1,54 +1,64 @@
{ {
"name": "shuffler", "name": "shuffler",
"homepage": "https://shuffler.io", "homepage": "https://shuffler.io",
"version": "1.4.0", "version": "1.3.3",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@codemirror/commands": "^6.2.4", "@codemirror/commands": "^6.2.4",
"@emotion/is-prop-valid": "^1.1.1", "@codemirror/lang-python": "^6.1.3",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.6.0",
"@lezer/highlight": "^1.1.3", "@lezer/highlight": "^1.1.3",
"@metamask/detect-provider": "^1.2.0",
"@mui/icons-material": "^5.14.0", "@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0", "@mui/material": "^5.14.0",
"@mui/styles": "^5.14.0", "@mui/styles": "^5.14.0",
"@mui/x-data-grid": "^5.17.11", "@mui/x-data-grid": "^5.17.11",
"@mui/x-date-pickers": "^6.11.1", "@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-theme-vscode": "^4.21.20",
"@uiw/codemirror-themes": "^4.21.9", "@uiw/codemirror-themes": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9", "@uiw/react-codemirror": "^4.21.21",
"@use-it/interval": "^1.0.0", "@use-it/interval": "^0.1.3",
"algoliasearch": "^4.13.1", "algoliasearch": "^4.8.3",
"calculate-size": "^1.1.1", "class-transformer": "^0.2.0",
"class-transformer": "^0.4.0", "codemirror": "^6.0.1",
"cpx": "^1.5.0",
"create-react-app": "^5.0.1", "create-react-app": "^5.0.1",
"cytoscape": "^3.15.1", "cytoscape": "^3.23.0",
"cytoscape-edgehandles": "^3.6.0", "cytoscape-clipboard": "^2.2.1",
"cytoscape-node-html-label": "^1.1.5", "cytoscape-cxtmenu": "^3.4.0",
"d3": "^7.1.1", "cytoscape-edgehandles": "^3.5.1",
"dayjs": "^1.11.9", "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", "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", "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", "import": "0.0.6",
"interweave": "^11.2.0", "interweave": "^11.2.0",
"jss": "^10.10.0", "is-plain-obj": "^4.1.0",
"jss-camel-case": "^6.1.0", "json-bigint": "^1.0.0",
"jss-default-unit": "^8.0.2", "match-sorter": "^6.3.1",
"jss-global": "^3.0.0",
"jss-nested": "^6.0.1",
"jss-props-sort": "^6.0.0",
"jss-vendor-prefixer": "^8.0.1",
"md5-file": "^4.0.0", "md5-file": "^4.0.0",
"mdbreact": "^4.21.1", "mdbreact": "^4.21.1",
"mime": "^3.0.0", "moment": "~2.29.4",
"moment": "^2.29.1",
"mui-chips-input": "^2.1.3", "mui-chips-input": "^2.1.3",
"mui-nested-menu": "^3.2.1", "mui-nested-menu": "^3.2.1",
"process": "^0.11.10",
"react": "^18.2.0", "react": "^18.2.0",
"react-alert": "^7.0.3",
"react-alert-template-basic": "^1.0.0",
"react-alice-carousel": "^2.6.4", "react-alice-carousel": "^2.6.4",
"react-avatar-editor": "^11.1.0", "react-avatar-editor": "^11.1.0",
"react-beforeunload": "^2.2.1", "react-beforeunload": "^2.2.1",
@@ -57,31 +67,40 @@
"react-cytoscapejs": "^2.0.0", "react-cytoscapejs": "^2.0.0",
"react-device-detect": "^2.2.3", "react-device-detect": "^2.2.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-draggable": "^3.3.2", "react-draggable": "^4.4.5",
"react-driftjs": "^1.2.2", "react-driftjs": "^1.2.2",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-ga4": "^2.0.0", "react-ga4": "^2.0.0",
"react-hotkeys": "^2.0.0", "react-hotkeys": "^2.0.0",
"react-i18next": "^13.1.2",
"react-instantsearch-dom": "^6.28.0", "react-instantsearch-dom": "^6.28.0",
"react-json-pretty": "^2.2.0", "react-json-pretty": "^2.2.0",
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-json-view-ssr": "^1.19.1",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-markdown-github": "^3.3.1", "react-markdown-github": "^3.3.1",
"react-powerhooks": "^0.0.7", "react-powerhooks": "^0.0.7",
"react-router": "^6.14.1", "react-router": "^6.14.1",
"react-router-dom": "^6.14.1", "react-router-dom": "^6.14.1",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-social-icons": "^5.15.0",
"react-stripe-elements": "^6.1.2",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"reaviz": "^14.9.4", "reaviz": "^14.9.7",
"remark-gfm": "^3.0.1", "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", "search-insights": "^2.2.1",
"shellwords": "^0.1.1", "shellwords": "^1.0.1",
"simplebar": "^4.2.3", "simplebar": "^4.2.3",
"styled-components": "^4.4.0", "styled-components": "^4.4.1",
"webpack": "^5.88.2", "sync-fetch": "^0.3.0",
"yaml": "^1.7.2", "terser-webpack-plugin": "^4.2.3",
"yaml": "^1.10.0",
"yamljs": "^0.3.0", "yamljs": "^0.3.0",
"zone.js": "^0.13.1" "zone.js": "~0.8.26"
}, },
"scripts": { "scripts": {
"start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start", "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'" "lint_file": "eslint 'src/views/AngularWorkflow.jsx'"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app", "extends": "react-app"
"rules": {
"jsx-a11y/img-redundant-alt": "off",
"no-redeclare": "off",
"no-loop-func": "off"
}
}, },
"browserslist": [ "browserslist": [
">0.2%", ">0.2%",
@@ -106,13 +120,19 @@
"not op_mini all" "not op_mini all"
], ],
"devDependencies": { "devDependencies": {
"@babel/core": "^7.15.8", "@babel/cli": "^7.22.10",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/core": "^7.22.10",
"babel-eslint": "^10.1.0", "@babel/plugin-proposal-export-default-from": "^7.22.5",
"prettier": "2.4.1", "@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", "promise-window": "^1.2.1",
"react-16": "npm:react@16.13.1", "react-hot-loader": "^4.13.0",
"react-dom-16": "npm:react-dom@16.13.1", "webpack-cli": "^5.1.4"
"react-error-overlay": "6.0.9"
} }
} }
+1 -8
View File
@@ -37,7 +37,6 @@ import UpdateAuthentication from "./views/UpdateAuthentication.jsx";
import FrameworkWrapper from "./views/FrameworkWrapper.jsx"; import FrameworkWrapper from "./views/FrameworkWrapper.jsx";
import ScrollToTop from "./components/ScrollToTop"; import ScrollToTop from "./components/ScrollToTop";
import AlertTemplate from "./components/AlertTemplate"; import AlertTemplate from "./components/AlertTemplate";
import { useAlert, positions, Provider } from "react-alert";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import RuntimeDebugger from "./components/RuntimeDebugger.jsx" 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) // 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) => { const handleFirstInteraction = (event) => {
console.log("First interaction: ", event) console.log("First interaction: ", event)
@@ -566,9 +561,7 @@ const App = (message, props) => {
<CssBaseline /> <CssBaseline />
<CookiesProvider> <CookiesProvider>
<BrowserRouter> <BrowserRouter>
<Provider template={AlertTemplate} {...options}> {includedData}
{includedData}
</Provider>
</BrowserRouter> </BrowserRouter>
<ToastContainer <ToastContainer
position="bottom-center" position="bottom-center"
+13 -2
View File
@@ -150,11 +150,22 @@ const CacheView = (props) => {
const deleteCache = (orgId, key) => { const deleteCache = (orgId, key) => {
toast("Attempting to delete Cache"); 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: { headers: {
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(parsed),
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
+126 -59
View File
@@ -32,6 +32,8 @@ import {
import { FixName } from "../views/Apps.jsx"; import { FixName } from "../views/Apps.jsx";
import aa from 'search-insights' import aa from 'search-insights'
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
// Handles workflow updates on first open to highlight the issues of the workflow // Handles workflow updates on first open to highlight the issues of the workflow
// Variables // Variables
// Action (exists, missing fields) // Action (exists, missing fields)
@@ -60,6 +62,8 @@ const ConfigureWorkflow = (props) => {
setAuthenticationType, setAuthenticationType,
setAuthenticationModalOpen, setAuthenticationModalOpen,
setConfigureWorkflowModalOpen, setConfigureWorkflowModalOpen,
setConfigurationFinished,
} = props; } = props;
const [requiredActions, setRequiredActions] = React.useState([]); const [requiredActions, setRequiredActions] = React.useState([]);
@@ -70,9 +74,19 @@ const ConfigureWorkflow = (props) => {
const [firstLoad, setFirstLoad] = React.useState(""); const [firstLoad, setFirstLoad] = React.useState("");
const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false); const [showFinalizeAnimation, setShowFinalizeAnimation] = React.useState(false);
const [loopRunning, setLoopRunning] = useState(false) const [loopRunning, setLoopRunning] = useState(false)
const [checkStarted, setCheckStarted] = React.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 = () => { const stop = () => {
setLoopRunning(false) setLoopRunning(false)
} }
@@ -81,6 +95,7 @@ const ConfigureWorkflow = (props) => {
setLoopRunning(true) setLoopRunning(true)
} }
useEffect(() => { useEffect(() => {
if (loopRunning) { if (loopRunning) {
const intervalId = setInterval(() => { const intervalId = setInterval(() => {
@@ -159,7 +174,6 @@ const ConfigureWorkflow = (props) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
console.log("ACTION: ", responseJson);
if ( if (
responseJson.actions !== undefined && responseJson.actions !== undefined &&
responseJson.actions !== null responseJson.actions !== null
@@ -215,6 +229,9 @@ const ConfigureWorkflow = (props) => {
// without version match // without version match
const newappname = action.app_name.toLowerCase().replaceAll(" ", "_") const newappname = action.app_name.toLowerCase().replaceAll(" ", "_")
const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname) const app = apps.find((app) => app.id === action.app_id || app.name.toLowerCase().replaceAll(" ", "_") === newappname)
if (app === undefined || app === null) { if (app === undefined || app === null) {
const subapp = apps.find(app => app.name === action.app_name) const subapp = apps.find(app => app.name === action.app_name)
@@ -229,47 +246,51 @@ const ConfigureWorkflow = (props) => {
"required": true, "required": true,
}) })
} else { } else {
if (action.authentication_id === "" && app.authentication.required === true && action.parameters !== undefined && action.parameters !== null) { 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; var filled = true;
for (let [key,keyval] in Object.entries(action.parameters)) { for (let [key,keyval] in Object.entries(action.parameters)) {
if (action.parameters[key].configuration) { 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; filled = false;
break; break;
} }
} }
} }
if (app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app") {
filled = false
action.auth_type = "oauth2"
}
newaction.steps.push({ newaction.steps.push({
"title": "Authenticate app", "title": "Authenticate app",
"type": "authenticate", "type": "authenticate",
"required": true, "required": true,
"auth_type": app.authentication.type,
}) })
if (!filled) { if (!filled) {
newaction.must_authenticate = true; newaction.must_authenticate = true;
newaction.action_ids.push(action.id); 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; newaction.app = app;
} }
if (
action.errors !== undefined && if (newaction.errors !== undefined && newaction.errors !== null && newaction.errors.length > 0) {
action.errors !== null &&
action.errors.length > 0
) {
//console.log("Node has errors!: ", action.errors) //console.log("Node has errors!: ", action.errors)
} }
//console.log(newaction.app_name,"AUTH: ", newaction.must_authenticate, " ACTIVATE: ", newaction.must_activate)
if (newaction.must_authenticate) { if (newaction.must_authenticate) {
var authenticationOptions = []; var authenticationOptions = [];
for (let [key,keyval] in Object.entries(appAuthentication)) { for (let [key,keyval] in Object.entries(appAuthentication)) {
@@ -395,29 +416,29 @@ const ConfigureWorkflow = (props) => {
setRequiredActions(newactions); setRequiredActions(newactions);
} }
if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) { if (appAuthentication !== undefined && previousAuth !== undefined && appAuthentication.length !== previousAuth.length) {
var newactions = [] var newactions = []
for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) { for (let [actionkey, actionkeyval] in Object.entries(requiredActions)) {
var newaction = requiredActions[actionkey]; var newaction = requiredActions[actionkey];
const app = newaction.app; const app = newaction.app;
for (let [key,keyval] in Object.entries(appAuthentication)) { for (let [key,keyval] in Object.entries(appAuthentication)) {
const auth = appAuthentication[key]; const auth = appAuthentication[key];
// Does this account for all the different ones of the same? // Does this account for all the different ones of the same?
if (auth.app.name === app.name && auth.active === true) { if (auth.app.name === app.name && auth.active === true) {
newaction.auth_done = true; newaction.auth_done = true;
break; break;
}
} }
newactions.push(newaction);
} }
newactions.push(newaction); setRequiredActions(newactions);
} setPreviousAuth(appAuthentication);
// Set auth done to true
setRequiredActions(newactions); //"auth_done": false
setPreviousAuth(appAuthentication);
// Set auth done to true
//"auth_done": false
} }
const TriggerSection = (props) => { const TriggerSection = (props) => {
@@ -610,14 +631,24 @@ const ConfigureWorkflow = (props) => {
const [authFields, setAuthFields] = useState([]) const [authFields, setAuthFields] = useState([])
const [sensitiveFields, setSensitiveFields] = useState([]) const [sensitiveFields, setSensitiveFields] = useState([])
//console.log("ACTION", action)
useEffect(() => {
if (finalized === true) {
setOpened(false)
setFilled(true)
}
}, [finalized])
if (authFields.length === 0 && opened === true) { if (authFields.length === 0 && opened === true) {
// Loop through fields of the action // Loop through fields of the action
var newfields = [] var newfields = []
const params = action.action.parameters
var sensitiveIndexes = []
var index = 0 var index = 0
var sensitiveIndexes = []
const params = action.action.parameters
for (let key in params) { for (let key in params) {
const param = params[key] const param = params[key]
@@ -732,9 +763,34 @@ const ConfigureWorkflow = (props) => {
<CheckIcon style={{color: theme.palette.green, marginLeft: 10, marginTop: 10, flex: 1, }} /> <CheckIcon style={{color: theme.palette.green, marginLeft: 10, marginTop: 10, flex: 1, }} />
: null} : null}
</div> </div>
{opened ? {opened ?
<div style={{padding: 12, }}> <div style={{padding: 12, }}>
{authFields.map((field, index) => {
{action.app.authentication.type === "oauth2-app" || action.app.authentication.type === "oauth2" || action.auth_type === "oauth2" ?
<div>
<AuthenticationOauth2
selectedApp={action.app}
selectedAction={{
"app_name": action.app.name,
"app_id": action.app.id,
"app_version": action.app.version,
"large_image": action.app.large_image,
}}
authenticationType={action.app.authentication}
isCloud={isCloud}
authButtonOnly={true}
isLoggedIn={true}
getAppAuthentication={undefined}
setFinalized={setFinalized}
/>
</div>
:
authFields.map((field, index) => {
console.log("THESE FIELDS?: ", field)
var parsedName = field.key var parsedName = field.key
// Remove _basic at the end if it exists // Remove _basic at the end if it exists
if (parsedName.toLowerCase().endsWith("_basic")) { if (parsedName.toLowerCase().endsWith("_basic")) {
@@ -807,25 +863,28 @@ const ConfigureWorkflow = (props) => {
</div> </div>
) )
})} })}
<Button
variant="contained"
color="primary"
style={{
marginTop: 15,
width: 150,
marginLeft: 135,
}}
disabled={!filled || submitted}
onClick={() => {
setSubmitted(true);
// const submitLocalAuth = (app, fields) => { {action.app.authentication.type !== "oauth2-app" && action.app.authentication.type !== "oauth2" ?
submitLocalAuth({"id": action.app.id, "name": action.app.name, "version": action.app.version, "large_image": action.large_image, }, authFields); <Button
variant="contained"
}} color="primary"
> style={{
{submitted ? <CircularProgress style={{color: theme.palette.primary.main, }} /> : "Submit"} marginTop: 15,
</Button> width: 150,
marginLeft: 135,
}}
disabled={!filled || submitted}
onClick={() => {
setSubmitted(true);
// const submitLocalAuth = (app, fields) => {
submitLocalAuth({"id": action.app.id, "name": action.app.name, "version": action.app.version, "large_image": action.large_image, }, authFields);
}}
>
{submitted ? <CircularProgress style={{color: theme.palette.primary.main, }} /> : "Submit"}
</Button>
: null}
</div> </div>
: null} : null}
</div> </div>
@@ -1237,19 +1296,22 @@ const ConfigureWorkflow = (props) => {
return ( return (
<div> <div>
<div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px", maxHeight: 475, }}> <div style={{margin: setConfigureWorkflowModalOpen !== undefined ? "0px 50px 0px 50px" : "35px 0px 0px 0px", maxHeight: 475, }}>
{setConfigureWorkflowModalOpen !== undefined ? {setConfigureWorkflowModalOpen !== undefined ?
<Typography variant="h6">{workflow.name}</Typography> <Typography variant="h6">
{workflow.name}
</Typography>
: null : null
} }
<Typography variant="body2" style={{}}>
Please configure the following apps for automatic startup of automation:
</Typography>
{requiredActions.length > 0 ? ( {requiredActions.length > 0 ? (
<span> <span>
<Typography variant="body2" style={{}}>
Please configure the following steps to help us complete your workflow. This can also be done later.
</Typography>
{setConfigureWorkflowModalOpen !== undefined ? {setConfigureWorkflowModalOpen !== undefined ?
<Typography variant="body1" style={{ marginTop: 10, }}> <Typography variant="body1" style={{ marginTop: 10, }}>
Required Actions Required Actions
@@ -1258,6 +1320,11 @@ const ConfigureWorkflow = (props) => {
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}> <List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}>
{requiredActions.map((data, index) => { {requiredActions.map((data, index) => {
// AppWrapper = Default in a workflow, only shows with steps
// AppSection =
// AppSectionSelfcontained = default for template generator
return ( return (
<div key={index} style={{marginBottom: 10, }}> <div key={index} style={{marginBottom: 10, }}>
{data.steps !== undefined && data.steps !== null && data.show_steps === true && setConfigureWorkflowModalOpen !== undefined ? {data.steps !== undefined && data.steps !== null && data.show_steps === true && setConfigureWorkflowModalOpen !== undefined ?
+1 -1
View File
@@ -615,7 +615,7 @@ const Header = (props) => {
<Divider style={{marginBottom: 10, }}/> <Divider style={{marginBottom: 10, }}/>
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 5, marginBottom: 5,}}> <Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 5, marginBottom: 5,}}>
Version: 1.3.2 Version: 1.3.3
</Typography> </Typography>
</Menu> </Menu>
</span> </span>
+17 -8
View File
@@ -97,6 +97,8 @@ const AuthenticationOauth2 = (props) => {
autoAuth, autoAuth,
authButtonOnly, authButtonOnly,
isLoggedIn, isLoggedIn,
setFinalized,
} = props; } = props;
let navigate = useNavigate(); let navigate = useNavigate();
@@ -489,20 +491,27 @@ const AuthenticationOauth2 = (props) => {
var open = true; var open = true;
const timer = setInterval(() => { const timer = setInterval(() => {
if (newwin.closed) { if (newwin.closed) {
console.log("Closing?") console.log("Closing?")
setButtonClicked(false); setButtonClicked(false);
clearInterval(timer); clearInterval(timer);
//alert('"Secure Payment" window closed!'); //alert('"Secure Payment" window closed!');
//
if (getAppAuthentication !== undefined) { if (getAppAuthentication !== undefined) {
getAppAuthentication(true, true, true); 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 { } else {
console.log("Not closed") console.log("Not closed")
} }
}, 1000); }, 1000);
//do { //do {
// setTimeout(() => { // setTimeout(() => {
+10 -8
View File
@@ -3373,14 +3373,16 @@ const ParsedAction = (props) => {
variant="outlined" variant="outlined"
color="secondary" color="secondary"
/> />
: null} : null}
<Chip {data.app.app_version !== undefined && data.app.app_version !== null && data.app.app_version !== "" && data.app.app_version !== "undefined" ?
style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}} <Chip
label={data.app.app_version} style={{marginLeft: 0, padding: 0, marginRight: 10, cursor: "pointer",}}
variant="outlined" label={data.app.app_version}
color="secondary" variant="outlined"
/> color="secondary"
{data.label} />
: null}
{data.label}
</MenuItem> </MenuItem>
); );
})} })}
+108 -23
View File
@@ -47,6 +47,7 @@ import ReactJson from "react-json-view";
import PaperComponent from "../components/PaperComponent.jsx"; import PaperComponent from "../components/PaperComponent.jsx";
import CodeMirror from '@uiw/react-codemirror'; import CodeMirror from '@uiw/react-codemirror';
import { python } from '@codemirror/lang-python';
//import 'codemirror/keymap/sublime'; //import 'codemirror/keymap/sublime';
//import 'codemirror/addon/selection/mark-selection.js' //import 'codemirror/addon/selection/mark-selection.js'
//import 'codemirror/theme/gruvbox-dark.css'; //import 'codemirror/theme/gruvbox-dark.css';
@@ -212,6 +213,80 @@ const CodeEditor = (props) => {
expectedOutput(localcodedata) 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) => { const aiSubmit = (value, inputAction) => {
if (value === undefined || value === "") { if (value === undefined || value === "") {
console.log("No value input!") console.log("No value input!")
@@ -938,10 +1013,10 @@ const CodeEditor = (props) => {
style: { style: {
zIndex: 12501, zIndex: 12501,
color: "white", color: "white",
minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200, minWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100,
maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1200, maxWidth: isMobile ? "100%" : isFileEditor ? 650 : 1100,
minHeight: isMobile ? "100%" : 720, minHeight: isMobile ? "100%" : 620,
maxHeight: isMobile ? "100%" : 720, maxHeight: isMobile ? "100%" : 620,
border: theme.palette.defaultBorder, border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25, padding: isMobile ? "25px 10px 25px 10px" : 25,
}, },
@@ -951,8 +1026,8 @@ const CodeEditor = (props) => {
style={{ style={{
zIndex: 5000, zIndex: 5000,
position: "absolute", position: "absolute",
top: 14, top: 6,
right: 18, right: 6,
color: "grey", color: "grey",
}} }}
onClick={() => { onClick={() => {
@@ -1425,22 +1500,26 @@ const CodeEditor = (props) => {
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
position: "relative", position: "relative",
paddingTop: 0, paddingTop: 0,
minHeight: 548, // minHeight: 548,
overflow: "hidden", // overflow: "hidden",
}}> }}>
<CodeMirror <CodeMirror
theme={vscodeDark} theme={vscodeDark}
value={localcodedata} value={localcodedata}
height={isFileEditor ? 450 : 500} extensions={[python({ py: true })]}
height={isFileEditor ? 450 : 450}
width={isFileEditor ? 650 : 600} width={isFileEditor ? 650 : 600}
style={{ style={{
maxWidth: isFileEditor ? 450 : 600, maxWidth: isFileEditor ? 450 : 600,
maxHeight: 548, maxHeight: 450,
minHeight: 548, minHeight: 450,
wordBreak: "break-word", wordBreak: "break-word",
marginTop: 0, marginTop: 0,
paddingBottom: 10, paddingBottom: 10,
overflow: "hidden", // overflow: "hidden",
overflowY: "auto",
whiteSpace: "pre-wrap",
wordWrap: "break-word",
}} }}
onCursorActivity = {(value) => { onCursorActivity = {(value) => {
console.log("CURSOR: ", value.getCursor()) console.log("CURSOR: ", value.getCursor())
@@ -1463,8 +1542,9 @@ const CodeEditor = (props) => {
}} }}
options={{ options={{
mode: validation === true ? "json" : "python", mode: validation === true ? "json" : "python",
lineWrapping: linewrap, lineWrapping: true,
theme: vscodeDark, theme: vscodeDark,
lineNumbers: true,
}} }}
/> />
</div> </div>
@@ -1604,7 +1684,7 @@ const CodeEditor = (props) => {
color="primary" color="primary"
style={{ style={{
border: `1px solid ${theme.palette.primary.main}`, border: `1px solid ${theme.palette.primary.main}`,
marginLeft: 200, marginLeft: 175,
maxHeight: 35, maxHeight: 35,
minWidth: 70, minWidth: 70,
}} }}
@@ -1633,8 +1713,8 @@ const CodeEditor = (props) => {
borderRadius: 5, borderRadius: 5,
border: `2px solid ${theme.palette.inputColor}`, border: `2px solid ${theme.palette.inputColor}`,
padding: 10, padding: 10,
maxHeight: 500, maxHeight: 450,
minheight: 500, minheight: 450,
overflow: "auto", overflow: "auto",
}} }}
collapsed={false} collapsed={false}
@@ -1643,7 +1723,12 @@ const CodeEditor = (props) => {
}} }}
displayDataTypes={false} displayDataTypes={false}
onSelect={(select) => { 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"} name={"JSON autocompletion"}
/> />
@@ -1659,10 +1744,10 @@ const CodeEditor = (props) => {
marginTop: -2, marginTop: -2,
border: `2px solid ${theme.palette.inputColor}`, border: `2px solid ${theme.palette.inputColor}`,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette.borderRadius,
maxHeight: 500, maxHeight: 450,
minHeight: 500, minHeight: 450,
minWidth: 580, minWidth: 480,
maxWidth: 580, maxWidth: 480,
overflow: "auto", overflow: "auto",
whiteSpace: "pre-wrap", whiteSpace: "pre-wrap",
}} }}
@@ -1689,7 +1774,7 @@ const CodeEditor = (props) => {
}} }}
displayDataTypes={false} displayDataTypes={false}
onSelect={(select) => { onSelect={(select) => {
//HandleJsonCopy(validate.result, select, "exec"); //HandleJsonCopy(executionResult.result, select, "exec");
}} }}
name={"Test result"} name={"Test result"}
/> />
@@ -1725,7 +1810,7 @@ const CodeEditor = (props) => {
</div> </div>
<div style={{display: 'flex',}}> <div style={{display: 'flex'}}>
<Button <Button
style={{ style={{
height: 35, height: 35,
@@ -43,6 +43,8 @@ const WorkflowTemplatePopup = (props) => {
const [missingSource, setMissingSource] = React.useState(undefined) const [missingSource, setMissingSource] = React.useState(undefined)
const [missingDestination, setMissingDestination] = React.useState(undefined); const [missingDestination, setMissingDestination] = React.useState(undefined);
const [configurationFinished, setConfigurationFinished] = React.useState(false);
useEffect(() => { useEffect(() => {
}, [missingSource, missingDestination]) }, [missingSource, missingDestination])
@@ -175,6 +177,92 @@ const WorkflowTemplatePopup = (props) => {
}); });
} }
// Can create and set workflows
const reloadWorkflow = (workflow_id) => {
const new_url = `${globalUrl}/api/v1/workflows/${workflow_id}`
return fetch(new_url, {
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;
}
//setSubmitLoading(false);
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
if (responseJson.reason !== undefined) {
toast("Error setting workflow: ", responseJson.reason)
} else {
toast("Error setting workflow.")
}
return
} else if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") {
setWorkflow(responseJson)
}
return responseJson;
})
.catch((error) => {
toast("Failed reloading configured workflow: ", error.toString());
});
};
// Can create and set workflows
const saveWorkflow = (workflowdata) => {
const new_url = `${globalUrl}/api/v1/workflows?set_auth=true`
return fetch(new_url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(workflowdata),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!");
return;
}
//setSubmitLoading(false);
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
if (responseJson.reason !== undefined) {
toast("Error setting workflow: ", responseJson.reason)
} else {
toast("Error setting workflow.")
}
return
}
// In case it got a new id, this is to make sure it loads with the correct config
if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "") {
reloadWorkflow(responseJson.id)
}
return responseJson;
})
.catch((error) => {
toast("Failed generating workflow: ", error.toString());
});
};
const getGeneratedWorkflow = () => { const getGeneratedWorkflow = () => {
// POST // POST
@@ -221,8 +309,10 @@ const WorkflowTemplatePopup = (props) => {
}, },
} }
//fetch(globalUrl + "/api/v1/workflows/merge", { const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
fetch("https://shuffler.io/api/v1/workflows/merge", { //const url = `https://shuffler.io/api/v1/workflows/merge`
fetch(url, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -243,13 +333,21 @@ const WorkflowTemplatePopup = (props) => {
if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") { if (responseJson.id !== undefined && responseJson.id !== null && responseJson.id !== "" && responseJson.name !== undefined && responseJson.name !== null && responseJson.name !== "") {
console.log("Success in workflow template (prebuilt): ", responseJson); console.log("Success in workflow template (prebuilt): ", responseJson);
setWorkflow(responseJson) setWorkflow(responseJson)
// Sets it in the database properly
saveWorkflow(responseJson)
return return
} }
if (responseJson.success === false) { if (responseJson.success === false) {
//console.log("Error in workflow template: ", responseJson.error); //console.log("Error in workflow template: ", responseJson.error);
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.") const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance."
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
setErrorMessage(defaultMessage + "\n\n" + responseJson.reason)
} else {
setErrorMessage(defaultMessage)
}
setIsActive(true) setIsActive(true)
//setTimeout(() => { //setTimeout(() => {
@@ -419,8 +517,22 @@ const WorkflowTemplatePopup = (props) => {
appAuthentication={appAuthentication} appAuthentication={appAuthentication}
setAppAuthentication={setAppAuthentication} setAppAuthentication={setAppAuthentication}
apps={apps} apps={apps}
setConfigurationFinished={setConfigurationFinished}
/> />
{errorMessage === "" ?
{errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
<Tooltip title="Workflow generated!" placement="top">
<span style={{display: "flex", }}>
{/*<CheckIcon color="primary" sx={{ borderRadius: 4 }} /> */}
<Typography variant="h6" style={{ marginLeft: 20, }}>
Workflow generated!
</Typography>
</span>
</Tooltip>
: null}
{/*errorMessage === "" ?
<Button <Button
style={{marginTop: 50, }} style={{marginTop: 50, }}
variant={isFinished() ? "contained" : "outlined"} variant={isFinished() ? "contained" : "outlined"}
@@ -430,7 +542,7 @@ const WorkflowTemplatePopup = (props) => {
> >
Done Done
</Button> </Button>
: null} : null*/}
</DialogContent> </DialogContent>
</Drawer> </Drawer>
) )
@@ -537,6 +649,8 @@ const WorkflowTemplatePopup = (props) => {
: ""} : ""}
</div> </div>
</div> </div>
</div> </div>
) )
} }
+13 -4
View File
@@ -1408,13 +1408,20 @@ const AngularWorkflow = (defaultprops) => {
const streamUrl = "https://stream.shuffler.io" const streamUrl = "https://stream.shuffler.io"
const url = `${streamUrl}/api/v1/workflows/${props.match.params.key}/stream` 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, { fetch(url, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, },
body: JSON.stringify(body), body: parsedbody,
credentials: "include", credentials: "include",
}) })
.then((response) => { .then((response) => {
@@ -2630,7 +2637,6 @@ const AngularWorkflow = (defaultprops) => {
const appendChunks = (result) => { const appendChunks = (result) => {
var chunk = decoder.decode(result.value || new Uint8Array, {stream: !result.done}); var chunk = decoder.decode(result.value || new Uint8Array, {stream: !result.done});
console.log("Got chunk: ", chunk)
if (chunk === undefined || chunk === null) { if (chunk === undefined || chunk === null) {
console.log("Chunk is undefined or null") console.log("Chunk is undefined or null")
@@ -2777,8 +2783,11 @@ const AngularWorkflow = (defaultprops) => {
if (response.status >= 500) { if (response.status >= 500) {
toast("Something went wrong while loading the workflow. Please reload.") toast("Something went wrong while loading the workflow. Please reload.")
} else { } else {
toast("You don't access to this workflow or loading failed.") toast("You don't access to this workflow or loading failed. Redirecting to workflows in a few seconds..")
window.location.pathname = "/workflows";
setTimeout(() => {
window.location.pathname = "/workflows";
}, 2000);
} }
} }
+53 -50
View File
@@ -916,11 +916,12 @@ const AppCreator = (defaultprops) => {
var categoryindex = -1; var categoryindex = -1;
// Stupid way of finding a category/grouping // Stupid way of finding a category/grouping
for (let splitkey in pathsplit) { for (let splitkey in pathsplit) {
if (pathsplit[splitkey].includes("_shuffle_replace_")) { if (pathsplit[splitkey].includes("_shuffle_replace_")) {
const regex = /_shuffle_replace_\d/i; //const regex = /_shuffle_replace_\d/i;
//console.log("NEW: ", const regex = /_shuffle_replace_\d+/i
pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "") //console.log("NEW: ",
} pathsplit[splitkey] = pathsplit[splitkey].replaceAll(new RegExp(regex, 'g'), "")
}
if ( if (
pathsplit[splitkey].length > 0 && pathsplit[splitkey].length > 0 &&
@@ -2016,9 +2017,10 @@ const AppCreator = (defaultprops) => {
var pathjoin = item.url+"_"+item.method.toLowerCase() var pathjoin = item.url+"_"+item.method.toLowerCase()
if (handledPaths.includes(pathjoin)) { if (handledPaths.includes(pathjoin)) {
// Max 100 of same lol // Max 1000 of same. Will it be ok for graphql longterm?
for (let i = 0; i < 100; i++) { const baseurl = item.url
item.url = item.url+"_shuffle_replace_"+i for (let i = 0; i < 1000; i++) {
item.url = baseurl+"_shuffle_replace_"+i
pathjoin = item.url+"_"+item.method.toLowerCase() pathjoin = item.url+"_"+item.method.toLowerCase()
if (handledPaths.includes(pathjoin)) { if (handledPaths.includes(pathjoin)) {
@@ -2596,9 +2598,16 @@ const AppCreator = (defaultprops) => {
}) })
.then((responseJson) => { .then((responseJson) => {
if (!responseJson.success) { if (!responseJson.success) {
if (responseJson.extra !== undefined && responseJson.extra !== null) {
toast("Failed building: " + responseJson.extra);
}
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
setErrorCode(responseJson.reason); setErrorCode(responseJson.reason);
toast("Failed to verify: " + responseJson.reason);
if (responseJson.extra === undefined && responseJson.extra === null) {
toast("Failed to verify: " + responseJson.reason);
}
} }
} else { } else {
toast("Successfully uploaded openapi"); toast("Successfully uploaded openapi");
@@ -3401,13 +3410,13 @@ const AppCreator = (defaultprops) => {
}} }}
label={parsedChip} label={parsedChip}
onClick={() => { onClick={() => {
if (chipRequired) { if (chipRequired) {
currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1) currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1)
} else { } else {
currentAction["required_bodyfields"].push(chipData) currentAction["required_bodyfields"].push(chipData)
} }
setCurrentAction(currentAction); setCurrentAction(currentAction);
setChipRequired(!chipRequired); setChipRequired(!chipRequired);
}} }}
/> />
@@ -3729,27 +3738,6 @@ const AppCreator = (defaultprops) => {
onClose={() => { onClose={() => {
console.log("Closing modal"); 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); console.log(currentAction);
const errors = getActionErrors(); const errors = getActionErrors();
addActionToView(errors); addActionToView(errors);
@@ -3789,7 +3777,8 @@ const AppCreator = (defaultprops) => {
variant="outlined" variant="outlined"
defaultValue={currentAction["name"]} defaultValue={currentAction["name"]}
onChange={(e) => { onChange={(e) => {
setActionField("name", e.target.value); var trimmed = e.target.value.trim();
setActionField("name", trimmed)
}} }}
onBlur={(e) => { onBlur={(e) => {
// Fix basic issues in frontend. Python functions run a-zA-Z0-9_ // Fix basic issues in frontend. Python functions run a-zA-Z0-9_
@@ -3798,6 +3787,18 @@ const AppCreator = (defaultprops) => {
if (found !== null) { if (found !== null) {
setActionField("name", found.join("")); 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} key={currentAction}
InputProps={{ InputProps={{
@@ -4283,6 +4284,7 @@ const AppCreator = (defaultprops) => {
> >
Submit Submit
</Button> </Button>
{/*
<Button <Button
style={{ marginLeft: 10, }} style={{ marginLeft: 10, }}
onClick={() => { onClick={() => {
@@ -4291,6 +4293,7 @@ const AppCreator = (defaultprops) => {
> >
Cancel Cancel
</Button> </Button>
*/}
</div> </div>
</FormControl> </FormControl>
</Drawer> </Drawer>
@@ -4501,7 +4504,7 @@ const AppCreator = (defaultprops) => {
style={{ borderRadius: 0, position: "absolute", top: 70, }} style={{ borderRadius: 0, position: "absolute", top: 70, }}
variant={actions.length === 0 ? "contained" : "outlined"} variant={actions.length === 0 ? "contained" : "outlined"}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setCurrentActionMethod(actionNonBodyRequest[0]); setCurrentActionMethod(actionNonBodyRequest[0]);
setCurrentAction({ setCurrentAction({
@@ -4515,8 +4518,8 @@ const AppCreator = (defaultprops) => {
body: "", body: "",
errors: [], errors: [],
method: actionNonBodyRequest[0], method: actionNonBodyRequest[0],
action_label: "No Label", action_label: "No Label",
required_bodyfields: [], required_bodyfields: [],
}); });
setActionsModalOpen(true); setActionsModalOpen(true);
}} }}
@@ -4533,20 +4536,20 @@ const AppCreator = (defaultprops) => {
//console.log("Actions: ", filteredActions) //console.log("Actions: ", filteredActions)
if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) { if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) {
return null return null
} }
return ( return (
<div> <div>
{filteredActions.slice(0, actionAmount).map((data, index) => { {filteredActions.slice(0, actionAmount).map((data, index) => {
//console.log("Found action: ", data) //console.log("Found action: ", data)
return ( return (
<ActionPaper key={index} index={index} data={data} /> <ActionPaper key={index} index={index} data={data} />
) )
})} })}
</div> </div>
) )
} }
+6
View File
@@ -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 ( return (
<Typography <Typography
onMouseOver={() => { onMouseOver={() => {
+10 -10
View File
@@ -2199,15 +2199,15 @@ const Workflows = (props) => {
// Can create and set workflows // Can create and set workflows
const setNewWorkflow = ( const setNewWorkflow = (
name, name,
description, description,
tags, tags,
defaultReturnValue, defaultReturnValue,
editingWorkflow, editingWorkflow,
redirect, redirect,
currentUsecases, currentUsecases,
inputblogpost, inputblogpost,
inputstatus, inputstatus,
) => { ) => {
var method = "POST"; var method = "POST";
var extraData = ""; var extraData = "";
@@ -2292,7 +2292,7 @@ const Workflows = (props) => {
}) })
.catch((error) => { .catch((error) => {
toast(error.toString()); toast(error.toString());
setSubmitLoading(false) setSubmitLoading(false)
setModalOpen(false); setModalOpen(false);
setSubmitLoading(false); setSubmitLoading(false);
}); });