import React, { useState, useEffect } from "react"; import { makeStyles } from "@material-ui/styles"; import { useTheme } from "@material-ui/core/styles"; import { BrowserView, MobileView } from "react-device-detect"; import { Paper, Typography, FormControlLabel, Button, Divider, Select, MenuItem, FormControl, Switch, Dialog, DialogTitle, DialogContent, DialogActions, TextField, Tooltip, Breadcrumbs, CircularProgress, Chip, } from "@material-ui/core"; import { LockOpen as LockOpenIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, Remove as RemoveIcon, Add as AddIcon, CheckCircle as CheckCircleIcon, AttachFile as AttachFileIcon, Apps as AppsIcon, ErrorOutline as ErrorOutlineIcon, } from "@material-ui/icons"; import { v4 as uuidv4 } from "uuid"; import { Link, useParams } from "react-router-dom"; import YAML from "yaml"; import ChipInput from "material-ui-chip-input"; import { useAlert } from "react-alert"; import words from "shellwords"; import AvatarEditor from "react-avatar-editor"; import AddAPhotoIcon from "@material-ui/icons/AddAPhoto"; import AddAPhotoOutlinedIcon from "@material-ui/icons/AddAPhotoOutlined"; import ZoomInOutlinedIcon from "@material-ui/icons/ZoomInOutlined"; import ZoomOutOutlinedIcon from "@material-ui/icons/ZoomOutOutlined"; import LoopIcon from "@material-ui/icons/Loop"; import AddPhotoAlternateIcon from "@material-ui/icons/AddPhotoAlternate"; const surfaceColor = "#27292D"; const inputColor = "#383B40"; const bodyDivStyle = { margin: "auto", width: "900px", }; const actionListStyle = { paddingLeft: "10px", paddingRight: "10px", paddingBottom: "10px", paddingTop: "10px", marginTop: "5px", backgroundColor: inputColor, display: "flex", color: "white", }; const boxStyle = { color: "white", flex: "1", marginLeft: "10px", marginRight: "10px", paddingLeft: "30px", paddingRight: "30px", paddingBottom: "30px", paddingTop: "30px", display: "flex", flexDirection: "column", backgroundColor: surfaceColor, }; const dividerStyle = { marginBottom: "10px", marginTop: "10px", height: "1px", width: "100%", backgroundColor: "grey", }; const appIconStyle = { marginLeft: "5px", }; const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, }); const rewrite = (args) => { return args.reduce(function (args, a) { if (0 === a.indexOf("-X")) { args.push("-X"); args.push(a.slice(2)); } else { args.push(a); } return args; }, []); }; const parseField = (s) => { return s.split(/: (.+)/); }; const isURL = (s) => { return /^https?:\/\//.test(s); }; // Parses CURL to a real request const parseCurl = (s) => { //console.log("CURL: ", s) if (0 != s.indexOf("curl ")) { console.log("Not curl start"); return ""; } try { var args = rewrite(words.split(s)); } catch (e) { return s; } var out = { method: "GET", header: {} }; var state = ""; args.forEach(function (arg) { switch (true) { case isURL(arg): out.url = arg; break; case arg === "-A" || arg === "--user-agent": state = "user-agent"; break; case arg === "-H" || arg === "--header": state = "header"; break; case arg === "-d" || arg === "--data" || arg === "--data-ascii": state = "data"; break; case arg === "-u" || arg === "--user": state = "user"; break; case arg === "-I" || arg === "--head": out.method = "HEAD"; break; case arg === "-X" || arg === "--request": state = "method"; break; case arg === "-b" || arg === "--cookie": state = "cookie"; break; case arg === "--compressed": out.header["Accept-Encoding"] = out.header["Accept-Encoding"] || "deflate, gzip"; break; case !!arg: switch (state) { case "header": var field = parseField(arg); out.header[field[0]] = field[1]; state = ""; break; case "user-agent": out.header["User-Agent"] = arg; state = ""; break; case "data": if (out.method === "GET" || out.method === "HEAD") out.method = "POST"; out.header["Content-Type"] = out.header["Content-Type"] || "application/x-www-form-urlencoded"; out.body = out.body ? out.body + "&" + arg : arg; state = ""; break; case "user": out.header["Authorization"] = "Basic " + btoa(arg); state = ""; break; case "method": out.method = arg; state = ""; break; case "cookie": out.header["Set-Cookie"] = arg; state = ""; break; } break; } }); return out; }; // Should be different if logged in :| const AppCreator = (defaultprops) => { const { globalUrl, isLoaded } = defaultprops; const classes = useStyles(); const alert = useAlert(); const theme = useTheme(); const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) props.match = {} props.match.params = params var upload = ""; const increaseAmount = 50; const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"]; const actionBodyRequest = ["POST", "PUT", "PATCH"]; const authenticationOptions = [ "No authentication", "API key", "Bearer auth", "Basic auth", "Oauth2", "JWT", ]; const apikeySelection = ["Header", "Query"]; const [name, setName] = useState(""); const [contact, setContact] = useState(""); const [file, setFile] = useState(""); const [fileBase64, setFileBase64] = useState(""); 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] ); const [newWorkflowTags, setNewWorkflowTags] = React.useState([]); const [newWorkflowCategories, setNewWorkflowCategories] = React.useState([]); const [parameterName, setParameterName] = useState(""); const [parameterLocation, setParameterLocation] = useState( apikeySelection.length > 0 ? apikeySelection[0] : "" ); const [refreshUrl, setRefreshUrl] = useState(""); const [oauth2Scopes, setOauth2Scopes] = useState([]); 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({}); const [actions, setActions] = useState([]); 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 = { name: "", type: "header", example: "", }; const [extraAuth, setExtraAuth] = useState([]); const [app, setApp] = useState({}); const [appAuthentication, setAppAuthentication] = React.useState([]); const [selectedAction, setSelectedAction] = useState({}); const [authLoaded, setAuthLoaded] = useState(false); //const [actions, setActions] = useState([{ // "name": "Get workflows", // "description": "Get workflows", // "url": "/workflows", // "headers": "", // "queries": [], // "paths": [], // "body": "", // "errors": ["wutface", "WOAH"], // "method": actionNonBodyRequest[0], //}, { // "name": "Get workflow", // "description": "Get workflow", // "url": "/workflows/{id}", // "headers": "", // "queries": [], // "paths": ["id"], // "body": "", // "errors": ["wutface", "WOAH"], // "method": actionNonBodyRequest[0], //}, // //]) const [currentActionMethod, setCurrentActionMethod] = useState( actionNonBodyRequest[0] ) const [currentAction, setCurrentAction] = useState({ name: "", file_field: "", description: "", url: "", headers: "", paths: [], queries: [], body: "", errors: [], example_response: "", method: actionNonBodyRequest[0], }); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; useEffect(() => { if (window.location.pathname.includes("apps/edit")) { setIsEditing(true); handleEditApp(); } else { checkQuery(); } }, []); const handleEditApp = () => { fetch(globalUrl + "/api/v1/apps/" + props.match.params.appid + "/config", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { window.location.pathname = "/apps"; } return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { alert.error("Failed to get the app"); setIsAppLoaded(true); window.location.pathname = "/search"; } else { parseIncomingOpenapiData(responseJson); } }) .catch((error) => { alert.error(error.toString()); }); }; // Checks if there is an ID in the query, and gets it if it doesn't exist. const checkQuery = () => { var urlParams = new URLSearchParams(window.location.search); if (!urlParams.has("id")) { setActionAmount(0); setIsAppLoaded(true); return; } fetch(globalUrl + "/api/v1/get_openapi/" + urlParams.get("id"), { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { throw new Error("NOT 200 :O"); } return response.json(); }) .then((responseJson) => { setIsAppLoaded(true); if (!responseJson.success) { alert.error("Failed to verify"); } else { parseIncomingOpenapiData(responseJson); } }) .catch((error) => { console.log("Error: ", error.toString()); alert.error(error.toString()); }); }; const setFileFromb64 = () => { //const img = document.getElementById('logo') //var canvas = document.createElement('canvas') //var ctx = canvas.getContext('2d') //img.onload = function() { // console.log("LOADED?") // ctx.drawImage(img, 0, 0) // const canvasUrl = canvas.toDataURL() // console.log(canvasUrl) // setFileBase64(canvasUrl) //} }; const handleGetRef = (parameter, data) => { try { if (parameter === null || parameter["$ref"] === undefined) { //console.log("$ref not found in getref for: ", parameter) return parameter; } } catch (e) { console.log("Failed getting $ref of ", parameter); return parameter; } const paramsplit = parameter["$ref"].split("/"); if (paramsplit[0] !== "#") { console.log("Bad param: ", paramsplit); return parameter; } var newitem = data; for (var key in paramsplit) { var tmpparam = paramsplit[key]; if (tmpparam === "#") { continue; } if (newitem[tmpparam] === undefined) { return parameter; } newitem = newitem[tmpparam]; } return newitem; }; const base64_decode = (str) => { return decodeURIComponent( atob(str) .split("") .map(function (c) { return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); }) .join("") ); }; // Sets the data up as it should be at later points // This is the data FROM the database, not what's being saved const parseIncomingOpenapiData = (data) => { var parsedDecoded = "" try { const decoded = base64_decode(data.openapi) parsedDecoded = decoded } catch (e) { console.log("Failed JSON parsing: ", e) parsedDecoded = data } if (data.openapi === null) { alert.info("Failed to load OpenAPI for app. Please contact support if this persists.") setIsAppLoaded(true); return } console.log("Decoded: ", parsedDecoded) const parsedapp = data.openapi === undefined || data.openapi === null ? data : JSON.parse(parsedDecoded); data = parsedapp.body === undefined ? parsedapp : parsedapp.body; var jsonvalid = false; var tmpvalue = ""; try { data = JSON.parse(data); jsonvalid = true; } catch (e) { console.log("Error JSON: ", e); } if (!jsonvalid) { try { data = YAML.parse(data); jsonvalid = true; } catch (e) { console.log("Error YAML: ", e); } } if (!jsonvalid) { alert.info("OpenAPI data is invalid."); return; } setBasedata(data); console.log("Info: ", data) if (data.info !== null && data.info !== undefined) { if (data.info.title !== undefined && data.info.title !== null) { if (data.info.title.length > 29) { setName(data.info.title.slice(0, 29)); } else { setName(data.info.title); } } setDescription(data.info.description); document.title = "Apps - " + data.info.title; if (data.info["x-logo"] !== undefined) { if (data.info["x-logo"].url !== undefined) { //console.log("PARSED LOGO: ", data.info["x-logo"].url); setFileBase64(data.info["x-logo"].url); } else { setFileBase64(data.info["x-logo"]); } //console.log(""); //console.log(""); //console.log("LOGO: ", data.info["x-logo"]); //console.log(""); //console.log(""); } if (data.info.contact !== undefined) { setContact(data.info.contact); } if ( data.info["x-categories"] !== undefined && data.info["x-categories"].length > 0 ) { if (typeof data.info["x-categories"] === "array") { } else { } setNewWorkflowCategories(data.info["x-categories"]); } } console.log("Tags: ", data.tags) if (data.tags !== undefined && data.tags.length > 0) { var newtags = []; for (var key in data.tags) { if (data.tags[key].name.length > 50) { console.log( "Skipping tag because it's too long: ", data.tags[key].name.length ); continue; } newtags.push(data.tags[key].name); } if (newtags.length > 10) { newtags = newtags.slice(0, 9); } setNewWorkflowTags(newtags); } // This is annoying (: var securitySchemes = data.components.securityDefinitions; if (securitySchemes === undefined) { securitySchemes = data.securitySchemes; } if (securitySchemes === undefined) { securitySchemes = data.components.securitySchemes; } const allowedfunctions = [ "GET", "CONNECT", "HEAD", "DELETE", "POST", "PATCH", "PUT", ]; var newActions = []; var wordlist = {}; var all_categories = []; console.log("Paths: ", data.paths) if (data.paths !== null && data.paths !== undefined) { for (let [path, pathvalue] of Object.entries(data.paths)) { for (let [method, methodvalue] of Object.entries(pathvalue)) { if (methodvalue === null) { alert.info("Skipped method (null)" + method); continue; } if (!allowedfunctions.includes(method.toUpperCase())) { // Typical YAML issue if (method !== "parameters") { console.log("Invalid method: ", method, "data: ", methodvalue); alert.info("Skipped method (not allowed): " + method); } continue; } //console.log("METHOD: ", methodvalue) var tmpname = methodvalue.summary; if ( methodvalue.operationId !== undefined && methodvalue.operationId !== null && methodvalue.operationId.length > 0 && (tmpname === undefined || tmpname.length === 0) ) { tmpname = methodvalue.operationId; } if (tmpname !== undefined && tmpname !== null) { tmpname = tmpname.replaceAll(".", " "); } if ((tmpname === undefined || tmpname === null) && methodvalue.description !== undefined && methodvalue.description !== null && methodvalue.description.length > 0) { tmpname = methodvalue.description.replaceAll(".", " ").replaceAll("_", " ") } var newaction = { name: tmpname, description: methodvalue.description, url: path, file_field: "", method: method.toUpperCase(), headers: "", queries: [], paths: [], body: "", errors: [], example_response: "", }; if (newaction.url !== undefined && newaction.url !== null && newaction.url.includes("_shuffle_replace_")) { const regex = /_shuffle_replace_\d/i; //console.log("NEW: ", newaction.url = newaction.url.replace(regex, "") } // Finding category if (path.includes("/")) { const pathsplit = path.split("/"); var categoryindex = -1; // Stupid way of finding a category/grouping for (var key in pathsplit) { if ( pathsplit[key].length > 0 && pathsplit[key] !== "v1" && pathsplit[key] !== "v2" && pathsplit[key] !== "api" && pathsplit[key] !== "1.0" && pathsplit[key] !== "apis" ) { newaction["category"] = pathsplit[key]; if (!all_categories.includes(pathsplit[key])) { all_categories.push(pathsplit[key]); } break; } } } if (path === "/files/{file_id}/content") { console.log("FILE DOWNLOAD Method: ", path, method, methodvalue) } // Typescript? I think not ;) if (methodvalue["requestBody"] !== undefined) { if (methodvalue["requestBody"]["$ref"] !== undefined && methodvalue["requestBody"]["$ref"] !== null) { // Handle ref // console.log("Ref: ", methodvalue["requestBody"]["$ref"]) const parameter = handleGetRef({ $ref: methodvalue["requestBody"]["$ref"]}, data); console.log("PARAM: ", parameter) if (parameter.content !== undefined && parameter.content !== null) { methodvalue["requestBody"]["content"] = parameter.content console.log("Set content!") } } if (methodvalue["requestBody"]["content"] !== undefined) { // Handle content - XML or JSON // if ( methodvalue["requestBody"]["content"]["application/json"] !== undefined ) { //newaction["headers"] = "" //"Content-Type=application/json\nAccept=application/json"; if ( 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, propvalue] of Object.entries( methodvalue["requestBody"]["content"]["application/json"]["schema"]["properties"] )) { tmpobject[prop] = `\$\{${prop}\}`; } //console.log("Data: ", data) for (var subkey in methodvalue["requestBody"]["content"][ "application/json" ]["schema"]["required"]) { const tmpitem = methodvalue["requestBody"]["content"][ "application/json" ]["schema"]["required"][subkey]; tmpobject[tmpitem] = `\$\{${tmpitem}\}`; } newaction["body"] = JSON.stringify(tmpobject, null, 2); } else if ( methodvalue["requestBody"]["content"]["application/json"]["schema"]["$ref"] !== undefined && methodvalue["requestBody"]["content"]["application/json"]["schema"]["$ref"] !== null) { const retRef = handleGetRef( methodvalue["requestBody"]["content"]["application/json"][ "schema" ], data ); var newbody = {}; // Can handle default, required, description and type for (var propkey in retRef.properties) { console.log("replace: ", propkey) const parsedkey = propkey.replaceAll(" ", "_").toLowerCase(); newbody[parsedkey] = "${" + parsedkey + "}"; } newaction["body"] = JSON.stringify(newbody, null, 2); } } } else if ( methodvalue["requestBody"]["content"]["application/xml"] !== undefined ) { console.log("METHOD XML: ", methodvalue); //newaction["headers"] = "" //"Content-Type=application/xml\nAccept=application/xml"; if ( methodvalue["requestBody"]["content"]["application/xml"][ "schema" ] !== undefined && methodvalue["requestBody"]["content"]["application/xml"][ "schema" ] !== null ) { if ( methodvalue["requestBody"]["content"]["application/xml"][ "schema" ]["properties"] !== undefined ) { var tmpobject = {}; for (let [prop, propvalue] of Object.entries( methodvalue["requestBody"]["content"]["application/xml"][ "schema" ]["properties"] )) { tmpobject[prop] = `\$\{${prop}\}`; } for (var subkey in methodvalue["requestBody"]["content"][ "application/xml" ]["schema"]["required"]) { const tmpitem = methodvalue["requestBody"]["content"][ "application/xml" ]["schema"]["required"][subkey]; tmpobject[tmpitem] = `\$\{${tmpitem}\}`; } //console.log("OBJ XML: ", tmpobject) //newaction["body"] = XML.stringify(tmpobject, null, 2) } } } else { if ( methodvalue["requestBody"]["content"]["example"] !== undefined ) { if ( methodvalue["requestBody"]["content"]["example"][ "example" ] !== undefined ) { newaction["body"] = methodvalue["requestBody"]["content"]["example"][ "example" ]; //JSON.stringify(tmpobject, null, 2) } } if ( methodvalue["requestBody"]["content"][ "multipart/form-data" ] !== undefined ) { if ( methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"] !== undefined && methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"] !== null ) { if ( methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"]["type"] === "object" ) { const fieldname = methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"]["properties"]["fieldname"]; if (fieldname !== undefined) { //console.log("FIELDNAME: ", fieldname); newaction.file_field = fieldname["value"]; } else { for (const [subkey, subvalue] of Object.entries(methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]["properties"])) { if (subkey.includes("file")) { console.log("Found subkey field for file: ", path, method, methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]["properties"]) newaction.file_field = subkey break } } if (newaction.file_field === undefined || newaction.file_field === null || newaction.file_field.length === 0) { console.log("No file fieldname found: ", methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]["properties"]) } } } else { console.log("No type found: ", methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]) } } } else { var schemas = []; const content = methodvalue["requestBody"]["content"]; if (content !== undefined && content !== null) { //console.log("CONTENT: ", content) for (const [subkey, subvalue] of Object.entries(content)) { if (subvalue["schema"] !== undefined && subvalue["schema"] !== null) { console.log("SCHEMA: ", subvalue["schema"]) if (subvalue["schema"]["$ref"] !== undefined && subvalue["schema"]["$ref"] !== null) { console.log("SCHEMA FOUND REF!") if (!schemas.includes(subvalue["schema"]["$ref"])) { schemas.push(subvalue["schema"]["$ref"]); } } } else { if (subvalue["example"] !== undefined && subvalue["example"] !== null) { newaction["body"] = subvalue["example"] } else { console.log("ERROR: couldn't find schema for ", subvalue, method, path); } } } } if (schemas.length === 1) { const parameter = handleGetRef({ $ref: schemas[0] }, data); if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (var propkey in parameter.properties) { console.log("propkey2: ", propkey) const parsedkey = propkey.replaceAll(" ", "_").toLowerCase(); if (parameter.properties[propkey].type === undefined) { console.log( "Skipping (4): ", parameter.properties[propkey] ); continue; } if (parameter.properties[propkey].type === "string") { if ( parameter.properties[propkey].description !== undefined ) { newbody[parsedkey] = parameter.properties[propkey].description; } else { newbody[parsedkey] = ""; } } else if ( parameter.properties[propkey].type.includes("int") || parameter.properties[propkey].type.includes("uint64") ) { newbody[parsedkey] = 0; } else if ( parameter.properties[propkey].type.includes("boolean") ) { newbody[parsedkey] = false; } else if ( parameter.properties[propkey].type.includes("array") ) { newbody[parsedkey] = []; } else { console.log( "CANT HANDLE JSON TYPE (4)", parameter.properties[propkey].type, parameter.properties[propkey], path ); newbody[parsedkey] = []; } } newaction["body"] = JSON.stringify(newbody, null, 2); } else { console.log( "CANT HANDLE PARAM: (4) ", parameter.properties, path ); } } } } } } // HAHAHA wtf is this. if ( methodvalue.responses !== undefined && methodvalue.responses !== null ) { if (methodvalue.responses.default !== undefined) { if (methodvalue.responses.default.content !== undefined) { if ( methodvalue.responses.default.content["text/plain"] !== undefined ) { console.log("RESP: ", path, methodvalue.responses.default.content["text/plain"]) if ( methodvalue.responses.default.content["text/plain"][ "schema" ] !== undefined ) { if ( methodvalue.responses.default.content["text/plain"][ "schema" ]["example"] !== undefined ) { newaction.example_response = methodvalue.responses.default.content["text/plain"][ "schema" ]["example"] } if (methodvalue.responses.default.content["text/plain"]["schema"]["format"] === "binary" && methodvalue.responses.default.content["text/plain"]["schema"]["type"] === "string") { newaction.example_response = "shuffle_file_download" } } } } } else { var selectedReturn = ""; if (methodvalue.responses["200"] !== undefined) { selectedReturn = "200"; } else if (methodvalue.responses["201"] !== undefined) { selectedReturn = "201"; } // Parsing examples. This should be standardized lol if (methodvalue.responses[selectedReturn] !== undefined) { const selectedExample = methodvalue.responses[selectedReturn]; if (selectedExample["content"] !== undefined) { if ( selectedExample["content"]["application/json"] !== undefined ) { if ( selectedExample["content"]["application/json"]["schema"] !== undefined && selectedExample["content"]["application/json"]["schema"] !== null ) { console.log("JSON: ", selectedExample["content"]["application/json"]["schema"]) if (selectedExample["content"]["application/json"]["schema"]["$ref"] !== undefined) { //console.log("REF EXAMPLE: ", selectedExample["content"]["application/json"]["schema"]) const parameter = handleGetRef( selectedExample["content"]["application/json"][ "schema" ], data ); //console.log("GOT REF RETURN AS EXAMPLE: ", parameter) if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (var propkey in parameter.properties) { console.log("propkey3: ", propkey) const parsedkey = propkey.replaceAll(" ", "_").toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { console.log( "Skipping (1): ", parameter.properties[propkey] ); continue; } if ( parameter.properties[propkey].type === "string" ) { if ( parameter.properties[propkey].description !== undefined ) { newbody[parsedkey] = parameter.properties[propkey].description; } else { newbody[parsedkey] = ""; } } else if ( parameter.properties[propkey].type.includes("int") ) { newbody[parsedkey] = 0; } else if ( parameter.properties[propkey].type.includes( "boolean" ) ) { newbody[parsedkey] = false; } else if ( parameter.properties[propkey].type.includes( "array" ) ) { //console.log("Added empty array. Base is: ", parameter.properties[propkey].type) //const parameter = handleGetRef(selectedExample["content"]["application/json"]["schema"], data) newbody[parsedkey] = []; } else { console.log( "CANT HANDLE JSON TYPE ", parameter.properties[propkey].type, parameter.properties[propkey] ); newbody[parsedkey] = []; } } newaction.example_response = JSON.stringify( newbody, null, 2 ); } else { console.log( "CANT HANDLE PARAM: (1) ", parameter.properties ); } } else { // Just selecting the first one. bleh. if ( selectedExample["content"]["application/json"][ "schema" ]["allOf"] !== undefined ) { //console.log("ALLOF: ", selectedExample["content"]["application/json"]["schema"]["allOf"]) //console.log("BAD EXAMPLE: (SKIP ALLOF) ", selectedExample["content"]["application/json"]["schema"]["allOf"]) var selectedComponent = selectedExample["content"]["application/json"][ "schema" ]["allOf"]; if (selectedComponent.length >= 1) { selectedComponent = selectedComponent[0]; const parameter = handleGetRef( selectedComponent, data ); if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (var propkey in parameter.properties) { console.log("propkey4: ", propkey) const parsedkey = propkey.replaceAll(" ", "_").toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { console.log( "Skipping (2): ", parameter.properties[propkey] ); continue; } if ( parameter.properties[propkey].type === "string" ) { if ( parameter.properties[propkey] .description !== undefined ) { newbody[parsedkey] = parameter.properties[propkey].description; } else { newbody[parsedkey] = ""; } } else if ( parameter.properties[propkey].type.includes( "int" ) ) { newbody[parsedkey] = 0; } else if ( parameter.properties[propkey].type.includes( "boolean" ) ) { newbody[parsedkey] = false; } else { console.log( "CANT HANDLE JSON TYPE (2) ", parameter.properties[propkey].type ); newbody[parsedkey] = []; } } newaction.example_response = JSON.stringify( newbody, null, 2 ); //newaction.example_response = JSON.stringify(parameter.properties, null, 2) } else { //newaction.example_response = parameter.properties console.log( "CANT HANDLE PARAM: (3) ", parameter.properties ); } } else { } } else if ( selectedExample["content"]["application/json"][ "schema" ]["properties"] !== undefined ) { if ( selectedExample["content"]["application/json"][ "schema" ]["properties"]["data"] !== undefined ) { const parameter = handleGetRef( selectedExample["content"]["application/json"][ "schema" ]["properties"]["data"], data ); if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (var propkey in parameter.properties) { console.log("propkey5: ", propkey) const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { console.log( "Skipping (3): ", parameter.properties[propkey] ); continue; } if ( parameter.properties[propkey].type === "string" ) { if ( parameter.properties[propkey] .description !== undefined ) { newbody[parsedkey] = parameter.properties[propkey].description; } else { newbody[parsedkey] = ""; } console.log(parameter.properties[propkey]); } else if ( parameter.properties[propkey].type.includes( "int" ) ) { newbody[parsedkey] = 0; } else { console.log( "CANT HANDLE JSON TYPE (3) ", parameter.properties[propkey].type ); newbody[parsedkey] = []; } } newaction.example_response = JSON.stringify( newbody, null, 2 ); //newaction.example_response = JSON.stringify(parameter.properties, null, 2) } else { //newaction.example_response = parameter.properties console.log( "CANT HANDLE PARAM: (3) ", parameter.properties ); } } } } } } } } } } for (var key in methodvalue.parameters) { const parameter = handleGetRef(methodvalue.parameters[key], data); if (parameter.in === "query") { var tmpaction = { description: parameter.description, name: parameter.name, required: parameter.required, in: "query", }; if (parameter.required === undefined) { tmpaction.required = false; } newaction.queries.push(tmpaction); } else if (parameter.in === "path") { // FIXME - parse this to the URL too newaction.paths.push(parameter.name); // FIXME: This doesn't follow OpenAPI3 exactly. // https://swagger.io/docs/specification/describing-request-body/ // https://swagger.io/docs/specification/describing-parameters/ // Need to split the data. } else if (parameter.in === "body") { // FIXME: Add tracking for components // E.G: https://raw.githubusercontent.com/owentl/Shuffle/master/gosecure.yaml if (parameter.example !== undefined) { newaction.body = parameter.example; } } else if (parameter.in === "header") { newaction.headers += `${parameter.name}=${parameter.example}\n`; } else { console.log( "WARNING: don't know how to handle this param: ", parameter ); } } if (newaction.name === "" || newaction.name === undefined) { // Find a unique part of the string // FIXME: Looks for length between /, find the one where they differ // Should find others with the same START to their path // Make a list of reserved names? Aka things that show up only once if (Object.getOwnPropertyNames(wordlist).length === 0) { for (let [newpath, pathvalue] of Object.entries(data.paths)) { const newpathsplit = newpath.split("/"); for (var key in newpathsplit) { const pathitem = newpathsplit[key].toLowerCase(); if (wordlist[pathitem] === undefined) { wordlist[pathitem] = 1; } else { wordlist[pathitem] += 1; } } } } //console.log("WORDLIST: ", wordlist) // Remove underscores and make it normal with upper case etc const urlsplit = path.split("/"); if (urlsplit.length > 0) { var curname = ""; for (var key in urlsplit) { var subpath = urlsplit[key]; if (wordlist[subpath] > 2 || subpath.length < 1) { continue; } curname = subpath; break; } // FIXME: If name exists, // FIXME: Check if first part of parsedname is verb, otherwise use method const parsedname = curname .split("_") .join(" ") .split("-") .join(" ") .split("{") .join(" ") .split("}") .join(" ") .trim(); if (parsedname.length === 0) { newaction.errors.push("Missing name"); } else { const newname = method.charAt(0).toUpperCase() + method.slice(1) + " " + parsedname; const searchactions = newActions.find( (data) => data.name === newname ); console.log("SEARCH: ", searchactions); if (searchactions !== undefined) { newaction.errors.push("Missing name"); } else { newaction.name = newname; } } } else { newaction.errors.push("Missing name"); } } newActions.push(newaction); } } if (data.servers !== undefined && data.servers.length > 0) { var firstUrl = data.servers[0].url; if ( firstUrl.includes("{") && firstUrl.includes("}") && data.servers[0].variables !== undefined ) { const regex = /{\w+}/g; const found = firstUrl.match(regex); if (found !== null) { for (var key in found) { const item = found[key].slice(1, found[key].length - 1); const foundVar = data.servers[0].variables[item]; if (foundVar["default"] !== undefined) { firstUrl = firstUrl.replace(found[key], foundVar["default"]); } } } } if (firstUrl.endsWith("/")) { setBaseUrl(firstUrl.slice(0, firstUrl.length - 1)); } else { setBaseUrl(firstUrl); } } } //console.log("SECURITYSCHEMES: ", securitySchemes) if (securitySchemes !== undefined) { console.log("NEWAUTH: ", securitySchemes) // FIXME: Should add Oauth2 (Microsoft) and JWT (Wazuh) //console.log("SECURITY: ", securitySchemes) //if (Object.entries(securitySchemes) > 1 && var newauth = []; try { var optionset = false for (const [key, value] of Object.entries(securitySchemes)) { console.log(key, value); if (key === "jwt") { setAuthenticationOption("JWT"); setAuthenticationRequired(true); if ( value.in !== undefined && value.in !== null && value.in.length > 0 ) { setParameterName(value.in); optionset = true } } else if (value.scheme === "bearer") { setAuthenticationOption("Bearer auth"); setAuthenticationRequired(true); optionset = true } else if (key === "ApiKeyAuth" || key === "Token" || ((value.in === "header" || value.in === "query") && value.name !== undefined)) { //if (optionset === false) { // optionset = true //} if (optionset === false) { optionset = true value.in = value.in.charAt(0).toUpperCase() + value.in.slice(1) setParameterLocation(value.in); if (!apikeySelection.includes(value.in)) { console.log("APIKEY SELECT: ", apikeySelection); alert.error("Might be error in setting up API key authentication"); } console.log("PARAM NAME: ", value.name); setAuthenticationOption("API key"); setParameterName(value.name); setAuthenticationRequired(true); newauth.push({ "name": key, "type": value.in.toLowerCase(), "in": value.in.toLowerCase(), "example": "", }) } else { newauth.push({ "name": key, "type": value.in.toLowerCase(), "in": value.in.toLowerCase(), "example": "", }) } if (value.description !== undefined && value.description !== null && value.description.length > 0) { // Don't want a real description - just the ones we're replacing with if ((value.description.split(" ").length - 1) <= 2) { setRefreshUrl(value.description) } } } else if (value.scheme === "basic") { setAuthenticationOption("Basic auth"); setAuthenticationRequired(true); optionset = true } else if (value.scheme === "oauth2") { setAuthenticationOption("Oauth2"); setAuthenticationRequired(true); optionset = true } else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) { //alert.info("Can't handle Oauth2 auth yet.") setAuthenticationOption("Oauth2"); setAuthenticationRequired(true); optionset = true //console.log("FLOW-1: ", value) const flowkey = value.flow === undefined ? "flows" : "flow"; //console.log("FLOW: ", value[flowkey]) const basekey = value[flowkey].authorizationCode !== undefined ? "authorizationCode" : "implicit"; //console.log("FLOW2: ", value[flowkey][basekey]) if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined ) { if ( value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0 ) { setParameterName(value[flowkey][basekey].authorizationUrl); } var tokenUrl = ""; if (value[flowkey][basekey].tokenUrl !== undefined) { setParameterLocation(value[flowkey][basekey].tokenUrl); tokenUrl = value[flowkey][basekey].tokenUrl; } else { setParameterLocation(""); } if (value[flowkey][basekey].refreshUrl !== undefined) { setRefreshUrl(value[flowkey][basekey].refreshUrl); } else if (tokenUrl.length > 0) { setRefreshUrl(tokenUrl); } if ( value[flowkey][basekey].scopes !== undefined && value[flowkey][basekey].scopes !== null ) { if (value[flowkey][basekey].scopes.length > 0) { setOauth2Scopes(value[flowkey][basekey].scopes); } else { var newscopes = []; for (let [scopekey, scopevalue] of Object.entries( value[flowkey][basekey].scopes )) { if (scopekey.startsWith("http")) { const scopekeysplit = scopekey.split("/"); if (scopekeysplit.length < 5) { console.log("Skipping scope: ", scopekey); alert.info("Skipping scope: " + scopekey); continue; } //console.log("Checking scope for: ", scopekey, scopekeysplit.length) } newscopes.push(scopekey); } setOauth2Scopes(newscopes); } } } else { console.log( "Bad flowkey and basekey for oauth2: ", flowkey, basekey ); } } else { alert.error("Couldn't handle AUTH type: ", key); //newauth.push({ // "name": key, // "type": value.in, // "example": "", //}) } } } catch (e) { alert.error("Failed to handle auth") console.log("Error: ", e) } if (newauth.length > 0) { newauth = newauth.filter(data => data.name != "ApiKeyAuth") setExtraAuth(newauth); } } if (newActions.length > increaseAmount - 1) { setActionAmount(increaseAmount); } else { setActionAmount(newActions.length); } //const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" if (newActions.length > 1000 && isCloud) { alert.error( "Cut down actions from " + newActions.length + " to 999 because of limit" ); newActions = newActions.slice(0, 999); } setProjectCategories(all_categories); setActions(newActions); setFilteredActions(newActions); setIsAppLoaded(true); }; // Saving the app that's been configured. // Save SAVE app const submitApp = () => { alert.info("Uploading and building app " + name); setAppBuilding(true); setErrorCode(""); // Format the information const splitBase = baseUrl.split("/"); const host = splitBase[2]; const schemes = [splitBase[0]]; const basePath = "/" + splitBase.slice(3).join("/"); const data = { openapi: "3.0.0", info: { title: name, description: description, version: "1.0", "x-logo": fileBase64, }, servers: [{ url: baseUrl }], host: host, basePath: basePath, schemes: schemes, paths: {}, editing: isEditing, components: { securitySchemes: {}, }, id: props.match.params.appid, }; if (isEditing === false) { var urlParams = new URLSearchParams(window.location.search); if (urlParams !== undefined && urlParams !== null && urlParams.has("id")) { data.id = urlParams.get("id") } //id: props.match.params.appid, } if (basedata.info !== undefined && basedata.info.contact !== undefined) { data.info["contact"] = basedata.info.contact; } else if (contact === "") { data.info["contact"] = { name: "@Anonymous Shuffle User", url: "https://twitter.com/shuffleio", email: "support@shuffler.io", }; } else { data.info["contact"] = contact; } if (newWorkflowTags.length > 0) { var newtags = []; for (var key in newWorkflowTags) { newtags.push({ name: newWorkflowTags[key] }); } data["tags"] = newtags; } if (newWorkflowCategories.length > 0) { data["info"]["x-categories"] = newWorkflowCategories; } // Handles actions var handledPaths = [] for (var key in actions) { var item = JSON.parse(JSON.stringify(actions[key])) if (item.errors.length > 0) { alert.error("Saving with error in action " + item.name); } if (item.name === undefined && item.description !== undefined) { item.name = item.description; } // Basic way to allow multiple of the same path var pathjoin = item.url+"_"+item.method.toLowerCase() if (handledPaths.includes(pathjoin)) { console.log("ALREADY INCLUDED: ", pathjoin) // Max 100 of same lol for (var i = 0; i < 100; i++) { item.url = item.url+"_shuffle_replace_"+i pathjoin = item.url+"_"+item.method.toLowerCase() if (handledPaths.includes(pathjoin)) { continue } console.log("FOUND NEW: ", item.url) break } } handledPaths.push(pathjoin) if (data.paths[item.url] === null || data.paths[item.url] === undefined) { data.paths[item.url] = {}; } const regex = /[A-Za-z0-9 _]/g; if (item.name === undefined) { console.log("Skipping action ", item); continue; } const found = item.name.match(regex); if (found !== null) { item.name = found.join(""); } // Workaround for proper responses. No default as JSON for now data.paths[item.url][item.method.toLowerCase()] = { responses: { default: { description: "default", content: { "text/plain": { schema: { type: "string", example: "", }, }, }, }, }, summary: item.name, operationId: item.name.split(" ").join("_"), description: item.description, parameters: [], requestBody: { content: {}, }, }; //console.log("ACTION: ", item) if (item.example_response !== undefined && item.example_response !== null && item.example_response.length > 0) { if (item["example_response"] === "shuffle_file_download") { data.paths[item.url][item.method.toLowerCase()].responses["default"]["content"]["text/plain"].schema.type = "string" data.paths[item.url][item.method.toLowerCase()].responses["default"]["content"]["text/plain"].schema.format = "binary" /* schema: type: object properties: username: type: string avatar: # <-- image embedded into JSON type: string format: byte description: Base64-encoded contents of the avatar image */ } else { // FIXME: Shallow copy of the string var showResult = Object.assign("", item.example_response).trim(); showResult = showResult.split(" None").join(' "None"'); showResult = showResult.split("'").join('"'); showResult = showResult.split(" False").join(" false"); showResult = showResult.split(" True").join(" true"); var jsonvalid = true; try { const tmp = String(JSON.parse(showResult)); if (!showResult.includes("{") && !showResult.includes("[")) { jsonvalid = false; } } catch (e) { jsonvalid = false; } data.paths[item.url][item.method.toLowerCase()].responses["default"][ "content" ]["text/plain"].schema.type = "string"; if (jsonvalid) { // FIXME: Add a JSON parser here - don't run it as a string. data.paths[item.url][item.method.toLowerCase()].responses["default"][ "content" ]["text/plain"].schema.example = showResult; } else { data.paths[item.url][item.method.toLowerCase()].responses["default"][ "content" ]["text/plain"].schema.example = item.example_response; } } } if (item.queries.length > 0) { var skipped = false; var querynames = [] for (var querykey in item.queries) { const queryitem = item.queries[querykey]; if (queryitem === undefined || queryitem === null || queryitem.name === undefined || queryitem.name === null) { continue } // A fix for duplicate items if (querynames.includes(queryitem.name.toLowerCase())) { continue } querynames.push(queryitem.name.toLowerCase()) if (queryitem.name.toLowerCase() == "url") { console.log(item.name + " uses a bad query: url"); continue; //skipped = true //break } if (queryitem.name.toLowerCase() == "file_id") { item.queries[querykey].name = "fileid" continue; //skipped = true //break } if ( queryitem.name.toLowerCase() == "url" || queryitem.name.toLowerCase() == "body" || queryitem.name.toLowerCase() == "self" || queryitem.name.toLowerCase() == "query" || queryitem.name.toLowerCase() == "ssl_verify" || queryitem.name.toLowerCase() == "queries" || queryitem.name.toLowerCase() == "headers" || queryitem.name.toLowerCase() == "access_token" || queryitem.name.includes("[") || queryitem.name.includes("]") || queryitem.name.includes("{") || queryitem.name.includes("}") || queryitem.name.includes("(") || queryitem.name.includes(")") || queryitem.name.includes("!") || queryitem.name.includes("@") || queryitem.name.includes("#") || queryitem.name.includes("$") || queryitem.name.includes("%") || queryitem.name.includes("^") || queryitem.name.includes("&") || queryitem.name.includes(":") || queryitem.name.includes(";") || queryitem.name.includes("<") || queryitem.name.includes(">") || queryitem.name.includes('"') || queryitem.name.includes("'") ) { console.log( item.name + " error: uses a bad query - not adding: ", queryitem.name ) continue; } var newitem = { in: "query", name: queryitem.name, description: "Generated by shuffler.io OpenAPI", required: queryitem.required, schema: { type: "string", }, }; if (queryitem.example !== undefined) { newitem.example = queryitem.example } if (queryitem.description !== undefined) { newitem.description = queryitem.description; } data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); //console.log(queryitem) } // Bad code as it doesn't allow for "anything". if (skipped) { alert.info( "Bad configuration of " + item.name + ". Skipping because queries are invalid." ); continue; } } //data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem) if (item.paths.length > 0) { for (querykey in item.paths) { const queryitem = item.paths[querykey]; if (queryitem.toLowerCase() == "url") { queryitem = "action_url"; } if (queryitem.toLowerCase() == "apikey") { queryitem = "action_apikey"; } newitem = { in: "path", name: queryitem, description: "Generated by shuffler.io OpenAPI", required: true, schema: { type: "string", }, }; if (queryitem.description !== undefined) { newitem.description = queryitem.description; } data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); //console.log(queryitem) } } else { // Always goes here if they didn't click anything :/ const values = getCurrentPaths(item.url); const paths = values[0]; for (querykey in paths) { const queryitem = paths[querykey]; newitem = { in: "path", name: queryitem, description: "Generated by shuffler.io OpenAPI", required: false, schema: { type: "string", }, }; if (queryitem.description !== undefined) { newitem.description = queryitem.description; } data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); //console.log(queryitem) } } const methodname = item.method.toLowerCase() if (methodname === "post" || methodname === "put" || methodname === "patch") { if ( item.body !== undefined && item.body !== null && item.body.length > 0 ) { console.log("GOT BODY: ", item.url, item.method) //var pathjoin = item.url+"_"+item.method.toLowerCase() const required = false; newitem = { in: "body", name: "body", multiline: true, description: "Generated by shuffler.io OpenAPI", required: required, example: item.body, schema: { type: "string", }, }; // FIXME - add application/json if JSON example? data.paths[item.url][item.method.toLowerCase()]["requestBody"] = { description: "Generated by Shuffler.io", required: required, content: { example: { example: item.body, }, }, }; data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); } else if (actionBodyRequest.includes(item.method.toUpperCase())) { // Appending an empty field const required = false; newitem = { in: "body", name: "body", multiline: true, description: "Generated by shuffler.io OpenAPI", required: required, example: "", schema: { type: "string", }, }; // FIXME - add application/json if JSON example? data.paths[item.url][item.method.toLowerCase()]["requestBody"] = { description: "Generated by Shuffler.io", required: required, content: { example: { example: "", }, }, }; data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); } else { //console.log("Nothing to append?") } } // 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); data.paths[item.url][item.method.toLowerCase()]["requestBody"][ "content" ]["multipart/form-data"] = { schema: { type: "object", properties: { fieldname: { type: "string", value: item.file_field, }, }, }, }; console.log( data.paths[item.url][item.method.toLowerCase()]["requestBody"][ "content" ]["multipart/form-data"] ); } if (item.headers.length > 0) { const required = false; const headersSplit = item.headers.split("\n"); for (var key in headersSplit) { const header = headersSplit[key]; var key = ""; var value = ""; if (header.length > 0 && header.includes("= ")) { const headersplit = header.split("= "); key = headersplit[0]; value = headersplit[1]; } else if (header.length > 0 && header.includes(" =")) { const headersplit = header.split(" ="); key = headersplit[0]; value = headersplit[1]; } else if (header.length > 0 && header.includes("=")) { const headersplit = header.split("="); key = headersplit[0]; value = headersplit[1]; } else if (header.length > 0 && header.includes(": ")) { const headersplit = header.split(": "); key = headersplit[0]; value = headersplit[1]; } else if (header.length > 0 && header.includes(" :")) { const headersplit = header.split(" :"); key = headersplit[0]; value = headersplit[1]; } else if (header.length > 0 && header.includes(":")) { const headersplit = header.split(":"); key = headersplit[0]; value = headersplit[1]; } else { continue; } if (key.length > 0 && value.length > 0) { newitem = { in: "header", name: key, multiline: false, description: "Header generated by shuffler.io OpenAPI", required: false, example: value, schema: { type: "string", }, }; data.paths[item.url][item.method.toLowerCase()].parameters.push( newitem ); } } } } if (authenticationOption === "API key") { if (parameterName.length === 0) { alert.error("A field name for the APIkey must be defined"); setAppBuilding(false); return; } console.log("Paramname: ", parameterName) var newparamName = parameterName.replaceAll('"', ""); newparamName = newparamName.replaceAll("'", ""); data.components.securitySchemes["ApiKeyAuth"] = { type: "apiKey", in: parameterLocation.toLowerCase(), name: newparamName, description: refreshUrl, } console.log("Full auth component: ", data.components.securitySchemes["ApiKeyAuth"]) } else if (authenticationOption === "Bearer auth") { data.components.securitySchemes["BearerAuth"] = { type: "http", scheme: "bearer", bearerFormat: "UUID", }; } else if (authenticationOption === "JWT") { data.components.securitySchemes["jwt"] = { type: "http", scheme: "bearer", bearerFormat: "JWT", in: parameterName, }; console.log("SECURITYSCHEMES: ", data.components); } else if (authenticationOption === "Basic auth") { data.components.securitySchemes["BasicAuth"] = { type: "http", scheme: "basic", }; } else if (authenticationOption === "Oauth2") { console.log("oauth2: ", parameterName) var newparamName = parameterName.replaceAll('"', ""); newparamName = newparamName.replaceAll("'", ""); //parameterName, parameterValue, revocationUrl data.components.securitySchemes["Oauth2"] = { type: "oauth2", description: "Oauth2.0 authorizationCode authentication", flow: { authorizationCode: { authorizationUrl: newparamName, tokenUrl: parameterLocation, refreshUrl: refreshUrl, scopes: oauth2Scopes === undefined || oauth2Scopes === null ? [] : oauth2Scopes, }, }, }; } if (setExtraAuth.length > 0) { for (var key in extraAuth) { const curauth = extraAuth[key]; if (curauth.name.toLowerCase() == "url") { alert.error("Can't add extra auth with Name URL"); setAppBuilding(false); return; } data.components.securitySchemes[curauth.name] = { type: "apiKey", in: curauth.type, name: curauth.name, }; } } fetch(globalUrl + "/api/v1/verify_openapi", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(data, null, 4), credentials: "include", }) .then((response) => { //if (response.status !== 200) { // setErrorCode("An error occurred during validation") // throw new Error("NOT 200 :O") //} setAppBuilding(false); return response.json(); }) .then((responseJson) => { if (!responseJson.success) { if (responseJson.reason !== undefined) { setErrorCode(responseJson.reason); alert.error("Failed to verify: " + responseJson.reason); } } else { alert.success("Successfully uploaded openapi"); if (window.location.pathname.includes("/new")) { if (responseJson.id !== undefined && responseJson.id !== null) { window.location = `/apps/edit/${responseJson.id}`; } } } }) .catch((error) => { setAppBuilding(false); setErrorCode(error.toString()); alert.error(error.toString()); }); }; const bearerAuth = authenticationOption === "Bearer auth" ? (