Continuing forparser for app_sdk

This commit is contained in:
frikky
2020-05-20 11:06:22 +02:00
parent 5d092ddd41
commit c557d232c9
9 changed files with 333 additions and 98 deletions
+16
View File
@@ -1338,6 +1338,21 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
// Check every app action and param to see whether they exist // Check every app action and param to see whether they exist
newActions = []Action{} newActions = []Action{}
for _, action := range workflow.Actions { for _, action := range workflow.Actions {
reservedApps := []string{
"0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e",
}
builtin := false
for _, id := range reservedApps {
if id == action.AppID {
builtin = true
break
}
}
if builtin {
newActions = append(newActions, action)
} else {
curapp := WorkflowApp{} curapp := WorkflowApp{}
// FIXME - can this work with ONLY AppID? // FIXME - can this work with ONLY AppID?
for _, app := range workflowApps { for _, app := range workflowApps {
@@ -1419,6 +1434,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
action.Parameters = newParams action.Parameters = newParams
newActions = append(newActions, action) newActions = append(newActions, action)
} }
}
workflow.Actions = newActions workflow.Actions = newActions
workflow.IsValid = true workflow.IsValid = true
File diff suppressed because one or more lines are too long
+63 -7
View File
@@ -285,7 +285,6 @@ const AngularWorkflow = (props) => {
currentnode = currentnode[0] currentnode = currentnode[0]
const outgoingEdges = currentnode.outgoers('edge') const outgoingEdges = currentnode.outgoers('edge')
const incomingEdges = currentnode.incomers('edge') const incomingEdges = currentnode.incomers('edge')
console.log("NODE: ", currentnode)
//currentnode.removeClass('success-highlight failure-highlight executing-highlight') //currentnode.removeClass('success-highlight failure-highlight executing-highlight')
switch (item.status) { switch (item.status) {
@@ -669,6 +668,56 @@ const AngularWorkflow = (props) => {
// //setVersionedApps(newapps) // //setVersionedApps(newapps)
//} //}
// Builtin actions that should ran in Worker and not apps
const getExtraApps = () => {
const data = [{
name: "Filter",
is_valid: true,
id: "0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e",
link: "https://shuffler.io",
app_version: "1.0.0",
generated: true,
downloaded: false,
sharing: false,
verified: false,
tested: false,
owner: "",
private_id: "",
description: "Filter",
environment: "Shuffle",
small_image: "",
large_image: "",
contact_info: {name: "", url: ""},
authentication: {required: false, parameters: [],},
actions: [{
description: "Filter cases",
id: "",
name: "filter_cases",
node_type: "action",
environment: "Shuffle",
sharing: false,
private_id: "",
app_id: "",
authentication: null,
tested: true,
parameters: [{
description: "",
id: "",
name: "Field to look for",
example: "$testing_1.#.id",
value: "",
multiline: true,
action_field: "",
variant: "",
required: true,
schema: {type: "string"},
}]
}],
}]
return data
}
const getApps = () => { const getApps = () => {
fetch(globalUrl+"/api/v1/workflows/apps", { fetch(globalUrl+"/api/v1/workflows/apps", {
method: 'GET', method: 'GET',
@@ -688,8 +737,11 @@ const AngularWorkflow = (props) => {
.then((responseJson) => { .then((responseJson) => {
// FIXME - handle versions on left bar // FIXME - handle versions on left bar
//handleAppVersioning(responseJson) //handleAppVersioning(responseJson)
setApps(responseJson) var tmpapps = []
setFilteredApps(responseJson) tmpapps = tmpapps.concat(getExtraApps())
tmpapps = tmpapps.concat(responseJson)
setApps(tmpapps)
setFilteredApps(tmpapps)
}) })
.catch(error => { .catch(error => {
alert.error(error.toString()) alert.error(error.toString())
@@ -780,6 +832,7 @@ const AngularWorkflow = (props) => {
} }
console.log("Selected: ", data.id) console.log("Selected: ", data.id)
console.log(curaction)
setRequiresAuthentication(curapp.authentication.required) setRequiresAuthentication(curapp.authentication.required)
setSelectedApp(curapp) setSelectedApp(curapp)
@@ -1848,7 +1901,7 @@ const AngularWorkflow = (props) => {
authentication: [], authentication: [],
} }
console.log(newAppData) // const image = "url("+app.large_image+")"
// FIXME - find the cytoscape offset position // FIXME - find the cytoscape offset position
// Can this be done with zoom calculations? // Can this be done with zoom calculations?
@@ -2358,7 +2411,8 @@ const AngularWorkflow = (props) => {
<b>{data.name}: </b> <b>{data.name}: </b>
</div> </div>
<Tooltip color="primary" title="Static data" placement="top"> <Tooltip color="primary" title="Static data" placement="top">
<div style={{cursor: "pointer", color: staticcolor}} onClick={() => { <div style={{cursor: "pointer", color: staticcolor}} onClick={(e) => {
e.preventDefault()
changeActionParameterVariant("STATIC_VALUE", count) changeActionParameterVariant("STATIC_VALUE", count)
}}> }}>
<CreateIcon /> <CreateIcon />
@@ -2366,7 +2420,8 @@ const AngularWorkflow = (props) => {
</Tooltip> </Tooltip>
&nbsp;|&nbsp; &nbsp;|&nbsp;
<Tooltip color="primary" title="Data from previous action" placement="top"> <Tooltip color="primary" title="Data from previous action" placement="top">
<div style={{cursor: "pointer", color: actioncolor}} onClick={() => { <div style={{cursor: "pointer", color: actioncolor}} onClick={(e) => {
e.preventDefault()
changeActionParameterVariant("ACTION_RESULT", count) changeActionParameterVariant("ACTION_RESULT", count)
}}> }}>
<AppsIcon /> <AppsIcon />
@@ -2374,7 +2429,8 @@ const AngularWorkflow = (props) => {
</Tooltip> </Tooltip>
&nbsp;|&nbsp; &nbsp;|&nbsp;
<Tooltip color="primary" title="Use local variable" placement="top"> <Tooltip color="primary" title="Use local variable" placement="top">
<div style={{cursor: "pointer", color: varcolor}} onClick={() => { <div style={{cursor: "pointer", color: varcolor}} onClick={(e) => {
e.preventDefault()
changeActionParameterVariant("WORKFLOW_VARIABLE", count) changeActionParameterVariant("WORKFLOW_VARIABLE", count)
}}> }}>
<FavoriteBorderIcon /> <FavoriteBorderIcon />
+2 -2
View File
@@ -38,12 +38,12 @@ import AlertTemplate from "react-alert-template-basic";
import { positions, Provider } from "react-alert"; import { positions, Provider } from "react-alert";
// Testing - localhost // Testing - localhost
//const globalUrl = "http://192.168.3.6:5001" const globalUrl = "http://192.168.3.6:5001"
//console.log("HOST: ", process.env) //console.log("HOST: ", process.env)
// Production - backend proxy forwarding in nginx // Production - backend proxy forwarding in nginx
const globalUrl = window.location.origin //const globalUrl = window.location.origin
const surfaceColor = "#27292D" const surfaceColor = "#27292D"
const inputColor = "#383B40" const inputColor = "#383B40"
+6 -9
View File
@@ -55,6 +55,11 @@ const Apps = (props) => {
useEffect(() => { useEffect(() => {
if (apps.length <= 0 && firstrequest) { if (apps.length <= 0 && firstrequest) {
document.title = "Shuffle - Apps" document.title = "Shuffle - Apps"
if (!isLoggedIn && isLoaded) {
window.location = "/login"
}
setFirstrequest(false) setFirstrequest(false)
getApps() getApps()
} }
@@ -452,15 +457,7 @@ const Apps = (props) => {
</div> </div>
</div> </div>
: :
<div style={{width: "600px", margin: "auto", color: "white", paddingBottom: "50px"}}> null
<h2>Available integrations</h2>
<Divider style={{marginBottom: "10px", height: "1px", width: "100%", backgroundColor: dividerColor}}/>
{apps.map(data => {
return (
appPaper(data)
)
})}
</div>
// Gets the URL itself (hopefully this works in most cases? // Gets the URL itself (hopefully this works in most cases?
// Will then forward the data to an internal endpoint to validate the api // Will then forward the data to an internal endpoint to validate the api
+4 -3
View File
@@ -22,6 +22,7 @@ import PlayArrowIcon from '@material-ui/icons/PlayArrow';
//import JSONPrettyMon from 'react-json-pretty/dist/monikai' //import JSONPrettyMon from 'react-json-pretty/dist/monikai'
import ReactJson from 'react-json-view' import ReactJson from 'react-json-view'
import {Link} from 'react-router-dom';
import { useAlert } from "react-alert"; import { useAlert } from "react-alert";
import Dialog from '@material-ui/core/Dialog'; import Dialog from '@material-ui/core/Dialog';
@@ -398,13 +399,13 @@ const Workflows = (props) => {
} }
}}> }}>
<Grid item style={{flex: "1", justifyContent: "center"}}> <Grid item style={{flex: "1", justifyContent: "center"}}>
<a href={"/workflows/"+data.id}> <Link to={"/workflows/"+data.id}>
<Tooltip color="primary" title="Edit workflow" placement="bottom"> <Tooltip color="primary" title="Edit workflow" placement="bottom">
<Button style={{}} color="primary" variant="text" style={{marginRight: 10}} onClick={() => {}}> <Button style={{}} color="primary" variant="text" style={{marginRight: 10}} onClick={() => {}}>
<EditIcon style={{marginRight: 10}}/> Edit <EditIcon style={{marginRight: 10}}/> Edit
</Button> </Button>
</Tooltip> </Tooltip>
</a> </Link>
<Tooltip color="primary" title="Execute workflow" placement="bottom"> <Tooltip color="primary" title="Execute workflow" placement="bottom">
<Button style={{}} color="secondary" variant="text" onClick={() => executeWorkflow(data.id)}> <Button style={{}} color="secondary" variant="text" onClick={() => executeWorkflow(data.id)}>
<PlayArrowIcon /> <PlayArrowIcon />
@@ -889,7 +890,7 @@ const Workflows = (props) => {
</div> </div>
<div> <div>
<p> <p>
<b>Shuffle</b> is a flexible, easy to use, automation framework allowing users to integrate their services and devices to reduce the amount of manual labor required for those tasks. <a href="/docs/workflows" style={{textDecoration: "none", color: "#f85a3e"}}>Click here for more information.</a> <b>Shuffle</b> is a flexible, easy to use, automation framework allowing users to integrate their services and devices to reduce the amount of manual labor required for those tasks. <Link to="/docs/workflows" style={{textDecoration: "none", color: "#f85a3e"}}>Click here for more information.</Link>
</p> </p>
</div> </div>
<div> <div>
+12 -7
View File
@@ -122,9 +122,11 @@ class AppBase:
baseresult = execution_data["execution_argument"] baseresult = execution_data["execution_argument"]
else: else:
for result in execution_data["results"]: for result in execution_data["results"]:
if result["action"]["label"].lower() == actionname.lower(): resultlabel = result["action"]["label"].replace(" ", "_", -1)
if resultlabel.lower() == actionname.lower():
baseresult = result["result"] baseresult = result["result"]
break break
except KeyError as error: except KeyError as error:
print(f"Error: {error}") print(f"Error: {error}")
@@ -144,8 +146,13 @@ class AppBase:
except json.decoder.JSONDecodeError as e: except json.decoder.JSONDecodeError as e:
return baseresult return baseresult
def loop_recursion(data):
for value in data:
pass
try: try:
for value in parsersplit[1:]: for value in parsersplit[1:]:
print("VALUE: %s", value)
if value == "#": if value == "#":
print("HANDLE RECURSIVE LOOP") print("HANDLE RECURSIVE LOOP")
pass pass
@@ -177,7 +184,7 @@ class AppBase:
data = parameter["value"] data = parameter["value"]
self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n") self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n")
match = ".*([$]{1}(\w+\.?){1,})" match = ".*([$]{1}([a-zA-Z0-9()# _-]+\.?){1,})"
actualitem = re.findall(match, data, re.MULTILINE) actualitem = re.findall(match, data, re.MULTILINE)
self.logger.info("PARSED: %s" % actualitem) self.logger.info("PARSED: %s" % actualitem)
if len(actualitem) > 0: if len(actualitem) > 0:
@@ -450,17 +457,15 @@ class AppBase:
calltimes = 1 calltimes = 1
result = "" result = ""
paramiter = [] paramiter = []
all_executions = []
for parameter in action["parameters"]: for parameter in action["parameters"]:
#self.logger.info(parameter)
#print(fullexecution)
check, value = parse_params(action, fullexecution, parameter) check, value = parse_params(action, fullexecution, parameter)
if check: if check:
raise Exception(check) raise Exception(check)
if isinstance(value, list):
params[parameter["name"]] = value params[parameter["name"]] = value
# p["value"]
# FIXME - this is horrible, but works for now # FIXME - this is horrible, but works for now
#for i in range(calltimes): #for i in range(calltimes):
Binary file not shown.
+24 -2
View File
@@ -78,6 +78,7 @@ type WorkflowExecution struct {
type Action struct { type Action struct {
AppName string `json:"app_name" datastore:"app_name"` AppName string `json:"app_name" datastore:"app_name"`
AppVersion string `json:"app_version" datastore:"app_version"` AppVersion string `json:"app_version" datastore:"app_version"`
AppID string `json:"app_id" datastore:"app_id"`
Errors []string `json:"errors" datastore:"errors"` Errors []string `json:"errors" datastore:"errors"`
ID string `json:"id" datastore:"id"` ID string `json:"id" datastore:"id"`
IsValid bool `json:"is_valid" datastore:"is_valid"` IsValid bool `json:"is_valid" datastore:"is_valid"`
@@ -258,7 +259,7 @@ func shutdown(executionId, workflowId string) {
} }
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", authorization) //req.Header.Add("Authorization", authorization)
client := &http.Client{} client := &http.Client{}
_, err = client.Do(req) _, err = client.Do(req)
if err != nil { if err != nil {
@@ -336,6 +337,21 @@ func removeContainer(containername string) error {
return nil return nil
} }
func runFilter(workflowExecution WorkflowExecution, action Action) {
// 1. Get the parameter $.#.id
if action.Label == "filter_cases" && len(action.Parameters) > 0 {
if action.Parameters[0].Variant == "ACTION_RESULT" {
param := action.Parameters[0]
value := param.Value
// Loop cases.. Hmm, that's tricky
}
} else {
log.Printf("No handler for filter %s with %d params", action.Label, len(action.Parameters))
}
}
func handleExecution(client *http.Client, req *http.Request, workflowExecution WorkflowExecution) error { func handleExecution(client *http.Client, req *http.Request, workflowExecution WorkflowExecution) error {
// if no onprem runs (shouldn't happen, but extra check), exit // if no onprem runs (shouldn't happen, but extra check), exit
// if there are some, load the images ASAP for the app // if there are some, load the images ASAP for the app
@@ -364,7 +380,6 @@ func handleExecution(client *http.Client, req *http.Request, workflowExecution W
} }
toExecuteOnprem = append(toExecuteOnprem, action.ID) toExecuteOnprem = append(toExecuteOnprem, action.ID)
actionName := fmt.Sprintf("%s:%s_%s", baseimagename, action.AppName, action.AppVersion) actionName := fmt.Sprintf("%s:%s_%s", baseimagename, action.AppName, action.AppVersion)
found := false found := false
for _, app := range onpremApps { for _, app := range onpremApps {
@@ -555,12 +570,19 @@ func handleExecution(client *http.Client, req *http.Request, workflowExecution W
// marshal action and put it in there rofl // marshal action and put it in there rofl
log.Printf("Time to execute %s with app %s:%s, function %s, env %s with %d parameters.", action.ID, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters)) log.Printf("Time to execute %s with app %s:%s, function %s, env %s with %d parameters.", action.ID, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters))
actionData, err := json.Marshal(action) actionData, err := json.Marshal(action)
if err != nil { if err != nil {
log.Printf("Failed unmarshalling action: %s", err) log.Printf("Failed unmarshalling action: %s", err)
continue continue
} }
if action.AppID == "0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e" {
log.Printf("\nShould run filter: %#v\n\n", action)
runFilter(workflowExecution, action)
continue
}
//log.Println(string(actionData)) //log.Println(string(actionData))
// FIXME - add proper FUNCTION_APIKEY from user definition // FIXME - add proper FUNCTION_APIKEY from user definition
env := []string{ env := []string{