package webhooks import ( "bytes" "encoding/json" "errors" "net/http" "time" "github.com/go-logr/logr" "edge-infra.dev/pkg/lib/logging" ) type SlackMessage struct { Text string `json:"text"` } type Slack interface { // GetClient() *http.Client SendMsg(msg *SlackMessage) error } type slack struct { logger logr.Logger client *http.Client webhookURL string } // Assert that the SlackMessage interface is implemented var _ Slack = (*slack)(nil) type Options struct { Logger *logr.Logger Client *http.Client } type Option func(*Options) func NewSlackWebhook(url string, opts ...Option) Slack { options := Options{} for _, opt := range opts { opt(&options) } options = setOptionsDefaults(options) return &slack{ logger: options.Logger.WithValues("webhook", "slack"), // todo - come up with consistency client: options.Client, webhookURL: url, } } func (s *slack) SendMsg(msg *SlackMessage) error { slackBody, err := json.Marshal(msg) if err != nil { return err } req, err := http.NewRequest(http.MethodPost, s.webhookURL, bytes.NewBuffer(slackBody)) if err != nil { return err } req.Header.Add("Content-Type", "application/json") // todo - need to figure out logging levels better and make it configurable // once running smoothly we likely won't need to log this every time // ideally want a way to identify the messages but don't want to interfere with the REST // call itself // s.logger.Info("sending message for incident", "incident", msg.ID) resp, err := s.client.Do(req) if err != nil { return err } buf := new(bytes.Buffer) if _, err = buf.ReadFrom(resp.Body); err != nil { return err } // todo - need to improve this. probably look at what the response string may contain // in testing this has been due to rate limiting. so something we may have to account // for via code if buf.String() != "ok" { return errors.New("INVALID RESPONSE RETURNED FROM THE SLACK API") } return nil } // setOptionsDefaults set default values for Options fields. func setOptionsDefaults(options Options) Options { if options.Logger == nil { options.Logger = &logging.NewLogger().Logger } if options.Client == nil { options.Client = &http.Client{Timeout: 10 * time.Second} } return options }