Skip to content

Commit

Permalink
Improve Error Handling (#8)
Browse files Browse the repository at this point in the history
* Delegate error handling to caller

* Temp solution for email issues

* Add cleaner error messages
  • Loading branch information
kkoutsilis authored Jan 13, 2024
1 parent b7ca680 commit e372d7e
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ export [email protected]
export EMAIL_USER=””
export EMAIL_PASSWORD=””
```
You can access the MailHog UI at `localhost:8025`
61 changes: 41 additions & 20 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ package cmd

import (
"encoding/json"
"errors"
"fmt"
"html/template"
"log"
"math/rand"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/spf13/cobra"

"github.com/pkg/errors"
"gopkg.in/gomail.v2"
)

var version string = "0.0.1"
var version string = "0.1.0"

type Data struct {
Name string `json:"name"`
Expand Down Expand Up @@ -66,13 +67,13 @@ func sendEmail(to, subject, body string) error {

port, err := strconv.Atoi(strPort)
if err != nil {
return err
return errors.Wrap(err, "error parsing email server port")
}

d := gomail.NewDialer(host, port, user, password)
s, err := d.Dial()
if err != nil {
return err
return errors.Wrap(err, "error dialing email server")
}

m := gomail.NewMessage()
Expand All @@ -84,36 +85,44 @@ func sendEmail(to, subject, body string) error {
return gomail.Send(s, m)
}

func sendEmails(matches []MatchPair) {
func sendEmails(matches []MatchPair) ([]string, error) {
// TODO: add retry logic for failed emails (maybe use a queue)
// instead of sending each email individually,
// generate all the emails and send them in bulk to avoid individual errors
// or prompt the user to retry
emailIssues := make([]string, 0, len(matches))
subject := "Your Secret Santa Match!"

templateFile := "./templates/email_template.html"
tmpl, err := template.ParseFiles(templateFile)
if err != nil {
log.Fatalf("Error parsing email template: %v", err)
return emailIssues, errors.Wrap(err, "failed to parse email template")
}

for _, match := range matches {
var emailBodyBuffer = &strings.Builder{}
err := tmpl.Execute(emailBodyBuffer, match)
if err != nil {
log.Printf("Error executing template for %s: %v", match.From.Email, err)
emailIssues = append(emailIssues, fmt.Sprintf("failed to execute template for %s: %v", match.From.Email, err))
continue

}
emailBody := emailBodyBuffer.String()

if err := sendEmail(match.From.Email, subject, emailBody); err != nil {
log.Printf("Error sending email to %s: %v", match.From.Email, err)
emailIssues = append(emailIssues, fmt.Sprintf("failed to send email to %s: %v", match.From.Email, err))
}
}
return emailIssues, nil
}

var rootCmd = &cobra.Command{
Use: "run [path]",
Short: "A cli tool that generates secret santa matches and notifies the participants by email",
ArgAliases: []string{"path"},
Version: version,
Run: func(cmd *cobra.Command, args []string) {
Use: "run [path]",
Short: "A cli tool that generates secret santa matches and notifies the participants by email",
ArgAliases: []string{"path"},
Version: version,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
var filePath string
if len(args) == 0 {
filePath = "data.json"
Expand All @@ -125,31 +134,42 @@ var rootCmd = &cobra.Command{
}

if !checkFileExists(filePath) {
log.Fatal("File ", filePath, " does not exist")
return fmt.Errorf("file %s does not exist", filePath)

}

if !checkIsJson(filePath) {
log.Fatal("File ", filePath, " is not a json file")
return fmt.Errorf("file %s is not a json file", filePath)
}

file, err := os.ReadFile(filePath)
if err != nil {
log.Fatal("Error when opening file: ", err)
return errors.Wrap(err, "error when opening file")
}

var payload []Data
err = json.Unmarshal(file, &payload)
if err != nil {
log.Fatal("Error reading file content: ", err)
return errors.Wrap(err, "error reading file content")
}

if len(payload) == 0 {
log.Fatal("File is empty")
return errors.New("file is empty")
}

matches := generateSecretSantaMatches(payload)
emailIssues, err := sendEmails(matches)
if err != nil {
return errors.Wrap(err, "error sending emails")
}
if len(emailIssues) > 0 {
for _, issue := range emailIssues {
fmt.Println(issue)
}
fmt.Println("Some emails failed to send, please check the logs for more details")
}

sendEmails(matches)
return nil
},
}

Expand All @@ -158,4 +178,5 @@ func Execute() {
if err != nil {
os.Exit(1)
}
os.Exit(0)
}
4 changes: 2 additions & 2 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestGenerateSecretSantaMachesReturnsAppropriateLenghtOfMatchPairs(t *testin
var payload []Data
payload = append(payload, Data{Name: "A", Email: "[email protected]"})
payload = append(payload, Data{Name: "B", Email: "[email protected]"})
payload = append(payload, Data{Name: "C", Email: "testb@test.org"})
payload = append(payload, Data{Name: "C", Email: "testc@test.org"})

matches := generateSecretSantaMatches(payload)

Expand All @@ -47,7 +47,7 @@ func TestCircularMatchingAlgorithmReturnsExpectedMatchPairs(t *testing.T) {
var payload []Data
payload = append(payload, Data{Name: "A", Email: "[email protected]"})
payload = append(payload, Data{Name: "B", Email: "[email protected]"})
payload = append(payload, Data{Name: "C", Email: "testb@test.org"})
payload = append(payload, Data{Name: "C", Email: "testc@test.org"})

var expected []MatchPair

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ go 1.21.4

require github.com/spf13/cobra v1.8.0

require github.com/pkg/errors v0.9.1 // indirect

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
Expand Down

0 comments on commit e372d7e

Please sign in to comment.