import React, { memo, useContext, useEffect, useState } from 'react';
import {
Edit as EditIcon,
SelectAll as SelectAllIcon,
Delete as DeleteIcon,
CheckCircle as CheckCircleIcon,
Cancel as CancelIcon,
Search as SearchIcon,
Clear as ClearIcon,
DragIndicator as DragIndicatorIcon,
Close as CloseIcon,
LockOpen as LockOpenIcon,
} from "@mui/icons-material";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import theme from "../theme.jsx";
import Markdown from "react-markdown";
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import { isMobile } from "react-device-detect"
import PaperComponent from "../components/PaperComponent.jsx";
import { CodeHandler, Img, OuterLink, } from '../views/Docs.jsx'
import { v4 as uuidv4} from "uuid";
import {
Divider,
List,
ListItem,
ListItemText,
IconButton,
Tooltip,
Chip,
Checkbox,
Typography,
TextField,
Button,
Dialog,
DialogTitle,
DialogActions,
DialogContent,
Select,
FormControl,
InputLabel,
MenuItem,
FormControlLabel,
InputAdornment,
Grid,
Zoom,
Paper,
Skeleton,
Link,
} from "@mui/material";
import algoliasearch from "algoliasearch/lite";
import {
InstantSearch,
Configure,
connectSearchBox,
connectHits,
connectHitInsights,
RefinementList,
ClearRefinements,
connectStateResults
} from "react-instantsearch-dom";
import aa from "search-insights";
import { Context } from '../context/ContextApi.jsx';
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"db08e40265e2941b9a7d8f644b6e5240"
)
const AppAuthTab = memo((props) => {
const { globalUrl, userdata, isCloud, selectedOrganization } = props;
const [selectedAuthentication, setSelectedAuthentication] = React.useState({});
const [authentication, setAuthentication] = React.useState([]);
const [selectedUserModalOpen, setSelectedUserModalOpen] = React.useState(false);
const [authenticationFields, setAuthenticationFields] = React.useState([]);
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false);
const [appAuthenticationGroupEnvironment, setAppAuthenticationGroupEnvironment] = React.useState("");
const [environments, setEnvironments] = React.useState([]);
const [listItemExpanded, setListItemExpanded] = React.useState(-1);
const [appAuthenticationGroupModalOpen, setAppAuthenticationGroupModalOpen] = React.useState(false);
const [appAuthenticationGroupId, setAppAuthenticationGroupId] = React.useState("");
const [appAuthenticationGroups, setAppAuthenticationGroups] = React.useState([]);
const [appAuthenticationGroupName, setAppAuthenticationGroupName] = React.useState("");
const [appAuthenticationGroupDescription, setAppAuthenticationGroupDescription] = React.useState("");
const [appsForAppAuthGroup, setAppsForAppAuthGroup] = React.useState([]);
const [searchQuery, setSearchQuery] = React.useState("");
const [showAppModal, setShowAppModal] = useState(false)
const [showAuthenticationLoader, setShowAuthenticationLoader] = useState(true)
const [showAppAuthGroupLoader, setShowAppAuthGroupLoader] = useState(true)
const changeDistribution = (data) => {
//changeDistributed(data, !isDistributed)
editAuthenticationConfig(data.id, "suborg_distribute")
}
useEffect(() => {
getAppAuthentication();
getAppAuthenticationGroups();
getEnvironments();
}, [])
const getAppAuthentication = () => {
fetch(globalUrl + "/api/v1/apps/authentication", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === true) {
setAuthentication(responseJson.data);
setShowAuthenticationLoader(false)
} else {
toast("Failed getting authentications");
}
})
.catch((error) => {
toast(error.toString());
});
};
const updateAppAuthentication = (field) => {
setSelectedAuthenticationModalOpen(true);
setSelectedAuthentication(field);
//{selectedAuthentication.fields.map((data, index) => {
var newfields = [];
for (var key in field.fields) {
newfields.push({
key: field.fields[key].key,
value: "",
});
}
setAuthenticationFields(newfields);
};
const saveAuthentication = (authentication) => {
const data = authentication;
const url = globalUrl + "/api/v1/apps/authentication";
fetch(url, {
mode: "cors",
method: "PUT",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
// Check if .reason exists
if (responseJson.reason !== undefined) {
toast("Failed changing authentication: " + responseJson.reason);
} else {
toast("Failed changing authentication");
}
} else {
getAppAuthentication();
setSelectedAuthentication({});
setSelectedAuthenticationModalOpen(false);
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
};
const deleteAuthentication = (data) => {
toast("Deleting auth " + data.label);
// Just use this one?
const url = globalUrl + "/api/v1/apps/authentication/" + data.id;
console.log("URL: ", url);
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson.success === false) {
toast("Failed deleting auth");
} else {
// Need to wait because query in ES is too fast
setTimeout(() => {
getAppAuthentication();
}, 1000);
//toast("Successfully deleted authentication!")
}
})
)
.catch((error) => {
console.log("Error in userdata: ", error);
});
};
const editAuthenticationConfig = (id, parentAction) => {
const data = {
id: id,
action: parentAction !== undefined && parentAction !== null ? parentAction : "assign_everywhere",
}
const url = globalUrl + "/api/v1/apps/authentication/" + id + "/config";
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed overwriting appauth");
} else {
toast("Successfully updated auth!");
setSelectedUserModalOpen(false);
setTimeout(() => {
getAppAuthentication();
}, 1000);
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
};
const editAuthenticationModal = selectedAuthenticationModalOpen ? (
{
setSelectedAuthenticationModalOpen(false);
}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: "800px",
minHeight: "320px",
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
Edit authentication for {selectedAuthentication.app.name.replaceAll("_", " ")} (
{selectedAuthentication.label})
You can not see the previous values for an authentication while editing. This is to keep your data secure. You can overwrite one- or multiple fields at a time.
Authentication Label
{
selectedAuthentication.label = e.target.value
}}
/>
{selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app" ?
Only the name and url can be modified for Oauth2/OpenID connect. Please remake the authentication if you want to change the other fields like Client ID, Secret, Scopes etc.
: null}
{selectedAuthentication.fields.map((data, index) => {
var fieldname = data.key.replaceAll("_", " ")
if (fieldname.endsWith(" basic")) {
fieldname = fieldname.substring(0, fieldname.length - 6)
}
if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") {
if (selectedAuthentication.fields[index].key !== "url") {
return null
}
}
//console.log("DATA: ", data, selectedAuthentication)
return (
{fieldname}
{
authenticationFields[index].value = e.target.value;
setAuthenticationFields(authenticationFields);
}}
/>
);
})}
setSelectedAuthenticationModalOpen(false)}
color="primary"
>
Cancel
{
var error = false;
var fails = 0
for (var key in authenticationFields) {
const item = authenticationFields[key];
if (item.value.length === 0) {
fails += 1
console.log("ITEM: ", item);
// var currentnode = cy.getElementById(data.id)
var textfield = document.getElementById(
`authentication-${key}`
);
if (textfield !== null && textfield !== undefined) {
console.log("HANDLE ERROR FOR KEY ", key);
}
error = true;
}
}
if (selectedAuthentication.type === "oauth" || selectedAuthentication.type === "oauth2" || selectedAuthentication.type === "oauth2-app") {
selectedAuthentication.fields = []
}
if (error && fails === authenticationFields.length) {
toast("Updating auth with new name only")
saveAuthentication(selectedAuthentication);
} else {
toast("Saving new version of this authentication");
selectedAuthentication.fields = authenticationFields;
saveAuthentication(selectedAuthentication);
}
}}
color="primary"
>
Submit
) : null;
const getEnvironments = () => {
fetch(globalUrl + "/api/v1/getenvironments", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
setEnvironments(responseJson);
// Helper info for users in case they have a large queue and don't know about queue flushing
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
if (responseJson.length === 1 && responseJson[0].Type !== "cloud") {
setListItemExpanded(0)
}
for (var i = 0; i < responseJson.length; i++) {
const env = responseJson[i];
// Check if queuesize is too large
if (
env.queue !== undefined &&
env.queue !== null &&
env.queue > 100
) {
toast(
"Queue size for " +
env.name +
" is very large. We recommend you to reduce it by flushing the queue before continuing.",
);
break;
}
}
}
})
.catch((error) => {
toast(error.toString());
});
};
const getAppAuthenticationGroups = () => {
//console.log("DEBUG: Skipping app auth group loading")
//return
fetch(globalUrl + "/api/v1/authentication/group", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for apps :O!");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === true) {
setAppAuthenticationGroups(responseJson.data);
setShowAppAuthGroupLoader(false)
}
})
.catch((error) => {
toast(error.toString());
});
};
const deleteAppAuthenticationGroup = (appAuthGroupId) => {
const url = `${globalUrl}/api/v1/authentication/group/${appAuthGroupId}`
fetch(url, {
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for deleting app auth group");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to delete app authentication group");
} else {
toast("App authentication group deleted")
getAppAuthenticationGroups()
}
})
.catch((error) => {
toast(error.toString())
})
}
const createAppAuthenticationGroup = (name, environment, description, appAuthIds) => {
// Makes list of ids into a full-on list of auth, but just with the ID
// The backend fills in the rest
console.log("INput auth: ", appAuthIds)
let app_auths = appAuthIds.map((appAuthId) => {
return { id: appAuthId };
})
var parsedAppGroup = {
label: name,
environment: environment,
description: description,
app_auths: app_auths
}
if (appAuthenticationGroupId !== undefined && appAuthenticationGroupId !== null && appAuthenticationGroupId !== "") {
parsedAppGroup.id = appAuthenticationGroupId
}
fetch(globalUrl + "/api/v1/authentication/group", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
body: JSON.stringify(parsedAppGroup),
})
.then((response) => {
if (response.status !== 200) {
throw new Error("Failed to create app authentication group");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === false) {
toast("Failed to create. Please try again, or contact support@shuffler.io")
} else {
// Close the modal
setAppAuthenticationGroupModalOpen(false)
toast("App authentication group created")
getAppAuthenticationGroups()
}
})
.catch((error) => {
toast(error.toString());
});
};
const handleAppAuthGroupCheckbox = (data) => {
//let groupApp = data.app.id
var newappauth = appsForAppAuthGroup
if (appsForAppAuthGroup.includes(data.app.id)) {
newappauth = newappauth.filter((item) => item !== data.app.id)
}
if (appsForAppAuthGroup.includes(data.id)) {
// Remove app from app auth group
newappauth = newappauth.filter((item) => item !== data.id)
setAppsForAppAuthGroup(newappauth)
return
}
for (var i = 0; i < authentication.length; i++) {
if (authentication[i].id === data.id) {
continue
}
if (!appsForAppAuthGroup.includes(authentication[i].id)) {
continue
}
if (authentication[i].app.id === data.app.id) {
// Remove app from app auth group
newappauth = newappauth.filter((item) => item !== authentication[i].id)
toast(`App ${data.app.name} is already in this group`)
}
}
setAppsForAppAuthGroup(newappauth.concat(data.id))
}
const authenticationView = appAuthenticationGroupModalOpen ?
(
{/* (appAuthenticationGroupModalOpen : { */}
{appAuthenticationGroupModalOpen && (
{
setAppAuthenticationGroupModalOpen(false);
setAppAuthenticationGroupId("")
setAppAuthenticationGroupName("")
setAppAuthenticationGroupEnvironment("")
setAppAuthenticationGroupDescription("")
setAppsForAppAuthGroup([])
}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: "1000px",
padding: "25px",
paddingLeft: "50px",
minHeight: "320px",
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
sx={{
"& .MuiDialog-paper": {
backgroundColor: "rgb(26, 26, 26)",
},
}}
>
App Authentication Groups
Name
{
setAppAuthenticationGroupName(event.target.value);
}}
/>
Evironment
{environments !== undefined && environments !== null && environments.length > 0 ?
{
setAppAuthenticationGroupEnvironment(e.target.value);
}}
>
{environments.map((env, index) => {
return (
{env.Name}
)
})}
:
Locations failed to load. Please try again
}
{
createAppAuthenticationGroup(
appAuthenticationGroupName,
appAuthenticationGroupEnvironment,
appAuthenticationGroupDescription,
appsForAppAuthGroup,
);
}}
>
Set Group
{/* Show a check box list of all app authentications to add to the auth group */}
{authentication.map((data, index) => {
var checked = data.checked
if (data.label !== undefined && data.label !== null && data.label.toLowerCase() === "kms shuffle storage") {
return null
}
if (checked === undefined || checked === null) {
checked = false
}
if (appsForAppAuthGroup.includes(data.id)) {
checked = true
}
return (
{
handleAppAuthGroupCheckbox(data)
}}
name={data.label}
disabled={data.app.id in appsForAppAuthGroup}
/>
}
label={data.label}
/>
)
})}
)}
): null
const appModal = showAppModal ? (
{
setShowAppModal(false);
}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
width: '700px',
maxWidth: '700px',
overflowY: 'hidden',
height: "600px",
maxHeight: "600px",
fontFamily: theme?.typography?.fontFamily,
zIndex: 1000,
'& .MuiDialogContent-root': {
padding: '30px',
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
>
Add App Authentication
) : null;
return (
{appModal}
setShowAppModal(true)}
>
Add App Auth
{/*
*/}
{["Valid", "Label", "App Name", "Workflows", "Fields", "Edited", "Actions", "Distribution"].map((header, index) => (
))}
{showAuthenticationLoader
?
[...Array(6)].map((_, rowIndex) => (
{Array(8)
.fill()
.map((_, colIndex) => (
))}
))
: authentication?.length === 0 ? (
No authentication found.
):authentication.map((data, index) => {
var bgColor = "#212121";
if (index % 2 === 0) {
bgColor = "#1A1A1A";
}
//console.log("Auth data: ", data)
if (data.type === "oauth2") {
data.fields = [
{
key: "url",
value: "Secret. Replaced during app execution!",
},
{
key: "client_id",
value: "Secret. Replaced during app execution!",
},
{
key: "client_secret",
value: "Secret. Replaced during app execution!",
},
{
key: "scope",
value: "Secret. Replaced during app execution!",
},
];
}
const isDistributed = data.suborg_distributed === true ? true : false;
var validIcon =
if (data.validation !== null && data.validation !== undefined && data.validation.valid === false) {
if (data.validation.changed_at === 0) {
validIcon = ""
} else {
validIcon =
}
}
return (
{validIcon}
)}
style={{ display: "table-cell", verticalAlign: 'middle', minWidth: 60 }}
primaryTypographyProps={{
style: {
padding: "8px 8px 8px 15px",
}
}}
onClick={() => {
if (data.validation === null || data.validation === undefined) {
return
}
if (data.validation.workflow_id === undefined || data.validation.workflow_id === null || data.validation.workflow_id.length === 0) {
toast.warn("No workflow runs found for this auth yet. Check back later.")
return
}
const url = `/workflows/${data.validation.workflow_id}?execution_id=${data.validation.execution_id}&node=${data.validation.node_id}`
window.open(url, "_blank")
}}
/>
{data?.app?.name?.replaceAll("_", " ")}
}
primaryTypographyProps={{
style: {
padding: 8
}
}}
style={{ marginLeft: 10, display: "table-cell", textAlign: 'center', verticalAlign: 'middle', padding: 8 }}
/>
{
return data.key;
})
.join(", ")
}
primaryTypographyProps={{
style: {
padding: 8
}
}}
style={{
overflow: "auto",
display: "table-cell",
verticalAlign: 'middle'
}}
/>
{
updateAppAuthentication(data);
}}
disabled={data.org_id !== selectedOrganization.id}
>
{data.defined ? (
{
editAuthenticationConfig(data.id);
}}
>
) : (
{}}
disabled={data.org_id !== selectedOrganization.id}
>
)}
{
deleteAuthentication(data);
}}
>
{selectedOrganization.id !== undefined && data.org_id !== selectedOrganization.id ?
:
{
changeDistribution(data, !isDistributed)
}}
/>
}
);
})}
{editAuthenticationModal}
{authenticationView}
App Authentication Groups
Disabled until further notice . Makes a workflow run replicate across all relevant authentications in an app auth group. Useful when the EXACT same workflow is supposed to run many times from one single input. {" "}
Learn more about App Authentication Groups
{
if (environments !== undefined && environments !== null && environments.length > 0) {
setAppAuthenticationGroupEnvironment(environments[0].Name)
}
setAppAuthenticationGroupModalOpen(true)
}}
disabled={true}
>
Add Group
{["Label", "Environment", "App Auth", "Created At", "Actions"].map((header, index) => (
))}
{showAppAuthGroupLoader ?
[...Array(6)].map((_, rowIndex) => (
{Array(5)
.fill()
.map((_, colIndex) => (
))}
))
: appAuthenticationGroups.length === 0 ? (
No authentication groups found.
): appAuthenticationGroups.map((data, index) => {
var bgColor = "#212121";
if (index % 2 === 0) {
bgColor = "#1A1A1A";
}
if (data.app_auths === undefined || data.app_auths === null) {
data.app_auths = []
}
return (
{data.app_auths.map((appAuth, index) => {
if (appAuth.app.large_image === undefined || appAuth.app.large_image === null || appAuth.app.large_image === "") {
const foundImage = authentication.find((auth) => auth.app.id === appAuth.app.id)
if (foundImage !== undefined) {
appAuth.app.large_image = foundImage.app.large_image
appAuth.app.name = foundImage.app.name
}
}
const tooltip = `${appAuth.app.name.replaceAll("_", " ")} (authname: ${appAuth.label})`
return (
)
})}
}
style={{ display:'table-cell', verticalAlign: 'middle' }}
/>
{
setAppAuthenticationGroupId(data.id)
setAppAuthenticationGroupName(data.label)
setAppAuthenticationGroupDescription(data.description)
setAppsForAppAuthGroup(data.app_auths.map((appAuth) => appAuth.id))
setAppAuthenticationGroupEnvironment(data.environment)
setAppAuthenticationGroupModalOpen(true)
}}
>
{
deleteAppAuthenticationGroup(data.id)
}}
>
}
style={{ display:'table-cell', verticalAlign: 'middle' }}
/>
);
}
)}
);
});
export default AppAuthTab;
const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, setSearchQuery }) => {
const handleSearch = (e) => {
refine(searchQuery.trim());
};
return (
);
};
const Hits = ({
hits,
insights,
setIsAnyAppActivated,
searchQuery,
isCloud,
globalUrl,
userdata,
getAppAuthentication
}) => {
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1);
const [selectedAppData, setSelectedAppData] = useState({})
const [appid, setAppId] = useState("")
const [selectedAuthentication, setSelectedAuthentication] = useState({})
const [authenticationModalOpen, setAuthenticationModalOpen] = useState(false)
const [authenticationType, setAuthenticationType] = React.useState("");
const [appAuthentication, setAppAuthentication] = useState([]);
const [selectedMeta, setSelectedMeta] = useState(undefined);
const [selectedAction, setSelectedAction] = useState(
{
"app_name": selectedAppData.name,
"app_id": selectedAppData.id,
"app_version": selectedAppData.version,
"large_image": selectedAppData.large_image,
}
)
const navigate = useNavigate();
const normalizedString = (name) => {
if (typeof name === 'string') {
return name.replace(/_/g, ' ');
} else {
return name;
}
};
let workflowDelay = 0;
const isHeader = true;
const paperStyle = {
color: "rgba(241, 241, 241, 1)",
padding: isHeader ? null : 15,
cursor: "pointer",
width: '100%',
maxHeight: 96,
borderRadius: 4,
transition: 'background-color 0.3s ease',
};
const base64_decode = (str) => {
return decodeURIComponent(
atob(str)
.split("")
.map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
};
const handleAppAuthenticationType = (selectedAppData)=> {
if (selectedAppData.authentication === undefined || selectedAppData.authentication === null) {
setAuthenticationType({
type: "",
})
selectedAppData.authentication = {
type: "",
required: false,
}
} else {
setAuthenticationType(
selectedAppData.authentication.type === "oauth2-app" || (selectedAppData.authentication.type === "oauth2" && selectedAppData.authentication.redirect_uri !== undefined && selectedAppData.authentication.redirect_uri !== null) ? {
type: selectedAppData.authentication.type,
redirect_uri: selectedAppData.authentication.redirect_uri,
refresh_uri: selectedAppData.authentication.refresh_uri,
token_uri: selectedAppData.authentication.token_uri,
scope: selectedAppData.authentication.scope,
client_id: selectedAppData.authentication.client_id,
client_secret: selectedAppData.authentication.client_secret,
grant_type: selectedAppData.authentication.grant_type,
} : {
type: "",
}
)
}
}
function Heading(props) {
const element = React.createElement(
`h${props.level}`,
{ style: { marginTop: 40 } },
props.children
);
return (
{props.level !== 1 ? (
) : null}
{element}
);
}
const getAppDocs = (appname, location, version) => {
fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, {
headers: {
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status === 200) {
//toast("Successfully GOT app "+appId)
} else {
//toast("Failed getting app");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === true) {
if (responseJson.meta !== undefined && responseJson.meta !== null && Object.getOwnPropertyNames(responseJson.meta).length > 0) {
setSelectedMeta(responseJson.meta)
}
if (responseJson.reason !== undefined && responseJson.reason !== undefined && responseJson.reason.length > 0) {
if (!responseJson.reason.includes("404: Not Found") && responseJson.reason.length > 25) {
// Translate into markdown ![]()
const imgRegex = / ({
...prevState,
documentation: newdata,
}));
}
}
}
})
.catch((error) => {
toast(error.toString());
});
};
const handleDecodeOfOpenApiData = (data) => {
var appexists = false;
var parsedapp = {};
if (data.app !== undefined && data.app !== null) {
var parsedBaseapp = "";
try {
parsedBaseapp = base64_decode(data.app);
} catch (e) {
parsedBaseapp = data;
}
parsedapp = JSON.parse(parsedBaseapp);
parsedapp.name = parsedapp.name.replaceAll("_", " ");
appexists =
parsedapp.name !== undefined &&
parsedapp.name !== null &&
parsedapp.name.length !== 0;
if(parsedapp?.id.length > 0){
setSelectedAppData(parsedapp)
handleAppAuthenticationType(parsedapp)
const apptype = selectedAppData?.generated === false ? "python" : "openapi"
getAppDocs(parsedapp.name, apptype, parsedapp.version);
setAuthenticationModalOpen(true);
}
}
if (data.openapi === undefined || data.openapi === null) {
return;
}
var parsedDecoded = "";
try {
parsedDecoded = base64_decode(data.openapi);
} catch (e) {
parsedDecoded = data;
}
parsedapp = JSON.parse(parsedDecoded);
data =
parsedapp.body === undefined ? parsedapp : JSON.parse(parsedapp.body);
};
const getAppData = (appid) => {
if (appid === undefined || appid === null || appid.length === 0) {
return;
}
const url = `${globalUrl}/api/v1/apps/${appid}/config`;
fetch(url, {
credentials: "include",
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status !== 200) {
toast.error("Failed to get app data or App doesn't. Please contact support@shuffler.io");
return;
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success === true) {
handleDecodeOfOpenApiData(responseJson);
} else {
toast.error("Failed to get app data or App doesn't exist");
}
})
.catch((error) => {
console.error("error for app is :", error);
});
};
const UpdateAppAuthentication = (data) => {
if (data === undefined || data === null) {
return;
}
const filteredData = data.filter((appAuth) => appAuth?.app?.id === appid);
if (filteredData.length === 0) {
setAppAuthentication([]);
setSelectedAuthentication({});
} else {
setAppAuthentication(filteredData);
setSelectedAuthentication(filteredData[0]);
}
};
const HandleAppAuthentication = ()=>{
const url = `${globalUrl}/api/v1/apps/authentication`;
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
}).then((response) => {
if (response.status !== 200) {
return;
}
return response.json();
}).then((responseJson) => {
if (responseJson.success === true) {
UpdateAppAuthentication(responseJson.data);
} else {
toast.error("Failed to get app authentication data");
}
}).catch((error) => {
console.error("error for app is :", error);
});
}
const handleAppAuthenticationNew = (data) => () => {
const appid = data.objectID;
if (appid > 0) {
setAppId(appid);
}
if (appid.length > 0) {
toast.info(`Getting authentication for ${data.name}. Please wait...`);
getAppData(appid);
HandleAppAuthentication();
}
}
const AuthenticationData = (props) => {
const selectedApp = props.app;
const [authenticationOption, setAuthenticationOptions] = React.useState({
app: JSON.parse(JSON.stringify(selectedApp)),
fields: {},
label: "",
usage: [
{
// workflow_id: workflow.id,
},
],
id: uuidv4(),
active: true,
});
if (
selectedApp.authentication === undefined ||
selectedApp.authentication.parameters === null ||
selectedApp.authentication.parameters === undefined ||
selectedApp.authentication.parameters.length === 0
) {
return (
{selectedApp.name} does not require authentication
);
}
authenticationOption.app.actions = [];
for (let paramkey in selectedApp.authentication.parameters) {
if (
authenticationOption.fields[
selectedApp.authentication.parameters[paramkey].name
] === undefined
) {
authenticationOption.fields[
selectedApp.authentication.parameters[paramkey].name
] = "";
}
}
const setNewAppAuth = (appAuthData, refresh) => {
setSelectedAuthentication(appAuthData);
var headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
headers["Org-Id"] = userdata?.active_org?.id
fetch(globalUrl + "/api/v1/apps/authentication", {
method: "PUT",
headers: headers,
body: JSON.stringify(appAuthData),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for setting app auth :O!");
if (response.status === 400) {
toast.error("Failed setting new auth. Please try again", {
"autoClose": true,
})
}
}
return response.json();
})
.then((responseJson) => {
if (!responseJson.success) {
toast.error("Error: " + responseJson.reason, {
"autoClose": false,
})
} else {
HandleAppAuthentication()
setAuthenticationModalOpen(false)
getAppAuthentication()
}
})
.catch((error) => {
console.log("New auth error: ", error.toString());
});
};
const handleSubmitCheck = () => {
if (authenticationOption.label.length === 0) {
authenticationOption.label = `Auth for ${selectedApp.name}`;
}
for (let paramkey in selectedApp.authentication.parameters) {
if (
authenticationOption.fields[
selectedApp.authentication.parameters[paramkey].name
].length === 0
) {
if (
selectedApp.authentication.parameters[paramkey].value !== undefined &&
selectedApp.authentication.parameters[paramkey].value !== null &&
selectedApp.authentication.parameters[paramkey].value.length > 0
) {
authenticationOption.fields[
selectedApp.authentication.parameters[paramkey].name
] = selectedApp.authentication.parameters[paramkey].value;
} else {
if (
selectedApp.authentication.parameters[paramkey].schema.type === "bool"
) {
authenticationOption.fields[
selectedApp.authentication.parameters[paramkey].name
] = "false";
} else {
toast(
"Field " +
selectedApp.authentication.parameters[paramkey].name +
" can't be empty"
);
return;
}
}
}
}
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
var newFields = [];
for (let authkey in newAuthOption.fields) {
const value = newAuthOption.fields[authkey];
newFields.push({
"key": authkey,
"value": value,
});
}
newAuthOption.fields = newFields
setNewAppAuth(newAuthOption)
}
if (authenticationOption.label === null || authenticationOption.label === undefined) {
authenticationOption.label = selectedApp.name + " authentication";
}
return (
Authentication for {selectedApp.name.replaceAll("_", " ", -1)}
What is app authentication?
These are required fields for authenticating with {selectedApp.name}
Label for you to remember
{
authenticationOption.label = event.target.value;
}}
/>
{selectedApp.authentication.parameters.map((data, index) => {
if (data.value === "" || data.value === null || data.value === undefined || data.name === "url") {
}
return (
{data.name}
{data.schema !== undefined &&
data.schema !== null &&
data.schema.type === "bool" ? (
{
authenticationOption.fields[data.name] = e.target.value;
}}
style={{
backgroundColor: theme.palette.surfaceColor,
color: "white",
height: 50,
}}
>
false
true
) : (
{
authenticationOption.fields[data.name] =
event.target.value;
}}
/>
)}
);
})}
{
setAuthenticationModalOpen(false);
}}
color="secondary"
>
Cancel
{
setAuthenticationOptions(authenticationOption);
handleSubmitCheck()
}}
color="primary"
>
Submit
);
};
const authenticationModal = authenticationModalOpen ? (
{setSelectedMeta(undefined)}}
PaperProps={{
style: {
pointerEvents: "auto",
color: "white",
minWidth: 1100,
minHeight: 700,
maxHeight: 700,
padding: 15,
overflow: "hidden",
zIndex: 10012,
border: theme.palette.defaultBorder,
},
}}
>
{ selectedAppData.reference_info === undefined ||
selectedAppData.reference_info === null ||
selectedAppData.reference_info.github_url === undefined ||
selectedAppData.reference_info.github_url === null ||
selectedAppData.reference_info.github_url.length === 0 ? (
) : (
)}
{
setAuthenticationModalOpen(false);
}}
>
{authenticationType.type === "oauth2" || authenticationType.type === "oauth2-app" ?
:
}
{selectedAppData.documentation === undefined ||
selectedAppData.documentation === null ||
selectedAppData.documentation.length === 0 ? (
{selectedAppData?.description}
There is no Shuffle-specific documentation for this app yet outside of the general description above. Documentation is written for each api, and is a community effort. We hope to see your contribution!
{
toast.success("Opening remote Github documentation link. Thanks for contributing!")
setTimeout(() => {
window.open(`https://github.com/Shuffle/openapi-apps/new/master/docs?filename=${selectedAppData.name.toLowerCase()}.md`, "_blank")
}, 2500)
}}
>
Create Docs
Want to help the making of, or improve this app?{" "}
Join the community on Discord!
Want to help change this app directly?
{selectedAppData.reference_info === undefined ||
selectedAppData.reference_info === null ||
selectedAppData.reference_info.github_url === undefined ||
selectedAppData.reference_info.github_url === null ||
selectedAppData.reference_info.github_url.length === 0 ? (
Check it out on Github!
) : (
Check it out on Github!
)}
) : (
{selectedMeta !== undefined && selectedMeta !== null && Object.getOwnPropertyNames(selectedMeta).length > 0 && selectedMeta.name !== undefined && selectedMeta.name !== null ?
{isMobile ? null : (
Edit
)}
{isMobile ? null : (
)}
{selectedMeta.read_time} minute
{selectedMeta.read_time === 1 ? "" : "s"} to read
{isMobile ||
selectedMeta.contributors === undefined ||
selectedMeta.contributors === null ? (
""
) : (
{selectedMeta.contributors.slice(0, 7).map((data, index) => {
return (
);
})}
)}
: null}
{selectedAppData.documentation}
)}
) : null;
return (
{authenticationModal}
{hits.length === 0 && searchQuery.length >= 0 ? (
No Apps Found
) : (
)}
);
};
const CustomSearchBox = connectSearchBox(SearchBox);
const CustomHits = connectHits(Hits);