Skip to content

Commit

Permalink
lesson 59: Send Email in Go using Gmail
Browse files Browse the repository at this point in the history
  • Loading branch information
blessedmadukoma committed Oct 2, 2023
1 parent 72a9ac1 commit 82e1f0e
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 1 deletion.
5 changes: 4 additions & 1 deletion app.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ ACCESS_TOKEN_DURATION=15m
REFRESH_TOKEN_DURATION=24h
MIGRATION_URL=file://db/migration
ENVIRONMENT=development
REDIS_ADDRESS=0.0.0.0:6379
REDIS_ADDRESS=0.0.0.0:6379
EMAIL_SENDER_NAME=Blessed Bank
EMAIL_SENDER_ADDRESS=bmadukoma@gmail.com
EMAIL_SENDER_PASSWORD=bmadukoma
4 changes: 4 additions & 0 deletions details.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,7 @@
58. Why delay added to Asynq Tasks is good
1. This is good to allow the transaction commit into the db and the sending of email to be done.

59. Send Email in Go using Gmail
1. ran `go get github.com/jordan-wright/email`.
2. created `mail` package and `mail/sender.go` to define mailer interfaces and structs.
3. added mail credentials `app.env`, updated `config.go` to include the email settings in the struct and wrote test for `sender.go` in `sender_test.go`.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
Expand Down
66 changes: 66 additions & 0 deletions mail/sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package mail

import (
"fmt"
"net/smtp"

"github.com/jordan-wright/email"
)

const (
smtpAuthAddress = "smtp.gmail.com"
smtpServerAddress = "smtp.gmail.com:587"
)

type EmailSender interface {
SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error
}

type GmailSender struct {
name string
fromEmailAddress string
fromEmailPassword string
}

func NewGmailSender(name, fromEmailAddress, fromEmailPassword string) EmailSender {
return &GmailSender{
name: name,
fromEmailAddress: fromEmailAddress,
fromEmailPassword: fromEmailPassword,
}
}

func (sender *GmailSender) SendEmail(
subject string,
content string,
to []string,
cc []string,
bcc []string,
attachFiles []string,
) error {
e := email.NewEmail()
e.From = fmt.Sprintf("%s <%s>", sender.name, sender.fromEmailAddress)
e.Subject = subject
e.HTML = []byte(content)
e.To = to
e.Cc = cc
e.Bcc = bcc

for _, file := range attachFiles {
_, err := e.AttachFile(file)
if err != nil {
return fmt.Errorf("failed to attach file - %s: %w", file, err)
}
}

smtpAuth := smtp.PlainAuth("", sender.fromEmailAddress, sender.fromEmailPassword, smtpAuthAddress)

return e.Send(smtpServerAddress, smtpAuth)
}
28 changes: 28 additions & 0 deletions mail/sender_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package mail

import (
"testing"

"github.com/blessedmadukoma/go-simple-bank/util"
"github.com/stretchr/testify/require"
)

func TestSendEmailWithGmail(t *testing.T) {
config, err := util.LoadConfig("../")
require.NoError(t, err)

sender := NewGmailSender(config.EmailSenderName, config.EmailSenderAddress, config.EmailSenderPassword)

subject := "Hello from Go!"
content := `
<h1>This is a test email from Go!</h1>
<p>Feel free to delete this email.</p>
`

to := []string{"bmadukoma@gmail.com"}
files := []string{"../go.mod"}

err = sender.SendEmail(subject, content, to, nil, nil, files)
require.NoError(t, err)

}
3 changes: 3 additions & 0 deletions util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type Config struct {
GRPCServerAddress string `mapstructure:"GRPC_SERVER_ADDRESS"`
AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"`
RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"`
EmailSenderName string `mapstructure:"EMAIL_SENDER_NAME"`
EmailSenderAddress string `mapstructure:"EMAIL_SENDER_ADDRESS"`
EmailSenderPassword string `mapstructure:"EMAIL_SENDER_PASSWORD"`
}

// LoadConfig reads configuration from file or env variables
Expand Down

0 comments on commit 82e1f0e

Please sign in to comment.