-
-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathnotifier.go
154 lines (135 loc) · 3.68 KB
/
notifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package webhooknotifier
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"sync"
"github.com/thomaspoignant/go-feature-flag/internal"
"github.com/thomaspoignant/go-feature-flag/internal/signer"
"github.com/thomaspoignant/go-feature-flag/notifier"
)
// webhookReqBody is the format we are sending to the webhook
type webhookReqBody struct {
Meta map[string]string `json:"meta"`
Flags notifier.DiffCache `json:"flags"`
}
// Notifier will call your endpoint URL with a POST request with the following format
//
// {
// "meta": {
// "hostname": "server01"
// },
// "flags": {
// "deleted": {
// "test-flag": {
// "rule": "key eq \"random-key\"",
// "percentage": 100,
// "true": true,
// "false": false,
// "default": false
// }
// },
// "added": {
// "test-flag3": {
// "percentage": 5,
// "true": "test",
// "false": "false",
// "default": "default"
// }
// },
// "updated": {
// "test-flag2": {
// "old_value": {
// "rule": "key eq \"not-a-key\"",
// "percentage": 100,
// "true": true,
// "false": false,
// "default": false
// },
// "new_value": {
// "disable": true,
// "rule": "key eq \"not-a-key\"",
// "percentage": 100,
// "true": true,
// "false": false,
// "default": false
// }
// }
// }
// }
// }
type Notifier struct {
// EndpointURL of your webhook
EndpointURL string
// Optional: Secret used to sign your request body.
Secret string
// Meta (optional) information that you want to send to your webhook
Meta map[string]string
// Headers (optional) the list of Headers to send to the endpoint
Headers map[string][]string
httpClient internal.HTTPClient
init sync.Once
}
// Notify is the notifying all the changes to the notifier.
func (c *Notifier) Notify(diff notifier.DiffCache) error {
if c.EndpointURL == "" {
return fmt.Errorf(
"invalid notifier configuration, no endpointURL provided for the webhook notifier",
)
}
// init the notifier
c.init.Do(func() {
if c.httpClient == nil {
c.httpClient = internal.DefaultHTTPClient()
}
if c.Meta == nil {
c.Meta = make(map[string]string)
}
// if no hostname provided we return the hostname of the current machine
if _, ok := c.Meta["hostname"]; !ok {
hostname, _ := os.Hostname()
c.Meta["hostname"] = hostname
}
})
endpointURL, err := url.Parse(c.EndpointURL)
if err != nil {
return fmt.Errorf("error: (Webhook Notifier) invalid EnpointURL:%v", c.EndpointURL)
}
// Create request body
reqBody := webhookReqBody{
Meta: c.Meta,
Flags: diff,
}
payload, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("error: (Webhook Notifier) impossible to read differences; %v", err)
}
if c.Headers == nil {
c.Headers = map[string][]string{}
}
c.Headers["Content-Type"] = []string{"application/json"}
// if a secret is provided we sign the body and add this signature as a header.
if c.Secret != "" {
c.Headers["X-Hub-Signature-256"] = []string{signer.Sign(payload, []byte(c.Secret))}
}
request := http.Request{
Method: "POST",
URL: endpointURL,
Header: c.Headers,
Body: io.NopCloser(bytes.NewReader(payload)),
}
response, err := c.httpClient.Do(&request)
// Log if something went wrong while calling the webhook.
if err != nil {
return fmt.Errorf("error: while calling webhook: %v", err)
}
defer func() { _ = response.Body.Close() }()
if response.StatusCode > 399 {
return fmt.Errorf("error: while calling webhook, statusCode = %d", response.StatusCode)
}
return nil
}