image = foundOrg.image === "" ?
:
{
//setFilteredWorkflows(newWorkflows)
}}/>
orgName = foundOrg.name
orgId = foundOrg.id
}
}
return (
{
addFilter(orgId)
//setFilters(["Org "+orgName])
//setFilteredWorkflows(newWorkflows)
}}>
{image}
{parsedName}
{actions}
{triggers}
{
if (subflows === 0) {
alert.info("No subflows for "+data.name)
return
}
var newWorkflows = [data]
for (var key in data.triggers) {
const trigger = data.triggers[key]
if (trigger.app_name !== "Shuffle Workflow") {
continue
}
if (trigger.parameters !== undefined && trigger.parameters !== null && trigger.parameters.length > 0 && trigger.parameters[0].name === "workflow") {
const newWorkflow = workflows.find(item => item.id === trigger.parameters[0].value)
if (newWorkflow !== null && newWorkflow !== undefined) {
newWorkflows.push(newWorkflow)
continue
}
}
}
setFilters(["Subflows of "+data.name])
setFilteredWorkflows(newWorkflows)
}}>
{subflows}
{/*
: null}
{schedules > 0 ?
: null}
*/}
{data.tags !== undefined ?
data.tags.map((tag, index) => {
if (index >= 3) {
return null
}
return (
)
})
: null}
{data.actions !== undefined && data.actions !== null ?
{/*
*/}
: null}
)
}
const dividerColor = "rgb(225, 228, 232)"
const resultPaperAppStyle = {
minHeight: "100px",
minWidth: "100%",
overflow: "hidden",
maxWidth: "100%",
marginTop: "5px",
color: "white",
backgroundColor: surfaceColor,
cursor: "pointer",
display: "flex",
}
function replaceAll(string, search, replace) {
return string.split(search).join(replace);
}
const resultsPaper = (data) => {
var boxWidth = "2px"
var boxColor = "orange"
if (data.status === "ABORTED" || data.status === "UNFINISHED" || data.status === "FAILURE"){
boxColor = "red"
} else if (data.status === "FINISHED" || data.status === "SUCCESS") {
boxColor = "green"
} else if (data.status === "SKIPPED" || data.status === "EXECUTING") {
boxColor = "yellow"
} else {
boxColor = "green"
}
var t = new Date(data.started_at*1000)
var showResult = data.result.trim()
const validate = validateJson(showResult)
if (validate.valid) {
showResult =
} else {
// FIXME - have everything parsed as json, either just for frontend
// or in the backend?
/*
const newdata = {"result": data.result}
showResult =
*/
}
return (
{}}>
Name : {data.action.label}
App: {data.action.app_name}, Version: {data.action.app_version}
Action: {data.action.name}, Environment: {data.action.environment}, Status: {data.status}
Started: {t.toISOString()}
{showResult}
)
}
const resultsHandler = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ?
{selectedExecution.results.sort((a, b) => a.started_at - b.started_at).map((data, index) => {
return (
{resultsPaper(data)}
)
})}
:
No results yet
const resultsLength = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ? selectedExecution.results.length : 0
const ExecutionDetails = () => {
var starttime = new Date(selectedExecution.started_at*1000)
var endtime = new Date(selectedExecution.started_at*1000)
var parsedArgument = selectedExecution.execution_argument
if (selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0) {
parsedArgument = replaceAll(parsedArgument, " None", " \"None\"");
}
var arg = null
if (selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0) {
var showResult = selectedExecution.execution_argument.trim()
const validate = validateJson(showResult)
arg = validate.valid ?
: showResult
}
var lastresult = null
if (selectedExecution.result !== undefined && selectedExecution.result.length > 0) {
var showResult = selectedExecution.result.trim()
const validate = validateJson(showResult)
lastresult = validate.valid ?
: showResult
}
/*
ID: {selectedExecution.execution_id}
Last node: {selectedExecution.workflow.actions.find(data => data.id === selectedExecution.last_node).actions[0].label}
*/
if (Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.workflow.actions !== null) {
return (
Status: {selectedExecution.status}
Started: {starttime.toISOString()}
Finished: {endtime.toISOString()}
{/*
Last Result: {lastresult}
*/}
{arg}
{resultsHandler}
)
}
return (
executionLoading ?
:
There are no executiondetails yet. Click "execute" to run your first one.
)
}
// Can create and set workflows
const setNewWorkflow = (name, description, tags, defaultReturnValue, editingWorkflow, redirect) => {
var method = "POST"
var extraData = ""
var workflowdata = {}
if (editingWorkflow.id !== undefined) {
console.log("Building original workflow")
method = "PUT"
extraData = "/"+editingWorkflow.id+"?skip_save=true"
workflowdata = editingWorkflow
console.log("REMOVING OWNER")
workflowdata["owner"] = ""
// FIXME: Loop triggers and turn them off?
}
workflowdata["name"] = name
workflowdata["description"] = description
if (tags !== undefined) {
workflowdata["tags"] = tags
}
if (defaultReturnValue !== undefined) {
workflowdata["default_return_value"] = defaultReturnValue
//console.log("WORKFLOW: ", workflowdata)
}
//console.log(workflowdata)
//return
return fetch(globalUrl+"/api/v1/workflows"+extraData, {
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(workflowdata),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for workflows :O!")
return
}
setSubmitLoading(false)
return response.json()
})
.then((responseJson) => {
if (method === "POST" && redirect) {
window.location.pathname = "/workflows/"+responseJson["id"]
setModalOpen(false)
} else if (!redirect) {
// Update :)
setTimeout(() => {
getAvailableWorkflows()
}, 1000)
setImportLoading(false)
setModalOpen(false)
} else {
alert.info("Successfully changed basic info for workflow")
setModalOpen(false)
}
return responseJson
})
.catch(error => {
alert.error(error.toString())
setImportLoading(false)
setModalOpen(false)
setSubmitLoading(false)
});
}
const importFiles = (event) => {
console.log("Importing!")
setImportLoading(true)
const file = event.target.value
if (event.target.files.length > 0) {
for (var key in event.target.files) {
const file = event.target.files[key]
if (file.type !== "application/json") {
if (file.type !== undefined) {
alert.error("File has to contain valid json")
}
continue
}
const reader = new FileReader()
// Waits for the read
reader.addEventListener('load', (event) => {
var data = reader.result
try {
data = JSON.parse(reader.result)
} catch (e) {
alert.error("Invalid JSON: "+e)
setImportLoading(false)
return
}
// Initialize the workflow itself
const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, {}, false)
.then((response) => {
if (response !== undefined) {
// SET THE FULL THING
data.id = response.id
data.first_save = false
data.previously_saved = false
data.is_valid = false
// Actually create it
const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, data, false)
.then((response) => {
if (response !== undefined) {
alert.success("Successfully imported "+data.name)
}
})
}
})
.catch(error => {
alert.error("Import error: "+error.toString())
});
})
// Actually reads
reader.readAsText(file)
}
}
setLoadWorkflowsModalOpen(false)
}
const getWorkflowMeta = (data) => {
let triggers = 0
let schedules = 0
let webhooks = 0
let subflows = 0
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
triggers = data.triggers.length
for (let key in data.triggers) {
if (data.triggers[key].app_name === "Webhook") {
webhooks += 1
//webhookImg = data.triggers[key].large_image
} else if (data.triggers[key].app_name === "Schedule") {
schedules += 1
//scheduleImg = data.triggers[key].large_image
} else if (data.triggers[key].app_name === "Shuffle Workflow") {
subflows += 1
}
}
}
return [triggers, schedules, webhooks, subflows]
}
const WorkflowListView = () => {
let workflowData = "";
if (workflows.length > 0) {
const columns = [
{ field: 'title', headerName: 'Title', width: 330, renderCell: (params) => {
const data = params.row.record
return (
{data.name}
)
}},
{/* field: 'category', headerName: 'category', width: 140, renderCell: (params) => {
const data = params.row.record
const category = data.category === undefined ? "not defined" : data.category
return (
{category}
)
} */},
{ field: 'actions', headerName: 'Options', width: 200, sortable: false,
disableClickEventBubbling: true,
renderCell: (params) => {
const data = params.row.record;
const actions = data.actions !== null ? data.actions.length : 0
let [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data);
return (
{actions}
{triggers}
{
if (subflows === 0) {
alert.info("No subflows for "+data.name)
return
}
var newWorkflows = [data]
for (var key in data.triggers) {
const trigger = data.triggers[key]
if (trigger.app_name !== "Shuffle Workflow") {
continue
}
if (trigger.parameters !== undefined && trigger.parameters !== null && trigger.parameters.length > 0 && trigger.parameters[0].name === "workflow") {
const newWorkflow = workflows.find(item => item.id === trigger.parameters[0].value)
if (newWorkflow !== null && newWorkflow !== undefined) {
newWorkflows.push(newWorkflow)
continue
}
}
}
setFilters(["Subflows of "+data.name])
setFilteredWorkflows(newWorkflows)
}}>
{subflows}
)
}
},
{/* field: 'tags', headerName: 'Tags', maxHeight: 15, width: 390, sortable: false,
disableClickEventBubbling: true,
renderCell: (params) => {
const data = params.row.record;
return (
{data.tags !== undefined ?
data.tags.map((tag, index) => {
if (index >= 3) {
return null
}
return (
)
})
: null}
)
}
*/}
];
let rows = [];
rows = workflows.map((data, index) => {
let obj = {
"id":index+1,
"title":data.name,
"record":data,
}
return obj;
});
workflowData =
}
return (
{workflowData}
);
}
const modalView = modalOpen ?
{setModalOpen(false)}}
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: "800px",
},
}}
>
{editingWorkflow.id !== undefined ? "Editing" : "New"} workflow
setNewWorkflowName(event.target.value)}
InputProps={{
style:{
color: "white",
},
}}
color="primary"
placeholder="Name"
margin="dense"
defaultValue={newWorkflowName}
fullWidth
/>
setNewWorkflowDescription(event.target.value)}
InputProps={{
style:{
color: "white",
},
}}
color="primary"
defaultValue={newWorkflowDescription}
placeholder="Description"
rows="3"
multiline
margin="dense"
fullWidth
/>
{
newWorkflowTags.push(chip)
setNewWorkflowTags(newWorkflowTags)
}}
onDelete={(chip, index) => {
newWorkflowTags.splice(index, 1)
setNewWorkflowTags(newWorkflowTags)
setUpdate("delete "+chip)
}}
/>
{showExtraOptions ?
setDefaultReturnValue(event.target.value)}
InputProps={{
style:{
color: "white",
},
}}
color="primary"
defaultValue={defaultReturnValue}
placeholder="Default return value (used for Subflows if the subflow fails)"
rows="3"
multiline
margin="dense"
fullWidth
/>
: null}
{
setNewWorkflowName("")
setNewWorkflowDescription("")
setDefaultReturnValue("")
setEditingWorkflow({})
setNewWorkflowTags([])
setModalOpen(false)
}} color="primary">
Cancel
{
console.log("Tags: ", newWorkflowTags)
if (editingWorkflow.id !== undefined) {
setNewWorkflow(newWorkflowName, newWorkflowDescription, newWorkflowTags, defaultReturnValue, editingWorkflow, false)
setNewWorkflowName("")
setDefaultReturnValue("")
setNewWorkflowDescription("")
setEditingWorkflow({})
setNewWorkflowTags([])
} else {
setNewWorkflow(newWorkflowName, newWorkflowDescription, newWorkflowTags, defaultReturnValue, {}, true)
}
setSubmitLoading(true)
}} color="primary">
{submitLoading ?
:
"Submit"
}
: null
const viewSize = {
workflowView: 4,
executionsView: 3,
executionResults: 4,
}
const workflowViewStyle = {
flex: viewSize.workflowView,
marginLeft: "10px",
marginRight: "10px",
}
if (viewSize.workflowView === 0) {
workflowViewStyle.display = "none"
}
const workflowButtons =
{/*workflows.length > 0 ?
setModalOpen(true)}>
: null*/}
{view === "list" && (
setView("grid")}>
)}
{view === "grid" && (
setView("list")}>
)}
{importLoading ?
{}}>
:
upload.click()}>
}
upload = ref} onChange={importFiles} />
{workflows.length > 0 ?
{
exportAllWorkflows()
}}>
: null}
{isCloud ? null :
setLoadWorkflowsModalOpen(true)}>
}
const WorkflowView = () => {
if (workflows.length === 0) {
return (
Welcome to Shuffle
Shuffle is a flexible, easy to use, automation platform allowing users to integrate their services and devices freely. It's made to significantly reduce the amount of manual labor, and is focused on security applications. Click here to learn more.
If you want to jump straight into it, click here to create your first workflow:
setModalOpen(true)}>New workflow
..OR
{workflowButtons}
)
}
return (
{/*
{workflows.length}
ACTIVE WORKFLOWS
{workflows.length}
AVAILABE WORKFLOWS
{workflows.length}
NOTIFICATIONS
*/}
{/*
chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {
console.log("VALUE: ", value)
return (
{value}
)
}}
*/}
{
addFilter(chip)
}}
onDelete={(chip, index) => {
removeFilter(index)
}}
/>
{workflowButtons}
{view === "grid" ?
{filteredWorkflows.map((data, index) => {
return (
)
})}
:
}
)
}
const importWorkflowsFromUrl = (url) => {
console.log("IMPORT WORKFLOWS FROM ", downloadUrl)
const parsedData = {
"url": url,
"field_3": downloadBranch || 'master'
}
if (field1.length > 0) {
parsedData["field_1"] = field1
}
if (field2.length > 0) {
parsedData["field_2"] = field2
}
alert.success("Getting specific workflows from your URL.")
var cors = "cors"
fetch(globalUrl+"/api/v1/workflows/download_remote", {
method: "POST",
mode: "cors",
headers: {
'Accept': 'application/json',
},
body: JSON.stringify(parsedData),
credentials: "include",
})
.then((response) => {
if (response.status === 200) {
alert.success("Successfully loaded workflows from "+downloadUrl)
setTimeout(() => {
getAvailableWorkflows()
}, 1000)
}
return response.json()
})
.then((responseJson) => {
//console.log("DATA: ", responseJson)
if (!responseJson.success) {
if (responseJson.reason !== undefined) {
alert.error("Failed loading: "+responseJson.reason)
} else {
alert.error("Failed loading")
}
}
})
.catch(error => {
alert.error(error.toString())
})
}
const handleGithubValidation = () => {
importWorkflowsFromUrl(downloadUrl)
setLoadWorkflowsModalOpen(false)
}
const workflowDownloadModalOpen = loadWorkflowsModalOpen ?
{
}}
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
Load workflows from github repo
Repository (supported: github, gitlab, bitbucket)
setDownloadUrl(e.target.value)}
placeholder="https://github.com/frikky/shuffle-apps"
fullWidth
/>
Branch (default value is "master"):
setDownloadBranch(e.target.value)}
placeholder="master"
fullWidth
/>
Authentication (optional - private repos etc):
setField1(e.target.value)}
type="username"
placeholder="Username / APIkey (optional)"
fullWidth
/>
setField2(e.target.value)}
type="password"
placeholder="Password (optional)"
fullWidth
/>
setLoadWorkflowsModalOpen(false)} color="primary">
Cancel
{
handleGithubValidation()
}} color="primary">
Submit
Submit
: null
const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
1366 ? 1366 : 1200, margin: "auto", padding: 20 }} onDrop={uploadFile}>
{modalView}
{deleteModal}
{publishModal}
{workflowDownloadModalOpen}
:
Loading Workflows
// Maybe use gridview or something, idk
return (
{loadedCheck}
)
}
export default Workflows