I send Weekly Email Reminders

Therefore I am

main

sendEmailToTenantUsers

sendEmailToUser

u

u

u

u

u

u

u

u

u

u

u

t

t

t

sendEmailToUser

sendEmailToTenantUsers

sendEmailToUser

sendEmailToUser

u

u

u

u

u

u

u

u

u

2 Tenant Workers

2 User Workers

sendCmd = &cobra.Command{
	Use:   "send",
	Short: "Sends weekly email reminders to students",
	Long: `Executing this command will cause all students of all tenants to
		receive the weekly email reminder if they have incomplete courses. If
		a command line flag of an array of tenant ids is passed, only those
		tenants will be processed
	`,
	Run: sendRun,
}


func init() {
  rootCmd.AddCommand(sendCmd)
  sendCmd.Flags().StringSliceVarP(&tenantIDs, "tenants", "t", make([]string, 0), "Ids of the tenants to process")
}

Send Command Definition

// sendRun initializes the DB connection and the repositories used by this package.
func sendRun(cmd *cobra.Command, args []string) {
  cfg = unmarshalConfig()

  thinkificDB, err := pg.NewDBConnection(cfg.Database.ThinkificDBURL)
  if err != nil {
    logger.Panicf(err)
  }
  defer ioutil.SafeClose(thinkificDB)
  tenantRepository = weeklyemailreminders.NewTenantRepository(thinkificDB)
  userRepository = weeklyemailreminders.NewUserRepository(thinkificDB)

  reminderDB, err := pg.NewDBConnection(cfg.Database.WERDBURL)
  if err != nil {
    logger.Panicf(err)
  }
  defer ioutil.SafeClose(reminderDB)
  taskRepository = weeklyemailreminders.NewTaskRepository(reminderDB)

  thinkificapp.SetHost(cfg.ThinkificApp.Host)
  mailgun.EnableDebugMode(cfg.Mailgun.DebugEnabled)

  defer logTimeTaken(time.Now())
  if err := executeTask(); err != nil {
    logger.Panic(err)
  }
}

sendRun

// executeTask ensures that there is no other weekly reminder email
// task running already, starts the task in the task repository, 
// and starts the sending of the emails.
func executeTask() error {
  isRunning, err := taskRepository.IsRunning()
  switch {
  case err != nil:
    return err
  case isRunning:
    return errors.New("Another weekly email reminder task is currently running. Exiting.")
  }

  t, err := taskRepository.Start()
  if err != nil {
    return errors.Wrap(err, "error starting task")
  }

  defer func() {
    if err := taskRepository.Complete(t); err != nil {
      logger.Errorf(err, "weekly email reminder task finished successfully, but failed to clear the repository")
    }
  }()

  return sendEmails(t.ID)
}

executeTask

// sendEmailsToTenantUsers fetches all users of the tenant and
// starts as many go routines as MaxUserWorkers to handle each 
// user of the tenant.
func sendEmailsToTenantUsers(ch chan *tenant.Tenant, taskID string) {
  for t := range ch {
    userChan := make(chan *user.User, cfg.Service.MaxUserWorkers)
    wg := sync.WaitGroup{}
    wg.Add(cfg.Service.MaxUserWorkers)

    et, err := parseTenantEmailTemplate(t)
    if err != nil {
      logger.Error(err)
      continue
    }

    mail := newMailer(t)
    for i := 0; i < cfg.Service.MaxUserWorkers; i++ {
      go func() {
        defer wg.Done()
        sendEmailToUser(mail, userChan, t, et)
      }()
    }

    err = fetchUsers(userChan, t.ID)
    close(userChan)
    if err != nil {
      logger.Error(err)
    }
    wg.Wait()
  }
}

sendEmailsToTenantUsers

// sendEmailToUser renders the email template for the user and sends it the email.
func sendEmailToUser(mail mailer.Mailer, ch chan *user.User, t *tenant.Tenant, et *emailTemplate) {
  for u := range ch {
    subject, body, err := et.render(t, u)
    if err != nil {
      logger.Error(err)
      continue
    }

    msg := newEmailMsg(mail, t, u, subject, body)
    emailID, err := mail.Send(msg)
    if err != nil {
      logger.Errorf(err, "Failed to send email to Tenant %s's User %s", t.ID, u.ID)
      continue
    }

    sentCount++
    logger.Printf("Email %s sent to Tenant %s's User %s successfully", emailID, t.ID, u.ID)
  }
}
sendEmailToUser
Made with Slides.com