From ad08271fbd0d5525388090442b31e7637d777a99 Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Wed, 18 Dec 2024 21:38:43 +0100 Subject: [PATCH 1/2] WIP Signed-off-by: Thomas Poignant --- internal/flagset/flagset.go | 23 +++++++ internal/flagset/flagsetBuilder.go | 65 +++++++++++++++++++ internal/flagset/flagsetBuilder_test.go | 84 +++++++++++++++++++++++++ internal/flagset/manager.go | 21 +++++++ 4 files changed, 193 insertions(+) create mode 100644 internal/flagset/flagset.go create mode 100644 internal/flagset/flagsetBuilder.go create mode 100644 internal/flagset/flagsetBuilder_test.go create mode 100644 internal/flagset/manager.go diff --git a/internal/flagset/flagset.go b/internal/flagset/flagset.go new file mode 100644 index 00000000000..195ba0fedbc --- /dev/null +++ b/internal/flagset/flagset.go @@ -0,0 +1,23 @@ +package flagset + +import ( + "github.com/thomaspoignant/go-feature-flag/exporter" + "github.com/thomaspoignant/go-feature-flag/internal/cache" + "github.com/thomaspoignant/go-feature-flag/retriever" +) + +type Type string + +const ( + FlagSetTypeStatic Type = "static" + FlagSetTypeDynamic Type = "dynamic" +) + +type FlagSet struct { + Name string + Type Type + + Cache cache.Manager + DataExporter *exporter.Scheduler + RetrieverManager *retriever.Manager +} diff --git a/internal/flagset/flagsetBuilder.go b/internal/flagset/flagsetBuilder.go new file mode 100644 index 00000000000..d243260cc5a --- /dev/null +++ b/internal/flagset/flagsetBuilder.go @@ -0,0 +1,65 @@ +package flagset + +import ( + "errors" + "github.com/thomaspoignant/go-feature-flag/exporter" + "github.com/thomaspoignant/go-feature-flag/notifier" + "github.com/thomaspoignant/go-feature-flag/retriever" +) + +var ( + ErrInvalidFlagSetType = errors.New("invalid flagset type") + ErrNameRequired = errors.New("flagset name is required") +) + +type Builder interface { + Notifiers([]notifier.Notifier) Builder + Retrievers([]retriever.Retriever) Builder + Exporter(exporter.CommonExporter) Builder + Build() (*FlagSet, error) +} + +type builderImpl struct { + name string + flagSetType Type + notifiers []notifier.Notifier + retrievers []retriever.Retriever + exporter exporter.CommonExporter +} + +func NewBuilder(name string, flagSetType Type) Builder { + return &builderImpl{ + name: name, + flagSetType: flagSetType, + } +} + +func (f *builderImpl) Notifiers(notifiers []notifier.Notifier) Builder { + f.notifiers = notifiers + return f +} + +func (f *builderImpl) Retrievers(retrievers []retriever.Retriever) Builder { + f.retrievers = retrievers + return f +} + +func (f *builderImpl) Exporter(exporter exporter.CommonExporter) Builder { + f.exporter = exporter + return f +} + +func (f *builderImpl) Build() (*FlagSet, error) { + if f.name == "" { + return nil, ErrNameRequired + } + if f.flagSetType != FlagSetTypeDynamic && f.flagSetType != FlagSetTypeStatic { + return nil, ErrInvalidFlagSetType + } + + return &FlagSet{ + Name: f.name, + Type: f.flagSetType, + }, nil + +} diff --git a/internal/flagset/flagsetBuilder_test.go b/internal/flagset/flagsetBuilder_test.go new file mode 100644 index 00000000000..ec6ea2d5b31 --- /dev/null +++ b/internal/flagset/flagsetBuilder_test.go @@ -0,0 +1,84 @@ +package flagset_test + +import ( + "github.com/stretchr/testify/assert" + "github.com/thomaspoignant/go-feature-flag/exporter" + "github.com/thomaspoignant/go-feature-flag/exporter/logsexporter" + "github.com/thomaspoignant/go-feature-flag/internal/flagset" + "github.com/thomaspoignant/go-feature-flag/notifier" + "github.com/thomaspoignant/go-feature-flag/notifier/logsnotifier" + "github.com/thomaspoignant/go-feature-flag/retriever" + "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" + "github.com/thomaspoignant/go-feature-flag/utils/fflog" + "log/slog" + "testing" +) + +func TestFlagSetBuilder(t *testing.T) { + tests := []struct { + name string + flagsetName string + flagsetType flagset.Type + wantErr assert.ErrorAssertionFunc + notifiers []notifier.Notifier + retrievers []retriever.Retriever + exporter exporter.CommonExporter + expected *flagset.FlagSet + expectedError string + }{ + { + name: "Should return an error if no name for flag set", + flagsetName: "", + flagsetType: flagset.FlagSetTypeDynamic, + wantErr: assert.Error, + expectedError: "flagset name is required", + }, + { + name: "Should return an error if no flag set type provided", + flagsetName: "test", + wantErr: assert.Error, + expectedError: "invalid flagset type", + }, + { + name: "Should return an error if wrong flag set type provided", + flagsetName: "test", + flagsetType: "foobar", + wantErr: assert.Error, + expectedError: "invalid flagset type", + }, + { + name: "Should return a flag set will everything set", + flagsetName: "test", + wantErr: assert.NoError, + notifiers: []notifier.Notifier{ + &logsnotifier.Notifier{ + Logger: &fflog.FFLogger{LeveledLogger: slog.Default()}, + }, + }, + retrievers: []retriever.Retriever{ + &fileretriever.Retriever{Path: "../../testdata/flag1.json"}, + }, + exporter: &logsexporter.Exporter{}, + expected: &flagset.FlagSet{ + Name: "test", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fsBuilder := flagset.NewBuilder(tt.flagsetName, tt.flagsetType) + fsBuilder.Notifiers(tt.notifiers) + fsBuilder.Retrievers(tt.retrievers) + fsBuilder.Exporter(tt.exporter) + got, err := fsBuilder.Build() + tt.wantErr(t, err) + + if tt.expectedError != "" { + assert.Equal(t, tt.expectedError, err.Error()) + return + } + assert.Equal(t, tt.expected, got) + }) + } +} diff --git a/internal/flagset/manager.go b/internal/flagset/manager.go new file mode 100644 index 00000000000..2d5374ed9d4 --- /dev/null +++ b/internal/flagset/manager.go @@ -0,0 +1,21 @@ +package flagset + +import "fmt" + +type Manager struct { + FlagSets map[string]*FlagSet +} + +func NewManager() *Manager { + return &Manager{ + FlagSets: make(map[string]*FlagSet), + } +} + +func (f *Manager) AddFlagSet(name string, flagSet *FlagSet) error { + if _, ok := f.FlagSets[name]; ok { + return fmt.Errorf("flagset %s already exists", name) + } + f.FlagSets[name] = flagSet + return nil +} From 2e97ed56b0f71ae182abba1e9d87f60bdd25d34f Mon Sep 17 00:00:00 2001 From: Thomas Poignant Date: Mon, 23 Dec 2024 21:14:15 +0100 Subject: [PATCH 2/2] WIP Signed-off-by: Thomas Poignant --- internal/flagset/flagsetBuilder.go | 1 + internal/flagset/flagsetBuilder_test.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/flagset/flagsetBuilder.go b/internal/flagset/flagsetBuilder.go index d243260cc5a..66680577da7 100644 --- a/internal/flagset/flagsetBuilder.go +++ b/internal/flagset/flagsetBuilder.go @@ -2,6 +2,7 @@ package flagset import ( "errors" + "github.com/thomaspoignant/go-feature-flag/exporter" "github.com/thomaspoignant/go-feature-flag/notifier" "github.com/thomaspoignant/go-feature-flag/retriever" diff --git a/internal/flagset/flagsetBuilder_test.go b/internal/flagset/flagsetBuilder_test.go index ec6ea2d5b31..c8951f256bc 100644 --- a/internal/flagset/flagsetBuilder_test.go +++ b/internal/flagset/flagsetBuilder_test.go @@ -1,6 +1,9 @@ package flagset_test import ( + "log/slog" + "testing" + "github.com/stretchr/testify/assert" "github.com/thomaspoignant/go-feature-flag/exporter" "github.com/thomaspoignant/go-feature-flag/exporter/logsexporter" @@ -10,8 +13,6 @@ import ( "github.com/thomaspoignant/go-feature-flag/retriever" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" "github.com/thomaspoignant/go-feature-flag/utils/fflog" - "log/slog" - "testing" ) func TestFlagSetBuilder(t *testing.T) {