From 3a8b61510b71cc91ea87e6534c85797eacbc29ea Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 3 May 2021 17:10:55 +0200 Subject: [PATCH] #353: Set default startnode to first existing action --- backend/go-app/go.mod | 2 +- backend/go-app/go.sum | 2 + backend/go-app/main.go | 8 +- backend/go-app/oauth2.go | 776 ++++++++----------------- frontend/src/defaultCytoscapeStyle.js | 35 +- frontend/src/views/AngularWorkflow.jsx | 81 ++- 6 files changed, 324 insertions(+), 580 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 74fffe85..abe78071 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle go 1.13 -//replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 1e8d5548..3c981bd2 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -113,6 +113,8 @@ github.com/frikky/shuffle-shared v0.0.32 h1:Uy/zcAetSVYtRr3HEkUb7aE7Ggm0oSFxVeUN github.com/frikky/shuffle-shared v0.0.32/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= github.com/frikky/shuffle-shared v0.0.37 h1:6nN1Im22TBuWUCG5L619xTgEPIPXCY68QThIfNiuG8k= github.com/frikky/shuffle-shared v0.0.37/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= +github.com/frikky/shuffle-shared v0.0.38 h1:OZSwU1HDOaPzdlG1s77svgXJKzlNewM6GjeH1/8EIUM= +github.com/frikky/shuffle-shared v0.0.38/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= github.com/getkin/kin-openapi v0.8.0 h1:a6TQjTqwkyscC4/hShJX7WhCVE+4bi9lzw61XHQW5hE= github.com/getkin/kin-openapi v0.8.0/go.mod h1:zZQMFkVgRHCdhgb6ihCTIo9dyDZFvX0k/xAKqw1FhPw= github.com/getkin/kin-openapi v0.52.0 h1:6WqsF5d6PfJ8AscdD+9Rtb2RP2iBWyC7V6GcjssWg7M= diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 0a011c8d..7cae58e8 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4612,7 +4612,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { if job.Action == "execute" { // FIXME: Get the email ctx := context.Background() - maildata := MailData{} + maildata := shuffle.MailData{} err := json.Unmarshal([]byte(job.ThirdItem), &maildata) if err != nil { log.Printf("Maildata unmarshal error: %s", err) @@ -4620,7 +4620,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { } hookId := job.Id - hook, err := getTriggerAuth(ctx, hookId) + hook, err := shuffle.GetTriggerAuth(ctx, hookId) if err != nil { log.Printf("[INFO] Failed getting trigger %s (callback cloud): %s", hookId, err) return err @@ -4639,7 +4639,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { //log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err) //type FullEmail struct { - email := FullEmail{} + email := shuffle.FullEmail{} if len(emails) == 1 { email = emails[0] } @@ -6247,7 +6247,7 @@ func initHandlers() { // Trigger hmm r.HandleFunc("/api/v1/triggers/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/triggers/outlook/getFolders", handleGetOutlookFolders).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/triggers/outlook/{key}/callback", handleOutlookCallback).Methods("POST", "OPTIONS") //r.HandleFunc("/api/v1/stats/{key}", handleGetSpecificStats).Methods("GET", "OPTIONS") diff --git a/backend/go-app/oauth2.go b/backend/go-app/oauth2.go index 71d3cbbd..9f9234c9 100644 --- a/backend/go-app/oauth2.go +++ b/backend/go-app/oauth2.go @@ -15,160 +15,10 @@ import ( "strings" "time" - "cloud.google.com/go/datastore" "golang.org/x/oauth2" ) -// This is what the structure should be when it's sent into a workflow -type ParsedShuffleMail struct { - Body struct { - URI []string `json:"uri"` - Email []string `json:"email"` - Domain []string `json:"domain"` - ContentHeader struct { - } `json:"content_header"` - Content string `json:"content"` - ContentType string `json:"content_type"` - Hash string `json:"hash"` - RawBody string `json:"raw_body"` - } `json:"body"` - Header struct { - Subject string `json:"subject"` - From string `json:"from"` - To []string `json:"to"` - Date string `json:"date"` - Received []struct { - Src string `json:"src"` - From []string `json:"from"` - By []string `json:"by"` - With string `json:"with"` - Date string `json:"date"` - } `json:"received"` - ReceivedDomain []string `json:"received_domain"` - ReceivedIP []string `json:"received_ip"` - Header struct { - } `json:"header"` - } `json:"header"` - MessageID string `json:"message_id"` - EmailFileid string `json:"email_fileid"` - AttachmentUids []string `json:"attachment_uids"` -} - -type FullEmail struct { - OdataContext string `json:"@odata.context"` - OdataEtag string `json:"@odata.etag"` - ID string `json:"id"` - Createddatetime time.Time `json:"createdDateTime"` - Lastmodifieddatetime time.Time `json:"lastModifiedDateTime"` - Changekey string `json:"changeKey"` - Categories []interface{} `json:"categories"` - Receiveddatetime time.Time `json:"receivedDateTime"` - Sentdatetime time.Time `json:"sentDateTime"` - Hasattachments bool `json:"hasAttachments"` - Internetmessageid string `json:"internetMessageId"` - Subject string `json:"subject"` - Bodypreview string `json:"bodyPreview"` - Importance string `json:"importance"` - Parentfolderid string `json:"parentFolderId"` - Conversationid string `json:"conversationId"` - Conversationindex string `json:"conversationIndex"` - Isdeliveryreceiptrequested interface{} `json:"isDeliveryReceiptRequested"` - Isreadreceiptrequested bool `json:"isReadReceiptRequested"` - Isread bool `json:"isRead"` - Isdraft bool `json:"isDraft"` - Weblink string `json:"webLink"` - Inferenceclassification string `json:"inferenceClassification"` - Body struct { - Contenttype string `json:"contentType"` - Content string `json:"content"` - } `json:"body"` - Sender struct { - Emailaddress struct { - Name string `json:"name"` - Address string `json:"address"` - } `json:"emailAddress"` - } `json:"sender"` - From struct { - Emailaddress struct { - Name string `json:"name"` - Address string `json:"address"` - } `json:"emailAddress"` - } `json:"from"` - Torecipients []struct { - Emailaddress struct { - Name string `json:"name"` - Address string `json:"address"` - } `json:"emailAddress"` - } `json:"toRecipients"` - Ccrecipients []interface{} `json:"ccRecipients"` - Bccrecipients []interface{} `json:"bccRecipients"` - Replyto []interface{} `json:"replyTo"` - Flag struct { - Flagstatus string `json:"flagStatus"` - } `json:"flag"` - Attachments []struct { - OdataType string `json:"@odata.type"` - OdataMediacontenttype string `json:"@odata.mediaContentType"` - ID string `json:"id"` - Lastmodifieddatetime time.Time `json:"lastModifiedDateTime"` - Name string `json:"name"` - Contenttype string `json:"contentType"` - Size int `json:"size"` - Isinline bool `json:"isInline"` - Contentid interface{} `json:"contentId"` - Contentlocation interface{} `json:"contentLocation"` - Contentbytes string `json:"contentBytes"` - } -} - -type MailData struct { - Value []struct { - Subscriptionid string `json:"subscriptionId"` - Subscriptionexpirationdatetime string `json:"subscriptionExpirationDateTime"` - Changetype string `json:"changeType"` - Resource string `json:"resource"` - Resourcedata struct { - OdataType string `json:"@odata.type"` - OdataID string `json:"@odata.id"` - OdataEtag string `json:"@odata.etag"` - ID string `json:"id"` - } `json:"resourceData"` - Clientstate string `json:"clientState"` - Tenantid string `json:"tenantId"` - } `json:"value"` -} - -type OutlookProfile struct { - OdataContext string `json:"@odata.context"` - BusinessPhones []string `json:"businessPhones"` - DisplayName string `json:"displayName"` - GivenName string `json:"givenName"` - JobTitle interface{} `json:"jobTitle"` - Mail string `json:"mail"` - MobilePhone interface{} `json:"mobilePhone"` - OfficeLocation interface{} `json:"officeLocation"` - PreferredLanguage interface{} `json:"preferredLanguage"` - Surname string `json:"surname"` - UserPrincipalName string `json:"userPrincipalName"` - ID string `json:"id"` -} - -type OutlookFolder struct { - ID string `json:"id"` - DisplayName string `json:"displayName"` - ParentFolderID string `json:"parentFolderId"` - ChildFolderCount int `json:"childFolderCount"` - UnreadItemCount int `json:"unreadItemCount"` - TotalItemCount int `json:"totalItemCount"` -} - -type OutlookFolders struct { - OdataContext string `json:"@odata.context"` - OdataNextLink string `json:"@odata.nextLink"` - Value []OutlookFolder `json:"value"` -} - -func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([]FullEmail, error) { +func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([]shuffle.FullEmail, error) { //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders") requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s/attachments/%s", emailId, attachmentId) @@ -177,20 +27,20 @@ func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([] ret, err := client.Get(requestUrl) if err != nil { log.Printf("[INFO] OutlookErr: %s", err) - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } body, err := ioutil.ReadAll(ret.Body) if err != nil { log.Printf("[WARNING] Failed body decoding from outlook email") - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } //type FullEmail struct { log.Printf("[INFO] Attachment Body: %s", string(body)) log.Printf("[INFO] Status email: %d", ret.StatusCode) if ret.StatusCode != 200 { - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } //log.Printf("Body: %s", string(body)) @@ -206,13 +56,13 @@ func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([] emails = append(emails, parsedmail) */ - return []FullEmail{}, nil + return []shuffle.FullEmail{}, nil } -func getOutlookEmail(client *http.Client, maildata MailData) ([]FullEmail, error) { +func getOutlookEmail(client *http.Client, maildata shuffle.MailData) ([]shuffle.FullEmail, error) { //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders") - emails := []FullEmail{} + emails := []shuffle.FullEmail{} for _, email := range maildata.Value { //messageId := email.Resourcedata.ID //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/%s", messageId) @@ -222,29 +72,29 @@ func getOutlookEmail(client *http.Client, maildata MailData) ([]FullEmail, error ret, err := client.Get(requestUrl) if err != nil { log.Printf("[INFO] OutlookErr: %s", err) - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } body, err := ioutil.ReadAll(ret.Body) if err != nil { log.Printf("[WARNING] Failed body decoding from outlook email") - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } - //type FullEmail struct { + //type shuffle.FullEmail struct { //log.Printf("[INFO] EMAIL Body: %s", string(body)) //log.Printf("[INFO] Status email: %d", ret.StatusCode) if ret.StatusCode != 200 { - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } //log.Printf("Body: %s", string(body)) - parsedmail := FullEmail{} + parsedmail := shuffle.FullEmail{} err = json.Unmarshal(body, &parsedmail) if err != nil { log.Printf("[INFO] Email unmarshal error: %s", err) - return []FullEmail{}, err + return []shuffle.FullEmail{}, err } emails = append(emails, parsedmail) @@ -253,77 +103,48 @@ func getOutlookEmail(client *http.Client, maildata MailData) ([]FullEmail, error return emails, nil } -func getOutlookFolders(client *http.Client) (OutlookFolders, error) { - //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders") - requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/mailFolders") - - ret, err := client.Get(requestUrl) - if err != nil { - log.Printf("[INFO] FolderErr: %s", err) - return OutlookFolders{}, err - } - - body, err := ioutil.ReadAll(ret.Body) - if err != nil { - log.Printf("[WARNING] Failed body decoding from mailfolders") - return OutlookFolders{}, err - } - - //log.Printf("[INFO] Folder Body: %s", string(body)) - log.Printf("[INFO] Status folders: %d", ret.StatusCode) - if ret.StatusCode != 200 { - return OutlookFolders{}, err - } - - //log.Printf("Body: %s", string(body)) - - mailfolders := OutlookFolders{} - err = json.Unmarshal(body, &mailfolders) - if err != nil { - log.Printf("Unmarshal: %s", err) - return OutlookFolders{}, err - } - - //fmt.Printf("%#v", mailfolders) - // FIXME - recursion for subfolders - // Recursive struct - // folderEndpoint := fmt.Sprintf("%s/%s/childfolders?$top=40", requestUrl, parentId) - //for _, folder := range mailfolders.Value { - // log.Println(folder.DisplayName) - //} - - return mailfolders, nil -} - -func getOutlookProfile(client *http.Client) (OutlookProfile, error) { +func getOutlookProfile(client *http.Client) (shuffle.OutlookProfile, error) { requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me?$select=mail") ret, err := client.Get(requestUrl) if err != nil { log.Printf("[INFO] Folder error: %s", err) - return OutlookProfile{}, err + return shuffle.OutlookProfile{}, err } log.Printf("[INFO] Status profile: %d", ret.StatusCode) body, err := ioutil.ReadAll(ret.Body) if err != nil { log.Printf("[INFO] Body: %s", err) - return OutlookProfile{}, err + return shuffle.OutlookProfile{}, err } log.Printf("[INFO] BODY: %s", string(body)) - profile := OutlookProfile{} + profile := shuffle.OutlookProfile{} err = json.Unmarshal(body, &profile) if err != nil { log.Printf("Unmarshal: %s", err) - return OutlookProfile{}, err + return shuffle.OutlookProfile{}, err } return profile, nil } func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { + cors := shuffle.HandleCors(resp, request) + if cors { + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + code := request.URL.Query().Get("code") if len(code) == 0 { log.Println("No code") @@ -334,7 +155,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath()) log.Println(url) ctx := context.Background() - _, accessToken, err := getOutlookClient(ctx, code, OauthToken{}, url) + _, accessToken, err := getOutlookClient(ctx, code, shuffle.OauthToken{}, url) if err != nil { log.Printf("Oauth client failure - outlook register: %s", err) resp.WriteHeader(401) @@ -367,7 +188,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { // FIXME - trigger auth senderUser := "" - trigger := TriggerAuth{} + trigger := shuffle.TriggerAuth{} for _, item := range stateitems { itemsplit := strings.Split(item, "%3D") if len(itemsplit) == 1 { @@ -404,7 +225,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { */ trigger.Code = code - trigger.OauthToken = OauthToken{ + trigger.OauthToken = shuffle.OauthToken{ AccessToken: accessToken.AccessToken, TokenType: accessToken.TokenType, RefreshToken: accessToken.RefreshToken, @@ -425,7 +246,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { } // Should also update the user - Userdata, err := shuffle.GetUser(ctx, senderUser) + Userdata, err := shuffle.GetUser(ctx, user.Id) if err != nil { log.Printf("[INFO] Username %s doesn't exist (oauth2): %s", trigger.Username, err) resp.WriteHeader(401) @@ -465,7 +286,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { return } - err = setTriggerAuth(ctx, trigger) + err = shuffle.SetTriggerAuth(ctx, trigger) if err != nil { log.Printf("Failed to set trigger auth for %s - %s", trigger.Username, err) resp.WriteHeader(401) @@ -476,50 +297,8 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { resp.Write([]byte(`{"success": true}`)) } -type OauthToken struct { - AccessToken string `json:"AccessToken" datastore:"AccessToken,noindex"` - TokenType string `json:"TokenType" datastore:"TokenType,noindex"` - RefreshToken string `json:"RefreshToken" datastore:"RefreshToken,noindex"` - Expiry time.Time `json:"Expiry" datastore:"Expiry,noindex"` -} - -type TriggerAuth struct { - Id string `json:"id" datastore:"id"` - SubscriptionId string `json:"subscriptionId" datastore:"subscriptionId"` - - Username string `json:"username" datastore:"username,noindex"` - WorkflowId string `json:"workflow_id" datastore:"workflow_id,noindex"` - Owner string `json:"owner" datastore:"owner"` - Type string `json:"type" datastore:"type"` - Code string `json:"code,omitempty" datastore:"code,noindex"` - Start string `json:"start" datastore:"start"` - OauthToken OauthToken `json:"oauth_token,omitempty" datastore:"oauth_token"` -} - -func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) { - key := datastore.NameKey("trigger_auth", strings.ToLower(id), nil) - triggerauth := &TriggerAuth{} - if err := dbclient.Get(ctx, key, triggerauth); err != nil { - return &TriggerAuth{}, err - } - - return triggerauth, nil -} - -func setTriggerAuth(ctx context.Context, trigger TriggerAuth) error { - key1 := datastore.NameKey("trigger_auth", strings.ToLower(trigger.Id), nil) - - // New struct, to not add body, author etc - if _, err := dbclient.Put(ctx, key1, &trigger); err != nil { - log.Printf("Error adding trigger auth: %s", err) - return err - } - - return nil -} - // THis all of a sudden became really horrible.. fml -func getOutlookClient(ctx context.Context, code string, accessToken OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) { +func getOutlookClient(ctx context.Context, code string, accessToken shuffle.OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) { conf := &oauth2.Config{ ClientID: "fd55c175-aa30-4fa6-b303-09a29fb3f750", @@ -557,81 +336,6 @@ func getOutlookClient(ctx context.Context, code string, accessToken OauthToken, return client, access_token, nil } -func handleGetOutlookFolders(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - // Exchange every time hmm - // FIXME - // Should really just get the code from the trigger that's being used OR the user - triggerId := request.URL.Query().Get("trigger_id") - if len(triggerId) == 0 { - log.Println("No trigger_id supplied") - resp.WriteHeader(401) - return - } - - ctx := context.Background() - trigger, err := getTriggerAuth(ctx, triggerId) - if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - outlook folders.", triggerId) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "Trigger doesn't exist."}`)) - return - } - - //client, accessToken, err := getOutlookClient(ctx, code, OauthToken{}, url) - //if err != nil { - // log.Printf("Oauth client failure - outlook register: %s", err) - // resp.WriteHeader(401) - // return - //} - - // FIXME - should be shuffler in literally every case except testing lol - //log.Printf("TRIGGER: %#v", trigger) - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("[WARNING] Oauth client failure - outlook folders: %s", err) - resp.Write([]byte(`{"success": false, "reason": "Failed creating outlook client"}`)) - resp.WriteHeader(401) - return - } - - // This should be possible, and will also give the actual username - /* - profile, err := getOutlookProfile(outlookClient) - if err != nil { - log.Printf("Outlook profile failure: %s", err) - resp.WriteHeader(401) - return - } - log.Printf("PROFILE: %#v", profile) - */ - - folders, err := getOutlookFolders(outlookClient) - if err != nil { - log.Printf("[WARNING] Failed setting outlook folders: %s", err) - resp.Write([]byte(`{"success": false, "reason": "Failed getting outlook folders"}`)) - resp.WriteHeader(401) - return - } - - b, err := json.Marshal(folders.Value) - if err != nil { - log.Println("[INFO] Failed to marshal folderdata") - resp.Write([]byte(`{"success": false, "reason": "Failed decoding JSON"}`)) - resp.WriteHeader(401) - return - } - - resp.WriteHeader(200) - resp.Write(b) -} - func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -664,7 +368,7 @@ func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() - trigger, err := getTriggerAuth(ctx, workflowId) + trigger, err := shuffle.GetTriggerAuth(ctx, workflowId) if err != nil { log.Printf("[INFO] Trigger %s doesn't exist - specific trigger.", workflowId) resp.WriteHeader(401) @@ -678,7 +382,7 @@ func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { return } - trigger.OauthToken = OauthToken{} + trigger.OauthToken = shuffle.OauthToken{} trigger.Code = "" b, err := json.Marshal(trigger) @@ -692,201 +396,6 @@ func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { resp.Write(b) } -// This sets up the sub with outlook itself -// Parses data from the workflow to see whether access is right to subscribe it -// Creates the cloud function for outlook return -// Wait for it to be available, then schedule a workflow to it -func createOutlookSub(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - if location[1] == "api" { - if len(location) <= 4 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[4] - } - - ctx := context.Background() - workflow, err := shuffle.GetWorkflow(ctx, workflowId) - if err != nil { - log.Printf("Failed getting the workflow locally (outlook sub): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("Api authentication failed in outlook deploy: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // FIXME - have a check for org etc too.. - if user.Id != workflow.Owner && user.Role != "admin" { - log.Printf("Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - log.Println("[INFO] Handle outlook subscription for trigger") - - // Should already be authorized at this point, as the workflow is shared - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("Failed body read for workflow %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Based on the input data from frontend - type CurTrigger struct { - Name string `json:"name"` - Folders []string `json:"folders"` - ID string `json:"id"` - } - - //log.Println(string(body)) - var curTrigger CurTrigger - err = json.Unmarshal(body, &curTrigger) - if err != nil { - log.Printf("Failed body read unmarshal for trigger %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - if len(curTrigger.Folders) == 0 { - log.Printf("Error for %s. Choosing folders is required, currently 0", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Now that it's deployed - wait a few seconds before generating: - // 1. Oauth2 token thingies for outlook.office.com - // 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") - // 3. Set the callback URL to be the new trigger - // 4. Run subscription test - // 5. Set the subscriptionId to the trigger object - - // First - lets regenerate an oauth token for outlook.office.com from the original items - trigger, err := getTriggerAuth(ctx, curTrigger.ID) - if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - outlook sub.", curTrigger.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - return - } - - // url doesn't really matter here - //url := fmt.Sprintf("https://shuffler.io") - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("Oauth client failure - triggerauth: %s", err) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - resp.WriteHeader(401) - return - } - - // Location + - - // This is here simply to let the function start - // Usually takes 10 attempts minimum :O - // 10 * 5 = 50 seconds. That's waaay too much :( - - if runningEnvironment != "cloud" { - org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) - if err != nil { - log.Printf("Failed finding org %s: %s", org.Id, err) - return - } - log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) - - action := shuffle.CloudSyncJob{ - Type: "outlook", - Action: "start", - OrgId: org.Id, - PrimaryItemId: trigger.Id, - SecondaryItem: trigger.Start, - ThirdItem: workflowId, - } - - err = executeCloudAction(action, org.SyncConfig.Apikey) - if err != nil { - log.Printf("[INFO] Failed cloud action START outlook execution: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } else { - log.Printf("[INFO] Successfully set up cloud action trigger") - } - } else { - log.Printf("Should configure a running environment for CLOUD") - } - - notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) - curSubscriptions, err := getOutlookSubscriptions(outlookClient) - if err == nil { - for _, sub := range curSubscriptions.Value { - if sub.NotificationURL == notificationURL { - log.Printf("[INFO] Removing existing subscription %s", sub.Id) - removeOutlookSubscription(outlookClient, sub.Id) - } - } - } else { - log.Printf("[INFO] Failed to get subscriptions - need to overwrite") - } - - maxFails := 5 - failCnt := 0 - log.Println(curTrigger.Folders) - for { - subId, err := makeOutlookSubscription(outlookClient, curTrigger.Folders, notificationURL) - if err != nil { - failCnt += 1 - log.Printf("Failed making oauth subscription, retrying in 5 seconds: %s", err) - time.Sleep(5 * time.Second) - if failCnt == maxFails { - log.Printf("Failed to set up subscription %d times.", maxFails) - resp.WriteHeader(401) - return - } - - continue - } - - // Set the ID somewhere here - trigger.SubscriptionId = subId - err = setTriggerAuth(ctx, *trigger) - if err != nil { - log.Printf("Failed setting triggerauth: %s", err) - } - - break - } - - log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID) - - //log.Printf("%#v", user) - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - // Lists the users current subscriptions func getOutlookSubscriptions(outlookClient *http.Client) (SubscriptionsWrapper, error) { fullUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/subscriptions") @@ -1024,7 +533,7 @@ func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) { } //func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) { - hook, err := getTriggerAuth(ctx, hookId) + hook, err := shuffle.GetTriggerAuth(ctx, hookId) if err != nil { log.Printf("[INFO] Failed getting trigger %s (callback): %s", hookId, err) resp.WriteHeader(401) @@ -1054,7 +563,7 @@ func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) { // 1. Take the body and parse data -> Get the email itself - maildata := MailData{} + maildata := shuffle.MailData{} err = json.Unmarshal(body, &maildata) if err != nil { log.Printf("Maildata unmarshal error: %s", err) @@ -1076,14 +585,14 @@ func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) { log.Printf("[INFO] EMAILS: %d. If this is more than 1, please contact frikky@shuffler.io", len(emails)) //log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err) - //type FullEmail struct { - email := FullEmail{} + //type shuffle.FullEmail struct { + email := shuffle.FullEmail{} if len(emails) == 1 { email = emails[0] } // Parse indicators (domains, emails, ips, domains etc)! - newEmail := ParsedShuffleMail{} + newEmail := shuffle.ParsedShuffleMail{} newEmail.Body.ContentType = email.Body.Contenttype newEmail.Body.Content = email.Body.Content newEmail.Body.RawBody = email.Body.Content @@ -1203,7 +712,7 @@ func handleOutlookSubRemoval(ctx context.Context, user shuffle.User, workflowId, // 2. Stop the subscription // 3. Remove the function // 4. Remove the database entry for auth - trigger, err := getTriggerAuth(ctx, triggerId) + trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) if err != nil { log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId) return err @@ -1327,3 +836,198 @@ func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) } + +// This sets up the sub with outlook itself +// Parses data from the workflow to see whether access is right to subscribe it +// Creates the cloud function for outlook return +// Wait for it to be available, then schedule a workflow to it +func createOutlookSub(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + location := strings.Split(request.URL.String(), "/") + + var workflowId string + if location[1] == "api" { + if len(location) <= 4 { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + workflowId = location[4] + } + + ctx := context.Background() + workflow, err := shuffle.GetWorkflow(ctx, workflowId) + if err != nil { + log.Printf("Failed getting the workflow locally (outlook sub): %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("Api authentication failed in outlook deploy: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // FIXME - have a check for org etc too.. + if user.Id != workflow.Owner && user.Role != "admin" { + log.Printf("Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + log.Println("[INFO] Handle outlook subscription for trigger") + + // Should already be authorized at this point, as the workflow is shared + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("Failed body read for workflow %s", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // Based on the input data from frontend + type CurTrigger struct { + Name string `json:"name"` + Folders []string `json:"folders"` + ID string `json:"id"` + } + + //log.Println(string(body)) + var curTrigger CurTrigger + err = json.Unmarshal(body, &curTrigger) + if err != nil { + log.Printf("Failed body read unmarshal for trigger %s", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if len(curTrigger.Folders) == 0 { + log.Printf("Error for %s. Choosing folders is required, currently 0", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // Now that it's deployed - wait a few seconds before generating: + // 1. Oauth2 token thingies for outlook.office.com + // 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") + // 3. Set the callback URL to be the new trigger + // 4. Run subscription test + // 5. Set the subscriptionId to the trigger object + + // First - lets regenerate an oauth token for outlook.office.com from the original items + trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID) + if err != nil { + log.Printf("[INFO] Trigger %s doesn't exist - outlook sub.", curTrigger.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": ""}`)) + return + } + + // url doesn't really matter here + //url := fmt.Sprintf("https://shuffler.io") + redirectDomain := "localhost:5001" + url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) + outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url) + if err != nil { + log.Printf("Oauth client failure - triggerauth: %s", err) + resp.Write([]byte(`{"success": false, "reason": ""}`)) + resp.WriteHeader(401) + return + } + + // Location + + + // This is here simply to let the function start + // Usually takes 10 attempts minimum :O + // 10 * 5 = 50 seconds. That's waaay too much :( + + if runningEnvironment != "cloud" { + org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) + if err != nil { + log.Printf("Failed finding org %s: %s", org.Id, err) + return + } + log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) + + action := shuffle.CloudSyncJob{ + Type: "outlook", + Action: "start", + OrgId: org.Id, + PrimaryItemId: trigger.Id, + SecondaryItem: trigger.Start, + ThirdItem: workflowId, + } + + err = executeCloudAction(action, org.SyncConfig.Apikey) + if err != nil { + log.Printf("[INFO] Failed cloud action START outlook execution: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + return + } else { + log.Printf("[INFO] Successfully set up cloud action trigger") + } + } else { + log.Printf("Should configure a running environment for CLOUD") + } + + notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) + curSubscriptions, err := getOutlookSubscriptions(outlookClient) + if err == nil { + for _, sub := range curSubscriptions.Value { + if sub.NotificationURL == notificationURL { + log.Printf("[INFO] Removing existing subscription %s", sub.Id) + removeOutlookSubscription(outlookClient, sub.Id) + } + } + } else { + log.Printf("[INFO] Failed to get subscriptions - need to overwrite") + } + + maxFails := 5 + failCnt := 0 + log.Println(curTrigger.Folders) + for { + subId, err := makeOutlookSubscription(outlookClient, curTrigger.Folders, notificationURL) + if err != nil { + failCnt += 1 + log.Printf("Failed making oauth subscription, retrying in 5 seconds: %s", err) + time.Sleep(5 * time.Second) + if failCnt == maxFails { + log.Printf("Failed to set up subscription %d times.", maxFails) + resp.WriteHeader(401) + return + } + + continue + } + + // Set the ID somewhere here + trigger.SubscriptionId = subId + err = shuffle.SetTriggerAuth(ctx, *trigger) + if err != nil { + log.Printf("Failed setting triggerauth: %s", err) + } + + break + } + + log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID) + + //log.Printf("%#v", user) + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true}`)) +} diff --git a/frontend/src/defaultCytoscapeStyle.js b/frontend/src/defaultCytoscapeStyle.js index 8fd5d326..fb3a84d5 100644 --- a/frontend/src/defaultCytoscapeStyle.js +++ b/frontend/src/defaultCytoscapeStyle.js @@ -40,6 +40,7 @@ const data = [{ 'background-width': '100%', 'background-height': '100%', 'border-radius': '5px', + 'z-index': 5001, }, }, { @@ -47,6 +48,8 @@ const data = [{ css: { 'width': '30px', 'height': '30px', + 'z-index': 5000, + 'font-size': '0px', }, }, { @@ -54,6 +57,8 @@ const data = [{ css: { 'width': '30px', 'height': '30px', + 'z-index': 5000, + 'font-size': '0px', }, }, { @@ -119,6 +124,7 @@ const data = [{ 'border-color': '#80deea', 'width': '80px', 'height': '80px', + 'font-size': '18px', }, }, { @@ -204,8 +210,9 @@ const data = [{ 'border-width': '12px', 'transition-property': 'border-width', 'transition-duration': '0.25s', - 'font-size': '30px', 'label': 'data(label)', + 'font-size': '18px', + 'color': 'white', }, }, { @@ -240,17 +247,6 @@ const data = [{ "line-gradient-stop-colors": ["#41dcab", "#41dcab"], }, }, - { - selector: 'edge[?hasErrors]', - css: { - 'target-arrow-color': '#991818', - 'line-color': '#991818', - 'line-style': 'dashed', - "line-fill": "linear-gradient", - "line-gradient-stop-positions": ["0.0", "100"], - "line-gradient-stop-colors": ["#991818", "#991818"], - }, - }, { selector: '.eh-handle', style: { @@ -291,6 +287,19 @@ const data = [{ 'target-arrow-color': '#f85a3e', }, } - ] +] + +//{ +// selector: 'edge[?hasErrors]', +// css: { +// 'target-arrow-color': '#991818', +// 'line-color': '#991818', +// 'line-style': 'dashed', +// "line-fill": "linear-gradient", +// "line-gradient-stop-positions": ["0.0", "100"], +// "line-gradient-stop-colors": ["#991818", "#991818"], +// }, +//}, + export default data diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 667176f6..4bfc531d 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1715,26 +1715,38 @@ const AngularWorkflow = (props) => { const data = node.data() setLastSaved(false) - //var currentnode = cy.getElementById(data.id) - //if (currentnode.length === 0) { - //} - if (workflow.start === data.id && workflow.actions.length > 1) { - // FIXME - should check branches connected to startnode, as picking random - // might just be confusing - cy.nodes().some(function( ele ) { - if (ele.id() !== workflow.start && ele.data()["label"] !== undefined) { - //alert.success("Changed startnode to "+ele.data()["label"]) - ele.data("isStartNode", true) - workflow.start = ele.id() - //throw BreakException - return false - } - }) - } - - workflow.actions = workflow.actions.filter(a => a.id !== data.id) workflow.triggers = workflow.triggers.filter(a => a.id !== data.id) + if (workflow.start === data.id && workflow.actions.length > 0) { + // FIXME - should check branches connected to startnode, as picking random + // might just be confusing + if (workflow.actions[0].id !== data.id) { + const ele = cy.getElementById(workflow.actions[0].id) + if (ele !== undefined && ele !== null) { + ele.data("isStartNode", true) + workflow.start = ele.id() + } + } else { + if (workflow.actions.length > 1) { + const ele = cy.getElementById(workflow.actions[1].id) + if (ele !== undefined && ele !== null) { + ele.data("isStartNode", true) + workflow.start = ele.id() + } + } + } + + //cy.nodes().some(function( ele ) { + // if (ele.id() !== workflow.start && ele.data()["label"] !== undefined) { + // //alert.success("Changed startnode to "+ele.data()["label"]) + // ele.data("isStartNode", true) + // workflow.start = ele.id() + // throw BreakException + // return false + // } + //}) + } + setWorkflow(workflow) if (data.type === "TRIGGER") { @@ -1983,21 +1995,36 @@ const AngularWorkflow = (props) => { //var previousedgecolor = "" const animationDuration = 150 const onNodeHoverOut = (event) => { - event.target.animate({ - style: { + const nodedata = event.target.data() + var parsedStyle = { + "border-width": "1px", + 'font-size': '18px', + } + + if ((nodedata.app_name === "Testing" || nodedata.app_name === "Shuffle Tools") && !nodedata.isStartNode) { + parsedStyle = { "border-width": "1px", + 'font-size': '0px', } + } + + event.target.animate({ + style: parsedStyle, }, { duration: animationDuration, }) } const onNodeHover = (event) => { + const parsedStyle = { + "border-width": "7px", + "border-opacity": ".7", + 'font-size': '25px', + 'color': 'white', + } + event.target.animate({ - style: { - "border-width": "7px", - "border-opacity": ".7", - } + style: parsedStyle }, { duration: animationDuration, }) @@ -2598,7 +2625,7 @@ const AngularWorkflow = (props) => { "description": "Add your email provider", "trigger_type": "EMAIL", "errors": null, - "is_valid": (cloudSyncEnabled && !isCloud) ? true : false, + "is_valid": cloudSyncEnabled || isCloud ? true : false, "label": "Email", "environment": "cloud", "large_image": 'data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAytodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Nzg4QTJBMjVEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Nzg4QTJBMjZEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODhBMkEyM0QwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3ODhBMkEyNEQwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAgICAwICAgMDAwMEBgQEBAQECAYGBQYJCAoKCQgJCQoM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', @@ -4115,6 +4142,8 @@ const AngularWorkflow = (props) => { color="primary" onClick={() => { const redirectUri = isCloud ? "https%3A%2F%2Fshuffler.io%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister" : "http%3A%2F%2Flocalhost:5001%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister" + //const redirectUri = isCloud ? "http%3A%2F%2Flocalhost:5002%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister" : "http%3A%2F%2Flocalhost:5001%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister" + const client_id = "fd55c175-aa30-4fa6-b303-09a29fb3f750" const username = isCloud ? userdata.username : userdata.id console.log(redirectUri) @@ -4801,7 +4830,7 @@ const AngularWorkflow = (props) => { const item = splitItem[key] const curfolder = triggerFolders.find(a => a.displayName === item) if (curfolder === undefined) { - alert.error("Something went wrong with outlook folder "+item) + alert.error("Something went wrong with outlook folder: "+item) return }