From 39e00f11a8bdef55cb10a7a52edeed347e701a62 Mon Sep 17 00:00:00 2001 From: Mohamed MHAMDI Date: Sat, 24 Sep 2022 02:46:18 +0200 Subject: [PATCH] feat(config): add withFs option to file source (#2557) Co-authored-by: Mohamed MHAMDI --- config/source/file/file.go | 22 ++++++++++++++++++++-- config/source/file/file_test.go | 23 +++++++++++++++++++++++ config/source/file/options.go | 12 ++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/config/source/file/file.go b/config/source/file/file.go index 3ad0298c..a612d47a 100644 --- a/config/source/file/file.go +++ b/config/source/file/file.go @@ -3,12 +3,14 @@ package file import ( "io" + "io/fs" "os" "go-micro.dev/v4/config/source" ) type file struct { + fs fs.FS path string opts source.Options } @@ -18,7 +20,15 @@ var ( ) func (f *file) Read() (*source.ChangeSet, error) { - fh, err := os.Open(f.path) + var fh fs.File + var err error + + if f.fs != nil { + fh, err = f.fs.Open(f.path) + } else { + fh, err = os.Open(f.path) + } + if err != nil { return nil, err } @@ -48,6 +58,11 @@ func (f *file) String() string { } func (f *file) Watch() (source.Watcher, error) { + // do not watch if fs.FS instance is provided + if f.fs != nil { + return source.NewNoopWatcher() + } + if _, err := os.Stat(f.path); err != nil { return nil, err } @@ -60,10 +75,13 @@ func (f *file) Write(cs *source.ChangeSet) error { func NewSource(opts ...source.Option) source.Source { options := source.NewOptions(opts...) + + fs, _ := options.Context.Value(fsKey{}).(fs.FS) + path := DefaultPath f, ok := options.Context.Value(filePathKey{}).(string) if ok { path = f } - return &file{opts: options, path: path} + return &file{opts: options, fs: fs, path: path} } diff --git a/config/source/file/file_test.go b/config/source/file/file_test.go index 5f60d2bf..d204b1ad 100644 --- a/config/source/file/file_test.go +++ b/config/source/file/file_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "testing" + "testing/fstest" "time" "go-micro.dev/v4/config" @@ -64,3 +65,25 @@ func TestFile(t *testing.T) { t.Error("data from file does not match") } } + +func TestWithFS(t *testing.T) { + data := []byte(`{"foo": "bar"}`) + path := fmt.Sprintf("file.%d", time.Now().UnixNano()) + + fsMock := fstest.MapFS{ + path: &fstest.MapFile{ + Data: data, + Mode: 0666, + }, + } + + f := file.NewSource(file.WithFS(fsMock), file.WithPath(path)) + c, err := f.Read() + if err != nil { + t.Error(err) + } + if string(c.Data) != string(data) { + t.Logf("%+v", c) + t.Error("data from file does not match") + } +} diff --git a/config/source/file/options.go b/config/source/file/options.go index 8d014953..be55ed99 100644 --- a/config/source/file/options.go +++ b/config/source/file/options.go @@ -2,11 +2,13 @@ package file import ( "context" + "io/fs" "go-micro.dev/v4/config/source" ) type filePathKey struct{} +type fsKey struct{} // WithPath sets the path to file func WithPath(p string) source.Option { @@ -17,3 +19,13 @@ func WithPath(p string) source.Option { o.Context = context.WithValue(o.Context, filePathKey{}, p) } } + +// WithFS sets the underlying filesystem to lookup file from (default os.FS) +func WithFS(fs fs.FS) source.Option { + return func(o *source.Options) { + if o.Context == nil { + o.Context = context.Background() + } + o.Context = context.WithValue(o.Context, fsKey{}, fs) + } +}