diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 4a7b7da4..42b3b324 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -71,6 +71,7 @@ const actionListStyle = { backgroundColor: inputColor, display: "flex", color: "white", + position: "relative", }; const boxStyle = { @@ -313,9 +314,7 @@ const AppCreator = (defaultprops) => { const [isAppLoaded, setIsAppLoaded] = useState(false); const [isEditing, setIsEditing] = useState(false); const [description, setDescription] = useState(""); - const [updater, setUpdater] = useState("tmp"); const [baseUrl, setBaseUrl] = useState(""); - const [actionsModalOpen, setActionsModalOpen] = useState(false); const [authenticationRequired, setAuthenticationRequired] = useState(false); const [authenticationOption, setAuthenticationOption] = useState( authenticationOptions[0] @@ -331,9 +330,6 @@ const AppCreator = (defaultprops) => { const [projectCategories, setProjectCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(""); - const [urlPath, setUrlPath] = useState(""); - //const [urlPathQueries, setUrlPathQueries] = useState([{"name": "test", "required": false}]); - const [urlPathQueries, setUrlPathQueries] = useState([]); const [update, setUpdate] = useState(""); const [urlPathParameters] = useState([]); const [basedata, setBasedata] = React.useState({}); @@ -341,8 +337,6 @@ const AppCreator = (defaultprops) => { const [filteredActions, setFilteredActions] = useState([]); const [errorCode, setErrorCode] = useState(""); const [appBuilding, setAppBuilding] = useState(false); - const [extraBodyFields, setExtraBodyFields] = useState([]); - const [fileUploadEnabled, setFileUploadEnabled] = useState(false); const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false); const [actionAmount, setActionAmount] = useState(increaseAmount); const defaultAuth = { @@ -362,23 +356,7 @@ const AppCreator = (defaultprops) => { // and make categories + labels modifyable. // Categories are the main categories in the App Framework const [categories, setCategories] = useState(appCategories) - const [currentActionMethod, setCurrentActionMethod] = useState( - actionNonBodyRequest[0] - ) - const [currentAction, setCurrentAction] = useState({ - name: "", - file_field: "", - description: "", - url: "", - headers: "", - paths: [], - queries: [], - body: "", - errors: [], - example_response: "", - action_label: "No Label", - method: actionNonBodyRequest[0], - }); + const isCloud = window.location.host === "localhost:3002" || @@ -607,7 +585,6 @@ const AppCreator = (defaultprops) => { } } - console.log("Tags: ", data.tags) if (data.tags !== undefined && data.tags.length > 0) { var newtags = []; for (let tagkey in data.tags) { @@ -701,8 +678,19 @@ const AppCreator = (defaultprops) => { body: "", errors: [], example_response: "", + action_label: "No Label", + required_bodyfields: [], }; + if (methodvalue["x-label"] !== undefined && methodvalue["x-label"] !== null) { + // FIX: Map labels only if they're actually in the category list + newaction.action_label = methodvalue["x-label"] + } + + if (methodvalue["x-required-fields"] !== undefined && methodvalue["x-required-fields"] !== null) { + newaction.required_bodyfields = methodvalue["x-required-fields"] + } + if (newaction.url !== undefined && newaction.url !== null && newaction.url.includes("_shuffle_replace_")) { const regex = /_shuffle_replace_\d/i; //console.log("NEW: ", @@ -740,7 +728,7 @@ const AppCreator = (defaultprops) => { } if (path === "/files/{file_id}/content") { - console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) + //console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) } @@ -771,6 +759,7 @@ const AppCreator = (defaultprops) => { methodvalue["requestBody"]["content"]["application/json"]["schema"] !== undefined && methodvalue["requestBody"]["content"]["application/json"]["schema"] !== null ) { console.log("Schema: ", methodvalue["requestBody"]["content"]["application/json"]["schema"]) + if (methodvalue["requestBody"]["content"]["application/json"]["schema"]["properties"] !== undefined) { var tmpobject = {}; for (let prop of methodvalue["requestBody"]["content"]["application/json"]["schema"]["properties"]) { @@ -1408,7 +1397,7 @@ const AppCreator = (defaultprops) => { } } - newaction.action_label = "No Label" + //newaction.action_label = "No Label" newActions.push(newaction); } } @@ -1444,7 +1433,6 @@ const AppCreator = (defaultprops) => { //console.log("SECURITYSCHEMES: ", securitySchemes) if (securitySchemes !== undefined) { - console.log("NEWAUTH: ", securitySchemes) // FIXME: Should add Oauth2 (Microsoft) and JWT (Wazuh) //console.log("SECURITY: ", securitySchemes) var newauth = []; @@ -1773,6 +1761,16 @@ const AppCreator = (defaultprops) => { }, }; + if (item.action_label !== undefined && item.action_label !== "" && item.action_label !== "No Label") { + console.log("Action label: ", item.action_label) + data.paths[item.url][item.method.toLowerCase()]["x-label"] = item.action_label + } + + if (item.required_bodyfields !== undefined && item.required_bodyfields !== null && item.required_bodyfields.length > 0) { + console.log("Required bodyfields: ", item.required_bodyfields) + data.paths[item.url][item.method.toLowerCase()]["x-required-fields"] = item.required_bodyfields + } + //console.log("ACTION: ", item) if (item.example_response !== undefined && item.example_response !== null && item.example_response.length > 0) { @@ -1997,7 +1995,7 @@ const AppCreator = (defaultprops) => { item.body !== null && item.body.length > 0 ) { - console.log("GOT BODY: ", item.url, item.method, item.body) + //console.log("GOT BODY: ", item.url, item.method, item.body) // Replacing dollarsign insertions that aren't escaped // This is to stop it from messing with systems in Shuffle. @@ -2027,7 +2025,7 @@ const AppCreator = (defaultprops) => { } } - console.log("New body: ", newbody) + //console.log("New body: ", newbody) if (newbody !== item.body) { item.body = newbody } @@ -2096,12 +2094,7 @@ const AppCreator = (defaultprops) => { } // https://swagger.io/docs/specification/describing-request-body/file-upload/ - if ( - item.file_field !== undefined && - item.file_field !== null && - item.file_field.length > 0 - ) { - console.log("HANDLE FILEFIELD SAVE: ", item.file_field); + if (item.file_field !== undefined && item.file_field !== null && item.file_field.length > 0) { data.paths[item.url][item.method.toLowerCase()]["requestBody"][ "content" ]["multipart/form-data"] = { @@ -2116,11 +2109,7 @@ const AppCreator = (defaultprops) => { }, }; - console.log( - data.paths[item.url][item.method.toLowerCase()]["requestBody"][ - "content" - ]["multipart/form-data"] - ); + //console.log(data.paths[item.url][item.method.toLowerCase()]["requestBody"]["content"]["multipart/form-data"]) } if (item.headers.length > 0) { @@ -2342,35 +2331,7 @@ const AppCreator = (defaultprops) => { console.log(actions[index]); }; - const addPathQuery = () => { - urlPathQueries.push({ name: "", required: true, example: "", }); - if (updater === "addupdater") { - setUpdater("updater"); - } else { - setUpdater("addupdater"); - } - setUrlPathQueries(urlPathQueries); - }; - - const flipRequired = (index) => { - urlPathQueries[index].required = !urlPathQueries[index].required; - if (updater === "flipupdater") { - setUpdater("updater"); - } else { - setUpdater("flipupdater"); - } - setUrlPathQueries(urlPathQueries); - }; - - const deletePathQuery = (index) => { - urlPathQueries.splice(index, 1); - if (updater === "deleteupdater") { - setUpdater("updater"); - } else { - setUpdater("deleteupdater"); - } - setUrlPathQueries(urlPathQueries); - }; + const duplicateAction = (index) => { var newAction = JSON.parse(JSON.stringify(actions[index])); @@ -2390,18 +2351,18 @@ const AppCreator = (defaultprops) => { const deleteAction = (index) => { actions.splice(index, 1); - setCurrentAction({ - name: "", - description: "", - url: "", - file_field: "", - headers: "", - paths: [], - queries: [], - body: "", - errors: [], - method: actionNonBodyRequest[0], - }); + //setCurrentAction({ + // name: "", + // description: "", + // url: "", + // file_field: "", + // headers: "", + // paths: [], + // queries: [], + // body: "", + // errors: [], + // method: actionNonBodyRequest[0], + //}); setActions(actions); setFilteredActions(actions); @@ -2904,8 +2865,237 @@ const AppCreator = (defaultprops) => { ) : null; - const loopQueries = - urlPathQueries.length === 0 ? null : ( + + + const getCurrentPaths = (urlPath) => { + var paths = []; + var queries = []; + + if (urlPath.includes("{") && urlPath.includes("}")) { + var tmpWord = ""; + var record = false; + + var query = false; + for (var key in urlPath) { + if (urlPath[key] === "?") { + query = true; + } + + if (urlPath[key] === "}") { + if (tmpWord === parameterName) { + tmpWord = ""; + record = false; + continue; + } else if (query) { + queries.push(tmpWord); + } else { + paths.push(tmpWord); + } + + tmpWord = ""; + record = false; + } + + if (record) { + tmpWord += urlPath[key]; + } + + //if (urlPath[key] === "{" && urlPath[key-1] === "/") { + if (urlPath[key] === "{") { + record = true; + } + } + } + + if (urlPath.includes("<") && urlPath.includes(">")) { + var tmpWord = ""; + var record = false; + + var query = false; + for (var key in urlPath) { + if (urlPath[key] === "?") { + query = true; + } + + if (urlPath[key] === ">") { + if (tmpWord === parameterName) { + tmpWord = ""; + record = false; + continue; + } else if (query) { + queries.push(tmpWord); + } else { + paths.push(tmpWord); + } + + tmpWord = ""; + record = false; + } + + if (record) { + tmpWord += urlPath[key]; + } + + //if (urlPath[key] === "{" && urlPath[key-1] === "/") { + if (urlPath[key] === "<") { + record = true; + } + } + } + + return [paths, queries]; + }; + + const foundCategory = newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 ? categories.find((x) => x.name === newWorkflowCategories[0]) : undefined + const actionLabels = foundCategory !== undefined && foundCategory !== null && foundCategory.action_labels.length > 0 ? ["No Label"].concat(foundCategory.action_labels) : [] + + const ActionPaper = (props) => { + const { data, index } = props + + const [updater, setUpdater] = useState("tmp"); + const [actionsModalOpen, setActionsModalOpen] = useState(false); + const [urlPath, setUrlPath] = useState(""); + const [fileUploadEnabled, setFileUploadEnabled] = useState(false); + const [currentActionMethod, setCurrentActionMethod] = useState(actionNonBodyRequest[0]) + const [extraBodyFields, setExtraBodyFields] = useState([]); + const [urlPathQueries, setUrlPathQueries] = useState([]); + const [currentAction, setCurrentAction] = useState({ + name: "", + file_field: "", + description: "", + url: "", + headers: "", + paths: [], + queries: [], + body: "", + errors: [], + example_response: "", + method: actionNonBodyRequest[0], + action_label: "No Label", + required_bodyfields: [], + }); + + const findBodyParams = (body) => { + const regex = /\${(\w+)}/g; + const found = body.match(regex); + if (found === null) { + setExtraBodyFields([]); + } else { + setExtraBodyFields(found); + } + }; + + const UrlPathParameters = () => { + const values = getCurrentPaths(urlPath); + const paths = values[0]; + const queries = values[1]; + + if (currentAction.paths !== paths && urlPath.length > 0) { + //console.log("IN PATHS SETTER: !", paths) + setActionField("paths", paths); + } + + var tmpQueries = []; + + // No overlapping of names + for (var key in queries) { + const tmpquery = queries[key]; + const found = tmpQueries.find((query) => query.name === tmpquery); + if (found === undefined) { + tmpQueries.push({ name: queries[key], required: true }); + } + } + + // FIXME: Frontend isn't updating.. + if (tmpQueries.length > 0 && JSON.stringify(tmpQueries) !== JSON.stringify(urlPathQueries)) { + setUrlPathQueries(tmpQueries); + } + + return paths.length > 0 ? ( +
Required parameters: {paths.join(", ")}
+ ) : null; + }; + + + const HandleIndividualChip = (props) => { + const { chipData, index } = props; + const [chipRequired, setChipRequired] = useState(currentAction.required_bodyfields !== undefined ? currentAction.required_bodyfields.includes(chipData) : false); + + const parsedChip = chipData.startsWith("${") && chipData.endsWith("}") ? chipData.substring(2, chipData.length - 1) : chipData + + return ( + + { + if (chipRequired) { + currentAction["required_bodyfields"].splice(currentAction["required_bodyfields"].indexOf(chipData), 1) + } else { + currentAction["required_bodyfields"].push(chipData) + } + + setCurrentAction(currentAction); + setChipRequired(!chipRequired); + }} + /> + + ); + }; + + const setActionField = (field, value) => { + currentAction[field] = value + setCurrentAction(currentAction) + + //setUrlPathQueries(currentAction.queries) + }; + + const addPathQuery = () => { + urlPathQueries.push({ name: "", required: true, example: "", }); + if (updater === "addupdater") { + setUpdater("updater"); + } else { + setUpdater("addupdater"); + } + setUrlPathQueries(urlPathQueries); + }; + + const flipRequired = (index) => { + urlPathQueries[index].required = !urlPathQueries[index].required; + if (updater === "flipupdater") { + setUpdater("updater"); + } else { + setUpdater("flipupdater"); + } + setUrlPathQueries(urlPathQueries); + }; + + const deletePathQuery = (index) => { + console.log("Should delete index: ", index) + var tmpqueries = JSON.parse(JSON.stringify(urlPathQueries)) + tmpqueries.splice(index, 1) + + console.log("Queries: ", tmpqueries) + setUrlPathQueries(tmpqueries); + + if (updater === "deleteupdater") { + setUpdater("updater"); + } else { + setUpdater("deleteupdater"); + } + }; + + const loopQueries = urlPathQueries.length === 0 ? null : (
{ }} /> Queries - {urlPathQueries.map((data, index) => { - const requiredColor = data.required === true ? "green" : "red"; + {urlPathQueries.map((query, queryIndex) => { + const requiredColor = query.required === true ? "green" : "red"; //const required = data.required === true ?
{data.required.toString()}
:
{flipRequired(index)}} style={{display: "inline", color: "red", cursor: "pointer"}}>{data.required.toString()}
return ( - +
{ } onBlur={(e) => { console.log("IN BLUR: ", e.target.value); - urlPathQueries[index].name = e.target.value.replaceAll( - "=", - "" - ); - + urlPathQueries[queryIndex].name = e.target.value.replaceAll("=", ""); setUrlPathQueries(urlPathQueries); }} style={{flex: 3}} @@ -2953,11 +3139,11 @@ const AppCreator = (defaultprops) => { /> { - urlPathQueries[index].example = e.target.value.replaceAll( + urlPathQueries[queryIndex].example = e.target.value.replaceAll( "=", "" ) @@ -2975,19 +3161,19 @@ const AppCreator = (defaultprops) => {
{ - flipRequired(index); + flipRequired(queryIndex); }} > Required:{" "}
- {data.required.toString()} + {query.required.toString()}
{ - deletePathQuery(index); + deletePathQuery(queryIndex); }} > @@ -3007,1093 +3193,954 @@ const AppCreator = (defaultprops) => {
); - const foundCategory = newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 ? categories.find((x) => x.name === newWorkflowCategories[0]) : undefined - const actionLabels = foundCategory !== undefined && foundCategory !== null && foundCategory.action_labels.length > 0 ? ["No Label"].concat(foundCategory.action_labels) : [] - - const loopActions = - actions.length === 0 ? null : ( + const SetExtraBodyField = (props) => { + const { extraBodyFields } = props; + + if (extraBodyFields === undefined || extraBodyFields === null) { + return null; + } + + //const parsedlist = extraBodyFields.join(", ") + //console.log("LIST: ", parsedlist) + + return ( + + {extraBodyFields.map((data, index) => { + return ; + })} + + ); + }; + + const bodyInfo = actionBodyRequest.includes(currentActionMethod) ? ( +
+ Request Body:{" "} + {extraBodyFields.length > 0 ? ( + + + + ) : ( + + {`Add variables with \$\{ variable_name }`} + + )} + { + setActionField("body", e.target.value); + findBodyParams(e.target.value); + }} + key={currentAction} + helperText={ + + Shows an example body to the user. ${} creates variables. + + } + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> +
+
+ ) : null; + + const exampleResponse = fileDownloadEnabled ? null : ( +
+ Example success response + setActionField("example_response", e.target.value)} + helperText={ + + Helps with autocompletion and understanding of the endpoint + + } + key={currentAction} + InputProps={{ + style: { + color: "white", + }, + }} + /> +
+ ); + + const addActionToView = (errors) => { + currentAction.errors = errors; + currentAction.queries = urlPathQueries; + setUrlPathQueries([]); + + const actionIndex = actions.findIndex((data) => data.name === currentAction.name); + + if (actionIndex < 0) { + actions.push(currentAction); + } else { + actions[actionIndex] = currentAction; + } + + if (actions.length > actionAmount) { + setActionAmount(actions.length); + } + + setActions(actions); + setFilteredActions(actions); + }; + + const getActionErrors = () => { + var errormessage = []; + if (currentAction.name === undefined || currentAction.name.length === 0) { + errormessage.push("Name can't be empty"); + } + + // Url verification + //if (currentAction.url.length === 0) { + // errormessage.push("URL path can't be empty.") + if (!currentAction.url.startsWith("/") && baseUrl.length > 0 && currentAction.url.length > 0) { + errormessage.push("URL must start with /"); + } + + const check = urlPathQueries.findIndex((data) => data.name.length === 0); + if (check >= 0) { + errormessage.push("All queries must have a value"); + } + + return errormessage; + }; + + + + const newActionModal = ( + { + 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); + }} + > + + +
New action
+
+ + + Learn more about actions + +
+ Name + { + setActionField("name", e.target.value); + }} + onBlur={(e) => { + // Fix basic issues in frontend. Python functions run a-zA-Z0-9_ + const regex = /[A-Za-z0-9 _]/g; + const found = e.target.value.match(regex); + if (found !== null) { + setActionField("name", found.join("")); + } + }} + key={currentAction} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> +
+ Description + setActionField("description", e.target.value)} + InputProps={{ + style: { + color: "white", + }, + }} + /> + +

Request

+ +
+ URL path / Curl statement + { + setActionField("url", e.target.value); + setUrlPath(e.target.value); + }} + helperText={ + + The path to use. Must start with /. Use {"{variablename}"} to + have path variables + + } + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + input: classes.input, + }, + style: { + color: "white", + }, + }} + onBlur={(event) => { + var parsedurl = event.target.value; + //console.log("URL: ", parsedurl) + if (parsedurl.includes(" ")) { + parsedurl = parsedurl.replaceAll(" ", " "); + } + + if (parsedurl.includes(" ")) { + parsedurl = parsedurl.replaceAll(" ", " "); + } + + if (parsedurl.includes("[") && parsedurl.includes("]")) { + //console.log("REPLACE1") + parsedurl = parsedurl.replaceAll("[", "{"); + parsedurl = parsedurl.replaceAll("]", "}"); + } + + if (parsedurl.includes("<") && parsedurl.includes(">")) { + //console.log("REPLACE2") + parsedurl = parsedurl.replaceAll("<", "{"); + parsedurl = parsedurl.replaceAll(">", "}"); + } + + //console.log("URL2: ", parsedurl) + if ( + parsedurl.startsWith("PUT ") || + parsedurl.startsWith("GET ") || + parsedurl.startsWith("POST ") || + parsedurl.startsWith("DELETE ") || + parsedurl.startsWith("PATCH ") || + parsedurl.startsWith("CONNECT ") + ) { + const tmp = parsedurl.split(" "); + + if (tmp.length > 1) { + parsedurl = tmp[1].trim(); + setActionField("url", parsedurl); + + setCurrentActionMethod(tmp[0].toUpperCase()); + setActionField("method", tmp[0].toUpperCase()); + } + + console.log("URL3: ", parsedurl); + + //setUpdate(Math.random()); + } else if (parsedurl.startsWith("curl")) { + console.log("URL4: ", parsedurl); + + const request = parseCurl(event.target.value); + if ( + request !== event.target.value && + request.method !== undefined && + request.method !== null + ) { + if (request.method.toUpperCase() !== currentAction.Method) { + setCurrentActionMethod(request.method.toUpperCase()); + setActionField("method", request.method.toUpperCase()); + } + + if (request.header !== undefined && request.header !== null) { + var headers = []; + for (let [key, value] of Object.entries(request.header)) { + if (value === undefined) { + if (key.includes(":")) { + const keysplit = key.split(":") + key = keysplit[0].trim() + value = keysplit[1].trim() + + } else if (key.includes("=")) { + const keysplit = key.split("=") + key = keysplit[0].trim() + value = keysplit[1].trim() + + } else { + alert.error("Removed key: ", key) + continue + } + } + + if ( + parameterName !== undefined && + key.toLowerCase() === parameterName.toLowerCase() + ) { + continue; + } + + if (key === "Authorization") { + continue; + } + + headers += key + "=" + value + "\n"; + } + + try { + setActionField("headers", headers.trim()); + } catch (e) { + console.log("Failed to parse header: ", e) + } + } + + if (request.body !== undefined && request.body !== null) { + setActionField("body", request.body); + } + + // Parse URL + if (request.url !== undefined) { + parsedurl = request.url; + } + } + + console.log("PARSED: ", parsedurl); + if (parsedurl !== undefined) { + if (parsedurl.includes("<") && parsedurl.includes(">")) { + parsedurl = parsedurl.split("<").join("{"); + parsedurl = parsedurl.split(">").join("}"); + } + + if ( + parsedurl.startsWith("http") || + parsedurl.startsWith("ftp") + ) { + if ( + parsedurl !== undefined && + parsedurl.includes(parameterName) + ) { + // Remove <> etc. + // + + console.log("IT HAS THE PARAM NAME!"); + const newurl = new URL(encodeURI(parsedurl)); + newurl.searchParams.delete(parameterName); + parsedurl = decodeURI(newurl.href); + } + + // Remove the base URL itself + if ( + parsedurl !== undefined && + baseUrl !== undefined && + baseUrl.length > 0 && + parsedurl.includes(baseUrl) + ) { + parsedurl = parsedurl.replace(baseUrl, ""); + } + + // Check URL query && headers + //setActionField("url", parsedurl) + } + } + } + + if (baseUrl !== undefined && baseUrl !== null && parsedurl.startsWith(baseUrl)) { + parsedurl = parsedurl.replaceAll(baseUrl, ""); + } + + if (parsedurl.includes("?")) { + const parsedurlsplit = parsedurl.split("?") + parsedurl = parsedurlsplit[0] + + //var newqueries = selectedAction.queries === undefined || selectedAction.queries === null ? [] : selectedAction.queries + + const datasplit = parsedurlsplit[1].split("&") + for (var key in datasplit) { + console.log("Data: ", datasplit[key]) + var actualkey = datasplit[key] + var example = "" + if (datasplit[key].includes("=")) { + actualkey = datasplit[key].split("=")[0] + example = datasplit[key].split("=")[1] + } + + const foundPath = urlPathQueries.find(data => data.name === actualkey) + if (foundPath === null || foundPath === undefined) { + urlPathQueries.push({ name: actualkey, example: example, required: true }) + } + } + } + + // Found that dashes in the URL doesn't work + //parsedurl = parsedurl.replace("-", "_") + //console.log("Actions: ", actions) + + if (baseUrl.length === 0 && parsedurl.includes("http")) { + try { + const newurl = new URL(encodeURI(parsedurl)) + newurl.searchParams.delete(parameterName) + console.log("New url: ", newurl) + parsedurl = newurl.pathname + setBaseUrl(newurl.origin) + } catch (e) { + console.log("Failed to parse URL: ", e) + } + } + + if (event.target.value !== parsedurl) { + setUrlPath(parsedurl); + setActionField("url", parsedurl); + } + //console.log("URL: ", request.url) + }} + /> + + {loopQueries} + + {currentActionMethod === "POST" ? ( + + ) : null} + {/*currentActionMethod === "GET" ? ( + + ) : null*/} + {fileUploadEnabled ? ( + setActionField("file_field", e.target.value)} + helperText={ + + The File field to interact with + + } + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style: { + color: "white", + }, + }} + /> + ) : null} +
+ {fileUploadEnabled ? null : + + Headers + setActionField("headers", e.target.value)} + helperText={ + + Headers that are part of the request. Default: EMPTY + + } + InputProps={{ + style: { + color: "white", + }, + }} + /> + + } + {bodyInfo} + + {exampleResponse} + + + + + + +
+ ); + + + var error = + data.errors.length > 0 ? ( + + + + ) : ( + + + + ); + + var bgColor = "#61afee"; + if (data.method === "POST") { + bgColor = "#49cc90"; + } else if (data.method === "PUT") { + bgColor = "#fca130"; + } else if (data.method === "PATCH") { + bgColor = "#50e3c2"; + } else if (data.method === "DELETE") { + bgColor = "#f93e3e"; + } else if (data.method === "HEAD") { + bgColor = "#9012fe"; + } + + const url = data.url; + const hasFile = (data["file_field"] !== undefined && data["file_field"] !== null && data["file_field"].length > 0) || data["example_response"] === "shuffle_file_download" + + + return ( + + {newActionModal} + + {error} + +
{ + console.log("Data: ", data) + if (hasFile) { + //setActionField("headers", "") + //console.log("It has a file: ", data["file_field"]) + + setFileUploadEnabled(true); + data.headers = "" + } else { + console.log("No file") + } + + setCurrentAction(data); + setCurrentActionMethod(data.method); + setUrlPathQueries(data.queries); + setUrlPath(data.url); + setActionsModalOpen(true); + + if (data["body"] !== undefined && data["body"] !== null && data["body"].length > 0) { + findBodyParams(data["body"]); + } else { + console.log("No body param") + } + + }} + > +
+ + + {hasFile ? ( + + ) : null}{" "} + {url} - {data.name} + +
+
+
+ {/* + +
{testAction(index)}}> + Test +
+
+ */} + + {/* From 2023: Example of handling action labels */} + {actionLabels.length > 0 && newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 && categories.length > 0 ? + + : null} + + +
{ + duplicateAction(index); + }} + > + +
+
+ +
{ + deleteAction(index); + }} + > + +
+
+ + + { index === filteredActions.length - 1 || index === actionAmount - 1 ? + + : null} +
+ ) + } + + const LoopActions = (props) => { + const { filteredActions } = props; + + //console.log("Actions: ", filteredActions) + if (filteredActions === null || filteredActions === undefined || filteredActions.length === 0) { + return null + } + + return (
{filteredActions.slice(0, actionAmount).map((data, index) => { - var error = - data.errors.length > 0 ? ( - - - - ) : ( - - - - ); - - var bgColor = "#61afee"; - if (data.method === "POST") { - bgColor = "#49cc90"; - } else if (data.method === "PUT") { - bgColor = "#fca130"; - } else if (data.method === "PATCH") { - bgColor = "#50e3c2"; - } else if (data.method === "DELETE") { - bgColor = "#f93e3e"; - } else if (data.method === "HEAD") { - bgColor = "#9012fe"; - } - - const url = data.url; - const hasFile = - (data["file_field"] !== undefined && - data["file_field"] !== null && - data["file_field"].length > 0) || data["example_response"] === "shuffle_file_download" - - // In case of extremely long summaries/names from OpenAPI def - //const maxlen = 35 - //if (data.description === undefined || data.description === null || data.description.length === 0) { - // if (data.name !== undefined && data.name !== null && data.name.length > maxlen ) { - // var newname = [] - // for (var key in data.name.split(" ")) { - // console.log("Name: ", data.name[key]) - // if (newname.join(" ").length < maxlen) { - // newname.push(data.name[key]) - // } - // } - - // data.description = data.name.valueOf() - // data.name = newname.join(" ") - // } - //} - return ( - - {error} - -
{ - console.log("Data: ", data) - if (hasFile) { - setFileUploadEnabled(true); - //setActionField("headers", "") - console.log("It has a file: ", data["file_field"]) - data.headers = "" - } else { - console.log("No file") - } + + ) + })} +
+ ) + } - setCurrentAction(data); - setCurrentActionMethod(data.method); - setUrlPathQueries(data.queries); - setUrlPath(data.url); - setActionsModalOpen(true); - - if ( - data["body"] !== undefined && - data["body"] !== null && - data["body"].length > 0 - ) { - findBodyParams(data["body"]); - } else { - console.log("No body param") - } - - }} - > -
- - - {hasFile ? ( - - ) : null}{" "} - {url} - {data.name} - -
-
- - {/* - -
{testAction(index)}}> - Test -
-
- */} - - {/* From 2023: Example of handling action labels */} - {/*actionLabels.length > 0 && newWorkflowCategories !== undefined && newWorkflowCategories !== null && newWorkflowCategories.length > 0 && categories.length > 0 ? - ( - - ) - : null*/} - - -
{ - duplicateAction(index); - }} - > - -
-
- -
{ - deleteAction(index); - }} - > - -
-
- - ); - })} -
- ); - - const setActionField = (field, value) => { - currentAction[field] = value; - setCurrentAction(currentAction); - - //setUrlPathQueries(currentAction.queries) - }; - - const findBodyParams = (body) => { - const regex = /\${(\w+)}/g; - const found = body.match(regex); - if (found === null) { - setExtraBodyFields([]); - } else { - setExtraBodyFields(found); - } - }; - - const HandleIndividualChip = (props) => { - const { chipData, index } = props; - const [chipRequired, setChipRequired] = useState(false); - - return ( - - { - console.log("CLICK: ", chipData); - setChipRequired(!chipRequired); - }} - /> - - ); - }; - - const SetExtraBodyField = (props) => { - const { extraBodyFields } = props; - - if (extraBodyFields === undefined || extraBodyFields === null) { - return null; - } - - //const parsedlist = extraBodyFields.join(", ") - //console.log("LIST: ", parsedlist) - - return ( - - {extraBodyFields.map((data, index) => { - return ; - })} - - ); - }; - - const bodyInfo = actionBodyRequest.includes(currentActionMethod) ? ( -
- Request Body:{" "} - {extraBodyFields.length > 0 ? ( - - - - ) : ( - - {`Add variables with \$\{ variable_name }`} - - )} - { - setActionField("body", e.target.value); - findBodyParams(e.target.value); - }} - key={currentAction} - helperText={ - - Shows an example body to the user. ${} creates variables. - - } - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> -
-
- ) : null; - - const exampleResponse = fileDownloadEnabled ? null : ( -
- Example success response - setActionField("example_response", e.target.value)} - helperText={ - - Helps with autocompletion and understanding of the endpoint - - } - key={currentAction} - InputProps={{ - style: { - color: "white", - }, - }} - /> -
- ); - - const addActionToView = (errors) => { - currentAction.errors = errors; - currentAction.queries = urlPathQueries; - setUrlPathQueries([]); - - const actionIndex = actions.findIndex( - (data) => data.name === currentAction.name - ); - if (actionIndex < 0) { - actions.push(currentAction); - } else { - actions[actionIndex] = currentAction; - } - - if (actions.length > actionAmount) { - setActionAmount(actions.length); - } - - setActions(actions); - setFilteredActions(actions); - }; - - const getActionErrors = () => { - var errormessage = []; - if (currentAction.name === undefined || currentAction.name.length === 0) { - errormessage.push("Name can't be empty"); - } - - // Url verification - //if (currentAction.url.length === 0) { - // errormessage.push("URL path can't be empty.") - if ( - !currentAction.url.startsWith("/") && - baseUrl.length > 0 && - currentAction.url.length > 0 - ) { - errormessage.push("URL must start with /"); - } - - const check = urlPathQueries.findIndex((data) => data.name.length === 0); - if (check >= 0) { - errormessage.push("All queries must have a value"); - } - - return errormessage; - }; - - const getCurrentPaths = (urlPath) => { - var paths = []; - var queries = []; - - if (urlPath.includes("{") && urlPath.includes("}")) { - var tmpWord = ""; - var record = false; - - var query = false; - for (var key in urlPath) { - if (urlPath[key] === "?") { - query = true; - } - - if (urlPath[key] === "}") { - if (tmpWord === parameterName) { - tmpWord = ""; - record = false; - continue; - } else if (query) { - queries.push(tmpWord); - } else { - paths.push(tmpWord); - } - - tmpWord = ""; - record = false; - } - - if (record) { - tmpWord += urlPath[key]; - } - - //if (urlPath[key] === "{" && urlPath[key-1] === "/") { - if (urlPath[key] === "{") { - record = true; - } - } - } - - if (urlPath.includes("<") && urlPath.includes(">")) { - var tmpWord = ""; - var record = false; - - var query = false; - for (var key in urlPath) { - if (urlPath[key] === "?") { - query = true; - } - - if (urlPath[key] === ">") { - if (tmpWord === parameterName) { - tmpWord = ""; - record = false; - continue; - } else if (query) { - queries.push(tmpWord); - } else { - paths.push(tmpWord); - } - - tmpWord = ""; - record = false; - } - - if (record) { - tmpWord += urlPath[key]; - } - - //if (urlPath[key] === "{" && urlPath[key-1] === "/") { - if (urlPath[key] === "<") { - record = true; - } - } - } - - return [paths, queries]; - }; - - const UrlPathParameters = () => { - const values = getCurrentPaths(urlPath); - const paths = values[0]; - const queries = values[1]; - - if (currentAction.paths !== paths && urlPath.length > 0) { - //console.log("IN PATHS SETTER: !", paths) - setActionField("paths", paths); - } - - var tmpQueries = []; - - // No overlapping of names - for (var key in queries) { - const tmpquery = queries[key]; - const found = tmpQueries.find((query) => query.name === tmpquery); - if (found === undefined) { - tmpQueries.push({ name: queries[key], required: true }); - } - } - - // FIXME: Frontend isn't updating.. - if ( - tmpQueries.length > 0 && - JSON.stringify(tmpQueries) !== JSON.stringify(urlPathQueries) - ) { - setUrlPathQueries(tmpQueries); - } - - return paths.length > 0 ? ( -
Required parameters: {paths.join(", ")}
- ) : null; - }; - - const newActionModal = ( - { - setUrlPath(""); - setCurrentAction({ - name: "", - description: "", - url: "", - file_field: "", - headers: "", - paths: [], - queries: [], - body: "", - errors: [], - method: actionNonBodyRequest[0], - }); - setCurrentActionMethod(apikeySelection[0]); - setUrlPathQueries([]); - setActionsModalOpen(false); - setFileUploadEnabled(false); - }} - > - - -
New action
-
- - - Learn more about actions - -
- Name - { - setActionField("name", e.target.value); - }} - onBlur={(e) => { - // Fix basic issues in frontend. Python functions run a-zA-Z0-9_ - const regex = /[A-Za-z0-9 _]/g; - const found = e.target.value.match(regex); - if (found !== null) { - setActionField("name", found.join("")); - } - }} - key={currentAction} - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> -
- Description - setActionField("description", e.target.value)} - InputProps={{ - style: { - color: "white", - }, - }} - /> - -

Request

- -
- URL path / Curl statement - { - setActionField("url", e.target.value); - setUrlPath(e.target.value); - }} - helperText={ - - The path to use. Must start with /. Use {"{variablename}"} to - have path variables - - } - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - input: classes.input, - }, - style: { - color: "white", - }, - }} - onBlur={(event) => { - var parsedurl = event.target.value; - //console.log("URL: ", parsedurl) - if (parsedurl.includes(" ")) { - parsedurl = parsedurl.replaceAll(" ", " "); - } - - if (parsedurl.includes(" ")) { - parsedurl = parsedurl.replaceAll(" ", " "); - } - - if (parsedurl.includes("[") && parsedurl.includes("]")) { - //console.log("REPLACE1") - parsedurl = parsedurl.replaceAll("[", "{"); - parsedurl = parsedurl.replaceAll("]", "}"); - } - - if (parsedurl.includes("<") && parsedurl.includes(">")) { - //console.log("REPLACE2") - parsedurl = parsedurl.replaceAll("<", "{"); - parsedurl = parsedurl.replaceAll(">", "}"); - } - - //console.log("URL2: ", parsedurl) - if ( - parsedurl.startsWith("PUT ") || - parsedurl.startsWith("GET ") || - parsedurl.startsWith("POST ") || - parsedurl.startsWith("DELETE ") || - parsedurl.startsWith("PATCH ") || - parsedurl.startsWith("CONNECT ") - ) { - const tmp = parsedurl.split(" "); - - if (tmp.length > 1) { - parsedurl = tmp[1].trim(); - setActionField("url", parsedurl); - - setCurrentActionMethod(tmp[0].toUpperCase()); - setActionField("method", tmp[0].toUpperCase()); - } - - console.log("URL3: ", parsedurl); - - setUpdate(Math.random()); - } else if (parsedurl.startsWith("curl")) { - console.log("URL4: ", parsedurl); - - const request = parseCurl(event.target.value); - if ( - request !== event.target.value && - request.method !== undefined && - request.method !== null - ) { - if (request.method.toUpperCase() !== currentAction.Method) { - setCurrentActionMethod(request.method.toUpperCase()); - setActionField("method", request.method.toUpperCase()); - } - - if (request.header !== undefined && request.header !== null) { - var headers = []; - for (let [key, value] of Object.entries(request.header)) { - if (value === undefined) { - if (key.includes(":")) { - const keysplit = key.split(":") - key = keysplit[0].trim() - value = keysplit[1].trim() - - } else if (key.includes("=")) { - const keysplit = key.split("=") - key = keysplit[0].trim() - value = keysplit[1].trim() - - } else { - alert.error("Removed key: ", key) - continue - } - } - - if ( - parameterName !== undefined && - key.toLowerCase() === parameterName.toLowerCase() - ) { - continue; - } - - if (key === "Authorization") { - continue; - } - - headers += key + "=" + value + "\n"; - } - - try { - setActionField("headers", headers.trim()); - } catch (e) { - console.log("Failed to parse header: ", e) - } - } - - if (request.body !== undefined && request.body !== null) { - setActionField("body", request.body); - } - - // Parse URL - if (request.url !== undefined) { - parsedurl = request.url; - } - } - - console.log("PARSED: ", parsedurl); - if (parsedurl !== undefined) { - if (parsedurl.includes("<") && parsedurl.includes(">")) { - parsedurl = parsedurl.split("<").join("{"); - parsedurl = parsedurl.split(">").join("}"); - } - - if ( - parsedurl.startsWith("http") || - parsedurl.startsWith("ftp") - ) { - if ( - parsedurl !== undefined && - parsedurl.includes(parameterName) - ) { - // Remove <> etc. - // - - console.log("IT HAS THE PARAM NAME!"); - const newurl = new URL(encodeURI(parsedurl)); - newurl.searchParams.delete(parameterName); - parsedurl = decodeURI(newurl.href); - } - - // Remove the base URL itself - if ( - parsedurl !== undefined && - baseUrl !== undefined && - baseUrl.length > 0 && - parsedurl.includes(baseUrl) - ) { - parsedurl = parsedurl.replace(baseUrl, ""); - } - - // Check URL query && headers - //setActionField("url", parsedurl) - } - } - } - - if (baseUrl !== undefined && baseUrl !== null && parsedurl.startsWith(baseUrl)) { - parsedurl = parsedurl.replaceAll(baseUrl, ""); - } - - if (parsedurl.includes("?")) { - const parsedurlsplit = parsedurl.split("?") - parsedurl = parsedurlsplit[0] - - //var newqueries = selectedAction.queries === undefined || selectedAction.queries === null ? [] : selectedAction.queries - - const datasplit = parsedurlsplit[1].split("&") - for (var key in datasplit) { - console.log("Data: ", datasplit[key]) - var actualkey = datasplit[key] - var example = "" - if (datasplit[key].includes("=")) { - actualkey = datasplit[key].split("=")[0] - example = datasplit[key].split("=")[1] - } - - const foundPath = urlPathQueries.find(data => data.name === actualkey) - if (foundPath === null || foundPath === undefined) { - urlPathQueries.push({ name: actualkey, example: example, required: true }) - } - } - } - - // Found that dashes in the URL doesn't work - //parsedurl = parsedurl.replace("-", "_") - //console.log("Actions: ", actions) - - if (baseUrl.length === 0 && parsedurl.includes("http")) { - try { - const newurl = new URL(encodeURI(parsedurl)) - newurl.searchParams.delete(parameterName) - console.log("New url: ", newurl) - parsedurl = newurl.pathname - setBaseUrl(newurl.origin) - } catch (e) { - console.log("Failed to parse URL: ", e) - } - } - - if (event.target.value !== parsedurl) { - setUrlPath(parsedurl); - setActionField("url", parsedurl); - } - //console.log("URL: ", request.url) - }} - /> - - {loopQueries} - - {currentActionMethod === "POST" ? ( - - ) : null} - {/*currentActionMethod === "GET" ? ( - - ) : null*/} - {fileUploadEnabled ? ( - setActionField("file_field", e.target.value)} - helperText={ - - The File field to interact with - - } - InputProps={{ - classes: { - notchedOutline: classes.notchedOutline, - }, - style: { - color: "white", - }, - }} - /> - ) : null} -
- {fileUploadEnabled ? null : - - Headers - setActionField("headers", e.target.value)} - helperText={ - - Headers that are part of the request. Default: EMPTY - - } - InputProps={{ - style: { - color: "white", - }, - }} - /> - - } - {bodyInfo} - - {exampleResponse} - - - - - - -
- ); const tagView = ( @@ -4314,7 +4361,6 @@ const AppCreator = (defaultprops) => { console.log(selectedAction); }; - //{selectedAction.authentication !== undefined && selectedAction.authentication.length > 0 ? const getAppAuthentication = () => { fetch(globalUrl + "/api/v1/apps/authentication", { method: "GET", @@ -4389,26 +4435,16 @@ const AppCreator = (defaultprops) => { }); }; - if ( - !authLoaded && - appAuthentication.length === 0 && - selectedAction.id !== undefined - ) { + if (!authLoaded && appAuthentication.length === 0 && selectedAction.id !== undefined) { setAuthLoaded(true); getAppAuthentication(); - } else if ( - selectedAction.id === undefined && - currentAction.name !== undefined && - currentAction.name !== null && - currentAction.name.length > 0 - ) { + } + + /* + else if (selectedAction.id === undefined && currentAction.name !== undefined && currentAction.name !== null && currentAction.name.length > 0) { var methodName = `${currentAction.method}_${currentAction.name}`; - if ( - currentAction.method.toLowerCase() === "custom" || - currentAction.name - .toLowerCase() - .startsWith(currentAction.method.toLowerCase()) - ) { + if (currentAction.method.toLowerCase() === "custom" || + currentAction.name.toLowerCase().startsWith(currentAction.method.toLowerCase())) { methodName = currentAction.name; } @@ -4417,6 +4453,7 @@ const AppCreator = (defaultprops) => { var newselectedaction = app.actions.find( (item) => item.name.toLowerCase().replaceAll(" ", "_") === methodName ); + if (newselectedaction !== undefined && newselectedaction !== null) { newselectedaction.app_id = app.id; newselectedaction.app_name = app.name; @@ -4428,6 +4465,7 @@ const AppCreator = (defaultprops) => { } } } + */ const setNewAppAuth = (appAuthData) => { //console.log("DAta: ", appAuthData) @@ -4782,35 +4820,62 @@ const AppCreator = (defaultprops) => {
) : null}
- {loopActions} + + {/*
- {/* {actionAmount} {actions.length} - */}
+ */}
); @@ -5382,9 +5447,9 @@ const AppCreator = (defaultprops) => { { isLoaded && isAppLoaded ? (
{landingpageDataBrowser}
- {newActionModal}
) : (