Skip to content

Commit f9684b8

Browse files
authored
feat!: consolidated configuration change events into one event (#241)
## This PR Consolidates configuration change events into a singular event (avoid superfluous bulk emissions). Existing `configuration_change` event listeners will need to update their handling to consume the singular events. ### Related Issues <!-- add here the GitHub issue that this PR resolves if applicable --> Fixes #238 - [x] [Update go-sdk-contrib](open-feature/go-sdk-contrib#66) - [ ] Update java-sdk-contrib ### Notes <!-- any additional notes for this PR --> ### Follow-up Tasks <!-- anything that is related to this PR but not done here should be noted under this section --> <!-- if there is a need for a new issue, please link it here --> ### How to test <!-- if applicable, add testing instructions under this section --> Signed-off-by: Skye Gill <[email protected]>
1 parent 3f406b5 commit f9684b8

File tree

6 files changed

+61
-65
lines changed

6 files changed

+61
-65
lines changed

pkg/eval/ievaluator.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,13 @@ const (
1212
NotificationUpdate StateChangeNotificationType = "update"
1313
)
1414

15-
type StateChangeNotification struct {
16-
Type StateChangeNotificationType `json:"type"`
17-
Source string `json:"source"`
18-
FlagKey string `json:"flagKey"`
19-
}
20-
2115
/*
2216
IEvaluator implementations store the state of the flags,
2317
do parsing and validation of the flag state and evaluate flags in response to handlers.
2418
*/
2519
type IEvaluator interface {
2620
GetState() (string, error)
27-
SetState(source string, state string) ([]StateChangeNotification, error)
21+
SetState(source string, state string) (map[string]interface{}, error)
2822

2923
ResolveBooleanValue(
3024
reqID string,
@@ -47,11 +41,3 @@ type IEvaluator interface {
4741
flagKey string,
4842
context *structpb.Struct) (value map[string]any, variant string, reasons string, err error)
4943
}
50-
51-
func (s *StateChangeNotification) ToMap() map[string]interface{} {
52-
return map[string]interface{}{
53-
"type": string(s.Type),
54-
"source": s.Source,
55-
"flagKey": s.FlagKey,
56-
}
57-
}

pkg/eval/json_evaluator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (je *JSONEvaluator) GetState() (string, error) {
4949
return string(data), nil
5050
}
5151

52-
func (je *JSONEvaluator) SetState(source string, state string) ([]StateChangeNotification, error) {
52+
func (je *JSONEvaluator) SetState(source string, state string) (map[string]interface{}, error) {
5353
schemaLoader := gojsonschema.NewStringLoader(schema.FlagdDefinitions)
5454
flagStringLoader := gojsonschema.NewStringLoader(state)
5555
result, err := gojsonschema.Validate(schemaLoader, flagStringLoader)

pkg/eval/json_evaluator_model.go

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,17 @@ type Evaluators struct {
1616
Evaluators map[string]json.RawMessage `json:"$evaluators"`
1717
}
1818

19-
func (f Flags) Merge(logger *logger.Logger, source string, ff Flags) (Flags, []StateChangeNotification) {
20-
notifications := []StateChangeNotification{}
19+
func (f Flags) Merge(logger *logger.Logger, source string, ff Flags) (Flags, map[string]interface{}) {
20+
notifications := map[string]interface{}{}
2121
result := Flags{Flags: make(map[string]Flag)}
2222
for k, v := range f.Flags {
2323
if v.Source == source {
2424
if _, ok := ff.Flags[k]; !ok {
2525
// flag has been deleted
26-
notifications = append(notifications, StateChangeNotification{
27-
Type: NotificationDelete,
28-
Source: source,
29-
FlagKey: k,
30-
})
26+
notifications[k] = map[string]interface{}{
27+
"type": string(NotificationDelete),
28+
"source": source,
29+
}
3130
continue
3231
}
3332
}
@@ -37,11 +36,10 @@ func (f Flags) Merge(logger *logger.Logger, source string, ff Flags) (Flags, []S
3736
v.Source = source
3837
val, ok := result.Flags[k]
3938
if !ok {
40-
notifications = append(notifications, StateChangeNotification{
41-
Type: NotificationCreate,
42-
Source: source,
43-
FlagKey: k,
44-
})
39+
notifications[k] = map[string]interface{}{
40+
"type": string(NotificationCreate),
41+
"source": source,
42+
}
4543
} else if !reflect.DeepEqual(val, v) {
4644
if val.Source != source {
4745
logger.Warn(
@@ -53,11 +51,10 @@ func (f Flags) Merge(logger *logger.Logger, source string, ff Flags) (Flags, []S
5351
),
5452
)
5553
}
56-
notifications = append(notifications, StateChangeNotification{
57-
Type: NotificationUpdate,
58-
Source: source,
59-
FlagKey: k,
60-
})
54+
notifications[k] = map[string]interface{}{
55+
"type": string(NotificationUpdate),
56+
"source": source,
57+
}
6158
}
6259
result.Flags[k] = v
6360
}

pkg/eval/json_evaluator_test.go

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -739,35 +739,40 @@ func BenchmarkResolveObjectValue(b *testing.B) {
739739
func TestMergeFlags(t *testing.T) {
740740
t.Parallel()
741741
tests := []struct {
742-
name string
743-
current eval.Flags
744-
new eval.Flags
745-
newSource string
746-
want eval.Flags
742+
name string
743+
current eval.Flags
744+
new eval.Flags
745+
newSource string
746+
want eval.Flags
747+
wantNotifs map[string]interface{}
747748
}{
748749
{
749-
name: "both nil",
750-
current: eval.Flags{Flags: nil},
751-
new: eval.Flags{Flags: nil},
752-
want: eval.Flags{Flags: map[string]eval.Flag{}},
750+
name: "both nil",
751+
current: eval.Flags{Flags: nil},
752+
new: eval.Flags{Flags: nil},
753+
want: eval.Flags{Flags: map[string]eval.Flag{}},
754+
wantNotifs: map[string]interface{}{},
753755
},
754756
{
755-
name: "both empty flags",
756-
current: eval.Flags{Flags: map[string]eval.Flag{}},
757-
new: eval.Flags{Flags: map[string]eval.Flag{}},
758-
want: eval.Flags{Flags: map[string]eval.Flag{}},
757+
name: "both empty flags",
758+
current: eval.Flags{Flags: map[string]eval.Flag{}},
759+
new: eval.Flags{Flags: map[string]eval.Flag{}},
760+
want: eval.Flags{Flags: map[string]eval.Flag{}},
761+
wantNotifs: map[string]interface{}{},
759762
},
760763
{
761-
name: "empty current",
762-
current: eval.Flags{Flags: nil},
763-
new: eval.Flags{Flags: map[string]eval.Flag{}},
764-
want: eval.Flags{Flags: map[string]eval.Flag{}},
764+
name: "empty current",
765+
current: eval.Flags{Flags: nil},
766+
new: eval.Flags{Flags: map[string]eval.Flag{}},
767+
want: eval.Flags{Flags: map[string]eval.Flag{}},
768+
wantNotifs: map[string]interface{}{},
765769
},
766770
{
767-
name: "empty new",
768-
current: eval.Flags{Flags: map[string]eval.Flag{}},
769-
new: eval.Flags{Flags: nil},
770-
want: eval.Flags{Flags: map[string]eval.Flag{}},
771+
name: "empty new",
772+
current: eval.Flags{Flags: map[string]eval.Flag{}},
773+
new: eval.Flags{Flags: nil},
774+
want: eval.Flags{Flags: map[string]eval.Flag{}},
775+
wantNotifs: map[string]interface{}{},
771776
},
772777
{
773778
name: "extra fields on each",
@@ -793,6 +798,9 @@ func TestMergeFlags(t *testing.T) {
793798
Source: "2",
794799
},
795800
}},
801+
wantNotifs: map[string]interface{}{
802+
"paka": map[string]interface{}{"type": "write", "source": "2"},
803+
},
796804
},
797805
{
798806
name: "override",
@@ -807,6 +815,10 @@ func TestMergeFlags(t *testing.T) {
807815
"waka": {DefaultVariant: "on"},
808816
"paka": {DefaultVariant: "on"},
809817
}},
818+
wantNotifs: map[string]interface{}{
819+
"waka": map[string]interface{}{"type": "update", "source": ""},
820+
"paka": map[string]interface{}{"type": "write", "source": ""},
821+
},
810822
},
811823
{
812824
name: "identical",
@@ -819,15 +831,17 @@ func TestMergeFlags(t *testing.T) {
819831
want: eval.Flags{Flags: map[string]eval.Flag{
820832
"hello": {DefaultVariant: "off"},
821833
}},
834+
wantNotifs: map[string]interface{}{},
822835
},
823836
}
824837

825838
for _, tt := range tests {
826839
tt := tt
827840
t.Run(tt.name, func(t *testing.T) {
828841
t.Parallel()
829-
got, _ := tt.current.Merge(logger.NewLogger(nil, false), tt.newSource, tt.new)
842+
got, gotNotifs := tt.current.Merge(logger.NewLogger(nil, false), tt.newSource, tt.new)
830843
require.Equal(t, tt.want, got)
844+
require.Equal(t, tt.wantNotifs, gotNotifs)
831845
})
832846
}
833847
}

pkg/eval/mock/ievaluator.go

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/runtime/runtime.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ func (r *Runtime) updateState(ctx context.Context, syncr sync.ISync) error {
8080
if err != nil {
8181
return fmt.Errorf("set state: %w", err)
8282
}
83-
for _, n := range notifications {
84-
r.Logger.Info(fmt.Sprintf("configuration change (%s) for flagKey %s (%s)", n.Type, n.FlagKey, n.Source))
85-
r.Service.Notify(service.Notification{
86-
Type: service.ConfigurationChange,
87-
Data: n.ToMap(),
88-
})
89-
}
83+
84+
r.Service.Notify(service.Notification{
85+
Type: service.ConfigurationChange,
86+
Data: map[string]interface{}{
87+
"flags": notifications,
88+
},
89+
})
9090
return nil
9191
}

0 commit comments

Comments
 (0)