Added a UI for uploading docs and getting an OpenAPI output

This commit is contained in:
Frikky
2023-09-21 02:22:59 +02:00
parent 6de66413c7
commit c90059dd56
3 changed files with 279 additions and 11 deletions
+4 -3
View File
@@ -521,7 +521,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
}
// Handle top bar or something
const defaultTop = isCloud ? 0 : 6
const defaultTop = isCloud ? 0 : 7
const loginTextBrowser = !isLoggedIn ?
<div style={{display: "flex", minWidth: 1250, maxWidth: 1250, margin: "auto", textAlign: "center",}}>
<div style={{display: "flex", flex: 1, }}>
@@ -813,7 +813,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
}}
MenuProps={{
style: {
zIndex: 10002,
zIndex: 15000,
},
}}
style={{
@@ -823,6 +823,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
color: "white",
height: 45,
width: 85,
zIndex: 14999,
}}
value={userdata.active_org.id}
@@ -887,7 +888,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
<Tooltip color="primary" title={parsedTitle} placement="left">
<div style={{display: "flex"}}>
<Typography variant="body2" style={{borderRadius: theme.palette.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography> {image} <span style={{marginLeft: 8}}>{data.name}</span>
{isCloud?<Typography variant="body2" style={{borderRadius: theme.palette.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography>:null} {image} <span style={{marginLeft: 8}}>{data.name}</span>
</div>
</Tooltip>
</MenuItem>
+2
View File
@@ -2538,6 +2538,8 @@ const AngularWorkflow = (defaultprops) => {
const timeout = 60000
while (true) {
// Wait 1 second before next request just in case of timeouts
await new Promise(r => setTimeout(r, 1000));
await fetchWithTimeout(`${globalUrl}/api/v1/workflows/${workflowId}/stream`, {
method: "GET",
headers: {
+273 -8
View File
@@ -36,6 +36,7 @@ import {
} from "@mui/material";
import {
AutoFixHigh as AutoFixHighIcon,
LockOpen as LockOpenIcon,
OpenInNew as OpenInNewIcon,
Apps as AppsIcon,
@@ -293,6 +294,8 @@ const Apps = (props) => {
const [loadAppsModalOpen, setLoadAppsModalOpen] = React.useState(false);
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
const [openApiModal, setOpenApiModal] = React.useState(false);
const [generateAppModal, setGenerateAppModal] = React.useState(false);
const [openApiModalType, setOpenApiModalType] = React.useState("");
const [openApiError, setOpenApiError] = React.useState("");
const [field1, setField1] = React.useState("");
@@ -1333,11 +1336,79 @@ const Apps = (props) => {
</div>
) : null;
const AppCreateButton = (props) => {
const { text, func, icon } = props;
const [hover, setHover] = React.useState(false);
return (
<Paper
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={func}
style={{
flex: 1,
padding: 15,
margin: 10,
backgroundColor: hover ? theme.palette.surfaceColor : "transparent",
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
cursor: hover ? "pointer" : "default",
textAlign: "center",
height: 150,
}}
>
{icon}
<Typography>
{text}
</Typography>
</Paper>
)
}
return (
<div style={{}}>
<Paper square style={uploadViewPaperStyle}>
<div style={{ margin: 25 }}>
<h2>App Creator</h2>
<h2 style={{
textAlign: "center",
}}>
App Creator
</h2>
<div style={{display: "flex"}}>
<AppCreateButton
text="Generate from OpenAPI/Swagger"
func={() => {
setOpenApiModal(true)
}}
icon={<PublishIcon style={{minHeight: 50, maxHeigth: 50, }} />}
/>
<AppCreateButton
text="Generate from Documentation"
func={() => {
setGenerateAppModal(true)
}}
icon={<AutoFixHighIcon style={{minHeight: 50, maxHeigth: 50, }} />}
/>
</div>
<Link
to="/apps/new"
style={{
marginLeft: 5,
textDecoration: "none",
color: "#f85a3e",
}}
>
<Button
variant="outlined"
component="label"
color="secondary"
style={{}}
fullWidth
>
Create from scratch
</Button>
</Link>
{/*
<a
rel="noopener noreferrer"
href="https://shuffler.io/docs/apps"
@@ -1420,6 +1491,7 @@ const Apps = (props) => {
</Button>
</Link>
</div>
*/}
</div>
</Paper>
<Paper square style={uploadViewPaperStyle}>
@@ -1459,6 +1531,30 @@ const Apps = (props) => {
//}
};
const uploadFileDocumentation = (e) => {
const isDropzone = e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
const files = isDropzone ? e.dataTransfer.files : e.target.files;
const reader = new FileReader();
try {
reader.addEventListener("load", (e) => {
const content = e.target.result;
setOpenApiData(content);
setIsDropzone(isDropzone);
setOpenApiModal(true);
});
} catch (e) {
console.log("Error in dropzone: ", e);
}
try {
reader.readAsText(files[0]);
} catch (error) {
toast("Failed to read file");
}
};
const uploadFile = (e) => {
const isDropzone =
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
@@ -2248,6 +2344,58 @@ const Apps = (props) => {
});
};
const validateDocumentationUrl = () => {
setValidation(true);
// curl https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi -d '{"url": "https://gitlab.com/rhab/PyOTRS/-/raw/main/pyotrs/lib.py?ref_type=heads"}' -H "Content-Type: application/json"
const urldata = {
"url": openApi,
}
//fetch("http://localhost:8080/doc_to_openapi", {
fetch("https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(urldata),
})
.then((response) => {
setValidation(false);
if (response.status !== 200) {
toast("Error in generation: "+response.status);
setOpenApiError("Error in generation - bad status: "+response.status);
return response.text();
}
return response.json();
})
.then((responseJson) => {
// Check if openapi or swagger in string of the json
try {
const parsedtext = JSON.stringify(responseJson);
if (parsedtext.indexOf("openapi") === -1 && parsedtext.indexOf("swagger") === -1) {
setValidation(false);
setOpenApiError("Error in generation: "+parsedtext);
return;
}
} catch (e) {
setValidation(false);
setOpenApiError("Error in generation (2): "+e.toString());
return;
}
console.log("Validating response!");
validateOpenApi(responseJson);
})
.catch((error) => {
setValidation(false);
toast(error.toString());
setOpenApiError(error.toString());
});
}
const validateRemote = () => {
setValidation(true);
@@ -2544,14 +2692,13 @@ const Apps = (props) => {
</Dialog>
) : null;
const errorText =
openApiError.length > 0 ? (
<div style={{ marginTop: 10 }}>Error: {openApiError}</div>
) : null;
const modalView = openApiModal ? (
const errorText = openApiError.length > 0 ? ( <div style={{ marginTop: 10 }}>Error: {openApiError}</div>) : null;
const generateAppView = generateAppModal ? (
<Dialog
open={openApiModal}
open={generateAppModal}
onClose={() => {
setGenerateAppModal(false);
setOpenApiModal(false);
}}
PaperProps={{
@@ -2566,7 +2713,124 @@ const Apps = (props) => {
<FormControl>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
Create a new app
Generate an app based on documentation (beta)
</div>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
<Typography variant="body1">
Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. {isCloud ? "" : "Uses to Shuffle Cloud (https://shuffler.io) for processing."}
</Typography>
<TextField
style={{ backgroundColor: inputColor }}
variant="outlined"
margin="normal"
InputProps={{
style: {
color: "white",
height: "50px",
fontSize: "1em",
},
endAdornment: (
<Button
style={{
borderRadius: "0px",
marginTop: "0px",
height: "50px",
}}
variant="contained"
disabled={openApi.length === 0 || appValidation.length > 0}
color="primary"
onClick={() => {
setOpenApiError("");
validateDocumentationUrl();
}}
>
Validate
</Button>
),
}}
onChange={(e) => {
setOpenApi(e.target.value);
}}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
Should be a documentation page containing an API.
</span>
}
placeholder="API Documentation URL"
fullWidth
/>
<p>Or upload document with the content (coming soon)</p>
<input
hidden
type="file"
ref={upload}
multiple={false}
onChange={uploadFileDocumentation}
/>
<Button
variant="contained"
color="primary"
disabled
onClick={() => upload.current.click()}
>
Upload
</Button>
{errorText}
</DialogContent>
<DialogActions>
{circularLoader}
<Button
style={{ borderRadius: "0px" }}
onClick={() => {
setGenerateAppModal(false);
setOpenApiModal(false);
setAppValidation("");
setOpenApiError("");
setOpenApi("");
setOpenApiData("");
}}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "0px" }}
disabled={appValidation.length === 0}
onClick={() => {
redirectOpenApi();
}}
color="primary"
>
Continue
</Button>
</DialogActions>
</FormControl>
</Dialog>
) : null
const modalView = openApiModal ? (
<Dialog
open={openApiModal}
onClose={() => {
setOpenApiModal(false);
setGenerateAppModal(false);
}}
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
<FormControl>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
Create a new app from OpenAPI / Swagger
</div>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
@@ -2671,6 +2935,7 @@ const Apps = (props) => {
<div>
{appView}
{modalView}
{generateAppView}
{appsModalLoad}
{deleteModal}
</div>