...

Source file src/edge-infra.dev/pkg/lib/webhooks/slack.go

Documentation: edge-infra.dev/pkg/lib/webhooks

     1  package webhooks
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/go-logr/logr"
    11  
    12  	"edge-infra.dev/pkg/lib/logging"
    13  )
    14  
    15  type SlackMessage struct {
    16  	Text string `json:"text"`
    17  }
    18  
    19  type Slack interface {
    20  	// GetClient() *http.Client
    21  	SendMsg(msg *SlackMessage) error
    22  }
    23  
    24  type slack struct {
    25  	logger     logr.Logger
    26  	client     *http.Client
    27  	webhookURL string
    28  }
    29  
    30  // Assert that the SlackMessage interface is implemented
    31  var _ Slack = (*slack)(nil)
    32  
    33  type Options struct {
    34  	Logger *logr.Logger
    35  	Client *http.Client
    36  }
    37  
    38  type Option func(*Options)
    39  
    40  func NewSlackWebhook(url string, opts ...Option) Slack {
    41  	options := Options{}
    42  	for _, opt := range opts {
    43  		opt(&options)
    44  	}
    45  	options = setOptionsDefaults(options)
    46  
    47  	return &slack{
    48  		logger:     options.Logger.WithValues("webhook", "slack"), // todo - come up with consistency
    49  		client:     options.Client,
    50  		webhookURL: url,
    51  	}
    52  }
    53  
    54  func (s *slack) SendMsg(msg *SlackMessage) error {
    55  	slackBody, err := json.Marshal(msg)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	req, err := http.NewRequest(http.MethodPost, s.webhookURL, bytes.NewBuffer(slackBody))
    61  	if err != nil {
    62  		return err
    63  	}
    64  	req.Header.Add("Content-Type", "application/json")
    65  
    66  	// todo - need to figure out logging levels better and make it configurable
    67  	// once running smoothly we likely won't need to log this every time
    68  
    69  	// ideally want a way to identify the messages but don't want to interfere with the REST
    70  	// call itself
    71  	// s.logger.Info("sending message for incident", "incident", msg.ID)
    72  
    73  	resp, err := s.client.Do(req)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	buf := new(bytes.Buffer)
    79  	if _, err = buf.ReadFrom(resp.Body); err != nil {
    80  		return err
    81  	}
    82  
    83  	// todo - need to improve this. probably look at what the response string may contain
    84  	// in testing this has been due to rate limiting. so something we may have to account
    85  	// for via code
    86  	if buf.String() != "ok" {
    87  		return errors.New("INVALID RESPONSE RETURNED FROM THE SLACK API")
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // setOptionsDefaults set default values for Options fields.
    94  func setOptionsDefaults(options Options) Options {
    95  	if options.Logger == nil {
    96  		options.Logger = &logging.NewLogger().Logger
    97  	}
    98  	if options.Client == nil {
    99  		options.Client = &http.Client{Timeout: 10 * time.Second}
   100  	}
   101  	return options
   102  }
   103  

View as plain text