#141: File download through app sdk possible

This commit is contained in:
frikky
2020-11-22 09:50:12 +01:00
parent 0107169cad
commit 6b48cfe8cc
5 changed files with 146 additions and 101 deletions
+30 -2
View File
@@ -824,6 +824,26 @@ class AppBase:
return True, ""
# Things to consider for files:
# - How can you download / stream a file?
# - Can you decide if you want a stream or the files directly?
def get_files(full_execution, value):
print("FULL EXEC: %s" % full_execution)
org_id = full_execution["workflow"]["execution_org"]["id"]
print("SHOULD GET FILES BASED ON ORG %s, workflow %s and value(s) %s" % (org_id, full_execution["workflow"]["id"], value))
get_path = "/api/v1/files/%s/content?execution_id=%s" % (value, full_execution["execution_id"])
print("PATH: %s" % get_path)
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer %s" % self.authorization
}
ret = requests.get("%s%s" % (self.url, get_path), headers=headers)
print("RET CONTENT: %s" % ret.text)
print("RET CODE FILE: %d" % ret.status_code)
# r.HandleFunc("/api/v1/files/{fileId}/content", handleGetFileContent).Methods("GET", "OPTIONS")
# Checks whether conditions are met, otherwise set
branchcheck, tmpresult = check_branch_conditions(action, fullexecution)
if not branchcheck:
@@ -848,7 +868,7 @@ class AppBase:
actionname.replace(" ", "_", -1)
#if action.generated:
# actionname = actionname.lower()
# Runs the actual functions
try:
func = getattr(self, actionname, None)
@@ -891,8 +911,16 @@ class AppBase:
multiexecution = False
multi_execution_lists = []
for parameter in action["parameters"]:
check, value, is_loop = parse_params(action, fullexecution, parameter)
is_file = False
try:
if parameter["schema"]["type"] == "file":
print("SHOULD HANDLE FILE. Get based on value %s" % parameter["value"])
get_files(fullexecution, parameter["value"])
is_file = True
except KeyError as e:
print("SCHEMA ERROR: %s" % e)
check, value, is_loop = parse_params(action, fullexecution, parameter)
if check:
raise "Value check error: %s" % Exception(check)
+2 -2
View File
@@ -3,11 +3,11 @@ NAME=shuffle-app_sdk
VERSION=0.8.0
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
docker build . -t frikky/shuffle:$NAME -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
docker build . -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
#docker push frikky/$NAME:$VERSION
#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION
#docker push ghcr.io/frikky/$NAME:$VERSION
docker push frikky/shuffle:$NAME
docker push frikky/shuffle:app_sdk
docker push ghcr.io/frikky/$NAME:$VERSION
+87 -83
View File
@@ -620,9 +620,6 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U
return User{}, errors.New("Invalid format for apikey")
}
// fml
//log.Println(apikeyCheck)
// This is annoying af and is done because of maxlength lol
newApikey := apikeyCheck[1]
if len(newApikey) > 249 {
@@ -630,57 +627,15 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U
}
ctx := context.Background()
//if item, err := memcache.Get(ctx, newApikey); err == memcache.ErrCacheMiss {
// // Not in cache
//} else if err != nil {
// // Error with cache
// log.Printf("Error getting item: %v", err)
//} else {
// var Userdata User
// err = json.Unmarshal(item.Value, &Userdata)
// if err == nil {
// if len(Userdata.Username) > 0 {
// return Userdata, nil
// } else {
// return Userdata, errors.New("User is invalid")
// }
// }
//}
// Make specific check for just service user?
// Get the user based on APIkey here
//log.Println(apikeyCheck[1])
Userdata, err := getApikey(ctx, apikeyCheck[1])
if err != nil {
log.Printf("Apikey %s doesn't exist: %s", apikey, err)
return User{}, err
}
// Caching both bad and good apikeys :)
//b, err := json.Marshal(Userdata)
//if err != nil {
// log.Printf("Failed marshalling: %s", err)
// return User{}, err
//}
// Add to cache if it doesn't exist
//item := &memcache.Item{
// Key: newApikey,
// Value: b,
// Expiration: time.Minute * 60,
//}
//if err := memcache.Add(ctx, item); err == memcache.ErrNotStored {
// if err := memcache.Set(ctx, item); err != nil {
// log.Printf("Error setting item: %v", err)
// }
//} else if err != nil {
// log.Printf("error adding item: %v", err)
//} else {
// log.Printf("Set cache for %s", item.Key)
//}
if len(Userdata.Username) > 0 {
return Userdata, nil
} else {
@@ -3957,7 +3912,7 @@ func executeSchedule(resp http.ResponseWriter, request *http.Request) {
}
ctx := context.Background()
log.Printf("EXECUTING %s!", workflowId)
log.Printf("[INFO] EXECUTING %s!", workflowId)
idConfig, err := getSchedule(ctx, workflowId)
if err != nil {
log.Printf("Error getting schedule: %s", err)
@@ -7186,39 +7141,37 @@ func runInit(ctx context.Context) {
}
}
/*
fileq := datastore.NewQuery("Files").Limit(1)
count, err := dbclient.Count(ctx, fileq)
fileq := datastore.NewQuery("Files").Limit(1)
count, err := dbclient.Count(ctx, fileq)
log.Printf("FILECOUNT: %d", count)
if err == nil && count < 10 {
basepath := "."
filename := "testfile.txt"
fileId := uuid.NewV4().String()
log.Printf("Creating new file reference %s because none exist!", fileId)
workflowId := "2cf1169d-b460-41de-8c36-28b2092866f8"
downloadPath := fmt.Sprintf("%s/%s/%s/%s", basepath, activeOrgs[0].Id, workflowId, fileId)
if err == nil && count == 0 {
basepath := "."
filename := "testfile.txt"
fileId := uuid.NewV4().String()
log.Printf("Creating new file reference %s because none exist!", fileId)
workflowId := "2e9d6474-402c-4dcc-bb53-45f638ca18d3"
downloadPath := fmt.Sprintf("%s/%s/%s/%s", basepath, activeOrgs[0].Id, workflowId, fileId)
timeNow := time.Now().Unix()
newFile := File{
Id: fileId,
CreatedAt: timeNow,
UpdatedAt: timeNow,
Description: "Created by system for testing",
Status: "active",
Filename: filename,
OrgId: activeOrgs[0].Id,
WorkflowId: workflowId,
DownloadPath: downloadPath,
}
err = setFile(ctx, newFile)
if err != nil {
log.Printf("Failed setting file: %s", err)
} else {
log.Printf("Created file %s in init", newFile.DownloadPath)
}
timeNow := time.Now().Unix()
newFile := File{
Id: fileId,
CreatedAt: timeNow,
UpdatedAt: timeNow,
Description: "Created by system for testing",
Status: "active",
Filename: filename,
OrgId: activeOrgs[0].Id,
WorkflowId: workflowId,
DownloadPath: downloadPath,
}
*/
err = setFile(ctx, newFile)
if err != nil {
log.Printf("Failed setting file: %s", err)
} else {
log.Printf("Created file %s in init", newFile.DownloadPath)
}
}
var allworkflowapps []AppAuthenticationStorage
q = datastore.NewQuery("workflowappauth")
@@ -7241,7 +7194,7 @@ func runInit(ctx context.Context) {
var schedules []ScheduleOld
q = datastore.NewQuery("schedules")
_, err := dbclient.GetAll(ctx, q, &schedules)
_, err = dbclient.GetAll(ctx, q, &schedules)
if err == nil {
log.Printf("Setting up all schedules with org %s", activeOrgs[0].Id)
for _, item := range schedules {
@@ -8016,13 +7969,59 @@ func handleGetFileContent(resp http.ResponseWriter, request *http.Request) {
fileId = location[4]
}
//log.Printf("In file download")
log.Printf("\n\nUser is trying to get file %s\n\n", fileId)
// 1. Check user directly
// 2. Check workflow execution authorization
setOrgId := false
user, err := handleApiAuthentication(resp, request)
if err != nil {
log.Printf("Api authentication failed in file download: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
// r.HandleFunc("/api/v1/files/{fileId}/content", handleGetFileContent).Methods("GET", "OPTIONS")
log.Printf("INITIAL Api authentication failed in file download: %s", err)
executionId, ok := request.URL.Query()["execution_id"]
if ok && len(executionId) > 0 {
ctx := context.Background()
workflowExecution, err := getWorkflowExecution(ctx, executionId[0])
if err != nil {
log.Printf("Couldn't find execution ID %s", executionId[0])
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
apikey := request.Header.Get("Authorization")
if !strings.HasPrefix(apikey, "Bearer ") {
log.Printf("Apikey doesn't start with bearer (2)")
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
apikeyCheck := strings.Split(apikey, " ")
if len(apikeyCheck) != 2 {
log.Printf("Invalid format for apikey (2)")
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
// This is annoying af and is done because of maxlength lol
newApikey := apikeyCheck[1]
if newApikey != workflowExecution.Authorization {
log.Printf("Bad apikey for execution %s. %s vs %s", executionId[0], apikey, workflowExecution.Authorization)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
log.Printf("Authorization is correct for execution %s! %s vs %s", executionId, apikey, workflowExecution.Authorization)
setOrgId = true
} else {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
}
// 1. Verify if the user has access to the file: org_id and workflow
@@ -8037,6 +8036,11 @@ func handleGetFileContent(resp http.ResponseWriter, request *http.Request) {
return
}
// This is a workaround for file grabs from an app
if setOrgId == true {
user.ActiveOrg.Id = file.OrgId
}
found := false
if file.OrgId == user.ActiveOrg.Id {
found = true
+17 -13
View File
@@ -1089,14 +1089,14 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
}
}
log.Printf("Updating %s in %s from %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, workflowExecution.Results[outerindex].Status, actionResult.Status)
log.Printf("[INFO] Updating %s in %s from %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, workflowExecution.Results[outerindex].Status, actionResult.Status)
workflowExecution.Results[outerindex] = actionResult
} else {
log.Printf("Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status)
log.Printf("[INFO] Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status)
workflowExecution.Results = append(workflowExecution.Results, actionResult)
}
} else {
log.Printf("Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status)
log.Printf("[INFO] Setting value of %s in %s to %s", actionResult.Action.ID, workflowExecution.ExecutionId, actionResult.Status)
workflowExecution.Results = append(workflowExecution.Results, actionResult)
}
@@ -1179,7 +1179,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
_ = skippedNodes
if finished {
log.Printf("Execution of %s finished.", workflowExecution.ExecutionId)
log.Printf("[INFO] Execution of %s finished.", workflowExecution.ExecutionId)
//log.Println("Might be finished based on length of results and everything being SUCCESS or FINISHED - VERIFY THIS. Setting status to finished.")
workflowExecution.Result = lastResult
@@ -1316,15 +1316,15 @@ func handleExecutionStatistics(execution WorkflowExecution) {
for _, exampleresult := range appResults {
err := setExampleresult(ctx, exampleresult)
if err != nil {
log.Printf("Failed setting examplresult %s: %s", exampleresult.ExampleId, err)
log.Printf("[ERROR] Failed setting examplresult %s: %s", exampleresult.ExampleId, err)
} else {
successful += 1
}
}
log.Printf("Added %d exampleresults to backend", successful)
log.Printf("[INFO] Added %d exampleresults to backend", successful)
} else {
log.Printf("No example results necessary to be added for execution %s", execution.ExecutionId)
log.Printf("[INFO] No example results necessary to be added for execution %s", execution.ExecutionId)
}
}
@@ -1438,6 +1438,7 @@ func setNewWorkflow(resp http.ResponseWriter, request *http.Request) {
workflow.ID = uuid.NewV4().String()
workflow.Owner = user.Id
workflow.Sharing = "private"
user.ActiveOrg.Users = []User{}
workflow.ExecutingOrg = user.ActiveOrg
workflow.OrgId = user.ActiveOrg.Id
@@ -1816,7 +1817,8 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
}
if len(workflow.ExecutingOrg.Id) == 0 {
log.Printf("setting executing org")
log.Printf("Setting executing org for workflow")
user.ActiveOrg.Users = []User{}
workflow.ExecutingOrg = user.ActiveOrg
}
@@ -2339,17 +2341,17 @@ func abortExecution(resp http.ResponseWriter, request *http.Request) {
// FIXME - have a check for org etc too..
if user.Id != workflowExecution.Workflow.Owner && user.Role != "admin" {
log.Printf("Wrong user (%s) for workflowexecution workflow %s", user.Username, workflowExecution.Workflow.ID)
log.Printf("[INFO] Wrong user (%s) for workflowexecution workflow %s", user.Username, workflowExecution.Workflow.ID)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
} else {
log.Printf("API key to abort/finish execution %s is correct.", executionId)
log.Printf("[INFO] API key to abort/finish execution %s is correct.", executionId)
}
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" || workflowExecution.Status == "FINISHED" {
log.Printf("Stopped execution of %s with status %s", executionId, workflowExecution.Status)
log.Printf("[INFO] Stopped execution of %s with status %s", executionId, workflowExecution.Status)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Status for %s is %s, which can't be aborted."}`, executionId, workflowExecution.Status)))
return
@@ -2461,7 +2463,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
}
if len(workflow.ExecutingOrg.Id) == 0 {
log.Printf("Stopped execution because there is no executing org for workflow %s", workflow.ID)
log.Printf("[INFO] Stopped execution because there is no executing org for workflow %s", workflow.ID)
return WorkflowExecution{}, fmt.Sprintf("Workflow has no executing org defined"), errors.New("Workflow has no executing org defined")
}
@@ -2479,7 +2481,7 @@ func handleExecution(id string, workflow Workflow, request *http.Request) (Workf
}
if !workflow.IsValid {
log.Printf("Stopped execution as workflow %s is not valid.", workflow.ID)
log.Printf("[ERROR] Stopped execution as workflow %s is not valid.", workflow.ID)
return WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow")
}
@@ -3104,6 +3106,8 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
}
log.Printf("[INFO] Starting execution of %s!", fileId)
user.ActiveOrg.Users = []User{}
workflow.ExecutingOrg = user.ActiveOrg
workflowExecution, executionResp, err := handleExecution(fileId, *workflow, request)
+10 -1
View File
@@ -2836,7 +2836,16 @@ const AngularWorkflow = (props) => {
}}
/>
if (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0) {
console.log(selectedActionParameters[count])
if (selectedActionParameters[count].schema !== undefined && selectedActionParameters[count].schema !== null && selectedActionParameters[count].schema.type === "file") {
const fileId = "6daabec1-892b-469c-b603-c902e47223a9"
datafield = `SHOW FILES FROM OTHER NODES? Filename: ${selectedActionParameters[count].value}`
if (selectedActionParameters[count].value != fileId) {
changeActionParameter(fileId, count)
setUpdate(Math.random())
}
} else if (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0) {
if (selectedActionParameters[count].value === "" && selectedActionParameters[count].required) {
// Rofl, dirty workaround :)
const e = {