import ReactJson from "react-json-view-ssr"; import React, { useState, useEffect, useRef, useCallback, memo, useContext} from "react"; import { toast } from "react-toastify"; import { Search as SearchIcon, } from "@mui/icons-material"; import AceEditor from "react-ace"; import "ace-builds/src-noconflict/mode-json"; import "ace-builds/src-noconflict/theme-gruvbox"; import { Paper, Button, Box, MenuItem, TextField, Tabs, Tab, Table, TableBody, TableCell, TableContainer, TableHead, Select, Typography, TableRow, InputAdornment, Divider, LinearProgress, Tooltip, } from "@mui/material"; import throttle from "lodash/throttle"; import {getTheme} from "../theme.jsx"; import { validateJson, collapseField, } from "../views/Workflows2.jsx"; import DeleteIcon from "@mui/icons-material/Delete"; import { Context } from "../context/ContextApi.jsx"; function CustomTabPanel(props) { const { children, value, index, ...other } = props; const {themeMode} = useContext(Context); const theme = getTheme(themeMode) return ( ); } function a11yProps(index) { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}`, }; } const RequestMethods = [ { value: "GET", color: "#61afee", }, { value: "POST", color: "#49cc90", }, { value: "DELETE", color: "#f93e3e", }, { value: "PUT", color: "#fca130", }, { value: "PATCH", color: "#50e3c2", }, { value: "CONNECT", color: "#ff69b4", }, { value: "HEAD", color: "#9012fe", }, ]; const ApiExplorer = memo(({ openapi, globalUrl, userdata, HandleApiExecution, selectedAppData, ConfigurationTab, isLoggedIn, isLoaded }) => { const [actions, setActions] = useState([]); const [info, setInfo] = useState({}); const [serverurl, setServerUrl] = useState(""); const [selectedActionIndex, setSelectedActionIndex] = useState(0); const [ExampleBody, setExampleBody] = useState({}); const [filteredActions, setFilteredActions] = useState([]); const {themeMode} = useContext(Context); const theme = getTheme(themeMode) const [firstSendDone, setFirstSendDone] = useState(false) const getJsonObject = (properties) => { let jsonObject = {}; for (let key in properties) { const property = properties[key]; let subloop = false; if (property.hasOwnProperty("type")) { if (property.type === "object" || property.type === "array") { subloop = true; } } if (subloop) { if ( property.hasOwnProperty("items") && property.items.hasOwnProperty("properties") ) { const jsonret = getJsonObject(property.items.properties); if (property.type === "array") { jsonObject[key] = [jsonret]; } else { jsonObject[key] = jsonret; } } else { if (property.hasOwnProperty("properties")) { const jsonret = getJsonObject(property.properties); if (property.type === "array") { jsonObject[key] = [jsonret]; } else { jsonObject[key] = jsonret; } } else { } } } else { if (property.hasOwnProperty("example")) { jsonObject[key] = property.example; } else if ( property.hasOwnProperty("enum") && property.enum.length > 0 ) { jsonObject[key] = property.enum[0]; } else if (property.hasOwnProperty("default")) { jsonObject[key] = property.default; } else if (property.hasOwnProperty("maximum")) { jsonObject[key] = property.maximum; } else if (property.hasOwnProperty("minimum")) { jsonObject[key] = property.minimum; } else if (property.hasOwnProperty("type")) { if (property.type === "integer" || property.type === "number") { jsonObject[key] = 0; } else if (property.type === "boolean") { jsonObject[key] = false; } else if (property.type === "string") { jsonObject[key] = ""; } else { } } else { } } } return jsonObject; }; const handleGetRef = (parameter, data) => { try { if (parameter === null || parameter["$ref"] === undefined) { return parameter; } } catch (e) { return parameter; } const paramsplit = parameter["$ref"].split("/"); if (paramsplit[0] !== "#") { return parameter; } var newitem = data; for (let paramkey in paramsplit) { var tmpparam = paramsplit[paramkey]; if (tmpparam === "#") { continue; } if (newitem[tmpparam] === undefined) { return parameter; } newitem = newitem[tmpparam]; } return newitem; }; useEffect(() => { if (openapi !== undefined && openapi !== null) { parseIncomingOpenapiData(openapi); } }, [openapi]); const parseIncomingOpenapiData = useCallback((data) => { if (data.info !== null && data.info !== undefined) { setInfo(data.info); } try { if (data.info !== null && data.info !== undefined) { if (data.info.title !== undefined && data.info.title !== null) { if (data.info.title.endsWith(" API")) { data.info.title = data.info.title.substring( 0, data.info.title.length - 4 ); } else if (data.info.title.endsWith("API")) { data.info.title = data.info.title.substring( 0, data.info.title.length - 3 ); } } document.title = data.info.title + " Rest API" if ( data.info["x-catefies"] !== undefined && data.info["x-categories"].length > 0 ) { if (Array.isArray(data.info["x-categories"])) { } else { } } } } catch (e) {} try { if (data.tags !== undefined && data.tags.length > 0) { var newtags = []; for (let tagkey in data.tags) { if (data.tags[tagkey]?.name.length > 50) { continue; } newtags.push(data.tags[tagkey]?.name); } if (newtags.length > 10) { newtags = newtags.slice(0, 9); } } } catch (e) {} // This is annoying (: // Weird generator problems to be handle var securitySchemes = undefined; try { if (data.securitySchemes !== undefined) { securitySchemes = data.securitySchemes; if (securitySchemes === undefined) { securitySchemes = data.securityDefinitions; } } if (securitySchemes === undefined && data.components !== undefined) { securitySchemes = data.components.securitySchemes; if (securitySchemes === undefined) { securitySchemes = data.components.securityDefinitions; } } } catch (e) {} const allowedfunctions = [ "GET", "CONNECT", "HEAD", "DELETE", "POST", "PATCH", "PUT", ]; var newActions = []; var wordlist = {}; var all_categories = []; var parentUrl = ""; 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) { continue; } if (!allowedfunctions.includes(method.toUpperCase())) { // Typical YAML issue if (method !== "parameters") { //toast("Skipped method (not allowed): " + method); } continue; } 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: "", 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; const regex = /_shuffle_replace_\d+/i; newaction.url = newaction.url.replaceAll( new RegExp(regex, "g"), "" ); } // Finding category if (path.includes("/")) { const pathsplit = path.split("/"); // Stupid way of finding a category/grouping for (let splitkey in pathsplit) { if (pathsplit[splitkey].includes("_shuffle_replace_")) { //const regex = /_shuffle_replace_\d/i; const regex = /_shuffle_replace_\d+/i; pathsplit[splitkey] = pathsplit[splitkey].replaceAll( new RegExp(regex, "g"), "" ); } if ( pathsplit[splitkey].length > 0 && pathsplit[splitkey] !== "v1" && pathsplit[splitkey] !== "v2" && pathsplit[splitkey] !== "api" && pathsplit[splitkey] !== "1.0" && pathsplit[splitkey] !== "apis" ) { newaction["category"] = pathsplit[splitkey]; if (!all_categories.includes(pathsplit[splitkey])) { all_categories.push(pathsplit[splitkey]); } break; } } } if (path === "/files/{file_id}/content") { } // Typescript? I think not ;) if (methodvalue["requestBody"] !== undefined) { if ( methodvalue["requestBody"]["$ref"] !== undefined && methodvalue["requestBody"]["$ref"] !== null ) { // Handle ref const parameter = handleGetRef( { $ref: methodvalue["requestBody"]["$ref"] }, data ); if ( parameter.content !== undefined && parameter.content !== null ) { methodvalue["requestBody"]["content"] = parameter.content; } } if (methodvalue["requestBody"]["content"] !== undefined) { // Handle content - XML or JSON // if ( methodvalue["requestBody"]["content"]["application/json"] !== undefined ) { if ( methodvalue["requestBody"]["content"]["application/json"][ "schema" ] !== undefined && methodvalue["requestBody"]["content"]["application/json"][ "schema" ] !== null ) { try { if ( methodvalue["requestBody"]["content"]["application/json"][ "schema" ]["properties"] !== undefined ) { // Read out properties from a JSON object const jsonObject = getJsonObject( methodvalue["requestBody"]["content"][ "application/json" ]["schema"]["properties"] ); if (jsonObject !== undefined && jsonObject !== null) { try { newaction["body"] = JSON.stringify( jsonObject, null, 2 ); } catch (e) {} } //newaction["body"] = JSON.stringify(jsonObject, null, 2); var tmpobject = {}; for (let prop of methodvalue["requestBody"]["content"][ "application/json" ]["schema"]["properties"]) { tmpobject[prop] = `\$\{${prop}\}`; } for (let 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 = {}; for (let propkey in retRef.properties) { const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); newbody[parsedkey] = "${" + parsedkey + "}"; } newaction["body"] = JSON.stringify(newbody, null, 2); } } catch (e) {} } } else if ( methodvalue["requestBody"]["content"]["application/xml"] !== undefined ) { //newaction["headers"] = "" //"Content-Type=application/xml\nAccept=application/xml"; if ( methodvalue["requestBody"]["content"]["application/xml"][ "schema" ] !== undefined && methodvalue["requestBody"]["content"]["application/xml"][ "schema" ] !== null ) { try { if ( methodvalue["requestBody"]["content"]["application/xml"][ "schema" ]["properties"] !== undefined ) { for (let [prop, propvalue] of Object.entries( methodvalue["requestBody"]["content"][ "application/xml" ]["schema"]["properties"] )) { tmpobject[prop] = `\$\{${prop}\}`; } for (let [subkey, subkeyval] in Object.entries( methodvalue["requestBody"]["content"][ "application/xml" ]["schema"]["required"] )) { const tmpitem = methodvalue["requestBody"]["content"][ "application/xml" ]["schema"]["required"][subkey]; tmpobject[tmpitem] = `\$\{${tmpitem}\}`; } //newaction["body"] = XML.stringify(tmpobject, null, 2) } } catch (e) {} } } else { if ( methodvalue["requestBody"]["content"]["example"] !== undefined ) { if ( methodvalue["requestBody"]["content"]["example"][ "example" ] !== undefined ) { newaction["body"] = methodvalue["requestBody"]["content"]["example"][ "example" ]; } } 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 ) { try { if ( methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"]["type"] === "object" ) { const fieldname = methodvalue["requestBody"]["content"][ "multipart/form-data" ]["schema"]["properties"]["fieldname"]; if (fieldname !== undefined) { 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")) { newaction.file_field = subkey; break; } } if ( newaction.file_field === undefined || newaction.file_field === null || newaction.file_field.length === 0 ) { } } } else { } } catch (e) {} } } else { var schemas = []; const content = methodvalue["requestBody"]["content"]; if (content !== undefined && content !== null) { for (const [subkey, subvalue] of Object.entries(content)) { if ( subvalue["schema"] !== undefined && subvalue["schema"] !== null ) { if ( subvalue["schema"]["$ref"] !== undefined && subvalue["schema"]["$ref"] !== null ) { if (!schemas.includes(subvalue["schema"]["$ref"])) { schemas.push(subvalue["schema"]["$ref"]); } } } else { if ( subvalue["example"] !== undefined && subvalue["example"] !== null ) { newaction["body"] = subvalue["example"]; } else { } } } } try { if (schemas.length === 1) { const parameter = handleGetRef( { $ref: schemas[0] }, data ); if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (let propkey in parameter.properties) { const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { 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 { newbody[parsedkey] = []; } } newaction["body"] = JSON.stringify(newbody, null, 2); } else { } } } catch (e) {} } } } } 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 ) { 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 ) { if ( selectedExample["content"]["application/json"][ "schema" ]["properties"] !== undefined && selectedExample["content"]["application/json"][ "schema" ]["properties"] !== null ) { const jsonObject = getJsonObject( selectedExample["content"]["application/json"][ "schema" ]["properties"] ); if (jsonObject !== undefined && jsonObject !== null) { try { newaction.example_response = JSON.stringify( jsonObject, null, 2 ); } catch (e) {} } } if ( selectedExample["content"]["application/json"][ "schema" ]["$ref"] !== undefined ) { const parameter = handleGetRef( selectedExample["content"]["application/json"][ "schema" ], data ); if ( parameter.properties !== undefined && parameter["type"] === "object" ) { var newbody = {}; for (let propkey in parameter.properties) { const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { 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" ) ) { //const parameter = handleGetRef(selectedExample["content"]["application/json"]["schema"], data) newbody[parsedkey] = []; } else { newbody[parsedkey] = []; } } newaction.example_response = JSON.stringify( newbody, null, 2 ); } else { } } else { // Just selecting the first one. bleh. if ( selectedExample["content"]["application/json"][ "schema" ]["allOf"] !== undefined ) { 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 (let propkey in parameter.properties) { const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { 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 { 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 } } 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 (let propkey in parameter.properties) { const parsedkey = propkey .replaceAll(" ", "_") .toLowerCase(); if ( parameter.properties[propkey].type === undefined ) { 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 { 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 } } } } } } } } } } for (let paramkey in methodvalue.parameters) { const parameter = handleGetRef( methodvalue.parameters[paramkey], data ); if (parameter.in === "query") { var tmpaction = { description: parameter.description, name: parameter?.name, required: parameter.required, in: "query", }; if ( parameter.example !== undefined && parameter.example !== null ) { tmpaction.example = parameter.example; } 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 && parameter.example !== null ) { if ( newaction.body === undefined || newaction.body === null || newaction.body.length < 5 ) { newaction.body = parameter.example; } } } else if (parameter.in === "header") { newaction.headers += `${parameter?.name}=${parameter.example}\n`; } else { } } // Check if body is valid JSON. if ( newaction.body !== undefined && newaction.body !== null && newaction.body.length > 0 ) { // Trim starting / ending newlines, spaces and tabs newaction.body = newaction.body.trim(); } 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 (let splitkey in newpathsplit) { const pathitem = newpathsplit[splitkey].toLowerCase(); if (wordlist[pathitem] === undefined) { wordlist[pathitem] = 1; } else { wordlist[pathitem] += 1; } } } } // Remove underscores and make it normal with upper case etc const urlsplit = path.split("/"); if (urlsplit.length > 0) { var curname = ""; for (let urlkey in urlsplit) { var subpath = urlsplit[urlkey]; 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 ); if (searchactions !== undefined) { newaction.errors.push("Missing name"); } else { newaction.name = newname; } } } else { newaction.errors.push("Missing name"); } } //newaction.action_label = "No Label" 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 (let foundkey in found) { const item = found[foundkey].slice(1, found[foundkey].length - 1); const foundVar = data.servers[0].variables[item]; if (foundVar["default"] !== undefined) { firstUrl = firstUrl.replace( found[foundkey], foundVar["default"] ); } } } } if (firstUrl.endsWith("/")) { parentUrl = firstUrl.slice(0, firstUrl.length - 1); } else { parentUrl = firstUrl; } } } var prefixCheck = "/v1"; if (parentUrl.includes("/")) { const urlsplit = parentUrl.split("/"); if (urlsplit.length > 2) { // Skip if http:// in it too prefixCheck = "/" + urlsplit.slice(3).join("/"); } if ( prefixCheck.length > 0 && prefixCheck !== "/" && prefixCheck.startsWith("/") ) { for (var actionKey in newActions) { const action = newActions[actionKey]; if ( action.url !== undefined && action.url !== null && action.url.startsWith(prefixCheck) ) { newActions[actionKey].url = action.url.slice( prefixCheck.length, action.url.length ); } } } } setServerUrl(parentUrl); var newActions2 = []; // Remove with duplicate action URLs for (var actionKey in newActions) { const action = newActions[actionKey]; if (action.url === undefined || action.url === null) { continue; } var found = false; for (var actionKey2 in newActions2) { const action2 = newActions2[actionKey2]; if (action2.url === undefined || action2.url === null) { continue; } if (action.url === action2.url) { found = true; break; } } if (!found) { newActions2.push(action); } else { newActions2.push(action); } } newActions = newActions2 // Rearrange them by which has action_label const firstActions = newActions.filter( (data) => data.action_label !== undefined && data.action_label !== null && data.action_label !== "No Label" ); const secondActions = newActions.filter( (data) => data.action_label === undefined || data.action_label === null || data.action_label === "No Label" ); newActions = firstActions.concat(secondActions) setActions(newActions) setExampleBody(newActions[0]?.body) }, [openapi]); return (
); }); export default ApiExplorer; const ActionResponseAndRequest = memo(({ isLoggedIn, isLoaded, ConfigurationTab, selectedAppData,actions, info, HandleApiExecution, userdata, filteredActions, setFilteredActions, serverurl, globalUrl, setSelectedActionIndex, ExampleBody, setExampleBody, selectedActionIndex}) => { const [apiResponse, setApiResponse] = useState({}); const [isLoading, setIsLoading] = useState(false); const loadAction = 10; const loadedAction = useRef(null); const loadMoreActions = useCallback(() => { if (isLoading || filteredActions.length >= actions.length) return; setIsLoading(true); setFilteredActions((prevActions) => { const newActions = actions.slice(prevActions.length, prevActions.length + loadAction); setIsLoading(false); return [...prevActions, ...newActions]; }); }, [isLoading, actions.length, filteredActions.length, loadAction]); // Scroll position reference const scrollPosition = useRef(0); // Handle scroll event with debounce const handleScroll = useCallback(() => { const actionContainer = loadedAction.current; if ( actionContainer && actionContainer.scrollTop + actionContainer.clientHeight >= actionContainer.scrollHeight - 10 ) { loadMoreActions(); } scrollPosition.current = actionContainer?.scrollTop || 0; }, [loadMoreActions]); // Add scroll event listener on mount and remove on unmount useEffect(() => { const actionContainer = loadedAction.current; if (actionContainer) { actionContainer.addEventListener("scroll", handleScroll); } return () => { if (actionContainer) { actionContainer.removeEventListener("scroll", handleScroll); } }; }, [handleScroll]); // Restore scroll position when the component rerenders or new items are added useEffect(() => { const actionContainer = loadedAction.current; if (actionContainer) { actionContainer.scrollTop = scrollPosition.current; } }, [actions, filteredActions]); useEffect(() => { if (actions?.length > 0 && filteredActions?.length === 0) { setFilteredActions(actions.slice(0, loadAction)); } }, [actions?.length]); return (
{filteredActions.map((action, index) => (
))}
)}) const ActionsList = memo(({ actions, selectedActionIndex, setSelectedActionIndex, setExampleBody, setFilteredActions, filteredActions, userdata, info, openapi, isLoggedIn, isLoaded }) => { const [searchQuery, setSearchQuery] = useState(""); const [visibleActions, setVisibleActions] = useState([]); const {themeMode} = useContext(Context); const theme = getTheme(themeMode); useEffect(() => { if (visibleActions?.length === 0 && actions?.length > 0) { setVisibleActions(actions) } }, [actions?.length]) const handleActionClick = (index, action) => { const actionId = action.name.replace(/ /g, "-").replace(/_/g, "-"); setSelectedActionIndex(index); setExampleBody(action.example_response); const actionIndex = actions.findIndex((act) => { const id = act.name.replace(/ /g, "-").replace(/_/g, "-"); return id === actionId; }); if (actionIndex !== -1 && !filteredActions.some((act) => { const id = act.name.replace(/ /g, "-").replace(/_/g, "-"); return id === actionId; })) { const newActionToLoad = [ ...filteredActions, ...actions.slice(filteredActions.length, actionIndex + 1) ]; setFilteredActions(newActionToLoad); } // Update URL hash and scroll to action window.history.pushState(null, "", `#${actionId}`); const actionElement = document.getElementById(actionId); if (actionElement) { actionElement.scrollIntoView({ behavior: "smooth", block: "start" }); } }; const handleSearch = (e) => { const query = e?.target?.value?.toLowerCase().replaceAll("_", " "); setSearchQuery(query) if (query.length === 0) { setVisibleActions(actions) } else { setVisibleActions( actions?.filter((action) => action?.name?.toLowerCase()?.replaceAll("_", " ")?.includes(searchQuery) ) ) } } return (
{info?.title ? (
0 ? openapi?.info["x-logo"] : theme?.palette?.defaultImage} width={48} height={48} alt="app logo" style={{ marginLeft: 20, borderRadius: 8 }} /> {info.title}
) : ( Api Explorer )}
), style: { height: "100%", marginTop: 10, width: '90%', }, }} sx={{ marginLeft: 2, width:'100%', "& .MuiOutlinedInput-root fieldset": { border: "1px solid rgba(73, 73, 73, 1)", }, }} />
{visibleActions.length > 0 ? ( visibleActions.map((action, actionIndex) => ( )) ) : (
No actions found
)}
); }); const Action = memo(( { action, index, serverurl, setApiResponse, setExampleBody, globalUrl, info, setSelectedActionIndex, selectedActionIndex, HandleApiExecution, ConfigurationTab, }, ) => { const [RequestHeader, setRequestHeader] = useState([{ key: "Content-Type", value: "application/json" }]); const [RequestBody, setRequestBody] = useState(action?.body); const editorRef = useRef(null); const [AceEditorHeight, setAceEditorHeight] = useState(275) const [baseUrl, setBaseUrl] = useState(serverurl) const [path, setPath] = useState(action?.url) const inputRef = useRef(null); const [shouldChageInputFocus, setShouldChangeInputFocus] = useState(true); const [disableExecuteButton, setDisableExecuteButton] = useState(false); const [showResponseLoader, setShowResponseLoader] = useState(false); const [appAuthentication, setAppAuthentication] = useState([]) const {themeMode} = useContext(Context); const theme = getTheme(themeMode); const parseHeaders = (headersString) => { if (headersString?.length > 0) { const headersArray = headersString.split("\n"); const parsedHeaders = headersArray .map((header) => { const [key, value] = header.split("="); // Split by '=' to get key-value pairs // Only proceed if both key and value exist, and neither is undefined if (key && value) { return { key: key.trim(), value: value.trim() }; } return null; // Return null if the header is invalid }) .filter(Boolean); // Filter out any null values setRequestHeader(parsedHeaders); } }; useEffect(() => { if (action?.headers) { parseHeaders(action.headers); } }, []); const [RequestParams, setRequestParams] = useState([ { key: "", value: "", }, ]); const [curTab, setCurTab] = useState(0) const [actionUrl, setActionUrl] = useState(action?.url) const [selectedMethod, setSelectedMethod] = useState(action?.method) const fix_url = (newUrl) => { if (newUrl.includes("hhttp")) { newUrl = newUrl.replace("hhttp", "http"); } if (newUrl.includes("http:/") && !newUrl.includes("http://")) { newUrl = newUrl.replace("http:/", "http://"); } if (newUrl.includes("https:/") && !newUrl.includes("https://")) { newUrl = newUrl.replace("https:/", "https://"); } if (newUrl.includes("http:///")) { newUrl = newUrl.replace("http:///", "http://"); } if (newUrl.includes("https:///")) { newUrl = newUrl.replace("https:///", "https://"); } if (!newUrl.includes("http://") && !newUrl.includes("https://")) { newUrl = `http://${newUrl}`; } return newUrl; }; function isValidMethod(method) { const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]; method = method.toUpperCase(); if (validMethods.includes(method)) { return method; } else { throw new Error(`Invalid HTTP method: ${method}`); } } function fixHeader(headers) { if (Array.isArray(headers)) { return headers.reduce((acc, header) => { if (header.key.trim() !== "" || header.value.trim() !== "") { acc[header.key.trim()] = header.value.trim(); } return acc; }, {}); } const parsedHeaders = {}; if (typeof headers === 'string' && headers) { const splitHeaders = headers.split("\n"); splitHeaders.forEach(header => { let splitItem; if (header.includes(":")) { splitItem = ":"; } else if (header.includes("=")) { splitItem = "="; } else { return; } const splitHeader = header.split(splitItem); if (splitHeader.length >= 2) { const key = splitHeader[0].trim(); const value = splitHeader.slice(1).join(splitItem).trim(); parsedHeaders[key] = value; } }); } return parsedHeaders; } function fixParams(queries) { if (Array.isArray(queries)) { return queries .filter(query => query.key.trim() !== "" || query.value.trim() !== "") .map(query => ({ key: query.key.trim(), value: query.value.trim() })); } const parsedQueries = []; if (typeof queries === 'string') { if (!queries.trim()) return parsedQueries; const cleanedQueries = queries.trim().replace(/\s+/g, " "); const splittedQueries = cleanedQueries.split("&"); splittedQueries.forEach(query => { if (!query.includes("=")) { console.info("Skipping as there is no '=' in the query"); return; } const [key, value] = query.split("="); if (!key.trim() || !value.trim()) { console.info("Skipping because either key or value is not present in query"); return; } parsedQueries.push({ key: key.trim(), value: value.trim() }); }); } return parsedQueries; } async function prepareResponse(response) { try { const parsedHeaders = {}; response.headers.forEach((value, key) => { parsedHeaders[key] = value; }); const cookies = {}; if (response.headers.has("set-cookie")) { const cookieHeader = response.headers.get("set-cookie").split(";"); cookieHeader.forEach(cookie => { const [key, value] = cookie.split("="); if (key && value) { cookies[key.trim()] = value.trim(); } }); } const textData = await response.text(); let parsedBody; try { parsedBody = JSON.parse(textData); } catch (error) { console.error("Error parsing JSON response:", error); parsedBody = textData; } return { success: true, status: response.status, url: response.url, body: parsedBody, headers: parsedHeaders, cookies: cookies, }; } catch (error) { console.error("Error preparing response:", error); return { success: false, status: response?.status, error: error.message, }; } } const handleRequestWithCustomAction = async (selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action) => { if((HandleApiExecution !== undefined || HandleApiExecution !== null) && typeof HandleApiExecution === 'function'){ try { if (baseUrl.length === 0) { setBaseUrl(serverurl) } if (path.length === 0) { setPath(action.url) } const apiResponse = await HandleApiExecution( selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action, setCurTab, ) const response = { "action name" : action.name.replaceAll("_", " "), ...apiResponse }; if (typeof response.result === "string") { try { response.result = JSON.parse(response.result); } catch (parseError) { console.error("Error parsing result:", parseError); //toast.error("Error parsing response result."); } } setDisableExecuteButton(false); setApiResponse(response); // Set the response with the action name added setShowResponseLoader(false); return apiResponse; } catch (error) { console.error("Error during HandleApiExecution:", error); toast.error(`Error: ${error.message}`); return { error: error.message }; } } else{ const newUrl = fix_url(baseUrl); let validMethod; try { validMethod = isValidMethod(selectedMethod); } catch (error) { console.error(error); toast.error(error.message); return { error: error.message }; } try { if (path && !path.startsWith('/')) { path = '/' + path; } const finalUrl = newUrl + path; const newHeader = fixHeader(RequestHeader); const newParams = fixParams(RequestParams); if (typeof RequestBody === 'object') { try { RequestBody = JSON.stringify(RequestBody); } catch (error) { console.error(`Error: ${error}`); toast.error("Invalid JSON format for request body: ", error); return { error: "Invalid JSON format for request body" }; } } const queryString = new URLSearchParams(newParams.map(param => [param.key, param.value])).toString(); const fullUrl = queryString ? `${finalUrl}?${queryString}` : finalUrl; const response = await fetch(fullUrl, { method: validMethod, headers: newHeader, body: validMethod !== 'GET' ? RequestBody : undefined, }); const preparedResponse = await prepareResponse(response); setApiResponse(preparedResponse); return preparedResponse; } catch (error) { console.error("Error:", error); toast.error(`${error.message} Please ensure all fields are filled out correctly and try again.`); return { error: error.message }; } } }; const addRequestParamsRow = () => { setRequestParams((prevRows) => { const updatedRows = [...RequestParams, { key: "", value: "" }]; return updatedRows; }); }; const handleRequestParamsChange = (rowIndex, field, value) => { setRequestParams( RequestParams.map((row, index) => { return { ...row, [field]: index === rowIndex ? value : row[field], }; }) ); }; const addRow = () => { setRequestHeader((prevRows) => { const updatedRows = [...RequestHeader, { key: "", value: "" }]; return updatedRows; }); }; const handleInputChange = (rowIndex, field, value) => { setRequestHeader( RequestHeader.map((row, i) => { return { ...row, [field]: i === rowIndex ? value : row[field], }; }) ); }; const handleChangeTab = (actionIndex, newValue) => { setCurTab(newValue); }; const shouldShowBodyTab = ![ "GET", "CONNECT", "OPTIONS", "TRACE", "HEAD", ].includes(selectedMethod); const extractParamsFromText = (text) => { const params = []; const queryString = text.split("?")[1]; if (queryString) { const pairs = queryString.split("&"); pairs.forEach((pair) => { const [key, value] = pair.split("="); if (key && value) { params.push({ key, value }); } }); } return params.length > 0 ? params : [{ key: "", value: "" }]; }; const actionRef = useRef(null); const scrollTimeoutRef = useRef(null); const [isUserInteracting, setIsUserInteracting] = useState(false); useEffect(() => { const observer = new IntersectionObserver( throttle((entries) => { if (!isUserInteracting) return; let nextSelectedActionIndex = null; entries.forEach((entry) => { if (entry.isIntersecting && selectedActionIndex !== index) { nextSelectedActionIndex = index; } }); if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } if (nextSelectedActionIndex !== null) { scrollTimeoutRef.current = setTimeout(() => { if (selectedActionIndex !== nextSelectedActionIndex) { setSelectedActionIndex(nextSelectedActionIndex); const actionId = action.name.replace(/ /g, "-").replace(/_/g, "-"); window.history.pushState(null, "", `#${actionId}`); setExampleBody(action?.example_response); document.getElementById(`action-list-${nextSelectedActionIndex}`)?.scrollIntoView({ behavior: "smooth", block: "center" }); } }, 300); } }, 200), { threshold: 0.5 } ); if (actionRef.current) { observer.observe(actionRef.current); } return () => { if (actionRef.current) { observer.unobserve(actionRef.current); } if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } }; }, [isUserInteracting]); const handleAceEditorChange = (value) => { setRequestBody(value); if (editorRef.current) { const editor = editorRef.current.editor; const lineHeight = editor.renderer.lineHeight; const minHeight = 100; const maxHeight = 300; const session = editor.getSession(); const screenLength = session.getScreenLength(); const contentHeight = screenLength * lineHeight; const padding = 20; // Calculate new height let newHeight = Math.min( Math.max( minHeight, contentHeight + padding ), maxHeight ); if (newHeight !== AceEditorHeight) { if (value.length < (editorRef.current._lastValue || '').length) { if (contentHeight + padding < AceEditorHeight) { setAceEditorHeight(newHeight); } } else { if (contentHeight + padding > AceEditorHeight) { setAceEditorHeight(newHeight); } } } editorRef.current._lastValue = value; } }; useEffect(() => { if (editorRef.current) { const editor = editorRef.current.editor; editor.commands.addCommand({ name: "executeOnCtrlEnter", bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" }, exec: () => { setShowResponseLoader(true); setDisableExecuteButton(true); handleRequestWithCustomAction( selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action ); }, }); } }, [editorRef.current]); const actionname = action?.name.charAt(0).toUpperCase() + action?.name.slice(1).replaceAll("_", " ") return (
setIsUserInteracting(true)} >
{actionname}
{ if (e.key === "Enter") { if (actionUrl.length === 0) { toast.error("URL cannot be empty"); return; }else{ setShowResponseLoader(true); setDisableExecuteButton(true) handleRequestWithCustomAction(selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action); } }} } onClick={(e) => { const clickPosition = e.target.selectionStart; const baseUrlLength = baseUrl.length; if (shouldChageInputFocus) { e.target.setSelectionRange(baseUrlLength + clickPosition, baseUrlLength + clickPosition); setShouldChangeInputFocus(false); } }} onFocus={(e) => { const validParams = RequestParams.filter(param => param.key.trim().length > 0 && param.value.trim().length > 0); const fullUrl = validParams.length > 0 ? `${baseUrl}${path}?${validParams.map(param => `${param.key}=${param.value}`).join("&")}` : `${baseUrl}${path}`; if (fullUrl.length === 0) { setActionUrl(serverurl + action?.url); }else{ setActionUrl(fullUrl); } if(!shouldChageInputFocus){ setShouldChangeInputFocus(true); } }} onBlur={(e) => { if(e.target.value.trim().length === 0) { setActionUrl(path); }else if(path.length === 0){ setActionUrl(action?.url); }else if(baseUrl.length === 0){ setBaseUrl(serverurl) setActionUrl(path) }else{ const validParams = RequestParams.filter(param => param.key.trim().length > 0 && param.value.trim().length > 0); const validPath = validParams?.length > 0 ? `${path}?${validParams.map(param => `${param.key}=${param.value}`).join("&")}` : path; setActionUrl(validPath) } setShouldChangeInputFocus(false); }} onChange={(e) => { const newUrl = e.target.value setActionUrl(newUrl); const params = extractParamsFromText(newUrl); setRequestParams(params); if (newUrl.startsWith("http://") || newUrl.startsWith("https://")) { try { const url = new URL(newUrl); setBaseUrl(url.origin); const newPath = decodeURIComponent(url.pathname);; setPath(newPath); } catch (error) { console.error("Invalid URL:", newUrl, error); toast("The URL is not a valid one. Please check and try again.") } } else { //toast("The URL needs to start with http:// or https://") } }} />
{showResponseLoader? ( ) : null}
handleChangeTab(index, newValue) } aria-label="basic tabs example" > Headers {...a11yProps(0)} /> {shouldShowBodyTab && ( Body {...a11yProps(1)} /> )} Params {...a11yProps(shouldShowBodyTab ? 2 : 1)} /> {ConfigurationTab ? ( Configuration {...a11yProps(shouldShowBodyTab ? 3 : 2)} /> ) : null}
Key Value {RequestHeader.map((row, rowIndex) => ( handleInputChange( rowIndex, "key", e.target.value ) } inputProps={{ style: { backgroundColor: theme.palette.platformColor, padding: "4px 8px", }, }} sx={{ "& .MuiOutlinedInput-root": { "& fieldset": { border: "none", }, "&:hover fieldset": { border: "none", }, "&.Mui-focused fieldset": { border: "none", }, }, }} onKeyDown={(e) => { if (e.key === "Enter") { document.getElementById( `header-value-${index}-${rowIndex}` ).focus(); } if (e.key === "Backspace" && row.key.length === 0 && rowIndex !== 0) { setRequestHeader(RequestHeader.filter((header, i) => i !== rowIndex)); document.getElementById( `header-value-${index}-${rowIndex - 1}` ).focus(); e.preventDefault() } }} /> handleInputChange( rowIndex, "value", e.target.value ) } inputProps={{ endAdornment: ( ), style: { backgroundColor: theme.palette.platformColor, padding: "4px 8px", }, }} sx={{ "& .MuiOutlinedInput-root": { "& fieldset": { border: "none", }, "&:hover fieldset": { border: "none", }, "&.Mui-focused fieldset": { border: "none", }, }, }} onKeyDown={(e) => { if (e.key === "Enter" && !e.ctrlKey) { addRow(); setTimeout(() => { document.getElementById( `header-key-${index}-${rowIndex + 1}` ).focus(); }, 0); } if (e.ctrlKey && e.key === "Enter") { setShowResponseLoader(true); setDisableExecuteButton(true) handleRequestWithCustomAction(selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action); } if (e.key === "Backspace" && row.value.length === 0 && rowIndex !== 0) { setRequestHeader(RequestHeader.filter((header, i) => i !== rowIndex)); document.getElementById( `header-value-${index}-${rowIndex - 1}` ).focus(); e.preventDefault() }} } /> ))}
{shouldShowBodyTab && ( )} Key Value {RequestParams.map((row, rowIndex) => ( handleRequestParamsChange( rowIndex, "key", e.target.value ) } onKeyDown={(e) => { if (e.key === "Enter") { document.getElementById( `param-value-${index}-${rowIndex}` ).focus(); } if (e.key === "Backspace" && row.value.length === 0 && rowIndex !== 0) { setRequestParams(RequestParams.filter((param, i) => i !== rowIndex)); document.getElementById( `param-value-${index}-${rowIndex - 1}` ).focus(); } }} /> handleRequestParamsChange( rowIndex, "value", e.target.value ) } onKeyDown={(e) => { if (e.key === "Enter" && !e.ctrlKey) { addRequestParamsRow(); setTimeout(() => { document.getElementById( `param-key-${index}-${rowIndex + 1}` ).focus(); }, 0); } if (e.ctrlKey && e.key === "Enter") { setShowResponseLoader(true); setDisableExecuteButton(true) handleRequestWithCustomAction(selectedMethod, baseUrl, path, RequestHeader, RequestBody, RequestParams, info, action); } if (e.key === "Backspace" && row.value.length === 0 && rowIndex !== 0) { setRequestParams(RequestParams.filter((param, i) => i !== rowIndex)); document.getElementById( `param-value-${index}-${rowIndex - 1}` ).focus(); e.preventDefault() } } } /> ))}
{(ConfigurationTab && ((shouldShowBodyTab && curTab === 3 ) || (!shouldShowBodyTab && curTab === 2)))? ( ) : null}
{action.name.replaceAll("_", " ")}

{action.description ? action.description : ""}

); }) const ActionResponse = memo(({ apiResponse, ExampleBody, isLoggedIn, isLoaded }) => { const [height, setHeight] = useState("14vh") const [responseTabIndex, setResponseTabIndex] = useState(0) const [oldResponse, setOldResponse] = useState(apiResponse) const [highlight, setHighlight] = useState(false) const {themeMode } = useContext(Context) const theme = getTheme(themeMode) const MIN_HEIGHT = 50 useEffect(() => { var apiResp = apiResponse var oldResp = oldResponse try { apiResp = JSON.stringify(apiResponse) } catch (error) { //console.error("Error parsing JSON response:", error); } try { oldResp = JSON.stringify(oldResponse) } catch (error) { //console.error("Error parsing JSON response:", error); } if (apiResp === oldResp) { return } setOldResponse(apiResponse) //console.log("CHANGES MADE: ", apiResponse, oldResponse) //toast("CHANGES!") //console.log("HEIGHT: ", height) if (height === "14vh") { setHeight("30vh") } else { // Check if height is less than 250px var heightNum = 0 try { heightNum = parseInt(height.slice(0, -2)) } catch (error) { } if (heightNum < 350) { setHeight("350px") } } setHighlight(true) setTimeout(() => { setHighlight(false) }, 2000) }, [apiResponse, ExampleBody]) const handleReactJsonClipboard = (copy) => { const elementName = "copy_element_shuffle"; let copyText = document.getElementById(elementName); if (copyText) { if (copy.namespace && copy.name && copy.src) { copy = copy.src; } const clipboard = navigator.clipboard; if (!clipboard) { toast("Can only copy over HTTPS (port 3443)"); return; } let stringified = JSON.stringify(copy); if (stringified.startsWith('"') && stringified.endsWith('"')) { stringified = stringified.slice(1, -1); } navigator.clipboard.writeText(stringified); toast("Copied value to clipboard, NOT json path."); } else { console.log("Failed to copy from " + elementName + ": ", copyText); } }; const stopResizing = () => { window.removeEventListener("mousemove", startResizing); window.removeEventListener("mouseup", stopResizing); }; const initResize = (e) => { e.preventDefault(); window.addEventListener("mousemove", startResizing); window.addEventListener("mouseup", stopResizing); }; const startResizing = useCallback((e) => { const newHeight = window.innerHeight - e.clientY; if (newHeight >= MIN_HEIGHT) { setHeight(`${newHeight}px`); } }, []); const formData = (exampleBody) => { try { return exampleBody ? JSON.parse(exampleBody) : {}; } catch (error) { console.error("Error parsing the example string:", error); return {}; } }; useEffect(() => { const handleResize = () => { const newHeight = window.innerHeight * 0.1; setHeight(`${newHeight}px`); }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); return (
{highlight === true ? : null }
setResponseTabIndex(newValue)} > Response} {...a11yProps(0)} /> Example Response} disabled={ExampleBody === undefined || ExampleBody === null || ExampleBody === ""} {...a11yProps(1)} /> History} disabled={true} {...a11yProps(2)} />
); }); const ResponseTabWrapper = memo(({ apiResponse }) => { const {themeMode } = useContext(Context) const theme = getTheme(themeMode) const handleReactJsonClipboard = (copy) => { const elementName = "copy_element_shuffle"; let copyText = document.getElementById(elementName); if (copyText) { if (copy.namespace && copy.name && copy.src) { copy = copy.src; } const clipboard = navigator.clipboard; if (!clipboard) { toast("Can only copy over HTTPS (port 3443)"); return; } let stringified = JSON.stringify(copy); if (stringified.startsWith('"') && stringified.endsWith('"')) { stringified = stringified.slice(1, -1); } navigator.clipboard.writeText(stringified); toast("Copied value to clipboard, NOT json path."); } else { console.log("Failed to copy from " + elementName + ": ", copyText); } }; return( { return collapseField(jsonField) }} iconStyle={theme.palette.jsonIconStyle} collapseStringsAfterLength={theme.palette.jsonCollapseStringsAfterLength} enableClipboard={handleReactJsonClipboard} displayDataTypes={false} name={false} /> )}) const PaddingWrapper = memo(({ isLoggedIn, isLoaded, children }) => { const { leftSideBarOpenByClick, windowWidth, themeMode } = useContext(Context); const theme = getTheme(themeMode) return (
= 1920 ? "calc(100% - 630px)" : "calc(100% - 570px)" : windowWidth >= 1920 ? "calc(100vw - 460px)": "calc(100% - 410px)" : windowWidth >= 1920 ? "calc(100% - 370px)" : "calc(100% - 320px)", backgroundColor: theme.palette.backgroundColor, position: "fixed", bottom: 0, right: 0, display: "flex", flexDirection: "column", borderTop: "1px solid #212121", transition: "width 0.3s ease", minHeight: "10%", }} > {children}
); }); const ApiResponseWrapper = memo(({ children, isLoaded, isLoggedIn }) => { return ( {children} ); });