github.com/aldelo/common@v1.5.1/wrapper/ses/ses.go (about) 1 package ses 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 // ================================================================================================================= 20 // AWS CREDENTIAL: 21 // use $> aws configure (to set aws access key and secret to target machine) 22 // Store AWS Access ID and Secret Key into Default Profile Using '$ aws configure' cli 23 // 24 // To Install & Setup AWS CLI on Host: 25 // 1) https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html 26 // On Ubuntu, if host does not have zip and unzip: 27 // $> sudo apt install zip 28 // $> sudo apt install unzip 29 // On Ubuntu, to install AWS CLI v2: 30 // $> curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 31 // $> unzip awscliv2.zip 32 // $> sudo ./aws/install 33 // 2) $> aws configure set region awsRegionName --profile default 34 // 3) $> aws configure 35 // follow prompts to enter Access ID and Secret Key 36 // 37 // AWS Region Name Reference: 38 // us-west-2, us-east-1, ap-northeast-1, etc 39 // See: https://docs.aws.amazon.com/general/latest/gr/rande.html 40 // ================================================================================================================= 41 42 import ( 43 "bytes" 44 "context" 45 "encoding/base64" 46 "errors" 47 "fmt" 48 util "github.com/aldelo/common" 49 awshttp2 "github.com/aldelo/common/wrapper/aws" 50 "github.com/aldelo/common/wrapper/aws/awsregion" 51 "github.com/aldelo/common/wrapper/xray" 52 "github.com/aws/aws-sdk-go/aws" 53 "github.com/aws/aws-sdk-go/aws/awserr" 54 "github.com/aws/aws-sdk-go/aws/session" 55 "github.com/aws/aws-sdk-go/service/ses" 56 awsxray "github.com/aws/aws-xray-sdk-go/xray" 57 "mime/multipart" 58 "net/http" 59 "net/textproto" 60 "path/filepath" 61 "strings" 62 "time" 63 ) 64 65 // ================================================================================================================ 66 // STRUCTS 67 // ================================================================================================================ 68 69 // SES struct encapsulates the AWS SES access functionality 70 type SES struct { 71 // define the AWS region that SES is located at 72 AwsRegion awsregion.AWSRegion 73 74 // custom http2 client options 75 HttpOptions *awshttp2.HttpClientSettings 76 77 // define configuration set to use if any 78 ConfigurationSetName string 79 80 // store ses client object 81 sesClient *ses.SES 82 83 _parentSegment *xray.XRayParentSegment 84 } 85 86 // Email struct encapsulates the email message definition to send via AWS SES 87 type Email struct { 88 // required 89 From string 90 91 // at least one element is required 92 To []string 93 94 // optional 95 CC []string 96 97 // optional 98 BCC []string 99 100 // required 101 Subject string 102 103 // either BodyHtml or BodyText is required 104 BodyHtml string 105 BodyText string 106 107 // additional config info 108 ReturnPath string 109 ReplyTo []string 110 111 // defaults to UTF-8 if left blank 112 Charset string 113 } 114 115 // SendQuota struct encapsulates the ses send quota definition 116 type SendQuota struct { 117 Max24HourSendLimit int 118 MaxPerSecondSendLimit int 119 SentLast24Hours int 120 } 121 122 // EmailTarget defines a single email target contact for use with SendTemplateEmail or SendBulkTemplateEmail methods 123 type EmailTarget struct { 124 // at least one element is required 125 To []string 126 127 // optional 128 CC []string 129 130 // optional 131 BCC []string 132 133 // required - template data in json 134 TemplateDataJson string 135 } 136 137 // BulkEmailMessageResult contains result of a bulk email 138 type BulkEmailMessageResult struct { 139 MessageId string 140 Status string 141 ErrorInfo string 142 Success bool 143 } 144 145 // ================================================================================================================ 146 // STRUCTS FUNCTIONS 147 // ================================================================================================================ 148 149 // ---------------------------------------------------------------------------------------------------------------- 150 // utility functions 151 // ---------------------------------------------------------------------------------------------------------------- 152 153 // Connect will establish a connection to the SES service 154 func (s *SES) Connect(parentSegment ...*xray.XRayParentSegment) (err error) { 155 if xray.XRayServiceOn() { 156 if len(parentSegment) > 0 { 157 s._parentSegment = parentSegment[0] 158 } 159 160 seg := xray.NewSegment("SES-Connect", s._parentSegment) 161 defer seg.Close() 162 defer func() { 163 _ = seg.Seg.AddMetadata("SES-AWS-Region", s.AwsRegion) 164 165 if err != nil { 166 _ = seg.Seg.AddError(err) 167 } 168 }() 169 170 err = s.connectInternal() 171 172 if err == nil { 173 awsxray.AWS(s.sesClient.Client) 174 } 175 176 return err 177 } else { 178 return s.connectInternal() 179 } 180 } 181 182 // connectInternal will establish a connection to the SES service 183 func (s *SES) connectInternal() error { 184 // clean up prior object 185 s.sesClient = nil 186 187 if !s.AwsRegion.Valid() || s.AwsRegion == awsregion.UNKNOWN { 188 return errors.New("Connect To SES Failed: (AWS Session Error) " + "Region is Required") 189 } 190 191 // create custom http2 client if needed 192 var httpCli *http.Client 193 var httpErr error 194 195 if s.HttpOptions == nil { 196 s.HttpOptions = new(awshttp2.HttpClientSettings) 197 } 198 199 // use custom http2 client 200 h2 := &awshttp2.AwsHttp2Client{ 201 Options: s.HttpOptions, 202 } 203 204 if httpCli, httpErr = h2.NewHttp2Client(); httpErr != nil { 205 return errors.New("Connect to SES Failed: (AWS Session Error) " + "Create Custom Http2 Client Errored = " + httpErr.Error()) 206 } 207 208 // establish aws session connection and keep session object in struct 209 if sess, err := session.NewSession( 210 &aws.Config{ 211 Region: aws.String(s.AwsRegion.Key()), 212 HTTPClient: httpCli, 213 }); err != nil { 214 // aws session error 215 return errors.New("Connect To SES Failed: (AWS Session Error) " + err.Error()) 216 } else { 217 // create cached objects for shared use 218 s.sesClient = ses.New(sess) 219 220 if s.sesClient == nil { 221 return errors.New("Connect To SES Client Failed: (New SES Client Connection) " + "Connection Object Nil") 222 } 223 224 // session stored to struct 225 return nil 226 } 227 } 228 229 // Disconnect will disjoin from aws session by clearing it 230 func (s *SES) Disconnect() { 231 s.sesClient = nil 232 } 233 234 // UpdateParentSegment updates this struct's xray parent segment, if no parent segment, set nil 235 func (s *SES) UpdateParentSegment(parentSegment *xray.XRayParentSegment) { 236 s._parentSegment = parentSegment 237 } 238 239 // ---------------------------------------------------------------------------------------------------------------- 240 // Email struct methods 241 // ---------------------------------------------------------------------------------------------------------------- 242 243 // Initial sets new email message 244 // 245 // parameters: 246 // 247 // from = email sender from address 248 // to = email sender to address 249 // subject = email subject text 250 // bodyText = email body content in text (both bodyText and bodyHtml may be set at the same time) 251 // bodyHtml = email body content in html (both bodyText and bodyHtml may be set at the same time) 252 func (e *Email) Initial(from string, to string, subject string, bodyText string, bodyHtml ...string) *Email { 253 // set fields 254 e.From = from 255 e.To = append(e.To, to) 256 e.Subject = subject 257 e.BodyText = bodyText 258 259 // set body html 260 if len(bodyHtml) > 0 { 261 e.BodyHtml = bodyHtml[0] 262 } 263 264 // defaults 265 e.ReturnPath = from 266 267 if util.LenTrim(from) > 0 { 268 e.ReplyTo = append(e.ReplyTo, from) 269 } 270 271 e.Charset = "UTF-8" 272 273 return e 274 } 275 276 // SetTo adds additional 'To' addresses to email 277 func (e *Email) SetTo(toAddress ...string) *Email { 278 if len(toAddress) > 0 { 279 for _, v := range toAddress { 280 e.To = append(e.To, v) 281 } 282 } 283 284 return e 285 } 286 287 // SetCC adds 'CC' addresses to email 288 func (e *Email) SetCC(ccAddress ...string) *Email { 289 if len(ccAddress) > 0 { 290 for _, v := range ccAddress { 291 e.CC = append(e.CC, v) 292 } 293 } 294 295 return e 296 } 297 298 // SetBCC adds 'BCC' addresses to email 299 func (e *Email) SetBCC(bccAddress ...string) *Email { 300 if len(bccAddress) > 0 { 301 for _, v := range bccAddress { 302 e.BCC = append(e.BCC, v) 303 } 304 } 305 306 return e 307 } 308 309 // SetReplyTo adds 'Reply-To' addresses to email 310 func (e *Email) SetReplyTo(replyToAddress ...string) *Email { 311 if len(replyToAddress) > 0 { 312 for _, v := range replyToAddress { 313 e.ReplyTo = append(e.ReplyTo, v) 314 } 315 } 316 317 return e 318 } 319 320 // validateEmailFields will verify if the email fields are satisfied 321 func (e *Email) validateEmailFields() error { 322 if util.LenTrim(e.From) <= 0 { 323 return errors.New("Verify Email Fields Failed: " + "From Address is Required") 324 } 325 326 if len(e.To) <= 0 { 327 return errors.New("Verify Email Fields Failed: " + "To Address is Required") 328 } 329 330 if util.LenTrim(e.Subject) <= 0 { 331 return errors.New("Verify Email Fields Failed: " + "Subject is Required") 332 } 333 334 if util.LenTrim(e.BodyHtml) <= 0 && util.LenTrim(e.BodyText) <= 0 { 335 return errors.New("Verify Email Fields Failed: " + "Body (Html or Text) is Required") 336 } 337 338 if util.LenTrim(e.Charset) <= 0 { 339 e.Charset = "UTF-8" 340 } 341 342 // validate successful 343 return nil 344 } 345 346 // GenerateSendEmailInput will create the SendEmailInput object from the struct fields 347 func (e *Email) GenerateSendEmailInput() (*ses.SendEmailInput, error) { 348 // validate 349 if err := e.validateEmailFields(); err != nil { 350 return nil, err 351 } 352 353 // assemble SendEmailInput object 354 input := &ses.SendEmailInput{ 355 Source: aws.String(e.From), 356 Destination: &ses.Destination{ 357 ToAddresses: aws.StringSlice(e.To), 358 }, 359 Message: &ses.Message{ 360 Subject: &ses.Content{ 361 Data: aws.String(e.Subject), 362 Charset: aws.String(e.Charset), 363 }, 364 }, 365 } 366 367 // set cc and bcc if applicable 368 if len(e.CC) > 0 { 369 if input.Destination == nil { 370 input.Destination = new(ses.Destination) 371 } 372 373 input.Destination.CcAddresses = aws.StringSlice(e.CC) 374 } 375 376 if len(e.BCC) > 0 { 377 if input.Destination == nil { 378 input.Destination = new(ses.Destination) 379 } 380 381 input.Destination.BccAddresses = aws.StringSlice(e.BCC) 382 } 383 384 // set message body 385 if util.LenTrim(e.BodyHtml) > 0 { 386 if input.Message == nil { 387 input.Message = new(ses.Message) 388 } 389 390 if input.Message.Body == nil { 391 input.Message.Body = new(ses.Body) 392 } 393 394 if input.Message.Body.Html == nil { 395 input.Message.Body.Html = new(ses.Content) 396 } 397 398 input.Message.Body.Html.Data = aws.String(e.BodyHtml) 399 input.Message.Body.Html.Charset = aws.String(e.Charset) 400 } 401 402 if util.LenTrim(e.BodyText) > 0 { 403 if input.Message == nil { 404 input.Message = new(ses.Message) 405 } 406 407 if input.Message.Body == nil { 408 input.Message.Body = new(ses.Body) 409 } 410 411 if input.Message.Body.Text == nil { 412 input.Message.Body.Text = new(ses.Content) 413 } 414 415 input.Message.Body.Text.Data = aws.String(e.BodyText) 416 input.Message.Body.Text.Charset = aws.String(e.Charset) 417 } 418 419 if util.LenTrim(e.ReturnPath) > 0 { 420 input.ReturnPath = aws.String(e.ReturnPath) 421 } 422 423 if len(e.ReplyTo) > 0 { 424 input.ReplyToAddresses = aws.StringSlice(e.ReplyTo) 425 } 426 427 // return result 428 return input, nil 429 } 430 431 // GenerateSendRawEmailInput will create the SendRawEmailInput object from the struct fields 432 // 433 // attachmentContentType = See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types 434 func (e *Email) GenerateSendRawEmailInput(attachmentFileName string, attachmentContentType string, attachmentData []byte) (*ses.SendRawEmailInput, error) { 435 // validate 436 if err := e.validateEmailFields(); err != nil { 437 return nil, err 438 } 439 440 buf := new(bytes.Buffer) 441 writer := multipart.NewWriter(buf) 442 443 // email main header 444 h := make(textproto.MIMEHeader) 445 h.Set("From", e.From) 446 h.Set("To", util.SliceStringToCSVString(e.To, false)) 447 448 if len(e.CC) > 0 { 449 h.Set("CC", util.SliceStringToCSVString(e.CC, false)) 450 } 451 452 if len(e.BCC) > 0 { 453 h.Set("BCC", util.SliceStringToCSVString(e.BCC, false)) 454 } 455 456 if util.LenTrim(e.ReturnPath) > 0 { 457 h.Set("Return-Path", e.ReturnPath) 458 } 459 460 if len(e.ReplyTo) > 0 { 461 h.Set("Reply-To", e.ReplyTo[0]) 462 } 463 464 h.Set("Subject", e.Subject) 465 h.Set("Content-Language", "en-US") 466 h.Set("Content-Type", "multipart/mixed; boundary=\""+writer.Boundary()+"\"") 467 h.Set("MIME-Version", "1.0") 468 469 if _, err := writer.CreatePart(h); err != nil { 470 return nil, err 471 } 472 473 charset := "UTF-8" 474 475 if util.LenTrim(e.Charset) > 0 { 476 charset = e.Charset 477 } 478 479 // email body - plain text 480 if util.LenTrim(e.BodyText) > 0 { 481 h = make(textproto.MIMEHeader) 482 h.Set("Content-Transfer-Encoding", "quoted-printable") 483 h.Set("Content-Type", "text/plain; charset="+charset) 484 part, err := writer.CreatePart(h) 485 if err != nil { 486 return nil, err 487 } 488 489 if _, err = part.Write([]byte(e.BodyText)); err != nil { 490 return nil, err 491 } 492 } 493 494 // email body - html text 495 if util.LenTrim(e.BodyHtml) > 0 { 496 h = make(textproto.MIMEHeader) 497 h.Set("Content-Transfer-Encoding", "quoted-printable") 498 h.Set("Content-Type", "text/html; charset="+charset) 499 partH, err := writer.CreatePart(h) 500 if err != nil { 501 return nil, err 502 } 503 504 if _, err = partH.Write([]byte(e.BodyHtml)); err != nil { 505 return nil, err 506 } 507 } 508 509 // file attachment 510 if util.LenTrim(attachmentFileName) > 0 { 511 fn := filepath.Base(attachmentFileName) 512 513 h = make(textproto.MIMEHeader) 514 // h.Set("Content-Disposition", "attachment; filename="+fn) 515 h.Set("Content-Type", attachmentContentType+"; name=\""+fn+"\"") 516 h.Set("Content-Description", fn) 517 h.Set("Content-Disposition", "attachment; filename=\""+fn+"\"; size="+util.Itoa(len(attachmentData))+";") 518 h.Set("Content-Transfer-Encoding", "base64") 519 520 partA, err := writer.CreatePart(h) 521 if err != nil { 522 return nil, err 523 } 524 525 // must base64 encode first before write 526 if _, err = partA.Write([]byte(base64.StdEncoding.EncodeToString(attachmentData))); err != nil { 527 return nil, err 528 } 529 } 530 531 // clean up 532 if err := writer.Close(); err != nil { 533 return nil, err 534 } 535 536 // strip boundary line before header 537 s := buf.String() 538 539 if strings.Count(s, "\n") < 2 { 540 return nil, fmt.Errorf("Email Content Not Valid") 541 } 542 543 s = strings.SplitN(s, "\n", 2)[1] 544 545 raw := ses.RawMessage{ 546 Data: []byte(s), 547 } 548 549 // assemble SendRawEmailInput object 550 input := &ses.SendRawEmailInput{ 551 Destinations: aws.StringSlice(e.To), 552 Source: aws.String(e.From), 553 RawMessage: &raw, 554 } 555 556 // return result 557 return input, nil 558 } 559 560 // ---------------------------------------------------------------------------------------------------------------- 561 // EmailTarget struct methods 562 // ---------------------------------------------------------------------------------------------------------------- 563 564 // Initial will set template data in json, and list of toAddresses variadic 565 func (t *EmailTarget) Initial(templateDataJson string, toAddress ...string) *EmailTarget { 566 t.TemplateDataJson = templateDataJson 567 568 if len(toAddress) > 0 { 569 for _, v := range toAddress { 570 t.To = append(t.To, v) 571 } 572 } 573 574 return t 575 } 576 577 // SetCC will set list of ccAddress variadic to EmailTarget 578 func (t *EmailTarget) SetCC(ccAddress ...string) *EmailTarget { 579 if len(ccAddress) > 0 { 580 for _, v := range ccAddress { 581 t.CC = append(t.CC, v) 582 } 583 } 584 585 return t 586 } 587 588 // SetBCC will set list of bccAddress variadic to EmailTarget 589 func (t *EmailTarget) SetBCC(bccAddress ...string) *EmailTarget { 590 if len(bccAddress) > 0 { 591 for _, v := range bccAddress { 592 t.BCC = append(t.BCC, v) 593 } 594 } 595 596 return t 597 } 598 599 // ---------------------------------------------------------------------------------------------------------------- 600 // SES general methods 601 // ---------------------------------------------------------------------------------------------------------------- 602 603 // handleSESError will evaluate the error and return the revised error object 604 func (s *SES) handleSESError(err error) error { 605 if err != nil { 606 if aerr, ok := err.(awserr.Error); ok { 607 // aws error 608 return errors.New("[AWS] " + aerr.Code() + " - " + aerr.Message()) 609 } else { 610 // other error 611 return errors.New("[General] " + err.Error()) 612 } 613 } else { 614 return nil 615 } 616 } 617 618 // GetSendQuota will get the ses send quota 619 func (s *SES) GetSendQuota(timeOutDuration ...time.Duration) (sq *SendQuota, err error) { 620 segCtx := context.Background() 621 segCtxSet := false 622 623 seg := xray.NewSegmentNullable("SES-GetSendQuota", s._parentSegment) 624 625 if seg != nil { 626 segCtx = seg.Ctx 627 segCtxSet = true 628 629 defer seg.Close() 630 defer func() { 631 _ = seg.Seg.AddMetadata("SES-GetSendQuota-Result-SendQuota", sq) 632 633 if err != nil { 634 _ = seg.Seg.AddError(err) 635 } 636 }() 637 } 638 639 // validate 640 if s.sesClient == nil { 641 err = errors.New("GetSendQuota Failed: " + "SES Client is Required") 642 return nil, err 643 } 644 645 // compose input 646 input := &ses.GetSendQuotaInput{} 647 648 // get send quota from ses 649 var output *ses.GetSendQuotaOutput 650 651 if len(timeOutDuration) > 0 { 652 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 653 defer cancel() 654 655 output, err = s.sesClient.GetSendQuotaWithContext(ctx, input) 656 } else { 657 if segCtxSet { 658 output, err = s.sesClient.GetSendQuotaWithContext(segCtx, input) 659 } else { 660 output, err = s.sesClient.GetSendQuota(input) 661 } 662 } 663 664 // evaluate result 665 if err != nil { 666 err = errors.New("GetSendQuota Failed: " + err.Error()) 667 return nil, err 668 } 669 670 sq = &SendQuota{ 671 Max24HourSendLimit: util.Float64ToInt(*output.Max24HourSend), 672 MaxPerSecondSendLimit: util.Float64ToInt(*output.MaxSendRate), 673 SentLast24Hours: util.Float64ToInt(*output.SentLast24Hours), 674 } 675 676 return sq, nil 677 } 678 679 // SendEmail will send an email message out via SES 680 func (s *SES) SendEmail(email *Email, timeOutDuration ...time.Duration) (messageId string, err error) { 681 segCtx := context.Background() 682 segCtxSet := false 683 684 seg := xray.NewSegmentNullable("SES-SendEmail", s._parentSegment) 685 686 if seg != nil { 687 segCtx = seg.Ctx 688 segCtxSet = true 689 690 defer seg.Close() 691 defer func() { 692 if email != nil { 693 _ = seg.Seg.AddMetadata("SES-SendEmail-Email-From", email.From) 694 _ = seg.Seg.AddMetadata("SES-SendEmail-Email-To", email.To) 695 _ = seg.Seg.AddMetadata("SES-SendEmail-Email-Subject", email.Subject) 696 _ = seg.Seg.AddMetadata("SES-SendEmail-Result-MessageID", messageId) 697 } else { 698 _ = seg.Seg.AddMetadata("SES-SendEmail-Email-Fail", "Email Object Nil") 699 } 700 701 if err != nil { 702 _ = seg.Seg.AddError(err) 703 } 704 }() 705 } 706 707 // validate 708 if s.sesClient == nil { 709 err = errors.New("SendEmail Failed: " + "SES Client is Required") 710 return "", err 711 } 712 713 if email == nil { 714 err = errors.New("SendEmail Failed: " + "Email Object is Required") 715 return "", err 716 } 717 718 // generate email input 719 var input *ses.SendEmailInput 720 input, err = email.GenerateSendEmailInput() 721 722 if err != nil { 723 err = errors.New("SendEmail Failed: " + err.Error()) 724 return "", err 725 } 726 727 if input == nil { 728 err = errors.New("SendEmail Failed: " + "SendEmailInput is Nil") 729 return "", err 730 } 731 732 // set configurationSetName if applicable 733 if util.LenTrim(s.ConfigurationSetName) > 0 { 734 input.ConfigurationSetName = aws.String(s.ConfigurationSetName) 735 } 736 737 // send out email 738 var output *ses.SendEmailOutput 739 740 if len(timeOutDuration) > 0 { 741 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 742 defer cancel() 743 744 output, err = s.sesClient.SendEmailWithContext(ctx, input) 745 } else { 746 if segCtxSet { 747 output, err = s.sesClient.SendEmailWithContext(segCtx, input) 748 } else { 749 output, err = s.sesClient.SendEmail(input) 750 } 751 } 752 753 // evaluate output 754 if err != nil { 755 if err = s.handleSESError(err); err != nil { 756 return "", err 757 } 758 } 759 760 // email sent successfully 761 messageId = aws.StringValue(output.MessageId) 762 return messageId, nil 763 } 764 765 // SendRawEmail will send an raw email message out via SES, supporting attachments 766 // 767 // attachmentContentType = See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types 768 func (s *SES) SendRawEmail(email *Email, attachmentFileName string, attachmentContentType string, 769 timeOutDuration ...time.Duration) (messageId string, err error) { 770 segCtx := context.Background() 771 segCtxSet := false 772 773 seg := xray.NewSegmentNullable("SES-SendRawEmail", s._parentSegment) 774 775 if seg != nil { 776 segCtx = seg.Ctx 777 segCtxSet = true 778 779 defer seg.Close() 780 defer func() { 781 if email != nil { 782 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-From", email.From) 783 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-To", email.To) 784 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-Subject", email.Subject) 785 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-Attachment-FileName", attachmentFileName) 786 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-Attachment-ContentType", attachmentContentType) 787 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Result-MessageID", messageId) 788 } else { 789 _ = seg.Seg.AddMetadata("SES-SendRawEmail-Email-Fail", "Email Object Nil") 790 } 791 792 if err != nil { 793 _ = seg.Seg.AddError(err) 794 } 795 }() 796 } 797 798 // validate 799 if s.sesClient == nil { 800 err = errors.New("SendRawEmail Failed: " + "SES Client is Required") 801 return "", err 802 } 803 804 if email == nil { 805 err = errors.New("SendRawEmail Failed: " + "Email Object is Required") 806 return "", err 807 } 808 809 // load attachment info 810 attachmentData := []byte{} 811 812 if util.LenTrim(attachmentFileName) > 0 { 813 if attachmentData, err = util.FileReadBytes(attachmentFileName); err != nil { 814 err = fmt.Errorf("SendRawEmail Failed: (Load Attachment File '"+attachmentFileName+"' Error)", err) 815 return "", err 816 } else { 817 if len(attachmentData) == 0 { 818 err = fmt.Errorf("SendRawEmail Failed: ", "Attachment Data From File is Empty") 819 return "", err 820 } 821 822 if util.LenTrim(attachmentContentType) == 0 { 823 err = fmt.Errorf("SendRawEmail Failed: ", "Attachment Content-Type is Required") 824 return "", err 825 } 826 } 827 } else { 828 attachmentFileName = "" 829 attachmentContentType = "" 830 } 831 832 // generate email input 833 var input *ses.SendRawEmailInput 834 input, err = email.GenerateSendRawEmailInput(attachmentFileName, attachmentContentType, attachmentData) 835 836 if err != nil { 837 err = errors.New("SendRawEmail Failed: " + err.Error()) 838 return "", err 839 } 840 841 if input == nil { 842 err = errors.New("SendRawEmail Failed: " + "SendRawEmailInput is Nil") 843 return "", err 844 } 845 846 // set configurationSetName if applicable 847 if util.LenTrim(s.ConfigurationSetName) > 0 { 848 input.ConfigurationSetName = aws.String(s.ConfigurationSetName) 849 } 850 851 // send out email 852 var output *ses.SendRawEmailOutput 853 854 if len(timeOutDuration) > 0 { 855 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 856 defer cancel() 857 858 output, err = s.sesClient.SendRawEmailWithContext(ctx, input) 859 } else { 860 if segCtxSet { 861 output, err = s.sesClient.SendRawEmailWithContext(segCtx, input) 862 } else { 863 output, err = s.sesClient.SendRawEmail(input) 864 } 865 } 866 867 // evaluate output 868 if err != nil { 869 if err = s.handleSESError(err); err != nil { 870 return "", err 871 } 872 } 873 874 // email sent successfully 875 messageId = aws.StringValue(output.MessageId) 876 return messageId, nil 877 } 878 879 // ---------------------------------------------------------------------------------------------------------------- 880 // SES template & bulk email methods 881 // ---------------------------------------------------------------------------------------------------------------- 882 883 // CreateTemplate will create an email template for use with SendTemplateEmail and SendBulkTemplateEmail methods 884 // 885 // parameters: 886 // 1. templateName = name of the template, must be unique 887 // 2. subjectPart = subject text with tag name value replacements 888 // 3. textPart = body text with tag name value replacements 889 // 4. htmlPart = body html with tag name value replacements 890 // 5. timeOutDuration = optional time out value to use in context 891 // 892 // template tokens: 893 // 1. subjectPart, textPart, htmlPart = may contain Tag Names for personalization 894 // 2. Tag Name = using {{tagName}} within the string defined above in #1 895 // 3. example: 896 // a) Greetings, {{name}} 897 // b) <h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p> 898 // c) <html><head></head><body><h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p></body></html> 899 // 4. when sending template mail, the Tag Name values are replaced via key-Value pairs within TemplateData in SendTemplateEmail and SendBulkTemplateEmail methods: 900 // a) { "name":"Harry", "dayOfWeek":"Monday" } 901 func (s *SES) CreateTemplate(templateName string, subjectPart string, textPart string, htmlPart string, timeOutDuration ...time.Duration) (err error) { 902 segCtx := context.Background() 903 segCtxSet := false 904 905 seg := xray.NewSegmentNullable("SES-CreateTemplate", s._parentSegment) 906 907 if seg != nil { 908 segCtx = seg.Ctx 909 segCtxSet = true 910 911 defer seg.Close() 912 defer func() { 913 _ = seg.Seg.AddMetadata("SES-CreateTemplate-TemplateName", templateName) 914 _ = seg.Seg.AddMetadata("SES-CreateTemplate-SubjectPart", subjectPart) 915 _ = seg.Seg.AddMetadata("SES-CreateTemplate-TextPart", textPart) 916 _ = seg.Seg.AddMetadata("SES-CreateTemplate-HtmlPart", htmlPart) 917 918 if err != nil { 919 _ = seg.Seg.AddError(err) 920 } 921 }() 922 } 923 924 // validate 925 if s.sesClient == nil { 926 err = errors.New("CreateTemplate Failed: " + "SES Client is Required") 927 return err 928 } 929 930 if util.LenTrim(templateName) <= 0 { 931 err = errors.New("CreateTemplate Failed: " + "Template Name is Required") 932 return err 933 } 934 935 if util.LenTrim(subjectPart) <= 0 { 936 err = errors.New("CreateTemplate Failed: " + "Subject Part is Required") 937 return err 938 } 939 940 if util.LenTrim(textPart) <= 0 && util.LenTrim(htmlPart) <= 0 { 941 err = errors.New("CreateTemplate Failed: " + "Text or Html Part is Required") 942 return err 943 } 944 945 // create input object 946 input := &ses.CreateTemplateInput{ 947 Template: &ses.Template{ 948 TemplateName: aws.String(templateName), 949 SubjectPart: aws.String(subjectPart), 950 }, 951 } 952 953 if util.LenTrim(textPart) > 0 { 954 input.Template.TextPart = aws.String(textPart) 955 } 956 957 if util.LenTrim(htmlPart) > 0 { 958 input.Template.HtmlPart = aws.String(htmlPart) 959 } 960 961 // create template action 962 if len(timeOutDuration) > 0 { 963 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 964 defer cancel() 965 966 _, err = s.sesClient.CreateTemplateWithContext(ctx, input) 967 } else { 968 if segCtxSet { 969 _, err = s.sesClient.CreateTemplateWithContext(segCtx, input) 970 } else { 971 _, err = s.sesClient.CreateTemplate(input) 972 } 973 } 974 975 // evaluate result 976 if err != nil { 977 // failure 978 err = errors.New("CreateTemplate Failed: (Create Template Action) " + err.Error()) 979 return err 980 } else { 981 // success 982 return nil 983 } 984 } 985 986 // UpdateTemplate will update an existing email template for use with SendTemplateEmail and SendBulkTemplateEmail methods 987 // 988 // parameters: 989 // 1. templateName = name of the template, must be unique 990 // 2. subjectPart = subject text with tag name value replacements 991 // 3. textPart = body text with tag name value replacements 992 // 4. htmlPart = body html with tag name value replacements 993 // 5. timeOutDuration = optional time out value to use in context 994 // 995 // template tokens: 996 // 1. subjectPart, textPart, htmlPart = may contain Tag Names for personalization 997 // 2. Tag Name = using {{tagName}} within the string defined above in #1 998 // 3. example: 999 // a) Greetings, {{name}} 1000 // b) <h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p> 1001 // c) <html><head></head><body><h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p></body></html> 1002 // 4. when sending template mail, the Tag Name values are replaced via key-Value pairs within TemplateData in SendTemplateEmail and SendBulkTemplateEmail methods: 1003 // a) { "name":"Harry", "dayOfWeek":"Monday" } 1004 func (s *SES) UpdateTemplate(templateName string, subjectPart string, textPart string, htmlPart string, timeOutDuration ...time.Duration) (err error) { 1005 segCtx := context.Background() 1006 segCtxSet := false 1007 1008 seg := xray.NewSegmentNullable("SES-UpdateTemplate", s._parentSegment) 1009 1010 if seg != nil { 1011 segCtx = seg.Ctx 1012 segCtxSet = true 1013 1014 defer seg.Close() 1015 defer func() { 1016 _ = seg.Seg.AddMetadata("SES-UpdateTemplate-TemplateName", templateName) 1017 _ = seg.Seg.AddMetadata("SES-UpdateTemplate-SubjectPart", subjectPart) 1018 _ = seg.Seg.AddMetadata("SES-UpdateTemplate-TextPart", textPart) 1019 _ = seg.Seg.AddMetadata("SES-UpdateTemplate-HtmlPart", htmlPart) 1020 1021 if err != nil { 1022 _ = seg.Seg.AddError(err) 1023 } 1024 }() 1025 } 1026 1027 // validate 1028 if s.sesClient == nil { 1029 err = errors.New("UpdateTemplate Failed: " + "SES Client is Required") 1030 return err 1031 } 1032 1033 if util.LenTrim(templateName) <= 0 { 1034 err = errors.New("UpdateTemplate Failed: " + "Template Name is Required") 1035 return err 1036 } 1037 1038 if util.LenTrim(subjectPart) <= 0 { 1039 err = errors.New("UpdateTemplate Failed: " + "Subject Part is Required") 1040 return err 1041 } 1042 1043 if util.LenTrim(textPart) <= 0 && util.LenTrim(htmlPart) <= 0 { 1044 err = errors.New("UpdateTemplate Failed: " + "Text or Html Part is Required") 1045 return err 1046 } 1047 1048 // create input object 1049 input := &ses.UpdateTemplateInput{ 1050 Template: &ses.Template{ 1051 TemplateName: aws.String(templateName), 1052 SubjectPart: aws.String(subjectPart), 1053 }, 1054 } 1055 1056 if util.LenTrim(textPart) > 0 { 1057 input.Template.TextPart = aws.String(textPart) 1058 } 1059 1060 if util.LenTrim(htmlPart) > 0 { 1061 input.Template.HtmlPart = aws.String(htmlPart) 1062 } 1063 1064 // update template action 1065 if len(timeOutDuration) > 0 { 1066 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1067 defer cancel() 1068 1069 _, err = s.sesClient.UpdateTemplateWithContext(ctx, input) 1070 } else { 1071 if segCtxSet { 1072 _, err = s.sesClient.UpdateTemplateWithContext(segCtx, input) 1073 } else { 1074 _, err = s.sesClient.UpdateTemplate(input) 1075 } 1076 } 1077 1078 // evaluate result 1079 if err != nil { 1080 // failure 1081 err = errors.New("UpdateTemplate Failed: (Update Template Action) " + err.Error()) 1082 return err 1083 } else { 1084 // success 1085 return nil 1086 } 1087 } 1088 1089 // DeleteTemplate will delete an existing email template 1090 // 1091 // parameters: 1092 // 1. templateName = name of the template to delete 1093 // 2. timeOutDuration = optional time out value to use in context 1094 func (s *SES) DeleteTemplate(templateName string, timeOutDuration ...time.Duration) (err error) { 1095 segCtx := context.Background() 1096 segCtxSet := false 1097 1098 seg := xray.NewSegmentNullable("SES-DeleteTemplate", s._parentSegment) 1099 1100 if seg != nil { 1101 segCtx = seg.Ctx 1102 segCtxSet = true 1103 1104 defer seg.Close() 1105 defer func() { 1106 _ = seg.Seg.AddMetadata("SES-DeleteTemplate-TemplateName", templateName) 1107 1108 if err != nil { 1109 _ = seg.Seg.AddError(err) 1110 } 1111 }() 1112 } 1113 1114 // validate 1115 if s.sesClient == nil { 1116 err = errors.New("DeleteTemplate Failed: " + "SES Client is Required") 1117 return err 1118 } 1119 1120 if util.LenTrim(templateName) <= 0 { 1121 err = errors.New("DeleteTemplate Failed: " + "Template Name is Required") 1122 return err 1123 } 1124 1125 // create input object 1126 input := &ses.DeleteTemplateInput{ 1127 TemplateName: aws.String(templateName), 1128 } 1129 1130 // update template action 1131 if len(timeOutDuration) > 0 { 1132 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1133 defer cancel() 1134 1135 _, err = s.sesClient.DeleteTemplateWithContext(ctx, input) 1136 } else { 1137 if segCtxSet { 1138 _, err = s.sesClient.DeleteTemplateWithContext(segCtx, input) 1139 } else { 1140 _, err = s.sesClient.DeleteTemplate(input) 1141 } 1142 } 1143 1144 // evaluate result 1145 if err != nil { 1146 // failure 1147 err = errors.New("DeleteTemplate Failed: (Delete Template Action) " + err.Error()) 1148 return err 1149 } else { 1150 // success 1151 return nil 1152 } 1153 } 1154 1155 // SendTemplateEmail will send out email via ses using template 1156 // 1157 // parameters: 1158 // 1. senderEmail = sender's email address 1159 // 2. returnPath = optional, email return path address 1160 // 3. replyTo = optional, email reply to address 1161 // 4. templateName = the name of the template to use for sending out template emails 1162 // 5. emailTarget = pointer to email target object defining where the email is sent to, along with template data key value pairs in json 1163 // 6. timeOutDuration = optional, time out value to use in context 1164 // 1165 // template tokens: 1166 // 1. subjectPart, textPart, htmlPart = may contain Tag Names for personalization 1167 // 2. Tag Name = using {{tagName}} within the string defined above in #1 1168 // 3. example: 1169 // a) Greetings, {{name}} 1170 // b) <h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p> 1171 // c) <html><head></head><body><h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p></body></html> 1172 // 4. when sending template mail, the Tag Name values are replaced via key-Value pairs within TemplateData in SendTemplateEmail and SendBulkTemplateEmail methods: 1173 // a) { "name":"Harry", "dayOfWeek":"Monday" } 1174 // 1175 // notes: 1176 // 1177 // a) Template = name of template to apply to the email 1178 // b) TemplateData = json string holding key-value pair of Tag Name and Tag Value (see below about template tokens) 1179 // c) Source = email address of sender 1180 // d) ReturnPath = return email address (usually same as source) 1181 // e) ReplyToAddresses = list of email addresses for email reply to 1182 // f) Destination = email send to destination 1183 // g) ConfigurationSetName = if ses has a set defined, and need to be used herein 1184 func (s *SES) SendTemplateEmail(senderEmail string, 1185 returnPath string, 1186 replyTo string, 1187 templateName string, 1188 emailTarget *EmailTarget, 1189 timeOutDuration ...time.Duration) (messageId string, err error) { 1190 segCtx := context.Background() 1191 segCtxSet := false 1192 1193 seg := xray.NewSegmentNullable("SES-SendTemplateEmail", s._parentSegment) 1194 1195 if seg != nil { 1196 segCtx = seg.Ctx 1197 segCtxSet = true 1198 1199 defer seg.Close() 1200 defer func() { 1201 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Email-From", senderEmail) 1202 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Email-ReturnPath", returnPath) 1203 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Email-ReplyTo", replyTo) 1204 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-TemplateName", templateName) 1205 1206 if emailTarget != nil { 1207 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Email-To", emailTarget.To) 1208 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Result-MessageID", messageId) 1209 } else { 1210 _ = seg.Seg.AddMetadata("SES-SendTemplateEmail-Email-Fail", "Email Target Nil") 1211 } 1212 1213 if err != nil { 1214 _ = seg.Seg.AddError(err) 1215 } 1216 }() 1217 } 1218 1219 // validate 1220 if s.sesClient == nil { 1221 err = errors.New("SendTemplateEmail Failed: " + "SES Client is Required") 1222 return "", err 1223 } 1224 1225 if util.LenTrim(senderEmail) <= 0 { 1226 err = errors.New("SendTemplateEmail Failed: " + "Sender Email is Required") 1227 return "", err 1228 } 1229 1230 if util.LenTrim(templateName) <= 0 { 1231 err = errors.New("SendTemplateEmail Failed: " + "Template Name is Required") 1232 return "", err 1233 } 1234 1235 if emailTarget == nil { 1236 err = errors.New("SendTemplateEmail Failed: " + "Email Target is Required (Nil)") 1237 return "", err 1238 } 1239 1240 if len(emailTarget.To) <= 0 { 1241 err = errors.New("SendTemplateEmail Failed: " + "Email Target's To-Address is Required") 1242 return "", err 1243 } 1244 1245 // create input object 1246 input := &ses.SendTemplatedEmailInput{ 1247 Template: aws.String(templateName), 1248 Source: aws.String(senderEmail), 1249 } 1250 1251 // set additional input values 1252 if util.LenTrim(returnPath) > 0 { 1253 input.ReturnPath = aws.String(returnPath) 1254 } else { 1255 input.ReturnPath = aws.String(senderEmail) 1256 } 1257 1258 if util.LenTrim(replyTo) > 0 { 1259 input.ReplyToAddresses = []*string{aws.String(replyTo)} 1260 } else { 1261 input.ReplyToAddresses = []*string{aws.String(senderEmail)} 1262 } 1263 1264 if input.Destination == nil { 1265 input.Destination = new(ses.Destination) 1266 } 1267 1268 if len(emailTarget.To) > 0 { 1269 input.Destination.ToAddresses = aws.StringSlice(emailTarget.To) 1270 } 1271 1272 if len(emailTarget.CC) > 0 { 1273 input.Destination.CcAddresses = aws.StringSlice(emailTarget.CC) 1274 } 1275 1276 if len(emailTarget.BCC) > 0 { 1277 input.Destination.BccAddresses = aws.StringSlice(emailTarget.BCC) 1278 } 1279 1280 if util.LenTrim(emailTarget.TemplateDataJson) > 0 { 1281 input.TemplateData = aws.String(emailTarget.TemplateDataJson) 1282 } 1283 1284 if util.LenTrim(s.ConfigurationSetName) > 0 { 1285 input.ConfigurationSetName = aws.String(s.ConfigurationSetName) 1286 } 1287 1288 // perform action 1289 var output *ses.SendTemplatedEmailOutput 1290 1291 if len(timeOutDuration) > 0 { 1292 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1293 defer cancel() 1294 1295 output, err = s.sesClient.SendTemplatedEmailWithContext(ctx, input) 1296 } else { 1297 if segCtxSet { 1298 output, err = s.sesClient.SendTemplatedEmailWithContext(segCtx, input) 1299 } else { 1300 output, err = s.sesClient.SendTemplatedEmail(input) 1301 } 1302 } 1303 1304 // evaluate result 1305 if err != nil { 1306 err = errors.New("SendTemplateEmailFailed: (Send Action) " + err.Error()) 1307 return "", err 1308 } else { 1309 messageId = *output.MessageId 1310 return messageId, nil 1311 } 1312 } 1313 1314 // SendBulkTemplateEmail will send out bulk email via ses using template 1315 // 1316 // parameters: 1317 // 1. senderEmail = sender's email address 1318 // 2. returnPath = optional, email return path address 1319 // 3. replyTo = optional, email reply to address 1320 // 4. templateName = the name of the template to use for sending out template emails 1321 // 5. defaultTemplateDataJson = the default template data key value pairs in json, to be used if emailTarget did not define a template data json 1322 // 6. emailTargetList = slice of pointer to email target object defining where the email is sent to, along with template data key value pairs in json 1323 // 7. timeOutDuration = optional, time out value to use in context 1324 // 1325 // template tokens: 1326 // 1. subjectPart, textPart, htmlPart = may contain Tag Names for personalization 1327 // 2. Tag Name = using {{tagName}} within the string defined above in #1 1328 // 3. example: 1329 // a) Greetings, {{name}} 1330 // b) <h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p> 1331 // c) <html><head></head><body><h1>hello {{name}}, how are you</h1><p>Today is {{dayOfWeek}}</p></body></html> 1332 // 4. when sending template mail, the Tag Name values are replaced via key-Value pairs within TemplateData in SendTemplateEmail and SendBulkTemplateEmail methods: 1333 // a) { "name":"Harry", "dayOfWeek":"Monday" } 1334 // 1335 // notes: 1336 // 1337 // a) Template = name of template to apply to the email 1338 // b) DefaultTemplateData = json string holding key-value pair of Tag Name and Tag Value (see below about template tokens) 1339 // acting as a catch all default in case destination level replacement template data is not set 1340 // c) Source = email address of sender 1341 // d) ReturnPath = return email address (usually same as source) 1342 // e) ReplyToAddresses = list of email addresses for email reply to 1343 // f) Destinations = one or more Destinations, each defining target, and replacement template data for that specific destination 1344 // g) ConfigurationSetName = if ses has a set defined, and need to be used herein 1345 // 1346 // limits: 1347 // 1. bulk is limited to 50 destinations per single call (subject to aws ses limits on account) 1348 func (s *SES) SendBulkTemplateEmail(senderEmail string, 1349 returnPath string, 1350 replyTo string, 1351 templateName string, 1352 defaultTemplateDataJson string, 1353 emailTargetList []*EmailTarget, 1354 timeOutDuration ...time.Duration) (resultList []*BulkEmailMessageResult, failedCount int, err error) { 1355 segCtx := context.Background() 1356 segCtxSet := false 1357 1358 seg := xray.NewSegmentNullable("SES-SendBulkTemplateEmail", s._parentSegment) 1359 1360 if seg != nil { 1361 segCtx = seg.Ctx 1362 segCtxSet = true 1363 1364 defer seg.Close() 1365 defer func() { 1366 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-Email-From", senderEmail) 1367 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-ReturnPath", returnPath) 1368 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-ReplyTo", replyTo) 1369 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-TemplateName", templateName) 1370 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-DefaultTemplateDataJson", defaultTemplateDataJson) 1371 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-EmailTargetList-Count", len(emailTargetList)) 1372 _ = seg.Seg.AddMetadata("SES-SendBulkTemplateEmail-Result-FailCount", failedCount) 1373 1374 if err != nil { 1375 _ = seg.Seg.AddError(err) 1376 } 1377 }() 1378 } 1379 1380 // validate 1381 if s.sesClient == nil { 1382 err = errors.New("SendBulkTemplateEmail Failed: " + "SES Client is Required") 1383 return nil, 0, err 1384 } 1385 1386 if util.LenTrim(senderEmail) <= 0 { 1387 err = errors.New("SendBulkTemplateEmail Failed: " + "Sender Email is Required") 1388 return nil, 0, err 1389 } 1390 1391 if util.LenTrim(templateName) <= 0 { 1392 err = errors.New("SendBulkTemplateEmail Failed: " + "Template Name is Required") 1393 return nil, 0, err 1394 } 1395 1396 if len(emailTargetList) <= 0 { 1397 err = errors.New("SendBulkTemplateEmail Failed: " + "Email Target List is Required") 1398 return nil, 0, err 1399 } 1400 1401 // create input object 1402 input := &ses.SendBulkTemplatedEmailInput{ 1403 Template: aws.String(templateName), 1404 Source: aws.String(senderEmail), 1405 } 1406 1407 if util.LenTrim(defaultTemplateDataJson) > 0 { 1408 input.DefaultTemplateData = aws.String(defaultTemplateDataJson) 1409 } 1410 1411 if util.LenTrim(returnPath) > 0 { 1412 input.ReturnPath = aws.String(returnPath) 1413 } else { 1414 input.ReturnPath = aws.String(senderEmail) 1415 } 1416 1417 if util.LenTrim(replyTo) > 0 { 1418 input.ReplyToAddresses = []*string{aws.String(replyTo)} 1419 } else { 1420 input.ReplyToAddresses = []*string{aws.String(senderEmail)} 1421 } 1422 1423 if input.Destinations == nil { 1424 for _, v := range emailTargetList { 1425 dest := new(ses.BulkEmailDestination) 1426 1427 if dest.Destination == nil { 1428 dest.Destination = new(ses.Destination) 1429 } 1430 1431 isSet := false 1432 1433 if len(v.To) > 0 { 1434 isSet = true 1435 dest.Destination.ToAddresses = aws.StringSlice(v.To) 1436 } 1437 1438 if len(v.CC) > 0 { 1439 isSet = true 1440 dest.Destination.CcAddresses = aws.StringSlice(v.CC) 1441 } 1442 1443 if len(v.BCC) > 0 { 1444 isSet = true 1445 dest.Destination.BccAddresses = aws.StringSlice(v.BCC) 1446 } 1447 1448 if util.LenTrim(v.TemplateDataJson) > 0 { 1449 dest.ReplacementTemplateData = aws.String(v.TemplateDataJson) 1450 } 1451 1452 if isSet { 1453 input.Destinations = append(input.Destinations, dest) 1454 } 1455 } 1456 } 1457 1458 if util.LenTrim(s.ConfigurationSetName) > 0 { 1459 input.ConfigurationSetName = aws.String(s.ConfigurationSetName) 1460 } 1461 1462 // perform action 1463 var output *ses.SendBulkTemplatedEmailOutput 1464 1465 if len(timeOutDuration) > 0 { 1466 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1467 defer cancel() 1468 1469 output, err = s.sesClient.SendBulkTemplatedEmailWithContext(ctx, input) 1470 } else { 1471 if segCtxSet { 1472 output, err = s.sesClient.SendBulkTemplatedEmailWithContext(segCtx, input) 1473 } else { 1474 output, err = s.sesClient.SendBulkTemplatedEmail(input) 1475 } 1476 } 1477 1478 // evaluate result 1479 if err != nil { 1480 err = errors.New("SendBulkTemplateEmail Failed: (Send Action) " + err.Error()) 1481 return nil, 0, err 1482 } 1483 1484 for _, v := range output.Status { 1485 success := false 1486 1487 if strings.ToUpper(aws.StringValue(v.Status)) == "SUCCESS" { 1488 success = true 1489 } else { 1490 failedCount++ 1491 } 1492 1493 resultList = append(resultList, &BulkEmailMessageResult{ 1494 MessageId: aws.StringValue(v.MessageId), 1495 Status: aws.StringValue(v.Status), 1496 ErrorInfo: aws.StringValue(v.Error), 1497 Success: success, 1498 }) 1499 } 1500 1501 return resultList, failedCount, nil 1502 } 1503 1504 // ---------------------------------------------------------------------------------------------------------------- 1505 // SES custom verification email methods 1506 // ---------------------------------------------------------------------------------------------------------------- 1507 1508 // CreateCustomVerificationEmailTemplate will create a template for use with custom verification of email address 1509 // 1510 // parameters: 1511 // 1. templateName = the name of the template, must be unique 1512 // 2. templateSubject = the subject of the verification email (note that there is no tag name value replacement here) 1513 // 3. templateContent = the body of the email, can be html or text (note that there is no tag name value replacement here) 1514 // 4. fromEmailAddress = the email address that the verification email is sent from (must be email address) 1515 // 5. successRedirectionURL = the url that users are sent to if their email addresses are successfully verified 1516 // 6. failureRedirectionURL = the url that users are sent to if their email addresses are not successfully verified 1517 // 7. timeOutDuration = optional time out value to use in context 1518 func (s *SES) CreateCustomVerificationEmailTemplate(templateName string, 1519 templateSubject string, 1520 templateContent string, 1521 fromEmailAddress string, 1522 successRedirectionURL string, 1523 failureRedirectionURL string, 1524 timeOutDuration ...time.Duration) (err error) { 1525 segCtx := context.Background() 1526 segCtxSet := false 1527 1528 seg := xray.NewSegmentNullable("SES-CreateCustomVerificationEmailTemplate", s._parentSegment) 1529 1530 if seg != nil { 1531 segCtx = seg.Ctx 1532 segCtxSet = true 1533 1534 defer seg.Close() 1535 defer func() { 1536 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-TemplateName", templateName) 1537 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-TemplateSubject", templateSubject) 1538 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-TemplateContent", templateContent) 1539 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-Email-From", fromEmailAddress) 1540 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-Success-RedirectURL", successRedirectionURL) 1541 _ = seg.Seg.AddMetadata("SES-CreateCustomVerificationEmailTemplate-Failure-RedirectURL", failureRedirectionURL) 1542 1543 if err != nil { 1544 _ = seg.Seg.AddError(err) 1545 } 1546 }() 1547 } 1548 1549 // validate 1550 if s.sesClient == nil { 1551 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "SES Client is Required") 1552 return err 1553 } 1554 1555 if util.LenTrim(templateName) <= 0 { 1556 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "Template Name is Required") 1557 return err 1558 } 1559 1560 if util.LenTrim(templateSubject) <= 0 { 1561 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "Template Subject is Required") 1562 return err 1563 } 1564 1565 if util.LenTrim(templateContent) <= 0 { 1566 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "Template Content is Required") 1567 return err 1568 } 1569 1570 if util.LenTrim(fromEmailAddress) <= 0 { 1571 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "From Email Address is Required") 1572 return err 1573 } 1574 1575 if util.LenTrim(successRedirectionURL) <= 0 { 1576 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "Success Redirection URL is Required") 1577 return err 1578 } 1579 1580 if util.LenTrim(failureRedirectionURL) <= 0 { 1581 err = errors.New("CreateCustomVerificationEmailTemplate Failed: " + "Failure Redirection URL is Required") 1582 return err 1583 } 1584 1585 // create input object 1586 input := &ses.CreateCustomVerificationEmailTemplateInput{ 1587 TemplateName: aws.String(templateName), 1588 TemplateSubject: aws.String(templateSubject), 1589 TemplateContent: aws.String(templateContent), 1590 FromEmailAddress: aws.String(fromEmailAddress), 1591 SuccessRedirectionURL: aws.String(successRedirectionURL), 1592 FailureRedirectionURL: aws.String(failureRedirectionURL), 1593 } 1594 1595 // perform action 1596 if len(timeOutDuration) > 0 { 1597 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1598 defer cancel() 1599 1600 _, err = s.sesClient.CreateCustomVerificationEmailTemplateWithContext(ctx, input) 1601 } else { 1602 if segCtxSet { 1603 _, err = s.sesClient.CreateCustomVerificationEmailTemplateWithContext(segCtx, input) 1604 } else { 1605 _, err = s.sesClient.CreateCustomVerificationEmailTemplate(input) 1606 } 1607 } 1608 1609 // evaluate result 1610 if err != nil { 1611 // error 1612 err = errors.New("CreateCustomVerificationEmailTemplate Failed: (Create Template Action) " + err.Error()) 1613 return err 1614 } else { 1615 // success 1616 return nil 1617 } 1618 } 1619 1620 // UpdateCustomVerificationEmailTemplate will update a template for use with custom verification of email address 1621 // 1622 // parameters: 1623 // 1. templateName = the name of the template, must be unique 1624 // 2. templateSubject = the subject of the verification email (note that there is no tag name value replacement here) 1625 // 3. templateContent = the body of the email, can be html or text (note that there is no tag name value replacement here) 1626 // 4. fromEmailAddress = the email address that the verification email is sent from (must be email address) 1627 // 5. successRedirectionURL = the url that users are sent to if their email addresses are successfully verified 1628 // 6. failureRedirectionURL = the url that users are sent to if their email addresses are not successfully verified 1629 // 7. timeOutDuration = optional time out value to use in context 1630 func (s *SES) UpdateCustomVerificationEmailTemplate(templateName string, 1631 templateSubject string, 1632 templateContent string, 1633 fromEmailAddress string, 1634 successRedirectionURL string, 1635 failureRedirectionURL string, 1636 timeOutDuration ...time.Duration) (err error) { 1637 segCtx := context.Background() 1638 segCtxSet := false 1639 1640 seg := xray.NewSegmentNullable("SES-UpdateCustomVerificationEmailTemplate", s._parentSegment) 1641 1642 if seg != nil { 1643 segCtx = seg.Ctx 1644 segCtxSet = true 1645 1646 defer seg.Close() 1647 defer func() { 1648 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-TemplateName", templateName) 1649 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-TemplateSubject", templateSubject) 1650 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-TemplateContent", templateContent) 1651 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-Email-From", fromEmailAddress) 1652 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-Success-RedirectURL", successRedirectionURL) 1653 _ = seg.Seg.AddMetadata("SES-UpdateCustomVerificationEmailTemplate-Failure-RedirectURL", failureRedirectionURL) 1654 1655 if err != nil { 1656 _ = seg.Seg.AddError(err) 1657 } 1658 }() 1659 } 1660 1661 // validate 1662 if s.sesClient == nil { 1663 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "SES Client is Required") 1664 return err 1665 } 1666 1667 if util.LenTrim(templateName) <= 0 { 1668 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "Template Name is Required") 1669 return err 1670 } 1671 1672 if util.LenTrim(templateSubject) <= 0 { 1673 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "Template Subject is Required") 1674 return err 1675 } 1676 1677 if util.LenTrim(templateContent) <= 0 { 1678 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "Template Content is Required") 1679 return err 1680 } 1681 1682 if util.LenTrim(fromEmailAddress) <= 0 { 1683 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "From Email Address is Required") 1684 return err 1685 } 1686 1687 if util.LenTrim(successRedirectionURL) <= 0 { 1688 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "Success Redirection URL is Required") 1689 return err 1690 } 1691 1692 if util.LenTrim(failureRedirectionURL) <= 0 { 1693 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: " + "Failure Redirection URL is Required") 1694 return err 1695 } 1696 1697 // create input object 1698 input := &ses.UpdateCustomVerificationEmailTemplateInput{ 1699 TemplateName: aws.String(templateName), 1700 TemplateSubject: aws.String(templateSubject), 1701 TemplateContent: aws.String(templateContent), 1702 FromEmailAddress: aws.String(fromEmailAddress), 1703 SuccessRedirectionURL: aws.String(successRedirectionURL), 1704 FailureRedirectionURL: aws.String(failureRedirectionURL), 1705 } 1706 1707 // perform action 1708 if len(timeOutDuration) > 0 { 1709 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1710 defer cancel() 1711 1712 _, err = s.sesClient.UpdateCustomVerificationEmailTemplateWithContext(ctx, input) 1713 } else { 1714 if segCtxSet { 1715 _, err = s.sesClient.UpdateCustomVerificationEmailTemplateWithContext(segCtx, input) 1716 } else { 1717 _, err = s.sesClient.UpdateCustomVerificationEmailTemplate(input) 1718 } 1719 } 1720 1721 // evaluate result 1722 if err != nil { 1723 // error 1724 err = errors.New("UpdateCustomVerificationEmailTemplate Failed: (Update Template Action) " + err.Error()) 1725 return err 1726 } else { 1727 // success 1728 return nil 1729 } 1730 } 1731 1732 // DeleteCustomVerificationEmailTemplate will delete a custom verification email template 1733 // 1734 // parameters: 1735 // 1. templateName = name of the template to delete 1736 // 2. timeOutDuration = optional time out value to use in context 1737 func (s *SES) DeleteCustomVerificationEmailTemplate(templateName string, timeOutDuration ...time.Duration) (err error) { 1738 segCtx := context.Background() 1739 segCtxSet := false 1740 1741 seg := xray.NewSegmentNullable("SES-DeleteCustomVerificationEmailTemplate", s._parentSegment) 1742 1743 if seg != nil { 1744 segCtx = seg.Ctx 1745 segCtxSet = true 1746 1747 defer seg.Close() 1748 defer func() { 1749 _ = seg.Seg.AddMetadata("SES-DeleteCustomVerificationEmailTemplate-TemplateName", templateName) 1750 1751 if err != nil { 1752 _ = seg.Seg.AddError(err) 1753 } 1754 }() 1755 } 1756 1757 // validate 1758 if s.sesClient == nil { 1759 err = errors.New("DeleteCustomVerificationEmailTemplate Failed: " + "SES Client is Required") 1760 return err 1761 } 1762 1763 if util.LenTrim(templateName) <= 0 { 1764 err = errors.New("DeleteCustomVerificationEmailTemplate Failed: " + "Template Name is Required") 1765 return err 1766 } 1767 1768 // create input object 1769 input := &ses.DeleteCustomVerificationEmailTemplateInput{ 1770 TemplateName: aws.String(templateName), 1771 } 1772 1773 // perform action 1774 if len(timeOutDuration) > 0 { 1775 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1776 defer cancel() 1777 1778 _, err = s.sesClient.DeleteCustomVerificationEmailTemplateWithContext(ctx, input) 1779 } else { 1780 if segCtxSet { 1781 _, err = s.sesClient.DeleteCustomVerificationEmailTemplateWithContext(segCtx, input) 1782 } else { 1783 _, err = s.sesClient.DeleteCustomVerificationEmailTemplate(input) 1784 } 1785 } 1786 1787 // evaluate result 1788 if err != nil { 1789 err = errors.New("DeleteCustomVerificationEmailTemplate Failed: (Delete Template Action) " + err.Error()) 1790 return err 1791 } else { 1792 return nil 1793 } 1794 } 1795 1796 // SendCustomVerificationEmail will send out a custom verification email to recipient 1797 // 1798 // parameters: 1799 // 1. templateName = name of the template to use for the verification email 1800 // 2. toEmailAddress = the email address to send verification to 1801 // 3. timeOutDuration = optional time out value to use in context 1802 func (s *SES) SendCustomVerificationEmail(templateName string, toEmailAddress string, timeOutDuration ...time.Duration) (messageId string, err error) { 1803 segCtx := context.Background() 1804 segCtxSet := false 1805 1806 seg := xray.NewSegmentNullable("SES-SendCustomVerificationEmail", s._parentSegment) 1807 1808 if seg != nil { 1809 segCtx = seg.Ctx 1810 segCtxSet = true 1811 1812 defer seg.Close() 1813 defer func() { 1814 _ = seg.Seg.AddMetadata("SES-SendCustomVerificationEmail-TemplateName", templateName) 1815 _ = seg.Seg.AddMetadata("SES-SendCustomVerificationEmail-Email-To", toEmailAddress) 1816 _ = seg.Seg.AddMetadata("SES-SendCustomVerificationEmail-Result-MessageID", messageId) 1817 1818 if err != nil { 1819 _ = seg.Seg.AddError(err) 1820 } 1821 }() 1822 } 1823 1824 // validate 1825 if s.sesClient == nil { 1826 err = errors.New("SendCustomVerificationEmail Failed: " + "SES Client is Required") 1827 return "", err 1828 } 1829 1830 if util.LenTrim(templateName) <= 0 { 1831 err = errors.New("SendCustomVerificationEmail Failed: " + "Template Name is Required") 1832 return "", err 1833 } 1834 1835 if util.LenTrim(toEmailAddress) <= 0 { 1836 err = errors.New("SendCustomVerificationEmail Failed: " + "To-Email Address is Required") 1837 return "", err 1838 } 1839 1840 // create input object 1841 input := &ses.SendCustomVerificationEmailInput{ 1842 TemplateName: aws.String(templateName), 1843 EmailAddress: aws.String(toEmailAddress), 1844 } 1845 1846 if util.LenTrim(s.ConfigurationSetName) > 0 { 1847 input.ConfigurationSetName = aws.String(s.ConfigurationSetName) 1848 } 1849 1850 // perform action 1851 var output *ses.SendCustomVerificationEmailOutput 1852 1853 if len(timeOutDuration) > 0 { 1854 ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0]) 1855 defer cancel() 1856 1857 output, err = s.sesClient.SendCustomVerificationEmailWithContext(ctx, input) 1858 } else { 1859 if segCtxSet { 1860 output, err = s.sesClient.SendCustomVerificationEmailWithContext(segCtx, input) 1861 } else { 1862 output, err = s.sesClient.SendCustomVerificationEmail(input) 1863 } 1864 } 1865 1866 // evaluate result 1867 if err != nil { 1868 // failure 1869 err = errors.New("SendCustomVerificationEmail Failed: (Send Action) " + err.Error()) 1870 return "", err 1871 } else { 1872 // success 1873 messageId = *output.MessageId 1874 return messageId, nil 1875 } 1876 }