Merged upstream
This commit is contained in:
@@ -20,7 +20,7 @@ require (
|
|||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shuffle/shuffle-shared v0.6.47
|
github.com/shuffle/shuffle-shared v0.6.50
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.22.0
|
||||||
google.golang.org/api v0.176.1
|
google.golang.org/api v0.176.1
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.63.2
|
||||||
@@ -100,6 +100,8 @@ require (
|
|||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||||
|
|||||||
@@ -431,6 +431,10 @@ github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6
|
|||||||
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
@@ -451,6 +455,8 @@ github.com/shuffle/shuffle-shared v0.6.46 h1:v/IXc+4V8DCWflGKGgaI37uuGnsylMjqKRy
|
|||||||
github.com/shuffle/shuffle-shared v0.6.46/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
github.com/shuffle/shuffle-shared v0.6.46/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
github.com/shuffle/shuffle-shared v0.6.47 h1:EOalfIIBX97uGkgRRPsdUApts6yCCcXf1iSphm6UoE0=
|
github.com/shuffle/shuffle-shared v0.6.47 h1:EOalfIIBX97uGkgRRPsdUApts6yCCcXf1iSphm6UoE0=
|
||||||
github.com/shuffle/shuffle-shared v0.6.47/go.mod h1:XVIcR2/GyIk+6qmlpOG3NvIao6kCOma36EdMym4AeSY=
|
github.com/shuffle/shuffle-shared v0.6.47/go.mod h1:XVIcR2/GyIk+6qmlpOG3NvIao6kCOma36EdMym4AeSY=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50 h1:MBeGAiBNkw9Eg+3YTJIlBOuskWntGvT0uefFUYOBhbY=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
|||||||
@@ -3616,7 +3616,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if org.SyncConfig.WorkflowBackup {
|
if org.SyncConfig.WorkflowBackup {
|
||||||
workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser)
|
workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser, 250, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
|
log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
|||||||
// return workflowExecution, fmt.Sprintf("%s", err), nil
|
// return workflowExecution, fmt.Sprintf("%s", err), nil
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
|
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
|
||||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err
|
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed running: %s", err), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1775,7 +1775,7 @@ const Billing = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{ marginTop: 15, textTransform: 'none' }}
|
style={{ marginTop: 15, textTransform: 'none' }}
|
||||||
onClick={addAlertThreshold}
|
onClick={addAlertThreshold}
|
||||||
@@ -1783,7 +1783,7 @@ const Billing = (props) => {
|
|||||||
Add Threshold
|
Add Threshold
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{ marginTop: 15, textTransform: 'none', marginLeft: 20 }}
|
style={{ marginTop: 15, textTransform: 'none', marginLeft: 20 }}
|
||||||
onClick={() => { HandleEditOrgForAlertThreshold(selectedOrganization.id) }}
|
onClick={() => { HandleEditOrgForAlertThreshold(selectedOrganization.id) }}
|
||||||
|
|||||||
@@ -338,26 +338,6 @@ const EditWorkflow = (props) => {
|
|||||||
autoFocus
|
autoFocus
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<div style={{display: "flex", }}>
|
|
||||||
<TextField
|
|
||||||
onBlur={(event) => {
|
|
||||||
setDescription(event.target.value)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
maxRows={4}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.description}
|
|
||||||
placeholder="Description"
|
|
||||||
multiline
|
|
||||||
label="Description"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||||
<FormControl style={{flex: 1, marginRight: 5,}}>
|
<FormControl style={{flex: 1, marginRight: 5,}}>
|
||||||
@@ -425,27 +405,56 @@ const EditWorkflow = (props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
value={newWorkflowTags}
|
value={newWorkflowTags}
|
||||||
onChange={(chip) => {
|
onChange={(chip) => {
|
||||||
console.log("Chip: ", chip)
|
|
||||||
//newWorkflowTags.push(chip);
|
|
||||||
setNewWorkflowTags(chip);
|
setNewWorkflowTags(chip);
|
||||||
}}
|
}}
|
||||||
|
onBlur={(event) => {
|
||||||
|
if (event.target.value.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWorkflowTags.includes(event.target.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newWorkflowTags.push(event.target.value)
|
||||||
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
|
|
||||||
|
setUpdate(Math.random())
|
||||||
|
}}
|
||||||
onAdd={(chip) => {
|
onAdd={(chip) => {
|
||||||
newWorkflowTags.push(chip);
|
newWorkflowTags.push(chip)
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
}}
|
}}
|
||||||
onDelete={(chip, index) => {
|
onDelete={(chip, index) => {
|
||||||
console.log("Deleting: ", chip, index)
|
console.log("Deleting: ", chip, index)
|
||||||
newWorkflowTags.splice(index, 1);
|
newWorkflowTags.splice(index, 1)
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showMoreClicked === true ?
|
{showMoreClicked === true ?
|
||||||
<span style={{marginTop: 25, }}>
|
<div style={{marginTop: 50, }}>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
setDescription(event.target.value)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
multiLine
|
||||||
|
rows={3}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={innerWorkflow.description}
|
||||||
|
placeholder="Description"
|
||||||
|
multiline
|
||||||
|
label="Description"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
<FormControl style={{marginTop: 15, }}>
|
<FormControl style={{marginTop: 15, }}>
|
||||||
@@ -564,6 +573,8 @@ const EditWorkflow = (props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
|
|
||||||
<Typography variant="body1" style={{marginTop: 50, }}>
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
MSSP Suborg Distribution (beta - contact support@shuffler.io for more info)
|
MSSP Suborg Distribution (beta - contact support@shuffler.io for more info)
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -669,12 +680,13 @@ const EditWorkflow = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
|
|
||||||
<Typography variant="h6" style={{marginTop: 50, }}>
|
<Typography variant="h6" style={{marginTop: 50, }}>
|
||||||
Input fields
|
Input fields
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="textSecondary" style={{marginBottom: 20, }}>
|
<Typography variant="body2" color="textSecondary" style={{marginBottom: 20, }}>
|
||||||
Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>.
|
Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>. If chosen in the User Input node, these will be required fields.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
|
||||||
@@ -781,6 +793,7 @@ const EditWorkflow = (props) => {
|
|||||||
<AddIcon style={{}} />
|
<AddIcon style={{}} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
|
|
||||||
<Typography variant="body1" style={{marginTop: 50, }}>
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
Git Backup Repository
|
Git Backup Repository
|
||||||
@@ -931,7 +944,7 @@ const EditWorkflow = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</span>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -554,7 +554,8 @@ const Header = (props) => {
|
|||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={(event) => { }}
|
onClick={(event) => { }}
|
||||||
>
|
>
|
||||||
<svg viewBox="0 0 256 199" width="256" height="199" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" style={{height: 30, width: 30, }}><path d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z" fill="#5865F2"/></svg>
|
<svg viewBox="0 0 256 199" width="256" height="199" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" style={{height: 30, width: 30, }}><path d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z" fill="#dadae1"/></svg>
|
||||||
|
{/*#f865f2*/}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -471,32 +471,26 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
state += `%26refresh_uri%3d${authentication_url}`;
|
state += `%26refresh_uri%3d${authentication_url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No prompt forcing
|
// FIXME: Should this be =consent?
|
||||||
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
|
||||||
var defaultPrompt = "login"
|
var defaultPrompt = "login"
|
||||||
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
||||||
defaultPrompt = prompt
|
defaultPrompt = prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=${defaultPrompt}&scope=${resources}&state=${state}&access_type=offline`;
|
var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=${defaultPrompt}&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
|
|
||||||
if (admin_consent === true) {
|
if (admin_consent === true) {
|
||||||
console.log("Running Oauth2 WITH admin consent")
|
console.log("Running Oauth2 WITH admin consent")
|
||||||
//url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=consent&scope=${resources}&state=${state}&access_type=offline`;
|
//url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=consent&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
|
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("OAUTH2 URL: ", url)
|
||||||
|
|
||||||
// Force new consent
|
// Force new consent
|
||||||
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
||||||
|
|
||||||
// Admin consent
|
// Admin consent
|
||||||
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
||||||
|
|
||||||
// &resource=https%3A%2F%2Fgraph.microsoft.com&
|
|
||||||
|
|
||||||
// FIXME: Awful, but works for prototyping
|
|
||||||
// How can we get a callback properly realtime?
|
|
||||||
// How can we properly try-catch without breaks on error?
|
|
||||||
try {
|
try {
|
||||||
var newwin = window.open(url, "", "width=582,height=700");
|
var newwin = window.open(url, "", "width=582,height=700");
|
||||||
//console.log(newwin)
|
//console.log(newwin)
|
||||||
|
|||||||
@@ -823,7 +823,7 @@ const ParsedAction = (props) => {
|
|||||||
if (selectedAction.app_name === "Shuffle Tools" && selectedAction.name === "filter_list" && data.name === "input_list") {
|
if (selectedAction.app_name === "Shuffle Tools" && selectedAction.name === "filter_list" && data.name === "input_list") {
|
||||||
//console.log("FILTER LIST!: ", event, count, data)
|
//console.log("FILTER LIST!: ", event, count, data)
|
||||||
const parsedvalue = event.target.value
|
const parsedvalue = event.target.value
|
||||||
if (parsedvalue.includes("#")) {
|
if (parsedvalue.includes(".#")) {
|
||||||
const splitparsed = parsedvalue.split(".#.")
|
const splitparsed = parsedvalue.split(".#.")
|
||||||
//console.log("Cant contain #: ", splitparsed)
|
//console.log("Cant contain #: ", splitparsed)
|
||||||
if (splitparsed.length > 1) {
|
if (splitparsed.length > 1) {
|
||||||
@@ -834,8 +834,24 @@ const ParsedAction = (props) => {
|
|||||||
|
|
||||||
selectedActionParameters[1].value = splitparsed[1]
|
selectedActionParameters[1].value = splitparsed[1]
|
||||||
selectedAction.parameters[1].value = splitparsed[1]
|
selectedAction.parameters[1].value = splitparsed[1]
|
||||||
forceUpdate = true
|
} else {
|
||||||
|
// Remove .# and after
|
||||||
|
const splitparsed = parsedvalue.split(".#")
|
||||||
|
data.value = splitparsed[0]
|
||||||
|
selectedActionParameters[0].value = splitparsed[0]
|
||||||
|
selectedAction.parameters[0].value = splitparsed[0]
|
||||||
|
|
||||||
|
selectedActionParameters[1].value = ""
|
||||||
|
selectedAction.parameters[1].value = ""
|
||||||
|
|
||||||
|
toast.warn("No value found in the list. Please select an item in the list to filter based on.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceUpdate = true
|
||||||
|
selectedActionParameters[0].autocompleted = true
|
||||||
|
selectedAction.parameters[0].autocompleted = true
|
||||||
|
selectedActionParameters[1].autocompleted = true
|
||||||
|
selectedAction.parameters[1].autocompleted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -910,6 +926,7 @@ const ParsedAction = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// bad detection mechanism probably
|
// bad detection mechanism probably
|
||||||
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
|
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
|
||||||
console.log("GET THE LAST ARGUMENT FOR NODE!")
|
console.log("GET THE LAST ARGUMENT FOR NODE!")
|
||||||
@@ -3839,7 +3856,8 @@ const ParsedAction = (props) => {
|
|||||||
newname = newname.slice(0, newname.length-5)
|
newname = newname.slice(0, newname.length-5)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}`
|
//selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}`
|
||||||
|
selectedActionParameters[count].value += `$${innerdata.name}.${newname}`
|
||||||
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
selectedAction.parameters[count].value = selectedActionParameters[count].value;
|
||||||
setSelectedAction(selectedAction);
|
setSelectedAction(selectedAction);
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random());
|
||||||
|
|||||||
@@ -1568,6 +1568,11 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
responseJson.lead_info !== null
|
responseJson.lead_info !== null
|
||||||
) {
|
) {
|
||||||
var leads = [];
|
var leads = [];
|
||||||
|
console.log("LEADINFO: ", responseJson.lead_info)
|
||||||
|
if (responseJson.lead_info.testing_shuffle) {
|
||||||
|
leads.push("testing shuffle");
|
||||||
|
}
|
||||||
|
|
||||||
if (responseJson.lead_info.contacted) {
|
if (responseJson.lead_info.contacted) {
|
||||||
leads.push("contacted");
|
leads.push("contacted");
|
||||||
}
|
}
|
||||||
@@ -3572,7 +3577,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
<FormControl sx={{ m: 1, width: 300 }} style={{}}>
|
<FormControl sx={{ m: 1, width: 300 }} style={{}}>
|
||||||
<InputLabel id="">Status</InputLabel>
|
<InputLabel id="">Status</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
style={{ minWidth: 150, maxWidth: 150 }}
|
style={{ minWidth: 150, maxWidth: 150, }}
|
||||||
labelId="multiselect-status"
|
labelId="multiselect-status"
|
||||||
id="multiselect-status"
|
id="multiselect-status"
|
||||||
multiple
|
multiple
|
||||||
@@ -3583,9 +3588,10 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
MenuProps={MenuProps}
|
MenuProps={MenuProps}
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
|
"testing shuffle",
|
||||||
"contacted",
|
"contacted",
|
||||||
"lead",
|
"lead",
|
||||||
"demo done",
|
//"demo done",
|
||||||
"pov",
|
"pov",
|
||||||
"customer",
|
"customer",
|
||||||
"open source",
|
"open source",
|
||||||
@@ -3745,7 +3751,7 @@ If you're interested, please let me know a time that works for you, or set up a
|
|||||||
aria-label="disabled tabs example"
|
aria-label="disabled tabs example"
|
||||||
>
|
>
|
||||||
<Tab label=<span>Edit Details</span> />
|
<Tab label=<span>Edit Details</span> />
|
||||||
<Tab label=<span>Org Limits & Cloud Sync</span> />
|
<Tab label=<span>Limits & Cloud Sync</span> />
|
||||||
<Tab label=<span>Priorities</span> />
|
<Tab label=<span>Priorities</span> />
|
||||||
<Tab label=<span>Billing & Stats</span> />
|
<Tab label=<span>Billing & Stats</span> />
|
||||||
<Tab disabled={!isCloud} label=<span>Branding (Beta)</span> />
|
<Tab disabled={!isCloud} label=<span>Branding (Beta)</span> />
|
||||||
|
|||||||
@@ -1073,8 +1073,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
// User Input & Subflow nodes
|
// User Input & Subflow nodes
|
||||||
if (param.name === "workflow" || param.name === "subflow") {
|
if (param.name === "workflow" || param.name === "subflow") {
|
||||||
const paramIndex = param.name === "workflow" ? 0 : 5
|
const paramIndex = param.name === "workflow" ? 0 : 5
|
||||||
console.log("Current vs new: ", workflow.triggers[trigger_index].parameters[paramIndex].value, subworkflow.id)
|
|
||||||
|
|
||||||
if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) {
|
if (workflow.triggers[trigger_index].parameters[paramIndex].value !== subworkflow.id) {
|
||||||
if (param.value === workflow.id) {
|
if (param.value === workflow.id) {
|
||||||
setSubworkflow(workflow);
|
setSubworkflow(workflow);
|
||||||
@@ -3255,7 +3253,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
|
|
||||||
const getChildWorkflows = (parentWorkflowId) => {
|
const getChildWorkflows = (parentWorkflowId) => {
|
||||||
if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) {
|
if (originalWorkflow.suborg_distribution === undefined || originalWorkflow.suborg_distribution === null || originalWorkflow.suborg_distribution.length === 0) {
|
||||||
console.log("No suborg distribution")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5300,7 +5297,59 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setSelectedTriggerIndex(trigger_index);
|
if (trigger_index !== -1) {
|
||||||
|
const trigger = workflow.triggers[trigger_index]
|
||||||
|
if (trigger !== undefined && trigger !== null) {
|
||||||
|
|
||||||
|
// Autofixer
|
||||||
|
if (trigger.trigger_type === "USERINPUT") {
|
||||||
|
const relevantparams = [
|
||||||
|
"alertinfo",
|
||||||
|
"options",
|
||||||
|
"type",
|
||||||
|
"email",
|
||||||
|
"sms",
|
||||||
|
"subflow",
|
||||||
|
]
|
||||||
|
var foundparams = 0
|
||||||
|
for (var paramkey in trigger.parameters) {
|
||||||
|
if (relevantparams.includes(trigger.parameters[paramkey].name)) {
|
||||||
|
foundparams++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundparams < 6) {
|
||||||
|
trigger.parameters = [{
|
||||||
|
name: "alertinfo",
|
||||||
|
value: "Do you want to continue the workflow? Start parameters: $exec",
|
||||||
|
},{
|
||||||
|
name: "options",
|
||||||
|
value: "boolean",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type",
|
||||||
|
value: "subflow",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "email",
|
||||||
|
value: "test@test.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sms",
|
||||||
|
value: "0000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subflow",
|
||||||
|
value: "",
|
||||||
|
}]
|
||||||
|
|
||||||
|
workflow.triggers[trigger_index].parameters = trigger.parameters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedTriggerIndex(trigger_index)
|
||||||
setSelectedTrigger(data)
|
setSelectedTrigger(data)
|
||||||
setSelectedActionEnvironment(data.env)
|
setSelectedActionEnvironment(data.env)
|
||||||
}, 25)
|
}, 25)
|
||||||
@@ -5650,15 +5699,16 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
|
|
||||||
// Checks for errors in edges when they're added
|
// Checks for errors in edges when they're added
|
||||||
const onEdgeAdded = (event) => {
|
const onEdgeAdded = (event) => {
|
||||||
const edge = event.target.data()
|
setLastSaved(false);
|
||||||
//console.log("EDGE ADDED!: ", edge)
|
const edge = event.target.data();
|
||||||
|
|
||||||
|
//console.log("edge added: ", edge)
|
||||||
if (edge.source === undefined && edge.target === undefined) {
|
if (edge.source === undefined && edge.target === undefined) {
|
||||||
// console.log("Edge source and target is undefined")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge.readded === true) {
|
if (edge.readded === true) {
|
||||||
// console.log("Readded edge - stopping")
|
console.log("Readded edge - stopping")
|
||||||
|
|
||||||
event.target.data("readded", false)
|
event.target.data("readded", false)
|
||||||
return
|
return
|
||||||
@@ -5667,7 +5717,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
const sourcenode = cy.getElementById(edge.source)
|
const sourcenode = cy.getElementById(edge.source)
|
||||||
const destinationnode = cy.getElementById(edge.target)
|
const destinationnode = cy.getElementById(edge.target)
|
||||||
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
|
if (sourcenode === undefined || sourcenode === null || destinationnode === undefined || destinationnode === null) {
|
||||||
// console.log("Source or destination node is undefined")
|
|
||||||
} else {
|
} else {
|
||||||
//console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
|
//console.log("Edge added: Is it a trigger? If so, check if it already has a branch and remove it: ", sourcenode.data())
|
||||||
if (sourcenode.data("type") === "TRIGGER") {
|
if (sourcenode.data("type") === "TRIGGER") {
|
||||||
@@ -5680,17 +5729,17 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
|
|
||||||
console.log("Node: ", targetedge)
|
console.log("Node: ", targetedge)
|
||||||
if (targetedge !== -1) {
|
if (targetedge !== -1) {
|
||||||
|
event.target.remove()
|
||||||
|
|
||||||
//console.log("Found branch already!")
|
//console.log("Found branch already!")
|
||||||
toast.error("Triggers can have exactly one target node")
|
toast.error("Triggers can have exactly one target node")
|
||||||
event.target.remove()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
// name: "Shuffle Workflow",
|
// name: "Shuffle Workflow",
|
||||||
// name: "User Input",
|
// name: "User Input",
|
||||||
} else {
|
} else {
|
||||||
// console.log("Node doesn't already have one")
|
console.log("Node doesn't already have one")
|
||||||
}
|
}
|
||||||
}, 50)
|
}, 50)
|
||||||
}
|
}
|
||||||
@@ -5704,10 +5753,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge.decorator === true) {
|
|
||||||
// console.log("Doing nothing to branch because decorator")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetnode = workflow.triggers.findIndex(
|
var targetnode = workflow.triggers.findIndex(
|
||||||
(data) => data.id === edge.target
|
(data) => data.id === edge.target
|
||||||
@@ -5725,7 +5770,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
if (eventTarget.data("isButton") === true) {
|
if (eventTarget.data("isButton") === true) {
|
||||||
const parentNode = cy.getElementById(eventTarget.data("attachedTo"))
|
const parentNode = cy.getElementById(eventTarget.data("attachedTo"))
|
||||||
event.target.remove()
|
event.target.remove()
|
||||||
// console.log("Setting it to parentnode: ", parentNode.data())
|
console.log("Setting it to parentnode: ", parentNode.data())
|
||||||
if (parentNode !== undefined && parentNode !== null) {
|
if (parentNode !== undefined && parentNode !== null) {
|
||||||
//event.target.data("target", eventTarget.data("attachedTo"))
|
//event.target.data("target", eventTarget.data("attachedTo"))
|
||||||
|
|
||||||
@@ -5747,14 +5792,15 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventTarget.data("isDescriptor") === true || eventTarget.data("type") === "COMMENT") {
|
if (
|
||||||
// console.log("Removing because of descriptor or comment")
|
eventTarget.data("isDescriptor") === true ||
|
||||||
event.target.remove()
|
eventTarget.data("type") === "COMMENT"
|
||||||
return
|
) {
|
||||||
|
console.log("Removing because of descriptor or comment")
|
||||||
|
event.target.remove();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setLastSaved(false)
|
|
||||||
targetnode = -1;
|
targetnode = -1;
|
||||||
|
|
||||||
// Check if:
|
// Check if:
|
||||||
@@ -5762,51 +5808,38 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
// dest == dest && source == source
|
// dest == dest && source == source
|
||||||
// backend: check all children? to stop recursion
|
// backend: check all children? to stop recursion
|
||||||
var found = false;
|
var found = false;
|
||||||
const branches = cy.edges().jsons()
|
for (let branchkey in workflow.branches) {
|
||||||
|
if (
|
||||||
const startNode = cy.nodes().jsons().find((node) => node.data.isStartNode === true)
|
workflow.branches[branchkey].destination_id === edge.source &&
|
||||||
var startnodeId = workflow.start
|
workflow.branches[branchkey].source_id === edge.target
|
||||||
if (startNode !== undefined && startNode !== null) {
|
) {
|
||||||
startnodeId = startNode.data.id
|
toast("A branch in the opposite direction already exists");
|
||||||
}
|
event.target.remove();
|
||||||
|
found = true;
|
||||||
//for (let branchkey in workflow.branches) {
|
break;
|
||||||
for (let branchkey in branches) {
|
} else if (
|
||||||
const branch = branches[branchkey].data
|
workflow.branches[branchkey].destination_id === edge.target &&
|
||||||
|
workflow.branches[branchkey].source_id === edge.source
|
||||||
//if (workflow.branches[branchkey].destination_id === edge.source && workflow.branches[branchkey].source_id === edge.target) {
|
) {
|
||||||
if (branch.target === edge.source && branch.source === edge.target) {
|
//toast("That branch already exists");
|
||||||
toast("A branch in the opposite direction already exists")
|
event.target.remove();
|
||||||
event.target.remove()
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
|
|
||||||
//} else if (workflow.branches[branchkey].destination_id === edge.target && workflow.branches[branchkey].source_id === edge.source) {
|
|
||||||
} else if (branch.target === edge.target && branch.source === edge.source) {
|
|
||||||
|
|
||||||
if (branch.conditions === undefined) {
|
|
||||||
// Edgehandles
|
|
||||||
} else {
|
|
||||||
// console.log("Removing because the same branch already exists")
|
|
||||||
event.target.remove()
|
|
||||||
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if (edge.target === startnodeId) {
|
|
||||||
targetnode = workflow.triggers.findIndex((data) => data.id === edge.source)
|
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else if (edge.target === workflow.start) {
|
||||||
|
targetnode = workflow.triggers.findIndex(
|
||||||
|
(data) => data.id === edge.source
|
||||||
|
);
|
||||||
if (targetnode === -1) {
|
if (targetnode === -1) {
|
||||||
if (targetnode.type !== "TRIGGER") {
|
if (targetnode.type !== "TRIGGER") {
|
||||||
toast("Can't make arrow to starting node")
|
toast("Can't make arrow to starting node");
|
||||||
event.target.remove()
|
event.target.remove();
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
//} else if (edge.source === workflow.branches[branchkey].source_id) {
|
} else if (edge.source === workflow.branches[branchkey].source_id) {
|
||||||
} else if (edge.source === branch.source) {
|
|
||||||
// FIXME: Verify multi-target for triggers
|
// FIXME: Verify multi-target for triggers
|
||||||
// 1. Check if destination exists
|
// 1. Check if destination exists
|
||||||
// 2. Check if source is a trigger
|
// 2. Check if source is a trigger
|
||||||
@@ -5850,6 +5883,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
newdst !== null
|
newdst !== null
|
||||||
) {
|
) {
|
||||||
const dstdata = RunAutocompleter(newdst.data());
|
const dstdata = RunAutocompleter(newdst.data());
|
||||||
|
//console.log("DST Autocompleter: ", dstdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newbranch = {
|
var newbranch = {
|
||||||
@@ -12404,7 +12438,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
setSubworkflow(e.target.value);
|
setSubworkflow(e.target.value);
|
||||||
|
|
||||||
// Sets the startnode
|
// Sets the startnode
|
||||||
if (e.target.value.id !== workflow.id) {
|
if (e.target.value.id !== workflow.id && e.target.value.id.length > 0 ) {
|
||||||
console.log("WORKFLOW: ", e.target.value);
|
console.log("WORKFLOW: ", e.target.value);
|
||||||
|
|
||||||
const startnode = e.target.value.actions.find((action) => action.id === e.target.value.start);
|
const startnode = e.target.value.actions.find((action) => action.id === e.target.value.start);
|
||||||
@@ -12502,9 +12536,9 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
let mappingWithName = {}
|
let mappingWithName = {}
|
||||||
let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("="))
|
let listWithValues = workflow.triggers[selectedTriggerIndex].parameters[5]?.value.split(";").filter(e => e).map(e => e.split("="))
|
||||||
console.log("LIST WITH VALUES: ", listWithValues)
|
console.log("LIST WITH VALUES: ", listWithValues)
|
||||||
// if (listWithValues === undefined || listWithValues === null || listWithValues.length === 0) {
|
if (listWithValues === undefined || listWithValues === null || listWithValues.length === 0) {
|
||||||
// return "no-overrides";
|
return "no-overrides";
|
||||||
// }
|
}
|
||||||
|
|
||||||
for (let i = 0; i < listWithValues.length; i++) {
|
for (let i = 0; i < listWithValues.length; i++) {
|
||||||
mappingWithName[listWithValues[i][0]] = listWithValues[i][1]
|
mappingWithName[listWithValues[i][0]] = listWithValues[i][1]
|
||||||
@@ -15016,7 +15050,12 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
const newname = (option.name.charAt(0).toUpperCase() + option.name.substring(1)).replaceAll("_", " ");
|
const newname = (option.name.charAt(0).toUpperCase() + option.name.substring(1)).replaceAll("_", " ");
|
||||||
return newname;
|
return newname;
|
||||||
}}
|
}}
|
||||||
options={workflows}
|
options={
|
||||||
|
[{
|
||||||
|
"id": "",
|
||||||
|
"name": "No Workflow Selected",
|
||||||
|
}].concat(workflows)
|
||||||
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.palette.inputColor,
|
backgroundColor: theme.palette.inputColor,
|
||||||
@@ -15171,14 +15210,40 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{marginTop: 20, }} />
|
<div style={{marginTop: 0, }} />
|
||||||
<b>Enabled Input-Questions</b>
|
<b>Required Input-Questions</b>
|
||||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||||
<div>
|
<div>
|
||||||
{workflow.input_questions.map((question, index) => {
|
{workflow.input_questions.map((question, index) => {
|
||||||
|
var foundParamIndex = workflow.triggers[selectedTriggerIndex].parameters.findIndex((param) => param.name === "input_questions")
|
||||||
|
|
||||||
const selectionClick = () => {
|
const selectionClick = () => {
|
||||||
console.log("Clicked input question: ", question)
|
if (foundParamIndex === -1) {
|
||||||
console.log("PARAMS: ", workflow.triggers[selectedTriggerIndex].parameters)
|
workflow.triggers[selectedTriggerIndex].parameters.push({
|
||||||
|
"name": "input_questions",
|
||||||
|
"value": [],
|
||||||
|
})
|
||||||
|
|
||||||
|
foundParamIndex = workflow.triggers[selectedTriggerIndex].parameters.length - 1
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value = JSON.parse(workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value)
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Couldn't parse input questions: ", e)
|
||||||
|
workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value.includes(question.name)) {
|
||||||
|
workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value = workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value.filter((item) => item !== question.name)
|
||||||
|
} else {
|
||||||
|
workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value.push(question.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it back to a string
|
||||||
|
workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value = JSON.stringify(workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value)
|
||||||
|
setWorkflow(workflow)
|
||||||
|
setUpdate(Math.random())
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -15186,7 +15251,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
selectionClick()
|
selectionClick()
|
||||||
}}>
|
}}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={false}
|
checked={foundParamIndex !== -1 ? workflow.triggers[selectedTriggerIndex].parameters[foundParamIndex].value.includes(question.name) : false}
|
||||||
/>
|
/>
|
||||||
<Typography variant="body2" style={{marginTop: 10, }}>
|
<Typography variant="body2" style={{marginTop: 10, }}>
|
||||||
{question.name}
|
{question.name}
|
||||||
@@ -15198,6 +15263,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
:
|
:
|
||||||
<div style={{cursor: "pointer", color: "#f85a3e", marginTop: 10, }} onClick={() => {
|
<div style={{cursor: "pointer", color: "#f85a3e", marginTop: 10, }} onClick={() => {
|
||||||
setEditWorkflowModalOpen(true)
|
setEditWorkflowModalOpen(true)
|
||||||
|
toast.info("Expand and scroll down to add input-questions")
|
||||||
}}>
|
}}>
|
||||||
<Typography variant="body2">No Input-Questions found. Click to add them!</Typography>
|
<Typography variant="body2">No Input-Questions found. Click to add them!</Typography>
|
||||||
</div>
|
</div>
|
||||||
@@ -15728,6 +15794,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
right: 0,
|
right: 0,
|
||||||
left: isMobile ? 20 : leftBarSize + 20,
|
left: isMobile ? 20 : leftBarSize + 20,
|
||||||
top: isMobile ? 30 : appBarSize + 20,
|
top: isMobile ? 30 : appBarSize + 20,
|
||||||
|
pointerEvents: "none",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -15748,14 +15815,17 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
<div style={topBarStyle}>
|
<div style={topBarStyle}>
|
||||||
<div style={{
|
<div style={{
|
||||||
margin: "0px 10px 0px 10px",
|
margin: "0px 10px 0px 10px",
|
||||||
|
pointerEvents: "auto",
|
||||||
}}>
|
}}>
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
aria-label="breadcrumb"
|
aria-label="breadcrumb"
|
||||||
separator="›"
|
separator="›"
|
||||||
style={{
|
style={{
|
||||||
color: "white"
|
color: "white",
|
||||||
|
pointerEvents: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
to="/workflows"
|
to="/workflows"
|
||||||
style={{ textDecoration: "none", color: "inherit" }}
|
style={{ textDecoration: "none", color: "inherit" }}
|
||||||
@@ -15772,6 +15842,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
</Link>
|
</Link>
|
||||||
<h2 style={{
|
<h2 style={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
|
pointerEvents: "none",
|
||||||
}}>{workflow.name}</h2>
|
}}>{workflow.name}</h2>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
|
|
||||||
@@ -15779,7 +15850,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
isCorrectOrg ? null :
|
isCorrectOrg ? null :
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<b>Warning</b>: Change <span
|
<b>Warning</b>: Change <span
|
||||||
style={{color: "#f85a3e", cursor: "pointer"}}
|
style={{color: "#f85a3e", cursor: "pointer", pointerEvents: "auto", }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toast("Changing to correct organisation. Please wait a few seconds.")
|
toast("Changing to correct organisation. Please wait a few seconds.")
|
||||||
|
|
||||||
@@ -16080,7 +16151,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
: null}
|
: null}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{display: "flex", marginLeft: 10, }}>
|
<div style={{display: "flex", marginLeft: 10, pointerEvents: "auto", }}>
|
||||||
{parentWorkflows.slice(0,5).map((wf, index) => {
|
{parentWorkflows.slice(0,5).map((wf, index) => {
|
||||||
return (
|
return (
|
||||||
<a href={`/workflows/${wf.id}`} target="_blank" rel="noopener noreferrer" key={index}>
|
<a href={`/workflows/${wf.id}`} target="_blank" rel="noopener noreferrer" key={index}>
|
||||||
@@ -17078,10 +17149,12 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
//defaultReturn = <UserinputSidebar />
|
//defaultReturn = <UserinputSidebar />
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
console.log(
|
console.log(
|
||||||
"Unable to handle invalid trigger type " +
|
"Unable to handle invalid trigger type " +
|
||||||
selectedTrigger.trigger_type
|
selectedTrigger.trigger_type
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (Object.getOwnPropertyNames(selectedEdge).length > 0) {
|
} else if (Object.getOwnPropertyNames(selectedEdge).length > 0) {
|
||||||
@@ -18387,7 +18460,7 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{foundnotifications > 0 ?
|
{foundnotifications > 0 ?
|
||||||
<Tooltip title={"This workflow created " + foundnotifications + " notification(s). Click to explore them."} placement="top">
|
<Tooltip title={"This workflow created " + foundnotifications + " notification(s)"} placement="top">
|
||||||
<ErrorOutlineIcon
|
<ErrorOutlineIcon
|
||||||
style={{color: "rgba(255,255,255,0.4)", marginTop: 10, marginRight: 10, }}
|
style={{color: "rgba(255,255,255,0.4)", marginTop: 10, marginRight: 10, }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@@ -21662,6 +21735,8 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
|
|
||||||
const changeActionParameterCodeMirror = (event, count, data, actionlist) => {
|
const changeActionParameterCodeMirror = (event, count, data, actionlist) => {
|
||||||
// Check if event.target.value is an array. If it is, split with comma
|
// Check if event.target.value is an array. If it is, split with comma
|
||||||
|
console.log("1 - SELECTED ACTION: ", selectedAction)
|
||||||
|
console.log("1 - DATA: ", data)
|
||||||
|
|
||||||
if (data.startsWith("${") && data.endsWith("}")) {
|
if (data.startsWith("${") && data.endsWith("}")) {
|
||||||
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
// PARAM FIX - Gonna use the ID field, even though it's a hack
|
||||||
@@ -21700,8 +21775,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
|
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
|
||||||
console.log("GET THE LAST ARGUMENT FOR NODE!")
|
|
||||||
|
|
||||||
var curstring = ""
|
var curstring = ""
|
||||||
var record = false
|
var record = false
|
||||||
for (let [key,keyval] in Object.entries(selectedAction.parameters[count].value)) {
|
for (let [key,keyval] in Object.entries(selectedAction.parameters[count].value)) {
|
||||||
@@ -21716,7 +21789,6 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log("CURSTRING: ", curstring)
|
|
||||||
if (curstring.length > 0 && actionlist !== null) {
|
if (curstring.length > 0 && actionlist !== null) {
|
||||||
// Search back in the action list
|
// Search back in the action list
|
||||||
curstring = curstring.split(" ").join("_").toLowerCase()
|
curstring = curstring.split(" ").join("_").toLowerCase()
|
||||||
@@ -21737,8 +21809,32 @@ const releaseToConnectLabel = "Release to Connect"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("2 - SELECTED ACTION: ", selectedAction)
|
||||||
|
console.log("2 - DATA: ", data)
|
||||||
|
|
||||||
|
if (selectedAction.app_name === "Shuffle Tools" && selectedAction.name === "filter_list" && count === 0) {
|
||||||
|
const parsedvalue = data
|
||||||
|
console.log("Parsed value: ", parsedvalue)
|
||||||
|
if (parsedvalue.includes("#")) {
|
||||||
|
const splitparsed = parsedvalue.split(".#.")
|
||||||
|
//console.log("Cant contain #: ", splitparsed)
|
||||||
|
if (splitparsed.length > 1) {
|
||||||
|
console.log("IN HERE AY")
|
||||||
|
//data.value = splitparsed[0]
|
||||||
|
|
||||||
|
selectedAction.parameters[0].value = splitparsed[0]
|
||||||
|
selectedAction.parameters[1].value = splitparsed[1]
|
||||||
|
|
||||||
|
selectedAction.parameters[0].autocompleted = true
|
||||||
|
selectedAction.parameters[1].autocompleted = true
|
||||||
|
setUpdate(Math.random())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
selectedAction.parameters[count].autocompleted = false
|
selectedAction.parameters[count].autocompleted = false
|
||||||
selectedAction.parameters[count].value = data
|
selectedAction.parameters[count].value = data
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedAction(selectedAction)
|
setSelectedAction(selectedAction)
|
||||||
//setUpdate(Math.random())
|
//setUpdate(Math.random())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import ReactDOM from "react-dom"
|
import ReactDOM from "react-dom"
|
||||||
|
|
||||||
|
import { ToastContainer, toast } from "react-toastify"
|
||||||
import { useInterval } from "react-powerhooks";
|
import { useInterval } from "react-powerhooks";
|
||||||
import { makeStyles } from '@mui/material/styles';
|
import { makeStyles } from '@mui/material/styles';
|
||||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
@@ -44,6 +45,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
|
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
const [workflow, setWorkflow] = React.useState({});
|
const [workflow, setWorkflow] = React.useState({});
|
||||||
const [executionRequest, setExecutionRequest] = React.useState({});
|
const [executionRequest, setExecutionRequest] = React.useState({});
|
||||||
@@ -55,6 +57,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
|
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
|
||||||
const [apps, setApps] = React.useState([]);
|
const [apps, setApps] = React.useState([]);
|
||||||
const [buttonClicked, setButtonClicked] = React.useState("");
|
const [buttonClicked, setButtonClicked] = React.useState("");
|
||||||
|
const [foundSourcenode, setFoundSourcenode] = React.useState(undefined);
|
||||||
|
|
||||||
const boxStyle = {
|
const boxStyle = {
|
||||||
color: "white",
|
color: "white",
|
||||||
@@ -340,6 +343,8 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("ONSUBMIT: ", event, execution_id, authorization, answer)
|
||||||
|
|
||||||
stop()
|
stop()
|
||||||
setMessage("")
|
setMessage("")
|
||||||
setExecutionLoading(true)
|
setExecutionLoading(true)
|
||||||
@@ -389,12 +394,27 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
|
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
|
||||||
data = {}
|
data = {}
|
||||||
fetchBody.method = "GET"
|
fetchBody.method = "GET"
|
||||||
|
|
||||||
|
if (executionArgument !== undefined && executionArgument !== null) {
|
||||||
|
try {
|
||||||
|
if (typeof executionArgument === "string") {
|
||||||
|
url += "¬e=" + executionArgument
|
||||||
|
} else {
|
||||||
|
url += "¬e=" + JSON.stringify(executionArgument)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
url += "¬e=" + executionArgument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fetchBody.method = "POST"
|
fetchBody.method = "POST"
|
||||||
fetchBody.body = JSON.stringify(data)
|
fetchBody.body = JSON.stringify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Pre request: ", url, fetchBody)
|
// IF there is an execution argument, we should use it
|
||||||
|
console.log("FULL URL: ", url)
|
||||||
|
|
||||||
fetch(url, fetchBody)
|
fetch(url, fetchBody)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200 && response.status !== 201) {
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
@@ -408,16 +428,26 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
start();
|
start();
|
||||||
return
|
return response.json()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json()
|
||||||
})
|
})
|
||||||
.then(responseJson => {
|
.then(responseJson => {
|
||||||
setExecutionLoading(false)
|
setExecutionLoading(false)
|
||||||
if (responseJson["success"] === false) {
|
if (responseJson.success === false) {
|
||||||
console.log("Failed sending execution request")
|
console.log("Failed sending execution request")
|
||||||
|
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||||
|
toast.warn(responseJson.reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
setMessage("")
|
||||||
|
setExecutionData({})
|
||||||
|
setExecutionInfo("")
|
||||||
|
setExecutionRunning(false)
|
||||||
|
setExecutionRequest({})
|
||||||
} else {
|
} else {
|
||||||
console.log("Started execution")
|
console.log("Started execution")
|
||||||
|
|
||||||
@@ -426,17 +456,24 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
} else {
|
} else {
|
||||||
setExecutionRunning(true);
|
setExecutionRunning(true);
|
||||||
setExecutionRequest(responseJson)
|
setExecutionRequest(responseJson)
|
||||||
start();
|
start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
//setExecutionInfo("Error in workflow startup: " + error)
|
//setExecutionInfo("Error in workflow startup: " + error)
|
||||||
|
toast.warn("Error in workflow startup: " + error)
|
||||||
|
|
||||||
|
stop()
|
||||||
|
setMessage("")
|
||||||
|
setExecutionData({})
|
||||||
|
setExecutionInfo("")
|
||||||
|
|
||||||
setExecutionLoading(false)
|
setExecutionLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getWorkflow = (workflow_id) => {
|
const getWorkflow = (workflow_id, selectedNode) => {
|
||||||
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -480,6 +517,48 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
setExecutionArgument(newexec)
|
setExecutionArgument(newexec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedNode !== undefined && selectedNode !== null && selectedNode.length > 0) {
|
||||||
|
var found = false
|
||||||
|
for (var actionkey in responseJson.actions) {
|
||||||
|
if (responseJson.actions[actionkey].id === selectedNode) {
|
||||||
|
found = true
|
||||||
|
setFoundSourcenode(responseJson.actions[actionkey])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
for (var triggerkey in responseJson.triggers) {
|
||||||
|
if (responseJson.triggers[triggerkey].id !== selectedNode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
setFoundSourcenode(responseJson.triggers[triggerkey])
|
||||||
|
|
||||||
|
if (responseJson.input_questions !== undefined && responseJson.input_questions !== null && responseJson.input_questions.length > 0 && responseJson.triggers[triggerkey].trigger_type === "USERINPUT") {
|
||||||
|
|
||||||
|
// Look for input questions param
|
||||||
|
for (var paramkey in responseJson.triggers[triggerkey].parameters) {
|
||||||
|
if (responseJson.triggers[triggerkey].parameters[paramkey].name === "input_questions") {
|
||||||
|
|
||||||
|
var relevantquestions = []
|
||||||
|
for (var questionkey in responseJson.input_questions) {
|
||||||
|
if (responseJson.triggers[triggerkey].parameters[paramkey].value.includes(responseJson.input_questions[questionkey].name)) {
|
||||||
|
relevantquestions.push(responseJson.input_questions[questionkey])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseJson.input_questions = relevantquestions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleGetOrg(responseJson.org_id)
|
handleGetOrg(responseJson.org_id)
|
||||||
setWorkflow(responseJson);
|
setWorkflow(responseJson);
|
||||||
})
|
})
|
||||||
@@ -489,7 +568,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { start, stop } = useInterval({
|
const { start, stop } = useInterval({
|
||||||
duration: 3000,
|
duration: 1500,
|
||||||
startImmediate: true,
|
startImmediate: true,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
|
fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
|
||||||
@@ -526,15 +605,11 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
|
|
||||||
for (var key in responseJson.results) {
|
for (var key in responseJson.results) {
|
||||||
if (responseJson.results[key].status === "WAITING") {
|
if (responseJson.results[key].status === "WAITING") {
|
||||||
console.log("Found: ", responseJson.results[key])
|
|
||||||
|
|
||||||
const validate = validateJson(responseJson.results[key].result)
|
const validate = validateJson(responseJson.results[key].result)
|
||||||
console.log("Validate: ", validate)
|
|
||||||
if (validate.valid && typeof validate.result === "string") {
|
if (validate.valid && typeof validate.result === "string") {
|
||||||
validate.result = JSON.parse(validate.result)
|
validate.result = JSON.parse(validate.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Newresult: ", validate.result)
|
|
||||||
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
|
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
|
||||||
setWorkflowQuestion(validate.result["information"])
|
setWorkflowQuestion(validate.result["information"])
|
||||||
}
|
}
|
||||||
@@ -651,11 +726,14 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const answer = new URLSearchParams(window.location.search).get("answer")
|
const searchParams = new URLSearchParams(window.location.search)
|
||||||
const execution_id = new URLSearchParams(window.location.search).get("reference_execution")
|
const answer = searchParams.get("answer")
|
||||||
const authorization = new URLSearchParams(window.location.search).get("authorization")
|
const execution_id = searchParams.get("reference_execution")
|
||||||
|
const authorization = searchParams.get("authorization")
|
||||||
|
const sourceNode = searchParams.get("source_node")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getWorkflow(props.match.params.key)
|
getWorkflow(props.match.params.key, sourceNode)
|
||||||
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
|
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
|
||||||
console.log("Get execution: ", execution_id)
|
console.log("Get execution: ", execution_id)
|
||||||
fetchUpdates(execution_id, authorization, true)
|
fetchUpdates(execution_id, authorization, true)
|
||||||
@@ -667,13 +745,60 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (executionData === undefined || executionData === null || executionData === {}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSourcenode === undefined || foundSourcenode === null || foundSourcenode === {}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSourcenode.trigger_type !== "USERINPUT") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var resultkey in executionData.results) {
|
||||||
|
const result = executionData.results[resultkey]
|
||||||
|
if (result.action.id !== foundSourcenode.id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedresult = result.result
|
||||||
|
try {
|
||||||
|
parsedresult = JSON.parse(parsedresult)
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error parsing result: ", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status !== "WAITING") {
|
||||||
|
if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) {
|
||||||
|
if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
|
||||||
|
setMessage("Already answered by " + parsedresult.click_info.user)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMessage("Answered.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [executionData, foundSourcenode])
|
||||||
|
|
||||||
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
||||||
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
|
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
|
||||||
|
|
||||||
//console.log("execdata: ", executionData)
|
|
||||||
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
|
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
|
||||||
|
|
||||||
|
//{disabledButtons ? null :
|
||||||
|
|
||||||
|
|
||||||
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
|
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
|
||||||
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
|
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
|
||||||
//const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
|
//const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
|
||||||
@@ -724,49 +849,54 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
||||||
{organization}
|
{organization}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="textSecondary" style={{marginBottom: 15, marginTop: 0, textAlign: "center",}}>
|
|
||||||
{contact}
|
|
||||||
</Typography>
|
|
||||||
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
||||||
<Typography color="textSecondary">{message}</Typography>
|
|
||||||
|
{disabledButtons && message.length > 0 ? null :
|
||||||
|
<Typography color="textSecondary" style={{textAlign: "center", }}>
|
||||||
|
{message}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
|
||||||
{answer !== undefined && answer !== null ? null :
|
{answer !== undefined && answer !== null ? null :
|
||||||
<Typography variant="h6" style={{marginBottom: 15, }}><b>{workflow.name}</b></Typography>
|
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
|
||||||
}
|
}
|
||||||
|
|
||||||
{workflowQuestion.length > 0 ?
|
{workflowQuestion.length > 0 ?
|
||||||
<Typography variant="body1" style={{ marginBottom: 35, marginTop: 30, marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
<div style={{
|
||||||
|
backgroundColor: theme.palette.inputColor,
|
||||||
|
padding: 20,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
marginBottom: 35,
|
||||||
|
marginTop: 30,
|
||||||
|
}}>
|
||||||
|
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
||||||
{workflowQuestion}
|
{workflowQuestion}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||||
<div style={{marginBottom: 5, }}>
|
<div style={{marginBottom: 5, }}>
|
||||||
{workflow.input_questions.map((question, index) => {
|
{workflow.input_questions.map((question, index) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginBottom: 5}}>
|
<div style={{marginBottom: 5}}>
|
||||||
{question.name}
|
{question.name}
|
||||||
<TextField
|
<TextField
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||||
multiLine
|
label={question.value}
|
||||||
maxRows={2}
|
required
|
||||||
InputProps={{
|
|
||||||
style:{
|
disabled={disabledButtons}
|
||||||
height: "50px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "1em",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
id="emailfield"
|
id="emailfield"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={(e) => {
|
onBlur={(e) => {
|
||||||
//setExecutionArgument(e.target.value)
|
//setExecutionArgument(e.target.value)
|
||||||
executionArgument[question.value] = e.target.value
|
executionArgument[question.value] = e.target.value
|
||||||
|
setUpdate(Math.random())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -813,20 +943,27 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
: null}
|
: null}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
:
|
:
|
||||||
answer !== undefined && answer !== null ?
|
((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) ?
|
||||||
<span style={{marginTop: 20, }}>
|
<span style={{marginTop: 20, }}>
|
||||||
|
|
||||||
|
{disabledButtons && message.length > 0 ?
|
||||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||||
{disabledButtons ? "Already answered. Nothing to do." : ""}
|
{message}. You may close this window.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
:
|
||||||
|
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||||
|
{disabledButtons ? "Answered. You may close this window." : ""}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
|
||||||
{disabledButtons ? null :
|
{disabledButtons ? null :
|
||||||
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
||||||
What do you want to do?
|
What do you want to do?
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
||||||
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{border: answer === "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1,}} onClick={() => {
|
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
|
||||||
onSubmit(null, execution_id, authorization, true)
|
onSubmit(null, execution_id, authorization, true)
|
||||||
|
|
||||||
setButtonClicked("FINISHED")
|
setButtonClicked("FINISHED")
|
||||||
@@ -837,7 +974,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
||||||
or
|
or
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{border: answer !== "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1, }} onClick={() => {
|
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
|
||||||
onSubmit(null, execution_id, authorization, false)
|
onSubmit(null, execution_id, authorization, false)
|
||||||
|
|
||||||
setButtonClicked("ABORTED")
|
setButtonClicked("ABORTED")
|
||||||
@@ -856,7 +993,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
{/*buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
||||||
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
||||||
onLoad={() => {
|
onLoad={() => {
|
||||||
console.log("Img loaded.")
|
console.log("Img loaded.")
|
||||||
@@ -868,7 +1005,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null*/}
|
||||||
|
|
||||||
<div style={{marginTop: "10px"}}>
|
<div style={{marginTop: "10px"}}>
|
||||||
{executionInfo}
|
{executionInfo}
|
||||||
|
|||||||
@@ -1162,10 +1162,11 @@ const Workflows = (props) => {
|
|||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (responseJson !== undefined) {
|
if (responseJson !== undefined) {
|
||||||
|
|
||||||
var newarray = []
|
var newarray = []
|
||||||
for (var key in responseJson) {
|
for (var wfkey in responseJson) {
|
||||||
const wf = responseJson[key]
|
const wf = responseJson[wfkey]
|
||||||
if (wf.public === true) {
|
if (wf.public === true || wf.hidden === true) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,9 +1179,9 @@ const Workflows = (props) => {
|
|||||||
var parsedactionlist = [];
|
var parsedactionlist = [];
|
||||||
for (var key in newarray) {
|
for (var key in newarray) {
|
||||||
const workflow = newarray[key]
|
const workflow = newarray[key]
|
||||||
if (workflow.status === "production") {
|
//if (workflow.status === "production") {
|
||||||
setProdFilter = true
|
// setProdFilter = true
|
||||||
}
|
//}
|
||||||
|
|
||||||
for (var actionkey in newarray[key].actions) {
|
for (var actionkey in newarray[key].actions) {
|
||||||
const action = newarray[key].actions[actionkey];
|
const action = newarray[key].actions[actionkey];
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ import (
|
|||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Starts jobs in bulk, so this could be increased
|
// Starts jobs in bulk, so this could be increased
|
||||||
@@ -659,13 +662,7 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deployK8sWorker(image string, identifier string, env []string) error {
|
||||||
func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error {
|
|
||||||
if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" {
|
|
||||||
env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL")))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isKubernetes == "true" {
|
|
||||||
env = append(env, fmt.Sprintf("IS_KUBERNETES=true"))
|
env = append(env, fmt.Sprintf("IS_KUBERNETES=true"))
|
||||||
env = append(env, fmt.Sprintf("KUBERNETES_NAMESPACE=%s", os.Getenv("KUBERNETES_NAMESPACE")))
|
env = append(env, fmt.Sprintf("KUBERNETES_NAMESPACE=%s", os.Getenv("KUBERNETES_NAMESPACE")))
|
||||||
|
|
||||||
@@ -673,6 +670,10 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_HOST=%s", os.Getenv("KUBERNETES_SERVICE_HOST")))
|
env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_HOST=%s", os.Getenv("KUBERNETES_SERVICE_HOST")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(os.Getenv("SHUFFLE_MEMCACHED")) > 0 {
|
||||||
|
env = append(env, fmt.Sprintf("SHUFFLE_MEMCACHED=%s", os.Getenv("SHUFFLE_MEMCACHED")))
|
||||||
|
}
|
||||||
|
|
||||||
if len(os.Getenv("KUBERNETES_SERVICE_PORT")) > 0 {
|
if len(os.Getenv("KUBERNETES_SERVICE_PORT")) > 0 {
|
||||||
env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_PORT=%s", os.Getenv("KUBERNETES_SERVICE_PORT")))
|
env = append(env, fmt.Sprintf("KUBERNETES_SERVICE_PORT=%s", os.Getenv("KUBERNETES_SERVICE_PORT")))
|
||||||
}
|
}
|
||||||
@@ -683,7 +684,6 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String()))
|
//env = append(env, fmt.Sprintf("KUBERNETES_CONFIG=%s", config.String()))
|
||||||
|
|
||||||
// FIXME: When a service account is used, the account is also mounted in the pod
|
// FIXME: When a service account is used, the account is also mounted in the pod
|
||||||
@@ -722,6 +722,9 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env = append(env, fmt.Sprintf("BASE_URL=%s", baseUrl))
|
||||||
|
env = append(env, fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", swarmConfig))
|
||||||
|
|
||||||
if len(kubernetesNamespace) == 0 {
|
if len(kubernetesNamespace) == 0 {
|
||||||
foundNamespace, err := shuffle.GetKubernetesNamespace()
|
foundNamespace, err := shuffle.GetKubernetesNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -771,7 +774,7 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if len(podname) > 0 {
|
if len(podname) > 0 {
|
||||||
currentPodStatus, err := shuffle.GetCurrentPodNetworkConfig(ctx, clientset, kubernetesNamespace, podname)
|
_, err := shuffle.GetCurrentPodNetworkConfig(ctx, clientset, kubernetesNamespace, podname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed getting current pod network: %s", err)
|
log.Printf("[ERROR] Failed getting current pod network: %s", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -790,7 +793,8 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
},
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
RestartPolicy: "Never",
|
RestartPolicy: "Never",
|
||||||
DNSPolicy: "Default",
|
// DNSPolicy: "Default",
|
||||||
|
DNSPolicy: corev1.DNSClusterFirst,
|
||||||
// NodeSelector: map[string]string{
|
// NodeSelector: map[string]string{
|
||||||
// "node": "master",
|
// "node": "master",
|
||||||
// },
|
// },
|
||||||
@@ -804,7 +808,7 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
|
|
||||||
// Get current pod's network so we can make the pod in it
|
// Get current pod's network so we can make the pod in it
|
||||||
|
|
||||||
networks, err := clientset.CoreV1().Pods(kubernetesNamespace).List(context.Background(), metav1.ListOptions{})
|
_, err = clientset.CoreV1().Pods(kubernetesNamespace).List(context.Background(), metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed listing pods: %s", err)
|
log.Printf("[ERROR] Failed listing pods: %s", err)
|
||||||
}
|
}
|
||||||
@@ -817,9 +821,51 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace)
|
log.Printf("[INFO] Created pod %q in namespace %q\n", createdPod.Name, createdPod.Namespace)
|
||||||
|
|
||||||
|
// kubectl expose pod shuffle-workers --type=LoadBalancer --port=33333
|
||||||
|
service := &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: identifier,
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Selector: map[string]string{
|
||||||
|
"container": "shuffle-worker",
|
||||||
|
},
|
||||||
|
Ports: []corev1.ServicePort{
|
||||||
|
{
|
||||||
|
Protocol: "TCP",
|
||||||
|
Port: 33333,
|
||||||
|
TargetPort: intstr.FromInt(33333),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = clientset.CoreV1().Services(kubernetesNamespace).Create(context.TODO(), service, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed creating service: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func deployWorker(image string, identifier string, env []string, executionRequest shuffle.ExecutionRequest) error {
|
||||||
|
if len(os.Getenv("REGISTRY_URL")) > 0 && os.Getenv("REGISTRY_URL") != "" {
|
||||||
|
env = append(env, fmt.Sprintf("REGISTRY_URL=%s", os.Getenv("REGISTRY_URL")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// if isKubernetes == "true" {
|
||||||
|
// err := deployK8sWorker(image, identifier, env, executionRequest)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Printf("[ERROR] Failed deploying Kubernetes worker: %s", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
// Binds is the actual "-v" volume.
|
// Binds is the actual "-v" volume.
|
||||||
// Max 20% CPU every second
|
// Max 20% CPU every second
|
||||||
|
|
||||||
@@ -844,23 +890,25 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId))
|
|
||||||
if strings.ToLower(cleanupEnv) != "false" {
|
|
||||||
hostConfig.AutoRemove = true
|
|
||||||
}
|
|
||||||
|
|
||||||
config := &container.Config{
|
config := &container.Config{
|
||||||
Image: image,
|
Image: image,
|
||||||
Env: env,
|
Env: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isKubernetes != "true" {
|
||||||
|
hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId))
|
||||||
|
if strings.ToLower(cleanupEnv) != "false" {
|
||||||
|
hostConfig.AutoRemove = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG")
|
//var swarmConfig = os.Getenv("SHUFFLE_SWARM_CONFIG")
|
||||||
parsedUuid := uuid.NewV4()
|
parsedUuid := uuid.NewV4()
|
||||||
if swarmConfig == "run" || swarmConfig == "swarm" {
|
if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" {
|
||||||
// FIXME: Should we handle replies properly?
|
// FIXME: Should we handle replies properly?
|
||||||
// In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns
|
// In certain cases, a workflow may e.g. be aborted already. If it's aborted, that returns
|
||||||
// a 401 from the worker, which returns an error here
|
// a 401 from the worker, which returns an error here
|
||||||
go sendWorkerRequest(executionRequest)
|
go sendWorkerRequest(executionRequest, image, env)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1401,6 +1449,115 @@ func main() {
|
|||||||
log.Printf("[INFO] Running inside k8s cluster")
|
log.Printf("[INFO] Running inside k8s cluster")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
clientset, _, err := shuffle.GetKubernetesClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Error getting kubernetes client:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
kubernetesNamespace := "default"
|
||||||
|
|
||||||
|
// Check if namespace exist as variable. If so, make it
|
||||||
|
if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 && !namespacemade {
|
||||||
|
kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix roles
|
||||||
|
// check if "service-creator" role is assigned to the service account "default"
|
||||||
|
roleBindingName := "service-creator-binding"
|
||||||
|
serviceAccountName := "default"
|
||||||
|
// Check if the RoleBinding exists
|
||||||
|
roleBinding, err := clientset.RbacV1().RoleBindings(kubernetesNamespace).Get(context.TODO(), roleBindingName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Failed to get RoleBinding %s: %s", roleBindingName, err)
|
||||||
|
// create role and rolebinding
|
||||||
|
role := &rbacv1.Role{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: roleBindingName,
|
||||||
|
},
|
||||||
|
Rules: []rbacv1.PolicyRule{
|
||||||
|
{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"services"},
|
||||||
|
Verbs: []string{"create"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
_, err := clientset.RbacV1().Roles(kubernetesNamespace).Create(ctx, role, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed to create Role %s: %s", roleBindingName, err)
|
||||||
|
if !strings.Contains(fmt.Sprintf("%s", err), "already exists") {
|
||||||
|
log.Printf("[INFO] role %s already exists", roleBindingName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roleBinding := &rbacv1.RoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: roleBindingName,
|
||||||
|
},
|
||||||
|
Subjects: []rbacv1.Subject{
|
||||||
|
{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: serviceAccountName,
|
||||||
|
Namespace: kubernetesNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RoleRef: rbacv1.RoleRef{
|
||||||
|
Kind: "Role",
|
||||||
|
Name: roleBindingName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_, err = clientset.RbacV1().RoleBindings(kubernetesNamespace).Create(ctx, roleBinding, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed to create RoleBinding %s: %s", roleBindingName, err)
|
||||||
|
if !strings.Contains(fmt.Sprintf("%s", err), "already exists") {
|
||||||
|
log.Printf("[INFO] rolebinding %s already exists", roleBindingName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.Printf("[INFO] Created Role %s and RoleBinding %s", roleBindingName, roleBindingName)
|
||||||
|
} else {
|
||||||
|
log.Printf("[INFO] RoleBinding %s exists", roleBindingName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the RoleBinding is assigned to the service account
|
||||||
|
var found bool
|
||||||
|
for _, subject := range roleBinding.Subjects {
|
||||||
|
if subject.Kind == "ServiceAccount" && subject.Name == serviceAccountName {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("[WARNING] Service account %s is not assigned to RoleBinding %s\n", serviceAccountName, roleBindingName)
|
||||||
|
// assign the service account to the rolebinding
|
||||||
|
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: serviceAccountName,
|
||||||
|
Namespace: kubernetesNamespace,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
_, err := clientset.RbacV1().RoleBindings(kubernetesNamespace).Update(ctx, roleBinding, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed to update RoleBinding %s: %s", roleBindingName, err)
|
||||||
|
if !strings.Contains(fmt.Sprintf("%s", err), "already exists") {
|
||||||
|
log.Printf("[INFO] rolebinding %s already exists", roleBindingName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
startupDelay := os.Getenv("SHUFFLE_ORBORUS_STARTUP_DELAY")
|
startupDelay := os.Getenv("SHUFFLE_ORBORUS_STARTUP_DELAY")
|
||||||
if len(startupDelay) > 0 {
|
if len(startupDelay) > 0 {
|
||||||
log.Printf("[DEBUG] Setting startup delay to %#v", startupDelay)
|
log.Printf("[DEBUG] Setting startup delay to %#v", startupDelay)
|
||||||
@@ -1501,16 +1658,25 @@ func main() {
|
|||||||
workerImage = newWorkerImage
|
workerImage = newWorkerImage
|
||||||
}
|
}
|
||||||
|
|
||||||
if swarmConfig == "run" || swarmConfig == "swarm" {
|
if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" {
|
||||||
|
if isKubernetes != "true" {
|
||||||
checkSwarmService(ctx)
|
checkSwarmService(ctx)
|
||||||
|
|
||||||
log.Printf("[DEBUG] Cleaning up containers from previous run")
|
log.Printf("[DEBUG] Cleaning up containers from previous run")
|
||||||
cleanupExistingNodes(ctx)
|
cleanupExistingNodes(ctx)
|
||||||
time.Sleep(time.Duration(5) * time.Second)
|
time.Sleep(time.Duration(5) * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Deploying worker image %s to swarm", workerImage)
|
log.Printf("[DEBUG] Deploying worker image %s to swarm", workerImage)
|
||||||
|
|
||||||
|
runString := "Run: \"docker service ls\" for more info"
|
||||||
|
|
||||||
|
if isKubernetes != "true" {
|
||||||
deployServiceWorkers(workerImage)
|
deployServiceWorkers(workerImage)
|
||||||
log.Printf("[DEBUG] Waiting 45 seconds to ensure workers are deployed. Run: \"docker service ls\" for more info")
|
} else {
|
||||||
|
deployK8sWorker(workerImage, "shuffle-workers", []string{})
|
||||||
|
runString = "Run: \"kubectl get pods\" for more info"
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Waiting 45 seconds to ensure workers are deployed. %s", runString)
|
||||||
time.Sleep(time.Duration(45) * time.Second)
|
time.Sleep(time.Duration(45) * time.Second)
|
||||||
|
|
||||||
//deployServiceWorkers(workerImage)
|
//deployServiceWorkers(workerImage)
|
||||||
@@ -1520,7 +1686,12 @@ func main() {
|
|||||||
|
|
||||||
client := shuffle.GetExternalClient(baseUrl)
|
client := shuffle.GetExternalClient(baseUrl)
|
||||||
fullUrl := fmt.Sprintf("%s/api/v1/workflows/queue", baseUrl)
|
fullUrl := fmt.Sprintf("%s/api/v1/workflows/queue", baseUrl)
|
||||||
|
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
log.Printf("[INFO] Finished configuring kubernetes environment. Connecting to %s", fullUrl)
|
||||||
|
} else {
|
||||||
log.Printf("[INFO] Finished configuring docker environment. Connecting to %s", fullUrl)
|
log.Printf("[INFO] Finished configuring docker environment. Connecting to %s", fullUrl)
|
||||||
|
}
|
||||||
|
|
||||||
forwardData := bytes.NewBuffer([]byte{})
|
forwardData := bytes.NewBuffer([]byte{})
|
||||||
forwardMethod := "POST"
|
forwardMethod := "POST"
|
||||||
@@ -2785,7 +2956,7 @@ func zombiecheck(ctx context.Context, workerTimeout int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
|
func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest, image string, env []string) error {
|
||||||
parsedRequest := shuffle.OrborusExecutionRequest{
|
parsedRequest := shuffle.OrborusExecutionRequest{
|
||||||
ExecutionId: workflowExecution.ExecutionId,
|
ExecutionId: workflowExecution.ExecutionId,
|
||||||
Authorization: workflowExecution.Authorization,
|
Authorization: workflowExecution.Authorization,
|
||||||
@@ -2818,6 +2989,15 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
|
|||||||
streamUrl = fmt.Sprintf("%s:33333/api/v1/execute", parsedBaseurl)
|
streamUrl = fmt.Sprintf("%s:33333/api/v1/execute", parsedBaseurl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
identifier := "shuffle-workers"
|
||||||
|
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
if shuffle.IsRunningInCluster() {
|
||||||
|
log.Printf("[INFO] Running in Kubernetes cluster")
|
||||||
|
// try getting the k8s worker server url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(workerServerUrl) > 0 {
|
if len(workerServerUrl) > 0 {
|
||||||
streamUrl = fmt.Sprintf("%s:33333/api/v1/execute", workerServerUrl)
|
streamUrl = fmt.Sprintf("%s:33333/api/v1/execute", workerServerUrl)
|
||||||
}
|
}
|
||||||
@@ -2842,7 +3022,12 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
|
|||||||
if len(newWorkerImage) > 0 {
|
if len(newWorkerImage) > 0 {
|
||||||
workerImage = newWorkerImage
|
workerImage = newWorkerImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
deployK8sWorker(workerImage, identifier, env)
|
||||||
|
} else {
|
||||||
deployServiceWorkers(workerImage)
|
deployServiceWorkers(workerImage)
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(time.Duration(10) * time.Second)
|
time.Sleep(time.Duration(10) * time.Second)
|
||||||
//err = sendWorkerRequest(executionRequest)
|
//err = sendWorkerRequest(executionRequest)
|
||||||
@@ -2861,7 +3046,11 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
|
|||||||
workerImage = newWorkerImage
|
workerImage = newWorkerImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
deployK8sWorker(workerImage, identifier, env)
|
||||||
|
} else {
|
||||||
deployServiceWorkers(workerImage)
|
deployServiceWorkers(workerImage)
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(time.Duration(10) * time.Second)
|
time.Sleep(time.Duration(10) * time.Second)
|
||||||
//err = sendWorkerRequest(executionRequest)
|
//err = sendWorkerRequest(executionRequest)
|
||||||
@@ -2890,6 +3079,11 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
|
|||||||
|
|
||||||
_ = body
|
_ = body
|
||||||
|
|
||||||
log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING:\ndocker service logs shuffle-workers 2>&1 -f | grep %s", workflowExecution.ExecutionId, streamUrl, workflowExecution.ExecutionId)
|
debugCommand := fmt.Sprintf("docker service logs shuffle-workers 2>&1 -f | grep %s", workflowExecution.ExecutionId)
|
||||||
|
if isKubernetes == "true" {
|
||||||
|
debugCommand = fmt.Sprintf("kubectl logs -n %s %s | grep %s", kubernetesNamespace, identifier, workflowExecution.ExecutionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING:\n%s", workflowExecution.ExecutionId, streamUrl, debugCommand)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user