From 7f9b1edfa17b3ae7cfb7bc7719d9875019fd53a7 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Sun, 27 Oct 2024 22:11:46 +0100 Subject: [PATCH 01/17] Updates --- cmd/relayproxy/config/notifier.go | 7 +- cmd/relayproxy/service/gofeatureflag.go | 3 + cmd/relayproxy/service/gofeatureflag_test.go | 6 + notifier/microsoftteamsnotifier/notifier.go | 190 ++++++++++++++++ .../microsoftteamsnotifier/notifier_test.go | 214 ++++++++++++++++++ ...d_call_webhook_and_have_valid_results.json | 78 +++++++ 6 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 notifier/microsoftteamsnotifier/notifier.go create mode 100644 notifier/microsoftteamsnotifier/notifier_test.go create mode 100644 notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 584d8a6dda9..c456ff2dfa7 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -5,6 +5,7 @@ import "fmt" type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` + MicrosoftTeamsWebhookURL string `mapstructure:"microsoftteamsWebhookUrl" koanf:"microsoftteamsWebhookUrl"` EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` Secret string `mapstructure:"secret" koanf:"secret"` Meta map[string]string `mapstructure:"meta" koanf:"meta"` @@ -18,6 +19,9 @@ func (c *NotifierConf) IsValid() error { if c.Kind == SlackNotifier && c.SlackWebhookURL == "" { return fmt.Errorf("invalid notifier: no \"slackWebhookUrl\" property found for kind \"%s\"", c.Kind) } + if c.Kind == MicrosoftTeamsNotifier && c.MicrosoftTeamsWebhookURL == "" { + return fmt.Errorf("invalid notifier: no \"microsoftteamsWebhookUrl\" property found for kind \"%s\"", c.Kind) + } if c.Kind == WebhookNotifier && c.EndpointURL == "" { return fmt.Errorf("invalid notifier: no \"endpointUrl\" property found for kind \"%s\"", c.Kind) } @@ -28,13 +32,14 @@ type NotifierKind string const ( SlackNotifier NotifierKind = "slack" + MicrosoftTeamsNotifier NotifierKind = "microsoftteams" WebhookNotifier NotifierKind = "webhook" ) // IsValid is checking if the value is part of the enum func (r NotifierKind) IsValid() error { switch r { - case SlackNotifier, WebhookNotifier: + case SlackNotifier, MicrosoftTeamsNotifier, WebhookNotifier: return nil } return fmt.Errorf("invalid notifier: kind \"%s\" is not supported", r) diff --git a/cmd/relayproxy/service/gofeatureflag.go b/cmd/relayproxy/service/gofeatureflag.go index f8bd96d8218..0006ab5246a 100644 --- a/cmd/relayproxy/service/gofeatureflag.go +++ b/cmd/relayproxy/service/gofeatureflag.go @@ -20,6 +20,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/exporter/webhookexporter" "github.com/thomaspoignant/go-feature-flag/notifier" "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" + "github.com/thomaspoignant/go-feature-flag/notifier/microsoftteamsnotifier" "github.com/thomaspoignant/go-feature-flag/notifier/webhooknotifier" "github.com/thomaspoignant/go-feature-flag/retriever" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" @@ -304,6 +305,8 @@ func initNotifier(c []config.NotifierConf) ([]notifier.Notifier, error) { switch cNotif.Kind { case config.SlackNotifier: notifiers = append(notifiers, &slacknotifier.Notifier{SlackWebhookURL: cNotif.SlackWebhookURL}) + case config.MicrosoftTeamsNotifier: + notifiers = append(notifiers, µsoftteamsnotifier.Notifier{MicrosoftTeamsWebhookURL: cNotif.MicrosoftTeamsWebhookURL}) case config.WebhookNotifier: notifiers = append(notifiers, diff --git a/cmd/relayproxy/service/gofeatureflag_test.go b/cmd/relayproxy/service/gofeatureflag_test.go index 597080ff042..ce499bd5f94 100644 --- a/cmd/relayproxy/service/gofeatureflag_test.go +++ b/cmd/relayproxy/service/gofeatureflag_test.go @@ -20,6 +20,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/exporter/webhookexporter" "github.com/thomaspoignant/go-feature-flag/notifier" "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" + "github.com/thomaspoignant/go-feature-flag/notifier/microsoftteamsnotifier" "github.com/thomaspoignant/go-feature-flag/notifier/webhooknotifier" "github.com/thomaspoignant/go-feature-flag/retriever" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" @@ -403,6 +404,10 @@ func Test_initNotifier(t *testing.T) { Kind: config.SlackNotifier, SlackWebhookURL: "http:xxxx.xxx", }, + { + Kind: config.MicrosoftTeamsNotifier, + MicrosoftTeamsWebhookURL: "http:zzzz.zzz", + }, { Kind: config.WebhookNotifier, EndpointURL: "http:yyyy.yyy", @@ -411,6 +416,7 @@ func Test_initNotifier(t *testing.T) { }, want: []notifier.Notifier{ &slacknotifier.Notifier{SlackWebhookURL: "http:xxxx.xxx"}, + µsoftteamsnotifier.Notifier{MicrosoftTeamsWebhookURL: "http:zzzz.zzz"}, &webhooknotifier.Notifier{EndpointURL: "http:yyyy.yyy"}, }, wantErr: assert.NoError, diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go new file mode 100644 index 00000000000..48f3a45a5f4 --- /dev/null +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -0,0 +1,190 @@ +package microsoftteamsnotifier + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "sort" + "strings" + "sync" + "time" + + "github.com/luci/go-render/render" + "github.com/r3labs/diff/v3" + "github.com/thomaspoignant/go-feature-flag/internal" + "github.com/thomaspoignant/go-feature-flag/notifier" +) + +const ( + goFFLogo = "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png" + microsoftteamsFooter = "go-feature-flag" + colorDeleted = "#FF0000" + colorUpdated = "#FFA500" + colorAdded = "#008000" + longMicrosoftTeamsAttachment = 35 +) + +type Notifier struct { + MicrosoftTeamsWebhookURL string + + httpClient internal.HTTPClient + init sync.Once +} + +func (c *Notifier) Notify(diff notifier.DiffCache) error { + if c.MicrosoftTeamsWebhookURL == "" { + return fmt.Errorf("error: (Microsoft Teams Notifier) invalid notifier configuration, no " + + "MicrosoftTeamsWebhookURL provided for the microsoft teams notifier") + } + + // init the notifier + c.init.Do(func() { + if c.httpClient == nil { + c.httpClient = internal.DefaultHTTPClient() + } + }) + + microsoftteamsURL, err := url.Parse(c.MicrosoftTeamsWebhookURL) + if err != nil { + return fmt.Errorf("error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: %v", c.MicrosoftTeamsWebhookURL) + } + + reqBody := convertToMicrosoftTeamsMessage(diff) + payload, err := json.Marshal(reqBody) + if err != nil { + return fmt.Errorf("error: (Microsoft Teams Notifier) impossible to read differences; %v", err) + } + request := http.Request{ + Method: http.MethodPost, + URL: microsoftteamsURL, + Body: io.NopCloser(bytes.NewReader(payload)), + Header: map[string][]string{"Content-type": {"application/json"}}, + } + response, err := c.httpClient.Do(&request) + if err != nil { + return fmt.Errorf("error: (Microsoft Teams Notifier) error: while calling webhook: %v", err) + } + + defer func() { _ = response.Body.Close() }() + if response.StatusCode > 399 { + return fmt.Errorf("error: (Microsoft Teams Notifier) while calling microsoft teams webhook, statusCode = %d", + response.StatusCode) + } + + return nil +} + +func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) microsoftteamsMessage { + hostname, _ := os.Hostname() + attachments := convertDeletedFlagsToMicrosoftTeamsMessage(diffCache) + attachments = append(attachments, convertUpdatedFlagsToMicrosoftTeamsMessage(diffCache)...) + attachments = append(attachments, convertAddedFlagsToMicrosoftTeamsMessage(diffCache)...) + currentDate := time.Now().Format("YYYY-MM-DD") + res := []map[string]interface{}{ + "@type": "MessageCard", + "@context": "https://schema.org/extensions", + "summary": "Changes detected in your feature flag", + "sections": []map[string]interface{}{ + { + "activityTitle": "Changes detected in your feature flag", + "activitySubtitle": fmt.Sprintf("On flag file: *%s*", hostname), + "activityImage": goFFLogo, + "text": fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname), + "facts": []map[string]string{ + {"name": "Date", "value": currentDate}, + }, + }, + }, + "attachments": []Attachment{attachments}, + // "attachments": attachments, + } + return res +} + +func convertDeletedFlagsToMicrosoftTeamsMessage(diffCache notifier.DiffCache) []attachment { + attachments := make([]attachment, 0) + for key := range diffCache.Deleted { + attachment := attachment{ + Title: fmt.Sprintf("❌ Flag \"%s\" deleted", key), + Color: colorDeleted, + FooterIcon: goFFLogo, + Footer: microsoftteamsFooter, + } + attachments = append(attachments, attachment) + } + return attachments +} + +func convertUpdatedFlagsToMicrosoftTeamsMessage(diffCache notifier.DiffCache) []attachment { + attachments := make([]attachment, 0) + for key, value := range diffCache.Updated { + attachment := attachment{ + Title: fmt.Sprintf("✏️ Flag \"%s\" updated", key), + Color: colorUpdated, + FooterIcon: goFFLogo, + Footer: microsoftteamsFooter, + Fields: []Field{}, + } + + changelog, _ := diff.Diff(value.Before, value.After, diff.AllowTypeMismatch(true)) + for _, change := range changelog { + if change.Type == "update" { + value := fmt.Sprintf("%s => %s", render.Render(change.From), render.Render(change.To)) + short := len(value) < longMicrosoftTeamsAttachment + attachment.Fields = append( + attachment.Fields, + Field{Title: strings.Join(change.Path, "."), Short: short, Value: value}, + ) + } + } + + sort.Sort(ByTitle(attachment.Fields)) + + attachments = append(attachments, attachment) + } + return attachments +} + +func convertAddedFlagsToMicrosoftTeamsMessage(diff notifier.DiffCache) []attachment { + attachments := make([]attachment, 0) + for key := range diff.Added { + attachment := attachment{ + Title: fmt.Sprintf("🆕 Flag \"%s\" created", key), + Color: colorAdded, + FooterIcon: goFFLogo, + Footer: microsoftteamsFooter, + } + attachments = append(attachments, attachment) + } + return attachments +} + +type microsoftteamsMessage struct { + IconURL string `json:"icon_url"` + Text string `json:"text"` + Attachments []attachment `json:"attachments"` +} + +type attachment struct { + Color string `json:"color"` + Title string `json:"title"` + Fields []Field `json:"fields"` + FooterIcon string `json:"footer_icon,omitempty"` + Footer string `json:"footer,omitempty"` +} + +type Field struct { + Title string `json:"title"` + Value string `json:"value"` + Short bool `json:"short"` +} + +type ByTitle []Field + +func (a ByTitle) Len() int { return len(a) } +func (a ByTitle) Less(i, j int) bool { return a[i].Title < a[j].Title } +func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/notifier/microsoftteamsnotifier/notifier_test.go b/notifier/microsoftteamsnotifier/notifier_test.go new file mode 100644 index 00000000000..f0687c4b4c4 --- /dev/null +++ b/notifier/microsoftteamsnotifier/notifier_test.go @@ -0,0 +1,214 @@ +package microsoftteamsnotifier + +import ( + "net/http" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/thomaspoignant/go-feature-flag/internal/flag" + "github.com/thomaspoignant/go-feature-flag/notifier" + "github.com/thomaspoignant/go-feature-flag/testutils" + "github.com/thomaspoignant/go-feature-flag/testutils/testconvert" +) + +func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { + type args struct { + diff notifier.DiffCache + statusCode int + forceError bool + url string + } + type expected struct { + err bool + errMsg string + bodyPath string + signature string + } + tests := []struct { + name string + args args + expected expected + }{ + { + name: "should call webhook and have valid results", + expected: expected{ + bodyPath: "./testdata/should_call_webhook_and_have_valid_results.json", + }, + args: args{ + url: "https://outlook.office.com/webhook/ZZZZ", + statusCode: http.StatusOK, + diff: notifier.DiffCache{ + Added: map[string]flag.Flag{ + "test-flag3": &flag.InternalFlag{ + Rules: &[]flag.Rule{ + { + Name: testconvert.String("rule1"), + Query: testconvert.String("key eq \"random-key\""), + Percentages: &map[string]float64{ + "False": 95, + "True": 5, + }, + }, + }, + Variations: &map[string]*interface{}{ + "Default": testconvert.Interface("default"), + "False": testconvert.Interface("false"), + "True": testconvert.Interface("test"), + }, + DefaultRule: &flag.Rule{ + Name: testconvert.String("defaultRule"), + VariationResult: testconvert.String("Default"), + }, + TrackEvents: testconvert.Bool(true), + Disable: testconvert.Bool(false), + Version: testconvert.String("1.1"), + }, + }, + Deleted: map[string]flag.Flag{ + "test-flag": &flag.InternalFlag{ + Rules: &[]flag.Rule{ + { + Name: testconvert.String("rule1"), + Query: testconvert.String("key eq \"random-key\""), + Percentages: &map[string]float64{ + "False": 0, + "True": 100, + }, + }, + }, + Variations: &map[string]*interface{}{ + "Default": testconvert.Interface(false), + "False": testconvert.Interface(false), + "True": testconvert.Interface(true), + }, + DefaultRule: &flag.Rule{ + Name: testconvert.String("defaultRule"), + VariationResult: testconvert.String("Default"), + }, + }, + }, + Updated: map[string]notifier.DiffUpdated{ + "test-flag2": { + Before: &flag.InternalFlag{ + Variations: &map[string]*interface{}{ + "Default": testconvert.Interface(false), + "False": testconvert.Interface(false), + "True": testconvert.Interface(true), + }, + DefaultRule: &flag.Rule{ + Name: testconvert.String("defaultRule"), + Percentages: &map[string]float64{ + "False": 0, + "True": 100, + }, + }, + Experimentation: &flag.ExperimentationRollout{ + Start: testconvert.Time(time.Unix(1095379400, 0)), + End: testconvert.Time(time.Unix(1095371000, 0)), + }, + }, + After: &flag.InternalFlag{ + Variations: &map[string]*interface{}{ + "Default": testconvert.Interface("strDefault"), + "False": testconvert.Interface("strFalse"), + "True": testconvert.Interface("strTrue"), + }, + Rules: &[]flag.Rule{ + { + Name: testconvert.String("rule1"), + Query: testconvert.String("key eq \"not-a-ke\""), + Percentages: &map[string]float64{ + "False": 20, + "True": 80, + }, + }, + }, + DefaultRule: &flag.Rule{ + Name: testconvert.String("defaultRule"), + VariationResult: testconvert.String("Default"), + }, + Disable: testconvert.Bool(true), + TrackEvents: testconvert.Bool(false), + Version: testconvert.String("1.1"), + }, + }, + }, + }, + }, + }, + { + name: "should err if http code is superior to 399", + expected: expected{ + err: true, + errMsg: "error: (Microsoft Teams Notifier) while calling microsoft teams webhook, statusCode = 400", + }, + args: args{ + url: "https://outlook.office.com/webhook/ZZZZ", + statusCode: http.StatusBadRequest, + diff: notifier.DiffCache{}, + }, + }, + { + name: "should err if error while calling webhook", + expected: expected{ + err: true, + errMsg: "error: (Microsoft Teams Notifier) error: while calling webhook: random error", + }, + args: args{ + url: "https://outlook.office.com/webhook/ZZZZ", + statusCode: http.StatusOK, + diff: notifier.DiffCache{}, + forceError: true, + }, + }, + { + name: "missing microsoft teams url", + expected: expected{ + err: true, + errMsg: "error: (Microsoft Teams Notifier) invalid notifier configuration, no MicrosoftTeamsWebhookURL provided for the microsoft teams notifier", + }, + args: args{ + statusCode: http.StatusOK, + diff: notifier.DiffCache{}, + }, + }, + { + name: "invalid microsoft teams url", + expected: expected{ + err: true, + errMsg: "error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: https://{}outlook.office.com/webhook/ZZZZ", + }, + args: args{ + url: "https://{}outlook.office.com/webhook/ZZZZ", + statusCode: http.StatusOK, + diff: notifier.DiffCache{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockHTTPClient := &testutils.HTTPClientMock{StatusCode: tt.args.statusCode, ForceError: tt.args.forceError} + + c := Notifier{ + MicrosoftTeamsWebhookURL: tt.args.url, + httpClient: mockHTTPClient, + } + + err := c.Notify(tt.args.diff) + + if tt.expected.err { + assert.ErrorContains(t, err, tt.expected.errMsg) + } else { + assert.NoError(t, err) + hostname, _ := os.Hostname() + content, _ := os.ReadFile(tt.expected.bodyPath) + expectedContent := strings.ReplaceAll(string(content), "{{hostname}}", hostname) + assert.JSONEq(t, expectedContent, mockHTTPClient.Body) + assert.Equal(t, tt.expected.signature, mockHTTPClient.Signature) + } + }) + } +} diff --git a/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json new file mode 100644 index 00000000000..0803b3a8f8f --- /dev/null +++ b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json @@ -0,0 +1,78 @@ +{ + "icon_url": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", + "text": "Changes detected in your feature flag file on: *{{hostname}}*", + "attachments": [ + { + "color": "#FF0000", + "title": "❌ Flag \"test-flag\" deleted", + "fields": null, + "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", + "footer": "go-feature-flag" + }, + { + "color": "#FFA500", + "title": "✏️ Flag \"test-flag2\" updated", + "fields": [ + { + "title": "DefaultRule.Percentages", + "value": "(*map[string]float64){\"False\":0, \"True\":100} =\u003e nil", + "short": false + }, + { + "title": "DefaultRule.VariationResult", + "value": "nil =\u003e (*string)(\"Default\")", + "short": true + }, + { + "title": "Disable", + "value": "nil =\u003e (*bool)(true)", + "short": true + }, + { + "title": "Experimentation", + "value": "(*flag.ExperimentationRollout){Start:(*time.Time){wall:0, ext:63230976200, loc:(*time.Location){name:\"\", zone:[]time.zone(nil), tx:[]time.zoneTrans(nil), extend:\"\", cacheStart:0, cacheEnd:0, cacheZone:(*time.zone)(nil)}}, End:(*time.Time){wall:0, ext:63230967800, loc:(*time.Location){name:\"\", zone:[]time.zone(nil), tx:[]time.zoneTrans(nil), extend:\"\", cacheStart:0, cacheEnd:0, cacheZone:(*time.zone)(nil)}}} =\u003e nil", + "short": false + }, + { + "title": "Rules", + "value": "nil =\u003e (*[]flag.Rule){flag.Rule{Name:(*string)(\"rule1\"), Query:(*string)(\"key eq \\\"not-a-ke\\\"\"), VariationResult:(*string)(nil), Percentages:(*map[string]float64){\"False\":20, \"True\":80}, ProgressiveRollout:(*flag.ProgressiveRollout)(nil), Disable:(*bool)(nil)}}", + "short": false + }, + { + "title": "TrackEvents", + "value": "nil =\u003e (*bool)(false)", + "short": true + }, + { + "title": "Variations.Default", + "value": "false =\u003e \"strDefault\"", + "short": true + }, + { + "title": "Variations.False", + "value": "false =\u003e \"strFalse\"", + "short": true + }, + { + "title": "Variations.True", + "value": "true =\u003e \"strTrue\"", + "short": true + }, + { + "title": "Version", + "value": "nil =\u003e (*string)(\"1.1\")", + "short": true + } + ], + "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", + "footer": "go-feature-flag" + }, + { + "color": "#008000", + "title": "🆕 Flag \"test-flag3\" created", + "fields": null, + "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", + "footer": "go-feature-flag" + } + ] +} From 217116f25838f90a999d50b8a96fd8b7f316fa67 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 30 Oct 2024 08:17:36 +0100 Subject: [PATCH 02/17] Updates --- notifier/microsoftteamsnotifier/notifier.go | 43 ++++++++----------- .../microsoftteamsnotifier/notifier_test.go | 10 ++--- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go index 48f3a45a5f4..e3bce961307 100644 --- a/notifier/microsoftteamsnotifier/notifier.go +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -58,11 +58,15 @@ func (c *Notifier) Notify(diff notifier.DiffCache) error { if err != nil { return fmt.Errorf("error: (Microsoft Teams Notifier) impossible to read differences; %v", err) } + microsoftTeamAccessToken := os.Getenv("MICROSOFT_TEAMS_ACCESS_TOKEN") request := http.Request{ Method: http.MethodPost, URL: microsoftteamsURL, Body: io.NopCloser(bytes.NewReader(payload)), - Header: map[string][]string{"Content-type": {"application/json"}}, + Header: map[string][]string{ + "Content-Type": {"application/json"}, + "Authorization": {"Bearer " + microsoftTeamAccessToken}, + }, } response, err := c.httpClient.Do(&request) if err != nil { @@ -83,26 +87,13 @@ func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) microsoftteams attachments := convertDeletedFlagsToMicrosoftTeamsMessage(diffCache) attachments = append(attachments, convertUpdatedFlagsToMicrosoftTeamsMessage(diffCache)...) attachments = append(attachments, convertAddedFlagsToMicrosoftTeamsMessage(diffCache)...) - currentDate := time.Now().Format("YYYY-MM-DD") - res := []map[string]interface{}{ - "@type": "MessageCard", - "@context": "https://schema.org/extensions", - "summary": "Changes detected in your feature flag", - "sections": []map[string]interface{}{ - { - "activityTitle": "Changes detected in your feature flag", - "activitySubtitle": fmt.Sprintf("On flag file: *%s*", hostname), - "activityImage": goFFLogo, - "text": fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname), - "facts": []map[string]string{ - {"name": "Date", "value": currentDate}, - }, - }, - }, - "attachments": []Attachment{attachments}, - // "attachments": attachments, + res := microsoftteamsMessage{ + IconURL: goFFLogo, + Body: MessageBody{Content: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname)}, + Attachments: attachments } return res + } func convertDeletedFlagsToMicrosoftTeamsMessage(diffCache notifier.DiffCache) []attachment { @@ -163,12 +154,6 @@ func convertAddedFlagsToMicrosoftTeamsMessage(diff notifier.DiffCache) []attachm return attachments } -type microsoftteamsMessage struct { - IconURL string `json:"icon_url"` - Text string `json:"text"` - Attachments []attachment `json:"attachments"` -} - type attachment struct { Color string `json:"color"` Title string `json:"title"` @@ -182,6 +167,14 @@ type Field struct { Value string `json:"value"` Short bool `json:"short"` } +type MessageBody struct { + Content string `json:"content"` +} +type microsoftteamsMessage struct { + IconURL string `json:"icon_url"` + Body MessageBody `json:"body"` + Attachments []attachment `json:"attachments"` +} type ByTitle []Field diff --git a/notifier/microsoftteamsnotifier/notifier_test.go b/notifier/microsoftteamsnotifier/notifier_test.go index f0687c4b4c4..d1982c98089 100644 --- a/notifier/microsoftteamsnotifier/notifier_test.go +++ b/notifier/microsoftteamsnotifier/notifier_test.go @@ -38,7 +38,7 @@ func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { bodyPath: "./testdata/should_call_webhook_and_have_valid_results.json", }, args: args{ - url: "https://outlook.office.com/webhook/ZZZZ", + url: "https://graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", statusCode: http.StatusOK, diff: notifier.DiffCache{ Added: map[string]flag.Flag{ @@ -146,7 +146,7 @@ func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { errMsg: "error: (Microsoft Teams Notifier) while calling microsoft teams webhook, statusCode = 400", }, args: args{ - url: "https://outlook.office.com/webhook/ZZZZ", + url: "https://graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", statusCode: http.StatusBadRequest, diff: notifier.DiffCache{}, }, @@ -158,7 +158,7 @@ func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { errMsg: "error: (Microsoft Teams Notifier) error: while calling webhook: random error", }, args: args{ - url: "https://outlook.office.com/webhook/ZZZZ", + url: "https://graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", statusCode: http.StatusOK, diff: notifier.DiffCache{}, forceError: true, @@ -179,10 +179,10 @@ func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { name: "invalid microsoft teams url", expected: expected{ err: true, - errMsg: "error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: https://{}outlook.office.com/webhook/ZZZZ", + errMsg: "error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: https://{}graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", }, args: args{ - url: "https://{}outlook.office.com/webhook/ZZZZ", + url: "https://{}graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", statusCode: http.StatusOK, diff: notifier.DiffCache{}, }, From 8f68cddc4ca104dc0ab91900cc493716cd2cf0a0 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Tue, 5 Nov 2024 22:49:19 +0100 Subject: [PATCH 03/17] Updates --- notifier/microsoftteamsnotifier/notifier.go | 5 ++--- .../should_call_webhook_and_have_valid_results.json | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go index e3bce961307..72fac1e41cb 100644 --- a/notifier/microsoftteamsnotifier/notifier.go +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -11,8 +11,7 @@ import ( "sort" "strings" "sync" - "time" - + "github.com/luci/go-render/render" "github.com/r3labs/diff/v3" "github.com/thomaspoignant/go-feature-flag/internal" @@ -90,7 +89,7 @@ func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) microsoftteams res := microsoftteamsMessage{ IconURL: goFFLogo, Body: MessageBody{Content: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname)}, - Attachments: attachments + Attachments: attachments, } return res diff --git a/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json index 0803b3a8f8f..222b8a1174a 100644 --- a/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json +++ b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json @@ -1,6 +1,8 @@ { "icon_url": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", - "text": "Changes detected in your feature flag file on: *{{hostname}}*", + "body": { + "content": "Changes detected in your feature flag file on: *{{hostname}}*" + }, "attachments": [ { "color": "#FF0000", @@ -75,4 +77,4 @@ "footer": "go-feature-flag" } ] -} +} \ No newline at end of file From 2ed3fb17ee78dd192843216252c12faeb419d0a6 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Tue, 5 Nov 2024 23:26:37 +0100 Subject: [PATCH 04/17] Updates --- README.md | 1 + .../go_module/notifier/microsoft-teams.md | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 website/docs/go_module/notifier/microsoft-teams.md diff --git a/README.md b/README.md index bb2322b3555..0943774f120 100644 --- a/README.md +++ b/README.md @@ -546,6 +546,7 @@ Available notifiers are: - **Slack** - **Webhook** - **Discord** +- **Microsoft Teams** ## Export data **GO Feature Flag** allows you to export data about the usage of your flags. diff --git a/website/docs/go_module/notifier/microsoft-teams.md b/website/docs/go_module/notifier/microsoft-teams.md new file mode 100644 index 00000000000..760130fa395 --- /dev/null +++ b/website/docs/go_module/notifier/microsoft-teams.md @@ -0,0 +1,40 @@ +--- +sidebar_position: 11 +--- + +# Microsoft Teams Notifier + +The microsoft teams notifier allows to get notified on your favorite microsoft teams channel when an instance of `go-feature-flag` is +detecting changes in the configuration of your flags. + +![Microsoft Teams Notification](/docs/notifier/microsoftteams1.png) + +## Configure Microsoft Teams Notifications + +https://learn.microsoft.com/en-us/graph/api/chatmessage-post + +1. First, get your microsoft team access token. + *Need a hand?*[Click here to see how it's done](https://learn.microsoft.com/en-us/graph/auth/auth-concepts?view=graph-rest-1.0#access-tokens) +2. Add the access token in your env file with the key `MICROSOFT_TEAMS_ACCESS_TOKEN` +3. Copy your Team ID (e.g XXXXXX) as well as well as the Channel ID (e.g YYYYYY) +4. Now, generate/copy your webhook URL. + It should look like: `https://graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages`. +5. In your init method add a microsoft teams notifier + +```go +err := ffclient.Init(ffclient.Config +{ + // ... + Notifiers: []notifier.Notifier{ + µsoftteamsnotifier.Notifier{ + MicrosoftTeamsWebhookURL: "https://graph.microsoft.com/teams/XXXXXX/channels/YYYYYY/messages", + }, + }, +}) +``` + +## **Configuration fields** + +| **Field** | **Description** | +|---------------------|-------------------------------------------| +| `MicrosoftTeamsWebhookURL` | The complete URL of your microsoft teams webhook. | From 2d74ae7becea068009e1f9b73c5e85b571a1e5d0 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 6 Nov 2024 07:46:35 +0100 Subject: [PATCH 05/17] Updates --- website/docs/go_module/notifier/microsoft-teams.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/website/docs/go_module/notifier/microsoft-teams.md b/website/docs/go_module/notifier/microsoft-teams.md index 760130fa395..8c70893b437 100644 --- a/website/docs/go_module/notifier/microsoft-teams.md +++ b/website/docs/go_module/notifier/microsoft-teams.md @@ -7,8 +7,6 @@ sidebar_position: 11 The microsoft teams notifier allows to get notified on your favorite microsoft teams channel when an instance of `go-feature-flag` is detecting changes in the configuration of your flags. -![Microsoft Teams Notification](/docs/notifier/microsoftteams1.png) - ## Configure Microsoft Teams Notifications https://learn.microsoft.com/en-us/graph/api/chatmessage-post From 3a5a80da8275e081be5d92c2a7e97dbaf95da296 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 6 Nov 2024 09:20:34 +0100 Subject: [PATCH 06/17] Updates --- notifier/microsoftteamsnotifier/notifier.go | 76 +++++++++++++------ ...d_call_webhook_and_have_valid_results.json | 27 +++---- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go index 72fac1e41cb..2602727573e 100644 --- a/notifier/microsoftteamsnotifier/notifier.go +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -19,12 +19,12 @@ import ( ) const ( - goFFLogo = "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png" - microsoftteamsFooter = "go-feature-flag" - colorDeleted = "#FF0000" - colorUpdated = "#FFA500" - colorAdded = "#008000" - longMicrosoftTeamsAttachment = 35 + colorDeleted = "#FF0000" + colorUpdated = "#FFA500" + colorAdded = "#008000" + goFFLogo = "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png" + microsoftteamsFooter = "go-feature-flag" + longMicrosoftTeamsAttachment = 100 ) type Notifier struct { @@ -81,15 +81,17 @@ func (c *Notifier) Notify(diff notifier.DiffCache) error { return nil } -func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) microsoftteamsMessage { +func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) AdaptiveCard { hostname, _ := os.Hostname() attachments := convertDeletedFlagsToMicrosoftTeamsMessage(diffCache) attachments = append(attachments, convertUpdatedFlagsToMicrosoftTeamsMessage(diffCache)...) attachments = append(attachments, convertAddedFlagsToMicrosoftTeamsMessage(diffCache)...) - res := microsoftteamsMessage{ - IconURL: goFFLogo, - Body: MessageBody{Content: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname)}, - Attachments: attachments, + sections := attachmentsToSections(attachments) + res := AdaptiveCard{ + Type: "MessageCard", + Context: "https://schema.org/extensions", + Summary: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname), + Sections: sections, } return res @@ -153,12 +155,48 @@ func convertAddedFlagsToMicrosoftTeamsMessage(diff notifier.DiffCache) []attachm return attachments } +func attachmentsToSections(attachments []attachment) []Section { + sections := make([]Section, len(attachments)) + for i, att := range attachments { + facts := make([]Fact, len(att.Fields)) + for j, field := range att.Fields { + facts[j] = Fact(field) + } + + sections[i] = Section{ + Title: att.Title, + Color: att.Color, + Facts: facts, + } + } + return sections +} + +type AdaptiveCard struct { + Type string `json:"@type"` + Context string `json:"@context"` + Summary string `json:"summary"` + Sections []Section `json:"sections"` +} + +type Section struct { + Title string `json:"title,omitempty"` + Text string `json:"text,omitempty"` + Color string `json:"color,omitempty"` + Facts []Fact `json:"facts,omitempty"` +} + +type Fact struct { + Title string `json:"title"` + Value string `json:"value"` + Short bool `json:"short"` +} type attachment struct { - Color string `json:"color"` Title string `json:"title"` - Fields []Field `json:"fields"` - FooterIcon string `json:"footer_icon,omitempty"` - Footer string `json:"footer,omitempty"` + Color string `json:"color"` + FooterIcon string `json:"footer_icon"` + Footer string `json:"footer"` + Fields []Field `json:"fields,omitempty"` } type Field struct { @@ -166,14 +204,6 @@ type Field struct { Value string `json:"value"` Short bool `json:"short"` } -type MessageBody struct { - Content string `json:"content"` -} -type microsoftteamsMessage struct { - IconURL string `json:"icon_url"` - Body MessageBody `json:"body"` - Attachments []attachment `json:"attachments"` -} type ByTitle []Field diff --git a/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json index 222b8a1174a..8e7ec8c269d 100644 --- a/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json +++ b/notifier/microsoftteamsnotifier/testdata/should_call_webhook_and_have_valid_results.json @@ -1,24 +1,20 @@ { - "icon_url": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", - "body": { - "content": "Changes detected in your feature flag file on: *{{hostname}}*" - }, - "attachments": [ + "@context": "https://schema.org/extensions", + "@type": "MessageCard", + "summary": "Changes detected in your feature flag file on: *{{hostname}}*", + "sections": [ { "color": "#FF0000", - "title": "❌ Flag \"test-flag\" deleted", - "fields": null, - "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", - "footer": "go-feature-flag" + "title": "❌ Flag \"test-flag\" deleted" }, { "color": "#FFA500", "title": "✏️ Flag \"test-flag2\" updated", - "fields": [ + "facts": [ { "title": "DefaultRule.Percentages", "value": "(*map[string]float64){\"False\":0, \"True\":100} =\u003e nil", - "short": false + "short": true }, { "title": "DefaultRule.VariationResult", @@ -65,16 +61,11 @@ "value": "nil =\u003e (*string)(\"1.1\")", "short": true } - ], - "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", - "footer": "go-feature-flag" + ] }, { "color": "#008000", - "title": "🆕 Flag \"test-flag3\" created", - "fields": null, - "footer_icon": "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png", - "footer": "go-feature-flag" + "title": "🆕 Flag \"test-flag3\" created" } ] } \ No newline at end of file From 919ff6783ae4edbc740f623344f6ec6999afffae Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 6 Nov 2024 11:02:24 +0100 Subject: [PATCH 07/17] Updates --- cmd/relayproxy/config/notifier.go | 23 ++++++------- cmd/relayproxy/service/gofeatureflag.go | 9 ++++-- cmd/relayproxy/service/gofeatureflag_test.go | 4 +-- notifier/microsoftteamsnotifier/notifier.go | 32 +++++++++---------- .../microsoftteamsnotifier/notifier_test.go | 2 +- 5 files changed, 38 insertions(+), 32 deletions(-) diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 379cd4f37c3..1852760fc64 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -3,14 +3,15 @@ package config import "fmt" type NotifierConf struct { - Kind NotifierKind `mapstructure:"kind" koanf:"kind"` - SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` + Kind NotifierKind `mapstructure:"kind" koanf:"kind"` + // Deprecated: Use WebhookURL instead + SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` MicrosoftTeamsWebhookURL string `mapstructure:"microsoftteamsWebhookUrl" koanf:"microsoftteamsWebhookUrl"` - EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` - Secret string `mapstructure:"secret" koanf:"secret"` - Meta map[string]string `mapstructure:"meta" koanf:"meta"` - Headers map[string][]string `mapstructure:"headers" koanf:"headers"` - WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` + EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` + Secret string `mapstructure:"secret" koanf:"secret"` + Meta map[string]string `mapstructure:"meta" koanf:"meta"` + Headers map[string][]string `mapstructure:"headers" koanf:"headers"` + WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` } func (c *NotifierConf) IsValid() error { @@ -35,10 +36,10 @@ func (c *NotifierConf) IsValid() error { type NotifierKind string const ( - SlackNotifier NotifierKind = "slack" - MicrosoftTeamsNotifier NotifierKind = "microsoftteams" - WebhookNotifier NotifierKind = "webhook" - DiscordNotifier NotifierKind = "discord" + SlackNotifier NotifierKind = "slack" + MicrosoftTeamsNotifier NotifierKind = "microsoftteams" + WebhookNotifier NotifierKind = "webhook" + DiscordNotifier NotifierKind = "discord" ) // IsValid is checking if the value is part of the enum diff --git a/cmd/relayproxy/service/gofeatureflag.go b/cmd/relayproxy/service/gofeatureflag.go index e03e0215918..dad9ac9721c 100644 --- a/cmd/relayproxy/service/gofeatureflag.go +++ b/cmd/relayproxy/service/gofeatureflag.go @@ -21,8 +21,8 @@ import ( "github.com/thomaspoignant/go-feature-flag/exporter/webhookexporter" "github.com/thomaspoignant/go-feature-flag/notifier" "github.com/thomaspoignant/go-feature-flag/notifier/discordnotifier" - "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" "github.com/thomaspoignant/go-feature-flag/notifier/microsoftteamsnotifier" + "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" "github.com/thomaspoignant/go-feature-flag/notifier/webhooknotifier" "github.com/thomaspoignant/go-feature-flag/retriever" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" @@ -323,7 +323,12 @@ func initNotifier(c []config.NotifierConf) ([]notifier.Notifier, error) { } notifiers = append(notifiers, &slacknotifier.Notifier{SlackWebhookURL: cNotif.WebhookURL}) case config.MicrosoftTeamsNotifier: - notifiers = append(notifiers, µsoftteamsnotifier.Notifier{MicrosoftTeamsWebhookURL: cNotif.MicrosoftTeamsWebhookURL}) + notifiers = append( + notifiers, + µsoftteamsnotifier.Notifier{ + MicrosoftTeamsWebhookURL: cNotif.MicrosoftTeamsWebhookURL, + }, + ) case config.WebhookNotifier: notifiers = append(notifiers, &webhooknotifier.Notifier{ diff --git a/cmd/relayproxy/service/gofeatureflag_test.go b/cmd/relayproxy/service/gofeatureflag_test.go index ea8721a87ee..3edfee28745 100644 --- a/cmd/relayproxy/service/gofeatureflag_test.go +++ b/cmd/relayproxy/service/gofeatureflag_test.go @@ -20,8 +20,8 @@ import ( "github.com/thomaspoignant/go-feature-flag/exporter/sqsexporter" "github.com/thomaspoignant/go-feature-flag/exporter/webhookexporter" "github.com/thomaspoignant/go-feature-flag/notifier" - "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" "github.com/thomaspoignant/go-feature-flag/notifier/microsoftteamsnotifier" + "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" "github.com/thomaspoignant/go-feature-flag/notifier/webhooknotifier" "github.com/thomaspoignant/go-feature-flag/retriever" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" @@ -426,7 +426,7 @@ func Test_initNotifier(t *testing.T) { SlackWebhookURL: "http:xxxx.xxx", }, { - Kind: config.MicrosoftTeamsNotifier, + Kind: config.MicrosoftTeamsNotifier, MicrosoftTeamsWebhookURL: "http:zzzz.zzz", }, { diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go index 2602727573e..ca3e268de2f 100644 --- a/notifier/microsoftteamsnotifier/notifier.go +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -11,7 +11,7 @@ import ( "sort" "strings" "sync" - + "github.com/luci/go-render/render" "github.com/r3labs/diff/v3" "github.com/thomaspoignant/go-feature-flag/internal" @@ -19,11 +19,11 @@ import ( ) const ( - colorDeleted = "#FF0000" - colorUpdated = "#FFA500" - colorAdded = "#008000" - goFFLogo = "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png" - microsoftteamsFooter = "go-feature-flag" + colorDeleted = "#FF0000" + colorUpdated = "#FFA500" + colorAdded = "#008000" + goFFLogo = "https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png" + microsoftteamsFooter = "go-feature-flag" longMicrosoftTeamsAttachment = 100 ) @@ -49,7 +49,8 @@ func (c *Notifier) Notify(diff notifier.DiffCache) error { microsoftteamsURL, err := url.Parse(c.MicrosoftTeamsWebhookURL) if err != nil { - return fmt.Errorf("error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: %v", c.MicrosoftTeamsWebhookURL) + return fmt.Errorf("error: (Microsoft Teams Notifier) invalid MicrosoftTeamsWebhookURL: %v", + c.MicrosoftTeamsWebhookURL) } reqBody := convertToMicrosoftTeamsMessage(diff) @@ -88,13 +89,12 @@ func convertToMicrosoftTeamsMessage(diffCache notifier.DiffCache) AdaptiveCard { attachments = append(attachments, convertAddedFlagsToMicrosoftTeamsMessage(diffCache)...) sections := attachmentsToSections(attachments) res := AdaptiveCard{ - Type: "MessageCard", - Context: "https://schema.org/extensions", - Summary: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname), - Sections: sections, + Type: "MessageCard", + Context: "https://schema.org/extensions", + Summary: fmt.Sprintf("Changes detected in your feature flag file on: *%s*", hostname), + Sections: sections, } return res - } func convertDeletedFlagsToMicrosoftTeamsMessage(diffCache notifier.DiffCache) []attachment { @@ -173,10 +173,10 @@ func attachmentsToSections(attachments []attachment) []Section { } type AdaptiveCard struct { - Type string `json:"@type"` - Context string `json:"@context"` - Summary string `json:"summary"` - Sections []Section `json:"sections"` + Type string `json:"@type"` + Context string `json:"@context"` + Summary string `json:"summary"` + Sections []Section `json:"sections"` } type Section struct { diff --git a/notifier/microsoftteamsnotifier/notifier_test.go b/notifier/microsoftteamsnotifier/notifier_test.go index d1982c98089..5b03d69b758 100644 --- a/notifier/microsoftteamsnotifier/notifier_test.go +++ b/notifier/microsoftteamsnotifier/notifier_test.go @@ -194,7 +194,7 @@ func TestMicrosoftTeamsNotifier_Notify(t *testing.T) { c := Notifier{ MicrosoftTeamsWebhookURL: tt.args.url, - httpClient: mockHTTPClient, + httpClient: mockHTTPClient, } err := c.Notify(tt.args.diff) From 9cc0cdd0b463ee74383b1a2a2754ae409ff58b6c Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 6 Nov 2024 18:33:05 +0100 Subject: [PATCH 08/17] Updates --- cmd/relayproxy/config/notifier.go | 5 ++--- notifier/microsoftteamsnotifier/notifier.go | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 1852760fc64..7310f18a8eb 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -6,7 +6,6 @@ type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` // Deprecated: Use WebhookURL instead SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` - MicrosoftTeamsWebhookURL string `mapstructure:"microsoftteamsWebhookUrl" koanf:"microsoftteamsWebhookUrl"` EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` Secret string `mapstructure:"secret" koanf:"secret"` Meta map[string]string `mapstructure:"meta" koanf:"meta"` @@ -21,8 +20,8 @@ func (c *NotifierConf) IsValid() error { if c.Kind == SlackNotifier && c.SlackWebhookURL == "" { return fmt.Errorf("invalid notifier: no \"slackWebhookUrl\" property found for kind \"%s\"", c.Kind) } - if c.Kind == MicrosoftTeamsNotifier && c.MicrosoftTeamsWebhookURL == "" { - return fmt.Errorf("invalid notifier: no \"microsoftteamsWebhookUrl\" property found for kind \"%s\"", c.Kind) + if c.Kind == MicrosoftTeamsNotifier && c.WebhookURL == "" { + return fmt.Errorf("invalid notifier: no \"webhookURL\" property found for kind \"%s\"", c.Kind) } if c.Kind == WebhookNotifier && c.EndpointURL == "" { return fmt.Errorf("invalid notifier: no \"endpointUrl\" property found for kind \"%s\"", c.Kind) diff --git a/notifier/microsoftteamsnotifier/notifier.go b/notifier/microsoftteamsnotifier/notifier.go index ca3e268de2f..96ccccee9cb 100644 --- a/notifier/microsoftteamsnotifier/notifier.go +++ b/notifier/microsoftteamsnotifier/notifier.go @@ -134,7 +134,7 @@ func convertUpdatedFlagsToMicrosoftTeamsMessage(diffCache notifier.DiffCache) [] } } - sort.Sort(ByTitle(attachment.Fields)) + sort.Sort(byTitle(attachment.Fields)) attachments = append(attachments, attachment) } @@ -205,8 +205,8 @@ type Field struct { Short bool `json:"short"` } -type ByTitle []Field +type byTitle []Field -func (a ByTitle) Len() int { return len(a) } -func (a ByTitle) Less(i, j int) bool { return a[i].Title < a[j].Title } -func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byTitle) Len() int { return len(a) } +func (a byTitle) Less(i, j int) bool { return a[i].Title < a[j].Title } +func (a byTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] } From e18915637e5a5755e4281e85708234cb7b600714 Mon Sep 17 00:00:00 2001 From: abdegenius Date: Wed, 6 Nov 2024 20:01:45 +0100 Subject: [PATCH 09/17] Updates --- cmd/relayproxy/service/gofeatureflag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/relayproxy/service/gofeatureflag.go b/cmd/relayproxy/service/gofeatureflag.go index dad9ac9721c..1d7ec3c2287 100644 --- a/cmd/relayproxy/service/gofeatureflag.go +++ b/cmd/relayproxy/service/gofeatureflag.go @@ -326,7 +326,7 @@ func initNotifier(c []config.NotifierConf) ([]notifier.Notifier, error) { notifiers = append( notifiers, µsoftteamsnotifier.Notifier{ - MicrosoftTeamsWebhookURL: cNotif.MicrosoftTeamsWebhookURL, + MicrosoftTeamsWebhookURL: cNotif.WebhookURL, }, ) case config.WebhookNotifier: From 01bf51848dfc6d5b9bd8cd61ff55507b843ef8db Mon Sep 17 00:00:00 2001 From: martin machiebe Date: Wed, 6 Nov 2024 20:05:20 +0100 Subject: [PATCH 10/17] updates --- CONTRIBUTING.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17c21a3aa20..62a7fbba5a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,14 +16,15 @@ Please take a moment to review the following guidelines. 6. [Documentation](#-documentation) 7. [License](#-license) - ## 🚓 Code of Conduct + We expect all contributors to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Please read it thoroughly before contributing. ## 🙇 How Can I Contribute? ### Bug Reports + If you encounter any bugs or issues with the project, please [create a new issue](../../issues/new?assignees=&labels=bug%2Cneeds-triage&projects=&template=bug.yaml&title=%28bug%29+%3Ctitle%3E) and include as many details as possible, such as: - A clear and descriptive title @@ -35,7 +36,7 @@ If you encounter any bugs or issues with the project, please [create a new issue ### Feature Requests -If you have a feature idea that you would like to see implemented, please [create a new issue](../../issues/new?assignees=&labels=enhancement%2Cneeds-triage&projects=&template=feature.yaml&title=(feature)+) with the following information: +If you have a feature idea that you would like to see implemented, please [create a new issue](<../../issues/new?assignees=&labels=enhancement%2Cneeds-triage&projects=&template=feature.yaml&title=(feature)+<title>>) with the following information: - A clear and descriptive title - A detailed description of the feature @@ -43,9 +44,9 @@ If you have a feature idea that you would like to see implemented, please [creat ### Pull Requests -We welcome contributions in the form of pull requests. +We welcome contributions in the form of pull requests. -Before opening a pull request, we kindly request you check if there is an open issue related to your proposed contribution. +Before opening a pull request, we kindly request you check if there is an open issue related to your proposed contributions. By doing so, we can initiate a discussion and provide feedback on your changes before proceeding with the pull request. This approach ensures that your efforts align with the project's goals and enhances the chances of your contribution being successfully integrated. Thank you for your understanding and cooperation! @@ -72,13 +73,15 @@ Please be patient, as it might take some time for us to get back to you Your contributions are highly valued! ## 🧑‍💻 Development Setup + We always strive to keep the project as simple as possible, so you will find everything you need in the `Makefile` at the root of the repository. -To start contributing please set up your GO environment and run: +To start contributing please set up your GO environment and run: ```shell make vendor ``` + It will download the dependencies and your project will be ready to be used. ### Coding standards @@ -86,15 +89,18 @@ It will download the dependencies and your project will be ready to be used. It is easier for contributors to work on the same project if it has a consistent, unified style, approach, and layout. To help with that, we are using [pre-commit](https://pre-commit.com/) to lint before each commit, I would recommend you to install it, and to apply it to the project by running: + ```bash pre-commit install ``` ### Tests + Every feature or bug should come with an associate test to keep the coverage as high as possible. We aim to have 90% of coverage for the project. ## 🤔 Where can I ask questions about the project? + If you want to contribute and you have any questions you can use different ways to contact us. 1. You can create an issue and ask your question - [Create an issue](https://github.com/thomaspoignant/go-feature-flag/issues/new/choose). @@ -102,24 +108,25 @@ If you want to contribute and you have any questions you can use different ways To join: - Request an invitation [here](https://invite.slack.golangbridge.org/) - Join the [`#go-feature-flag`](https://gophers.slack.com/archives/C029TH8KDFG) channel. -4. Send us an email to contact@gofeatureflag.org +3. Send us an email to contact@gofeatureflag.org ## 🎤 Community Meeting + Since everyone's voice is important we want to hear back from the community. For this reason, we are launching a community meeting every 2 weeks and it is the perfect place to discuss the future of GO Feature Flag and help you use it at full potential. -| Name | Meeting Time | Meeting Notes | Discussions | -| ---- | ------------ | ------------- | ----------- | +| Name | Meeting Time | Meeting Notes | Discussions | +| --------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | GO Feature Flag Community Meeting | Every other Thursday at 10:00 am ET / 16:00 CET | [Google Doc](https://docs.google.com/document/d/13hVS1Eoq6iHwegdk4lveTE3jV4mUehVGqUtf5TLH2pY/edit) | [VC Link (meet)](https://meet.google.com/fpg-ckxs-vmr) | - [Calendar](https://bit.ly/gofeatureflag-calendar) - [ICS](https://calendar.google.com/calendar/ical/30ba1a7fbba6dc31596a2686f6ab22e9971e8785289033f8bb32319c93dd3b59%40group.calendar.google.com/public/basic.ics) - [Web](https://calendar.google.com/calendar/embed?src=30ba1a7fbba6dc31596a2686f6ab22e9971e8785289033f8bb32319c93dd3b59%40group.calendar.google.com&ctz=Europe%2FParis) - ## 📚 Documentation We are maintaining 2 documentations: + - [README.md](README.md) which contains everything you need to know to start working with the module. - [go-feature-flag website](https://gofeatureflag.org) which is the full detail website containing the documentation. @@ -134,9 +141,11 @@ You can start locally the website. 1. Open a terminal and go to the root project of this repository. 2. Launch the command below, it will install the dependencies and run the local server for the documentation. + ```shell make watch-doc ``` + 3. You can now access to the documentation directly in your browser: [http://localhost:3000/](http://localhost:3000/). ## 🪪 License @@ -149,4 +158,4 @@ If you want to check the stats of GO Feature Flag you can have a look at https:/ --- -We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉 +We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉 From 6e7f59ca862351ce3ca614717e9afb6734321c18 Mon Sep 17 00:00:00 2001 From: abdegenius <johnnieabijah2000@gmail.com> Date: Wed, 6 Nov 2024 20:23:36 +0100 Subject: [PATCH 11/17] Updates --- cmd/relayproxy/service/gofeatureflag_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/relayproxy/service/gofeatureflag_test.go b/cmd/relayproxy/service/gofeatureflag_test.go index 3edfee28745..88abc13168c 100644 --- a/cmd/relayproxy/service/gofeatureflag_test.go +++ b/cmd/relayproxy/service/gofeatureflag_test.go @@ -20,7 +20,6 @@ import ( "github.com/thomaspoignant/go-feature-flag/exporter/sqsexporter" "github.com/thomaspoignant/go-feature-flag/exporter/webhookexporter" "github.com/thomaspoignant/go-feature-flag/notifier" - "github.com/thomaspoignant/go-feature-flag/notifier/microsoftteamsnotifier" "github.com/thomaspoignant/go-feature-flag/notifier/slacknotifier" "github.com/thomaspoignant/go-feature-flag/notifier/webhooknotifier" "github.com/thomaspoignant/go-feature-flag/retriever" @@ -425,10 +424,6 @@ func Test_initNotifier(t *testing.T) { Kind: config.SlackNotifier, SlackWebhookURL: "http:xxxx.xxx", }, - { - Kind: config.MicrosoftTeamsNotifier, - MicrosoftTeamsWebhookURL: "http:zzzz.zzz", - }, { Kind: config.WebhookNotifier, EndpointURL: "http:yyyy.yyy", @@ -437,7 +432,6 @@ func Test_initNotifier(t *testing.T) { }, want: []notifier.Notifier{ &slacknotifier.Notifier{SlackWebhookURL: "http:xxxx.xxx"}, - µsoftteamsnotifier.Notifier{MicrosoftTeamsWebhookURL: "http:zzzz.zzz"}, &webhooknotifier.Notifier{EndpointURL: "http:yyyy.yyy"}, }, wantErr: assert.NoError, From 7a59dd19a7243ee4ffad5d080866ba0ac101645a Mon Sep 17 00:00:00 2001 From: martin machiebe <martinmachiebe21@gmail.com> Date: Wed, 6 Nov 2024 20:40:19 +0100 Subject: [PATCH 12/17] updates --- cmd/relayproxy/config/notifier.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 133a3cbd09f..e4fc7af2a75 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -5,12 +5,12 @@ import "fmt" type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` // Deprecated: Use WebhookURL instead - SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` - EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` - Secret string `mapstructure:"secret" koanf:"secret"` - Meta map[string]string `mapstructure:"meta" koanf:"meta"` - Headers map[string][]string `mapstructure:"headers" koanf:"headers"` - WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` + SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` + EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` + Secret string `mapstructure:"secret" koanf:"secret"` + Meta map[string]string `mapstructure:"meta" koanf:"meta"` + Headers map[string][]string `mapstructure:"headers" koanf:"headers"` + WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` } func (c *NotifierConf) IsValid() error { From 90db4e03b64d974fee165572542f75ea89adddc8 Mon Sep 17 00:00:00 2001 From: abdegenius <johnnieabijah2000@gmail.com> Date: Wed, 6 Nov 2024 21:14:51 +0100 Subject: [PATCH 13/17] Updates --- CONTRIBUTING.md | 2 +- cmd/relayproxy/config/notifier.go | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17c21a3aa20..bfd6521bad5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -149,4 +149,4 @@ If you want to check the stats of GO Feature Flag you can have a look at https:/ --- -We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉 +We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉 \ No newline at end of file diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 7310f18a8eb..9dc888c7da8 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -5,12 +5,12 @@ import "fmt" type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` // Deprecated: Use WebhookURL instead - SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackWebhookUrl"` - EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointUrl"` - Secret string `mapstructure:"secret" koanf:"secret"` - Meta map[string]string `mapstructure:"meta" koanf:"meta"` - Headers map[string][]string `mapstructure:"headers" koanf:"headers"` - WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` + SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackwebhookurl"` + EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointurl"` + Secret string `mapstructure:"secret" koanf:"secret"` + Meta map[string]string `mapstructure:"meta" koanf:"meta"` + Headers map[string][]string `mapstructure:"headers" koanf:"headers"` + WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` } func (c *NotifierConf) IsValid() error { @@ -21,7 +21,7 @@ func (c *NotifierConf) IsValid() error { return fmt.Errorf("invalid notifier: no \"slackWebhookUrl\" property found for kind \"%s\"", c.Kind) } if c.Kind == MicrosoftTeamsNotifier && c.WebhookURL == "" { - return fmt.Errorf("invalid notifier: no \"webhookURL\" property found for kind \"%s\"", c.Kind) + return fmt.Errorf("invalid notifier: no \"WebhookURL\" property found for kind \"%s\"", c.Kind) } if c.Kind == WebhookNotifier && c.EndpointURL == "" { return fmt.Errorf("invalid notifier: no \"endpointUrl\" property found for kind \"%s\"", c.Kind) From b9a8d501c370582529f2ac310de51d7e6fb43248 Mon Sep 17 00:00:00 2001 From: abdegenius <johnnieabijah2000@gmail.com> Date: Wed, 6 Nov 2024 21:22:20 +0100 Subject: [PATCH 14/17] Updates --- cmd/relayproxy/config/notifier.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 9dc888c7da8..8ab7779254e 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -5,12 +5,12 @@ import "fmt" type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` // Deprecated: Use WebhookURL instead - SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackwebhookurl"` - EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointurl"` - Secret string `mapstructure:"secret" koanf:"secret"` - Meta map[string]string `mapstructure:"meta" koanf:"meta"` - Headers map[string][]string `mapstructure:"headers" koanf:"headers"` - WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` + SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackwebhookurl"` + EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointurl"` + Secret string `mapstructure:"secret" koanf:"secret"` + Meta map[string]string `mapstructure:"meta" koanf:"meta"` + Headers map[string][]string `mapstructure:"headers" koanf:"headers"` + WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` } func (c *NotifierConf) IsValid() error { From b10e67011b5a4c7e67312d3c4f3cf0469addaf4f Mon Sep 17 00:00:00 2001 From: abdegenius <johnnieabijah2000@gmail.com> Date: Wed, 6 Nov 2024 21:42:14 +0100 Subject: [PATCH 15/17] Updates --- cmd/relayproxy/config/notifier.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/relayproxy/config/notifier.go b/cmd/relayproxy/config/notifier.go index 8ab7779254e..9dc888c7da8 100644 --- a/cmd/relayproxy/config/notifier.go +++ b/cmd/relayproxy/config/notifier.go @@ -5,12 +5,12 @@ import "fmt" type NotifierConf struct { Kind NotifierKind `mapstructure:"kind" koanf:"kind"` // Deprecated: Use WebhookURL instead - SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackwebhookurl"` - EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointurl"` - Secret string `mapstructure:"secret" koanf:"secret"` - Meta map[string]string `mapstructure:"meta" koanf:"meta"` - Headers map[string][]string `mapstructure:"headers" koanf:"headers"` - WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` + SlackWebhookURL string `mapstructure:"slackWebhookUrl" koanf:"slackwebhookurl"` + EndpointURL string `mapstructure:"endpointUrl" koanf:"endpointurl"` + Secret string `mapstructure:"secret" koanf:"secret"` + Meta map[string]string `mapstructure:"meta" koanf:"meta"` + Headers map[string][]string `mapstructure:"headers" koanf:"headers"` + WebhookURL string `mapstructure:"webhookUrl" koanf:"webhookurl"` } func (c *NotifierConf) IsValid() error { From 0ac6b730162eaed6837270bb7ab7a7399edd6dd9 Mon Sep 17 00:00:00 2001 From: martin machiebe <martinmachiebe21@gmail.com> Date: Wed, 6 Nov 2024 21:50:38 +0100 Subject: [PATCH 16/17] updates --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62a7fbba5a3..47cd5be8a27 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,7 @@ If you encounter any bugs or issues with the project, please [create a new issue ### Feature Requests -If you have a feature idea that you would like to see implemented, please [create a new issue](<../../issues/new?assignees=&labels=enhancement%2Cneeds-triage&projects=&template=feature.yaml&title=(feature)+<title>>) with the following information: +If you have a feature idea that you would like to see implemented, please [create a new issue](../../issues/new?assignees=&labels=enhancement%2Cneeds-triage&projects=&template=feature.yaml&title=(feature)+<title>) with the following information: - A clear and descriptive title - A detailed description of the feature From 124aab7d9cea315e635609b9627112dc23fdbf91 Mon Sep 17 00:00:00 2001 From: Thomas Poignant <thomas.poignant@gofeatureflag.org> Date: Thu, 7 Nov 2024 16:44:17 +0100 Subject: [PATCH 17/17] fix: Remove changes for contributing.md --- CONTRIBUTING.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47cd5be8a27..17c21a3aa20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,15 +16,14 @@ Please take a moment to review the following guidelines. 6. [Documentation](#-documentation) 7. [License](#-license) -## 🚓 Code of Conduct +## 🚓 Code of Conduct We expect all contributors to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Please read it thoroughly before contributing. ## 🙇 How Can I Contribute? ### Bug Reports - If you encounter any bugs or issues with the project, please [create a new issue](../../issues/new?assignees=&labels=bug%2Cneeds-triage&projects=&template=bug.yaml&title=%28bug%29+%3Ctitle%3E) and include as many details as possible, such as: - A clear and descriptive title @@ -44,9 +43,9 @@ If you have a feature idea that you would like to see implemented, please [creat ### Pull Requests -We welcome contributions in the form of pull requests. +We welcome contributions in the form of pull requests. -Before opening a pull request, we kindly request you check if there is an open issue related to your proposed contributions. +Before opening a pull request, we kindly request you check if there is an open issue related to your proposed contribution. By doing so, we can initiate a discussion and provide feedback on your changes before proceeding with the pull request. This approach ensures that your efforts align with the project's goals and enhances the chances of your contribution being successfully integrated. Thank you for your understanding and cooperation! @@ -73,15 +72,13 @@ Please be patient, as it might take some time for us to get back to you Your contributions are highly valued! ## 🧑‍💻 Development Setup - We always strive to keep the project as simple as possible, so you will find everything you need in the `Makefile` at the root of the repository. -To start contributing please set up your GO environment and run: +To start contributing please set up your GO environment and run: ```shell make vendor ``` - It will download the dependencies and your project will be ready to be used. ### Coding standards @@ -89,18 +86,15 @@ It will download the dependencies and your project will be ready to be used. It is easier for contributors to work on the same project if it has a consistent, unified style, approach, and layout. To help with that, we are using [pre-commit](https://pre-commit.com/) to lint before each commit, I would recommend you to install it, and to apply it to the project by running: - ```bash pre-commit install ``` ### Tests - Every feature or bug should come with an associate test to keep the coverage as high as possible. We aim to have 90% of coverage for the project. ## 🤔 Where can I ask questions about the project? - If you want to contribute and you have any questions you can use different ways to contact us. 1. You can create an issue and ask your question - [Create an issue](https://github.com/thomaspoignant/go-feature-flag/issues/new/choose). @@ -108,25 +102,24 @@ If you want to contribute and you have any questions you can use different ways To join: - Request an invitation [here](https://invite.slack.golangbridge.org/) - Join the [`#go-feature-flag`](https://gophers.slack.com/archives/C029TH8KDFG) channel. -3. Send us an email to contact@gofeatureflag.org +4. Send us an email to contact@gofeatureflag.org ## 🎤 Community Meeting - Since everyone's voice is important we want to hear back from the community. For this reason, we are launching a community meeting every 2 weeks and it is the perfect place to discuss the future of GO Feature Flag and help you use it at full potential. -| Name | Meeting Time | Meeting Notes | Discussions | -| --------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| Name | Meeting Time | Meeting Notes | Discussions | +| ---- | ------------ | ------------- | ----------- | | GO Feature Flag Community Meeting | Every other Thursday at 10:00 am ET / 16:00 CET | [Google Doc](https://docs.google.com/document/d/13hVS1Eoq6iHwegdk4lveTE3jV4mUehVGqUtf5TLH2pY/edit) | [VC Link (meet)](https://meet.google.com/fpg-ckxs-vmr) | - [Calendar](https://bit.ly/gofeatureflag-calendar) - [ICS](https://calendar.google.com/calendar/ical/30ba1a7fbba6dc31596a2686f6ab22e9971e8785289033f8bb32319c93dd3b59%40group.calendar.google.com/public/basic.ics) - [Web](https://calendar.google.com/calendar/embed?src=30ba1a7fbba6dc31596a2686f6ab22e9971e8785289033f8bb32319c93dd3b59%40group.calendar.google.com&ctz=Europe%2FParis) + ## 📚 Documentation We are maintaining 2 documentations: - - [README.md](README.md) which contains everything you need to know to start working with the module. - [go-feature-flag website](https://gofeatureflag.org) which is the full detail website containing the documentation. @@ -141,11 +134,9 @@ You can start locally the website. 1. Open a terminal and go to the root project of this repository. 2. Launch the command below, it will install the dependencies and run the local server for the documentation. - ```shell make watch-doc ``` - 3. You can now access to the documentation directly in your browser: [http://localhost:3000/](http://localhost:3000/). ## 🪪 License @@ -158,4 +149,4 @@ If you want to check the stats of GO Feature Flag you can have a look at https:/ --- -We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉 +We encourage everyone to participate in this project and make it better for everyone. Happy contributing 🎉