github.com/argoproj/argo-events@v1.9.1/sensors/triggers/email/email.go (about)

     1  /*
     2  Copyright 2020 BlackRock, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  package email
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"regexp"
    24  
    25  	notifications "github.com/argoproj/notifications-engine/pkg/services"
    26  	"go.uber.org/zap"
    27  
    28  	"github.com/argoproj/argo-events/common"
    29  	"github.com/argoproj/argo-events/common/logging"
    30  	apicommon "github.com/argoproj/argo-events/pkg/apis/common"
    31  	"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
    32  	"github.com/argoproj/argo-events/sensors/triggers"
    33  )
    34  
    35  type EmailTrigger struct {
    36  	// Sensor refer to the sensor object
    37  	Sensor *v1alpha1.Sensor
    38  	// Trigger refers to the trigger resource
    39  	Trigger *v1alpha1.Trigger
    40  	// Logger to log stuff
    41  	Logger *zap.SugaredLogger
    42  	// emailSvc refers to the Email notification service.
    43  	emailSvc notifications.NotificationService
    44  }
    45  
    46  // NewEmailTrigger returns a new Email trigger context
    47  func NewEmailTrigger(sensor *v1alpha1.Sensor, trigger *v1alpha1.Trigger, logger *zap.SugaredLogger) (*EmailTrigger, error) {
    48  	emailTrigger := trigger.Template.Email
    49  	var smtpPassword string
    50  	if emailTrigger.SMTPPassword != nil {
    51  		var err error
    52  		smtpPassword, err = common.GetSecretFromVolume(emailTrigger.SMTPPassword)
    53  		if err != nil {
    54  			return nil, fmt.Errorf("failed to retrieve the smtp password, %w", err)
    55  		}
    56  	}
    57  	emailSvc := notifications.NewEmailService(
    58  		notifications.EmailOptions{
    59  			Host:     emailTrigger.Host,
    60  			Port:     int(emailTrigger.Port),
    61  			Username: emailTrigger.Username,
    62  			Password: smtpPassword,
    63  			From:     emailTrigger.From,
    64  		},
    65  	)
    66  	return &EmailTrigger{
    67  		Sensor:   sensor,
    68  		Trigger:  trigger,
    69  		Logger:   logger.With(logging.LabelTriggerType, apicommon.EmailTrigger),
    70  		emailSvc: emailSvc,
    71  	}, nil
    72  }
    73  
    74  // GetTriggerType returns the type of the trigger
    75  func (t *EmailTrigger) GetTriggerType() apicommon.TriggerType {
    76  	return apicommon.EmailTrigger
    77  }
    78  
    79  func (t *EmailTrigger) FetchResource(ctx context.Context) (interface{}, error) {
    80  	return t.Trigger.Template.Email, nil
    81  }
    82  
    83  func (t *EmailTrigger) ApplyResourceParameters(events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) {
    84  	resourceBytes, err := json.Marshal(resource)
    85  	if err != nil {
    86  		return nil, fmt.Errorf("failed to marshal the Email trigger resource, %w", err)
    87  	}
    88  	parameters := t.Trigger.Template.Email.Parameters
    89  
    90  	if parameters != nil {
    91  		updatedResourceBytes, err := triggers.ApplyParams(resourceBytes, t.Trigger.Template.Email.Parameters, events)
    92  		if err != nil {
    93  			return nil, err
    94  		}
    95  
    96  		var st *v1alpha1.EmailTrigger
    97  		if err := json.Unmarshal(updatedResourceBytes, &st); err != nil {
    98  			return nil, fmt.Errorf("failed to unmarshal the updated Email trigger resource after applying resource parameters, %w", err)
    99  		}
   100  
   101  		return st, nil
   102  	}
   103  
   104  	return resource, nil
   105  }
   106  
   107  // Execute executes the trigger
   108  func (t *EmailTrigger) Execute(ctx context.Context, events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) {
   109  	t.Logger.Info("executing EmailTrigger")
   110  	_, ok := resource.(*v1alpha1.EmailTrigger)
   111  	if !ok {
   112  		return nil, fmt.Errorf("failed to marshal the Email trigger resource")
   113  	}
   114  
   115  	emailTrigger := t.Trigger.Template.Email
   116  
   117  	if len(emailTrigger.To) == 0 {
   118  		return nil, fmt.Errorf("to can't be empty")
   119  	}
   120  	body := emailTrigger.Body
   121  	if body == "" {
   122  		return nil, fmt.Errorf("body can't be empty")
   123  	}
   124  	subject := emailTrigger.Subject
   125  	if subject == "" {
   126  		return nil, fmt.Errorf("subject can't be empty")
   127  	}
   128  	t.Logger.Infow("sending emails...", zap.Any("to", emailTrigger.To))
   129  	var errs error
   130  	validEmail := regexp.MustCompile(`^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`)
   131  	for _, addr := range emailTrigger.To {
   132  		if !validEmail.MatchString(addr) {
   133  			t.Logger.Error("invalid emailId", zap.Any("to", addr))
   134  			errs = errors.Join(errs, fmt.Errorf("to emailId can't be invalid %v", addr))
   135  			continue
   136  		}
   137  		notification := notifications.Notification{
   138  			Message: emailTrigger.Body,
   139  			Email: &notifications.EmailNotification{
   140  				Subject: emailTrigger.Subject,
   141  				Body:    emailTrigger.Body,
   142  			},
   143  		}
   144  		destination := notifications.Destination{
   145  			Service:   "email",
   146  			Recipient: addr,
   147  		}
   148  		err := t.emailSvc.Send(notification, destination)
   149  		if err != nil {
   150  			t.Logger.Errorw("unable to send emails to emailId", zap.Any("to", addr), zap.Error(err))
   151  			errs = errors.Join(errs, fmt.Errorf("failed to send emails to emailId %v, %w", addr, err))
   152  		}
   153  	}
   154  	if errs != nil {
   155  		return nil, errs
   156  	}
   157  	t.Logger.Infow("message successfully sent to emailIds", zap.Any("message", emailTrigger.Body), zap.Any("to", emailTrigger.To))
   158  	t.Logger.Info("finished executing EmailTrigger")
   159  	return nil, nil
   160  }
   161  
   162  // No Policies for EmailTrigger
   163  func (t *EmailTrigger) ApplyPolicy(ctx context.Context, resource interface{}) error {
   164  	return nil
   165  }