From 6ae79cc87d6a90b57f833cd052e8219820e21726 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 7 Dec 2020 16:05:30 +0100 Subject: [PATCH] #133: Added first part of example and body parser --- frontend/src/views/AngularWorkflow.jsx | 76 +++++++++++- frontend/src/views/AppCreator.jsx | 156 ++++++++++++++++++++++--- 2 files changed, 215 insertions(+), 17 deletions(-) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e47c7c6c..9dddd448 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -845,7 +845,7 @@ const AngularWorkflow = (props) => { return response.json() }) - .then((responseJson) => { + .then((responseJson) => { // FIXME - handle versions on left bar //handleAppVersioning(responseJson) //var tmpapps = [] @@ -2872,8 +2872,64 @@ const AngularWorkflow = (props) => { placeholder = data.example } + var disabled = false + var rows = "5" + var openApiHelperText = "This is an OpenAPI specific field" + if (selectedApp.generated && selectedApp.activated && data.name === "body") { + const regex = /\${(\w+)}/g + const found = placeholder.match(regex) + console.log("THERE MAY BE THINGS INSIDE THE BODY, NO: ", found) + if (found === null) { + //setExtraBodyFields([]) + } else { + rows = "1" + disabled = true + openApiHelperText = "OpenAPI spec: fill the following fields." + console.log("SHOULD ADD TO selectedActionParameters!: ", found, selectedActionParameters) + var changed = false + for (var specKey in found) { + const tmpitem = found[specKey] + var skip = false + for (var innerkey in selectedActionParameters) { + if (selectedActionParameters[innerkey].name === tmpitem) { + skip = true + break + } + } + + if (skip) { + console.log("SKIPPING ", tmpitem) + continue + } + + changed = true + selectedActionParameters.push({ + action_field: "", + configuration: false, + description: "Generated by OpenAPI body example", + example: "", + id: "", + multiline: false, + name: tmpitem, + options: null, + required: false, + schema: {type: "string"}, + skip_multicheck: false, + tags: null, + value: "", + variant: "STATIC_VALUE", + }) + } + + if (changed) { + setSelectedActionParameters(selectedActionParameters) + } + } + } + var datafield = { }} fullWidth multiline={multiline} - rows="5" + rows={rows} color="primary" defaultValue={data.value} type={placeholder.includes("***") ? "password" : "text"} @@ -2909,6 +2965,13 @@ const AngularWorkflow = (props) => { onChange={(event) => { changeActionParameter(event, count) }} + helperText={selectedApp.generated && selectedApp.activated && data.name === "body" ? + + {openApiHelperText} + + : + null + } onBlur={(event) => { // Super basic check //if (event.target.value.startsWith("{")) { @@ -3303,7 +3366,7 @@ const AngularWorkflow = (props) => {
}
- {data.name} + {data.name.charAt(0).toUpperCase()+data.name.substring(1)}
{selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 ? null : @@ -3685,7 +3748,7 @@ const AngularWorkflow = (props) => { {selectedAction.description}
: null}
- +
@@ -6253,6 +6316,11 @@ const AngularWorkflow = (props) => { getAppAuthentication() setUpdate(authenticationOption.id) + /* + {selectedAction.authentication.map(data => ( + + */ + } return ( diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index f47330c9..210a433d 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -4,6 +4,7 @@ import {BrowserView, MobileView} from "react-device-detect"; import {Link} from 'react-router-dom'; import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Button from '@material-ui/core/Button'; import Divider from '@material-ui/core/Divider'; @@ -218,6 +219,7 @@ const AppCreator = (props) => { const [actions, setActions] = useState([]) const [errorCode, setErrorCode] = useState("") const [appBuilding, setAppBuilding] = useState(false) + const [extraBodyFields, setExtraBodyFields] = useState([]) //const [actions, setActions] = useState([{ // "name": "Get workflows", @@ -253,6 +255,7 @@ const AppCreator = (props) => { "queries": [], "body": "", "errors": [], + "example_response": "", "method": actionNonBodyRequest[0], }); @@ -262,7 +265,7 @@ const AppCreator = (props) => { if (firstrequest) { setFirstrequest(false) if (window.location.pathname.includes("apps/edit")) { - setIsEditing(true) + setIsEditing(true) handleEditApp() } else { checkQuery() @@ -433,6 +436,22 @@ const AppCreator = (props) => { "paths": [], "body": "", "errors": [], + "example_response": "", + } + + // HAHAHA wtf is this. + if (methodvalue.responses !== undefined) { + 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"] + } + } + } + } + } } for (var key in methodvalue.parameters) { @@ -469,6 +488,7 @@ const AppCreator = (props) => { } } + if (newaction.name === "" || newaction.name === undefined) { // Find a unique part of the string // FIXME: Looks for length between /, find the one where they differ @@ -668,7 +688,14 @@ const AppCreator = (props) => { "responses": { "default": { "description": "default", - "schema": {} + "content": { + "text/plain": { + "schema": { + "type": "string", + "example": "", + }, + }, + }, } }, "summary": item.name, @@ -679,6 +706,35 @@ const AppCreator = (props) => { //console.log("ACTION: ", item) + if (item.example_response !== undefined && item.example_response.length > 0) { + // 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) { for (var querykey in item.queries) { const queryitem = item.queries[querykey] @@ -761,7 +817,7 @@ const AppCreator = (props) => { "type": "string", }, } - + // FIXME - add application/json if JSON example? data.paths[item.url][item.method.toLowerCase()]["requestBody"] = { "description": "Generated by Shuffler.io", @@ -773,6 +829,18 @@ const AppCreator = (props) => { }, } + /* + 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) } @@ -782,7 +850,6 @@ const AppCreator = (props) => { const headersSplit = item.headers.split("\n") for (var key in headersSplit) { const header = headersSplit[key] - console.log("HEADER: ", header) var key = "" var value = "" if (header.length > 0 && header.includes("= ")) { @@ -1137,12 +1204,15 @@ const AppCreator = (props) => { setUrlPathQueries(data.queries) setUrlPath(data.url) setActionsModalOpen(true) + + if (data["body"] !== undefined && data["body"] !== null && data["body"].length > 0) { + findBodyParams(data["body"]) + } }}>
{url} - {data.name} @@ -1177,24 +1247,52 @@ const AppCreator = (props) => { const setActionField = (field, value) => { currentAction[field] = value setCurrentAction(currentAction) + //setUrlPathQueries(currentAction.queries) } + const findBodyParams = (body) => { + const regex = /\${(\w+)}/g + const found = body.match(regex) + console.log("FOUND: ", found) + if (found === null) { + setExtraBodyFields([]) + } else { + setExtraBodyFields(found) + } + } + const bodyInfo = actionBodyRequest.includes(currentActionMethod) ? -
- Body - used as example in action argument +
+ Request Body: {extraBodyFields.length > 0 ? + + Variables: {extraBodyFields.join(", ")} + + : + + {`Add variables with \$\{ variable_name }`} + + } setActionField("body", e.target.value)} + onChange={e => { + setActionField("body", e.target.value) + findBodyParams(e.target.value) + }} key={currentAction} + helperText={ + + Shows an example body to the user. ${} creates variables. + + } InputProps={{ classes: { notchedOutline: classes.notchedOutline, @@ -1205,9 +1303,40 @@ const AppCreator = (props) => { }} /> +
+
: null + const exampleResponse = +
+ Example success response + setActionField("example_response", e.target.value)} + helperText={ + Helps with autocompletion and understanding of the endpoint + } + key={currentAction} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> +
+ const addActionToView = (errors) => { currentAction.errors = errors currentAction.queries = urlPathQueries @@ -1361,7 +1490,6 @@ const AppCreator = (props) => { open={actionsModalOpen} fullWidth onClose={() => { - console.log("CLOSED?") setUrlPath("") setCurrentAction({ "name": "", @@ -1564,7 +1692,7 @@ const AppCreator = (props) => { addPathQuery() }}>New query
- Headers - static for the action + Headers: static for the action { id="standard-required" defaultValue={currentAction["headers"]} multiline - rows="5" + rows="2" onChange={e => setActionField("headers", e.target.value)} helperText={Headers that are part of the request. Default: EMPTY} InputProps={{ @@ -1588,6 +1716,8 @@ const AppCreator = (props) => { }} /> {bodyInfo} + + {exampleResponse}