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