github.com/blend/go-sdk@v1.20240719.1/email/message.go (about)

     1  /*
     2  
     3  Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package email
     9  
    10  import (
    11  	"strings"
    12  
    13  	"github.com/blend/go-sdk/ex"
    14  )
    15  
    16  // Errors
    17  const (
    18  	ErrMessageFieldUnset    ex.Class = "email; message required field unset"
    19  	ErrMessageFieldNewlines ex.Class = "email; message field contains newlines"
    20  )
    21  
    22  // MergeMessages merges a given set of messages, such that
    23  // messages that appear last override the options in the output
    24  // message provided those fields are set.
    25  //
    26  // If message A has just the `To` field set, and message B
    27  // has just the `Subject`field set, and message C also has the
    28  // `To` field set, the result of:
    29  //
    30  //	MergeMessages(a,b,c)
    31  //
    32  // Will be `To` of C, and `Subject` of B, with C overwriting
    33  // the `To` of A.
    34  func MergeMessages(messages ...Message) (output Message) {
    35  	for _, message := range messages {
    36  		if message.From != "" {
    37  			output.From = message.From
    38  		}
    39  		if len(message.To) > 0 {
    40  			output.To = message.To
    41  		}
    42  		if len(message.CC) > 0 {
    43  			output.To = message.CC
    44  		}
    45  		if len(message.BCC) > 0 {
    46  			output.To = message.BCC
    47  		}
    48  		if message.Subject != "" {
    49  			output.Subject = message.Subject
    50  		}
    51  		if message.TextBody != "" {
    52  			output.TextBody = message.TextBody
    53  		}
    54  		if message.HTMLBody != "" {
    55  			output.HTMLBody = message.HTMLBody
    56  		}
    57  	}
    58  	return
    59  }
    60  
    61  // Message is a message to send via. ses.
    62  type Message struct {
    63  	From     string   `json:"from" yaml:"from"`
    64  	To       []string `json:"to" yaml:"to"`
    65  	CC       []string `json:"cc" yaml:"cc"`
    66  	BCC      []string `json:"bcc" yaml:"bcc"`
    67  	Subject  string   `json:"subject" yaml:"subject"`
    68  	TextBody string   `json:"textBody" yaml:"textBody"`
    69  	HTMLBody string   `json:"htmlBody" yaml:"htmlBody"`
    70  }
    71  
    72  // IsZero returns if the object is set or not.
    73  func (m Message) IsZero() bool {
    74  	return len(m.To) == 0
    75  }
    76  
    77  // Validate checks that a message can be sent.
    78  func (m Message) Validate() error {
    79  	if m.From == "" {
    80  		return ex.New(ErrMessageFieldUnset, ex.OptMessage("field: from"))
    81  	}
    82  	if strings.ContainsAny(m.From, "\r\n") {
    83  		return ex.New(ErrMessageFieldNewlines, ex.OptMessage("field: from"))
    84  	}
    85  	if len(m.To) == 0 {
    86  		return ex.New(ErrMessageFieldUnset, ex.OptMessage("field: to"))
    87  	}
    88  	for index, to := range m.To {
    89  		if strings.ContainsAny(to, "\r\n") {
    90  			return ex.New(ErrMessageFieldNewlines, ex.OptMessagef("field: to[%d]", index))
    91  		}
    92  	}
    93  	for index, cc := range m.CC {
    94  		if strings.ContainsAny(cc, "\r\n") {
    95  			return ex.New(ErrMessageFieldNewlines, ex.OptMessagef("field: cc[%d]", index))
    96  		}
    97  	}
    98  	for index, bcc := range m.BCC {
    99  		if strings.ContainsAny(bcc, "\r\n") {
   100  			return ex.New(ErrMessageFieldNewlines, ex.OptMessagef("field: bcc[%d]", index))
   101  		}
   102  	}
   103  	if strings.ContainsAny(m.Subject, "\r\n") {
   104  		return ex.New(ErrMessageFieldNewlines, ex.OptMessage("field: subject"))
   105  	}
   106  	if len(m.TextBody) == 0 && len(m.HTMLBody) == 0 {
   107  		return ex.New(ErrMessageFieldUnset, ex.OptMessage("fields: textBody and htmlBody"))
   108  	}
   109  	return nil
   110  }