Skip to content

Commit

Permalink
feat(MicrosoftAPI): Action received email -> Reaction mark as read
Browse files Browse the repository at this point in the history
  • Loading branch information
Djangss committed Dec 10, 2024
1 parent 828876d commit 59bc5f1
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 69 deletions.
2 changes: 1 addition & 1 deletion server/base_consumer/consts/const.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package consts

const EnvFile = ".env"
const EnvFileDirectory = "."
const EnvFileDirectory = ".."

const MessageQueue = "microsoft_queue"
const ExchangeName = "api_service_exchange"
Expand Down
89 changes: 54 additions & 35 deletions server/base_consumer/handlers/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,97 @@ package handlers
import (
"fmt"
"log"
"service/microsoft"
"service/models"
"service/utils"
)

func GetWorkflowAndToken(workflowEventID uint) (*models.Workflow, *models.User, string, error) {
func GetWorkflowAndToken(workflowEventID uint) (*models.Workflow, *models.User, *models.Event, string, error) {
type Result struct {
WorkflowID uint
WorkflowName string
UserID uint
UserEmail string
MicrosoftToken string
EventID uint
EventName string
EventType string
}

var result Result

query := `
SELECT
workflows.id AS workflow_id,
workflows.name AS workflow_name,
users.id AS user_id,
users.email AS user_email,
tokens.value AS microsoft_token
FROM
workflow_events
JOIN
workflows ON workflows.id = workflow_events.workflow_id
JOIN
users ON users.id = workflows.user_id
JOIN
tokens ON tokens.user_id = users.id
JOIN
services ON services.id = tokens.service_id
WHERE
workflow_events.id = ? AND services.name = 'microsoft'
LIMIT 1
`
SELECT
workflows.id AS workflow_id,
workflows.name AS workflow_name,
users.id AS user_id,
users.email AS user_email,
tokens.value AS microsoft_token,
events.id AS event_id,
events.name AS event_name,
events.type AS event_type
FROM
workflow_events
JOIN
workflows ON workflows.id = workflow_events.workflow_id
JOIN
users ON users.id = workflows.user_id
JOIN
tokens ON tokens.user_id = users.id
JOIN
services ON services.id = tokens.service_id
JOIN
events ON events.id = workflow_events.event_id
WHERE
workflow_events.id = ? AND services.name = 'microsoft'
LIMIT 1
`

var result Result
err := utils.DB.Raw(query, workflowEventID).Scan(&result).Error
if err != nil {
return nil, nil, "", fmt.Errorf("failed to retrieve data: %w", err)
return nil, nil, nil, "", fmt.Errorf("failed to retrieve data: %w", err)
}

var workflow = &models.Workflow{}
var user = &models.User{}
utils.DB.First(workflow, result.WorkflowID)
utils.DB.First(user, result.UserID)
var workflow models.Workflow
var user models.User
var event models.Event

return workflow, user, result.MicrosoftToken, nil
if err := utils.DB.First(&workflow, result.WorkflowID).Error; err != nil {
return nil, nil, nil, "", err
}
if err := utils.DB.First(&user, result.UserID).Error; err != nil {
return nil, nil, nil, "", err
}
if err := utils.DB.First(&event, result.EventID).Error; err != nil {
return nil, nil, nil, "", err
}

return &workflow, &user, &event, result.MicrosoftToken, nil
}

func HandleWorkflowEvent(workflowEvent models.WorkflowEvent) {
log.Printf("Processing workflow event: %+v", workflowEvent)

var tmpWorkflow models.Workflow
err := utils.DB.First(&tmpWorkflow, workflowEvent.WorkflowID).Error
if err != nil {
if err := utils.DB.First(&tmpWorkflow, workflowEvent.WorkflowID).Error; err != nil {
log.Printf("Workflow not found for WorkflowID: %d", workflowEvent.WorkflowID)
return
}
workflow, user, microsoftToken, err := GetWorkflowAndToken(workflowEvent.ID)

workflow, user, event, microsoftToken, err := GetWorkflowAndToken(workflowEvent.ID)
if err != nil {
log.Printf("Failed to fetch workflow, user, or token: %v", err)
return
}

log.Printf("Fetched Workflow: %s (ID: %d), User: %s (ID: %d), Microsoft Token: %s",
workflow.Name, workflow.ID, user.Email, microsoftToken)
microsoftAPI, err := utils.InitMicrosoftAPI(microsoftToken)
log.Printf("Fetched Workflow: %s (ID: %d), User: %s (ID: %d), Event: %s (%s), Microsoft Token: %s",
workflow.Name, workflow.ID, user.Email, user.ID, event.Name, event.Type, microsoftToken)

microsoftAPI, err := microsoft.InitMicrosoftAPI(microsoftToken)
if err != nil {
log.Printf("Failed to initialize Microsoft API: %v", err)
return
}

err = microsoftAPI.ProcessWorkflowEvent(workflow.ID, workflowEvent.EventID)
if err != nil {
log.Printf("Failed to process workflow event: %v", err)
Expand Down
59 changes: 59 additions & 0 deletions server/base_consumer/microsoft/microsoft.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package microsoft

import (
"fmt"
"log"
"service/models"
"service/utils"
)

type APIClient struct {
Token string
}

func (c *APIClient) ValidateToken() error {
// Add token validation logic here
return nil
}

func (c *APIClient) ProcessWorkflowEvent(workflowID, eventID uint) error {
var event models.Event
if err := utils.DB.First(&event, eventID).Error; err != nil {
return fmt.Errorf("event not found: %w", err)
}

log.Printf("Processing workflow event in Microsoft API: workflowID=%d, eventID=%d, eventName=%s, eventType=%s",
workflowID, eventID, event.Name, event.Type)

switch event.Type {
case models.ActionEventType:

switch event.Name {
case "New Email Received":
return c.HandleNewEmailReceived(workflowID)
/*case "File uploaded":
return c.HandleFileUploaded(workflowID)*/
}

case models.ReactionEventType:
/*if event.Name == "Send an Email" {
return c.SendEmail(workflowID, eventID)
} else if event.Name == "Upload a file" {
return c.UploadFileToOneDrive(workflowID, eventID)
}*/
}

return fmt.Errorf("unhandled event type: %s (%s)", event.Type, event.Name)
}

func InitMicrosoftAPI(token string) (*APIClient, error) {
client := &APIClient{
Token: token,
}

if err := client.ValidateToken(); err != nil {
return nil, fmt.Errorf("invalid token: %w", err)
}

return client, nil
}
98 changes: 98 additions & 0 deletions server/base_consumer/microsoft/receiveMail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package microsoft

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)

type MailMessage struct {
ID string `json:"id"`
Subject string `json:"subject"`
ReceivedDateTime string `json:"receivedDateTime"`
IsRead bool `json:"isRead"`
From struct {
EmailAddress struct {
Name string `json:"name"`
Address string `json:"address"`
} `json:"emailAddress"`
} `json:"from"`
}

type MailListResponse struct {
Value []MailMessage `json:"value"`
}

func (c *APIClient) HandleNewEmailReceived(workflowID uint) error {
endpoint := "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages?$filter=isRead%20eq%20false&$top=10&$orderby=receivedDateTime%20desc"

req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+c.Token)
req.Header.Set("Accept", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to retrieve emails: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
bodyBytes, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("non-200 response: %d, body: %s", resp.StatusCode, string(bodyBytes))
}

var mailList MailListResponse
if err := json.NewDecoder(resp.Body).Decode(&mailList); err != nil {
return fmt.Errorf("failed to decode mail response: %w", err)
}

if len(mailList.Value) == 0 {
log.Println("No new unread emails found.")
return nil
}

for _, message := range mailList.Value {
log.Printf("New Email Received: Subject=%q, From=%q, ReceivedAt=%s",
message.Subject, message.From.EmailAddress.Address, message.ReceivedDateTime)

if err := c.MarkEmailAsRead(message.ID); err != nil {
log.Printf("Failed to mark email %s as read: %v", message.ID, err)
}
}

return nil
}

func (c *APIClient) MarkEmailAsRead(messageID string) error {
endpoint := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/messages/%s", messageID)
reqBody := `{"isRead": true}`
req, err := http.NewRequest("PATCH", endpoint, strings.NewReader(reqBody))
if err != nil {
return fmt.Errorf("failed to create patch request: %w", err)
}

req.Header.Set("Authorization", "Bearer "+c.Token)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send PATCH request: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode < 200 || resp.StatusCode >= 300 {
bodyBytes, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("failed to mark email as read, status: %d, body: %s", resp.StatusCode, string(bodyBytes))
}

log.Printf("Email %s marked as read.", messageID)
return nil
}
33 changes: 0 additions & 33 deletions server/base_consumer/utils/microsoft.go

This file was deleted.

0 comments on commit 59bc5f1

Please sign in to comment.