Search code examples
gostructnotificationsslackslack-api

Send data in attachment as per data source


I'm trying to create Slack notification that sends data available in Notification struct, i.e. Notification struct will be a Data Source. I struggle to plug properly Notification struct into Attachment. Please see my code below. Anybody has any idea what I could do? Your help is much appreciated.

My expected results (i.e. Slack Notification) would be something like this:

UserID: 99
Type: Slack or Email
Level: Warning
Received: time.Time (now)
Originator: Originator Name
Immediate:  false
Data:  interface{}
Recipients: {[email protected]; Recipient Name}
Customer: {1, Customer Name}
package main

import (
    "fmt"

    "time"

    "github.com/parnurzeal/gorequest"
)

//NotificationType is enum of available notification types
type NotificationType string

const (
    //Email should be sent via email protocols
    Email NotificationType = "email"
)

//NotificationLevel is enum of notification levels
type NotificationLevel string

//Notification levels represent the importance of the notification
const (
    Error   NotificationLevel = "error"
    Warning NotificationLevel = "warning"
    Info    NotificationLevel = "info"
)

//Recipient represents the recipient of the notification
type Recipient struct {
    Email string
    Name  string
}

//Customer represents the customer the notification is about
type Customer struct {
    ID   int
    Name string
}

//Notification is struct containing all information about notification
//This will be used as Datasource in the Attachment
type Notification struct {
    UserID     int
    Type       NotificationType
    Level      NotificationLevel
    Received   time.Time
    Originator string
    Immediate  bool
    Data       interface{}

    // following field are populated by notification service itself
    Recipients []*Recipient
    Customer   *Customer
}

//Field defines fields to be used in the notification
type Field struct {
    Title string
    Value []*Notification
}

// Attachment holds data used in payload
type Attachment struct {
    Text   *string  //`json:"text"`
    Fields []*Field //`json:"fields"`
}

//Payload defines the notification structure
type Payload struct {
    Text        string       `json:"text,omitempty"`
    Attachments []Attachment `json:"attachments,omitempty"`
}

//AddField adds one or multiple fields into the notification
func (attachment *Attachment) AddField(field Field) *Attachment {
    attachment.Fields = append(attachment.Fields, &field)
    return attachment
}

func redirectPolicyFunc(req gorequest.Request, via []gorequest.Request) error {
    return fmt.Errorf("Incorrect token (redirection)")
}

//Send sends new POST request to the webhookURL
func Send(webhookURL string, payload Payload) []error {
    request := gorequest.New()
    resp, _, err := request.
        Post(webhookURL).
        RedirectPolicy(redirectPolicyFunc).
        Send(payload).
        End()

    if err != nil {
        return err
    }
    if resp.StatusCode >= 400 {
        return []error{fmt.Errorf("Error sending msg. Status: %v", resp.Status)}
    }

    return nil
}

func main() {
    webhookURL := "WEBHOOK_URL"

    attachment1 := Attachment{}
    attachment1.AddField(Field{Title: "UserID", Value: "AS PER NOTIFICATION STRUCT"}).AddField(Field{Title: "Type", Value: "AS PER NOTIFICATION STRUCT"}).AddField(Field{Title: "Level", Value: "AS PER NOTIFICATION STRUCT"})
    payload := Payload{
        Text:        "Hello Everyone, this is a new Slack notification",
        Attachments: []Attachment{attachment1},
    }
    err := Send(webhookURL, payload)
    if len(err) > 0 {
        fmt.Printf("error: %s\n", err)
    }
}


Solution

  • The following code is a solution to this request.

    package main
    
    import (
        "bytes"
        "encoding/json"
        "errors"
        "fmt"
        "net/http"
        "strings"
        "time"
        "log"
        "strconv"
        )
    
    //DataFeed contains data for the string concatenation
    type DataFeed struct {
        CustomerID    int
        CustomerName  string
        CustomerEmail string
        CustomerPhone string
        Received      time.Time
    }
    //ConcatenateDate takes data from DataFeed and concatenates them into a msg ('\n' separated)
    func ConcatenateData(d DataFeed) (msg string) {
        values := []string{}
        values = append(values, "New notification incoming, see more details below:")
        values = append(values, "ID: " + strconv.Itoa(d.CustomerID))
        values = append(values, "Name: " + d.CustomerName)
        values = append(values, "Email: " + d.CustomerEmail)
        values = append(values, "Phone Number: " + d.CustomerPhone)
        values = append(values, "Received: " + (d.Received).String())
    
        msg = strings.Join(values, "\n")
        //fmt.Println(values)
        //fmt.Println(msg)
    
        return msg
    }
    
    // SlackRequestBody comment here
    type SlackRequestBody struct {
        Text string `json:"text"`
    }
    
    func SendSlackNotification(webhookURL string, msg string) error {
        slackBody, _ := json.Marshal(SlackRequestBody{Text: msg})
        req, err := http.NewRequest(http.MethodPost, webhookURL, bytes.NewBuffer(slackBody))
        if err != nil {
            return err
        }
    
        req.Header.Add("Content-Type", "application/json")
        client := &http.Client{Timeout: 10 * time.Second}
        resp, err := client.Do(req)
        if err != nil {
            return err
        }
    
        buf := new(bytes.Buffer)
        buf.ReadFrom(resp.Body)
        if buf.String() != "ok" {
            return errors.New("Non-ok response returned from Slack")
        }
    
        return nil
    }
    
    func main() {
        webhookURL := "https://hooks.slack.com/services/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"
    
        e := DataFeed{
            CustomerID:    767,
            CustomerName:  "Tom",
            CustomerEmail: "[email protected]",
            CustomerPhone: "07479551111",
            Received:      time.Now(),
        }
    
        fmt.Println(e)
        fmt.Printf("\n")
        fmt.Println(ConcatenateData(e))
        
            err := SendSlackNotification(webhookURL, ConcatenateData(e)) //--> Set "e" as argument here
            if err != nil {
                log.Fatal(err)
            }