Skip to content

Instantly share code, notes, and snippets.

@ariexx
Created November 19, 2024 19:41
Show Gist options
  • Save ariexx/6f448645ecadb061b1488ef433e04c87 to your computer and use it in GitHub Desktop.
Save ariexx/6f448645ecadb061b1488ef433e04c87 to your computer and use it in GitHub Desktop.
Sending email with driver configuration in go

Email Service Configuration in Go

In this article, we will explore a Go implementation for sending emails using different drivers (AWS SES and SMTP). We will discuss the patterns used, how to use the implementation, and how to test it.

Patterns Used

1. Builder Pattern

The builder pattern is used to create different configurations for sending emails. Depending on the environment configuration, the appropriate email driver (AWS SES or SMTP) is instantiated.

2. Interface Segregation

Interfaces are used to define contracts for sending emails. This allows for flexibility and easy switching between different email drivers.

3. Dependency Injection

Configuration values are injected into the structs, making the code more modular and testable.

Code Explanation

Configuration Structs

We have two main configuration structs: AwsConfig and SmtpConfig.

type AwsConfig struct {
	AccessKeyID     string
	SecretAccessKey string
	Region          string
	Session         *session.Session
}

type SmtpConfig struct {
	Host     string
	Port     int
	Username string
	Password string
}

Creating Configurations

The NewAwsConfig and NewSmtpConfig functions create instances of these configurations using values from the environment.

func NewAwsConfig(configuration ViperConfig) *AwsConfig {
	return &AwsConfig{
		AccessKeyID:     configuration.GetEnv("AWS_ACCESS_KEY"),
		SecretAccessKey: configuration.GetEnv("AWS_SECRET_KEY"),
		Region:          configuration.GetEnv("AWS_REGION"),
	}
}

func NewSmtpConfig(configuration ViperConfig) *SmtpConfig {
	portInt, _ := strconv.ParseInt(configuration.GetEnv("SMTP_PORT"), 10, 32)
	return &SmtpConfig{
		Host:     configuration.GetEnv("SMTP_HOST"),
		Port:     int(portInt),
		Username: configuration.GetEnv("SMTP_USERNAME"),
		Password: configuration.GetEnv("SMTP_PASSWORD"),
	}
}

Sending Emails

Both AwsConfig and SmtpConfig implement the SendEmail method to send emails using their respective drivers.

AWS SES

func (c *AwsConfig) SendEmail(to string, subject string, body string) error {
	if err := c.validateCredentials(); err != nil {
		return err
	}

	if c.Session == nil {
		return errors.New("AWS session not set")
	}

	svc := ses.New(c.Session)
	input := c.createSendEmailInput(to, subject, body)

	response, err := svc.SendEmail(input)
	if err != nil {
		c.logSendEmailError(to, subject, err)
		return err
	}

	log.Printf("Email: %s - Subject: %s - MessageID: %s", to, subject, response.String())
	return nil
}

SMTP

func (c *SmtpConfig) SendEmail(to string, subject string, body string) error {
	gm := gomail.NewMessage()
	gm.SetAddressHeader("From", "[email protected]", "GICTrade")
	gm.SetHeader("To", to)
	gm.SetHeader("Subject", subject)
	gm.SetBody("text/html", body)

	dialer := gomail.NewDialer(c.Host, c.Port, c.Username, c.Password)
	if err := dialer.DialAndSend(gm); err != nil {
		return err
	}

	return nil
}

Builder Pattern for Email Configuration

The EmailConfig struct uses the builder pattern to create the appropriate email configuration based on the environment.

type EmailConfig struct {
	AwsConfig     *AwsConfig
	SmtpConfig    *SmtpConfig
	configuration ViperConfig
}

func NewEmailConfig() *EmailConfig {
	configuration := NewViperConfig()
	driver := configuration.GetEnv("EMAIL_DRIVER")
	switch driver {
	case "aws":
		return &EmailConfig{
			AwsConfig:     NewAwsConfig(configuration),
			configuration: configuration,
		}
	case "smtp":
		return &EmailConfig{
			SmtpConfig:    NewSmtpConfig(configuration),
			configuration: configuration,
		}
	default:
		panic("Email driver not set")
	}
}

Sending Emails Using the Configured Driver

The SendEmail method in EmailConfig sends an email using the configured driver.

func (c *EmailConfig) SendEmail(to string, subject string, body string) error {
	if c.AwsConfig != nil {
		if err := c.AwsConfig.SetNewSession(c.configuration); err != nil {
			log.Println("Error setting new session", err)
			return err
		}
		return c.AwsConfig.SendEmail(to, subject, body)
	}

	if c.SmtpConfig != nil {
		return c.SmtpConfig.SendEmail(to, subject, body)
	}

	return errors.New("Email driver not set")
}

How to Use

  1. Set Environment Variables: Ensure that the necessary environment variables are set for your chosen email driver (AWS SES or SMTP).

  2. Instantiate EmailConfig: Create an instance of EmailConfig using the NewEmailConfig function.

  3. Send Email: Use the SendEmail method to send an email.

func main() {
	emailConfig := NewEmailConfig()
	err := emailConfig.SendEmail("[email protected]", "Test Subject", "Test Body")
	if err != nil {
		log.Fatalf("Failed to send email: %v", err)
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment