diff --git a/asset/README.md b/asset/README.md index a890f19..8c76c1f 100644 --- a/asset/README.md +++ b/asset/README.md @@ -26,6 +26,8 @@ The information provided on this project is strictly for informational purposes - [type FileSystemRepository](<#FileSystemRepository>) - [func NewFileSystemRepository\(base string\) \*FileSystemRepository](<#NewFileSystemRepository>) + - [func \(r \*FileSystemRepository\) Append\(name string, snapshots \<\-chan \*Snapshot\) error](<#FileSystemRepository.Append>) + - [func \(r \*FileSystemRepository\) Assets\(\) \(\[\]string, error\)](<#FileSystemRepository.Assets>) - [func \(r \*FileSystemRepository\) Get\(name string\) \(\<\-chan \*Snapshot, error\)](<#FileSystemRepository.Get>) - [func \(r \*FileSystemRepository\) LastDate\(name string\) \(time.Time, error\)](<#FileSystemRepository.LastDate>) - [type Repository](<#Repository>) @@ -33,7 +35,7 @@ The information provided on this project is strictly for informational purposes -## type [FileSystemRepository]() +## type [FileSystemRepository]() FileSystemRepository stores and retrieves asset snapshots using the local file system. @@ -45,7 +47,7 @@ type FileSystemRepository struct { ``` -### func [NewFileSystemRepository]() +### func [NewFileSystemRepository]() ```go func NewFileSystemRepository(base string) *FileSystemRepository @@ -53,8 +55,26 @@ func NewFileSystemRepository(base string) *FileSystemRepository NewFileSystemRepository initializes a file system repository with the given base directory. + +### func \(\*FileSystemRepository\) [Append]() + +```go +func (r *FileSystemRepository) Append(name string, snapshots <-chan *Snapshot) error +``` + +Append adds the given snapshows to the asset with the given name. + + +### func \(\*FileSystemRepository\) [Assets]() + +```go +func (r *FileSystemRepository) Assets() ([]string, error) +``` + +Assets returns the names of all assets in the repository. + -### func \(\*FileSystemRepository\) [Get]() +### func \(\*FileSystemRepository\) [Get]() ```go func (r *FileSystemRepository) Get(name string) (<-chan *Snapshot, error) @@ -63,7 +83,7 @@ func (r *FileSystemRepository) Get(name string) (<-chan *Snapshot, error) Get attempts to return a channel of snapshots fo the asset with the given name. -### func \(\*FileSystemRepository\) [LastDate]() +### func \(\*FileSystemRepository\) [LastDate]() ```go func (r *FileSystemRepository) LastDate(name string) (time.Time, error) @@ -72,12 +92,15 @@ func (r *FileSystemRepository) LastDate(name string) (time.Time, error) LastDate returns the date of the last snapshot for the asset with the given name. -## type [Repository]() +## type [Repository]() Repository serves as a centralized storage and retrieval location for asset snapshots. ```go type Repository interface { + // Assets returns the names of all assets in the repository. + Assets() ([]string, error) + // Get attempts to return a channel of snapshots for // the asset with the given name. Get(name string) (<-chan *Snapshot, error) @@ -85,6 +108,10 @@ type Repository interface { // LastDate returns the date of the last snapshot for // the asset with the given name. LastDate(name string) (time.Time, error) + + // Append adds the given snapshows to the asset with the + // given name. + Append(name string, snapshots <-chan *Snapshot) error } ``` diff --git a/asset/file_system_repository.go b/asset/file_system_repository.go index 1f9c67e..6135ac7 100644 --- a/asset/file_system_repository.go +++ b/asset/file_system_repository.go @@ -7,7 +7,9 @@ package asset import ( "errors" "fmt" + "os" "path/filepath" + "strings" "time" "github.com/cinar/indicator/helper" @@ -30,10 +32,30 @@ func NewFileSystemRepository(base string) *FileSystemRepository { } } +// Assets returns the names of all assets in the repository. +func (r *FileSystemRepository) Assets() ([]string, error) { + files, err := os.ReadDir(r.base) + if err != nil { + return nil, err + } + + var assets []string + suffix := ".csv" + + for _, file := range files { + name := file.Name() + + if strings.HasSuffix(name, suffix) { + assets = append(assets, strings.TrimSuffix(name, suffix)) + } + } + + return assets, nil +} + // Get attempts to return a channel of snapshots fo the asset with the given name. func (r *FileSystemRepository) Get(name string) (<-chan *Snapshot, error) { - file := filepath.Join(r.base, fmt.Sprintf("%s.csv", name)) - return helper.ReadFromCsvFile[Snapshot](file, true) + return helper.ReadFromCsvFile[Snapshot](r.getCsvFileName(name), true) } // LastDate returns the date of the last snapshot for the asset with the given name. @@ -45,11 +67,20 @@ func (r *FileSystemRepository) LastDate(name string) (time.Time, error) { return last, err } - snapshot := helper.ChanToSlice(helper.Last(snapshots, 1)) - - if len(snapshot) != 1 { + snapshot, ok := <-helper.Last(snapshots, 1) + if !ok { return last, errors.New("empty asset") } - return snapshot[0].Date, nil + return snapshot.Date, nil +} + +// Append adds the given snapshows to the asset with the given name. +func (r *FileSystemRepository) Append(name string, snapshots <-chan *Snapshot) error { + return helper.AppendOrWriteToCsvFile(r.getCsvFileName(name), true, snapshots) +} + +// getCsvFileName gets the CSV file name for the given asset name. +func (r *FileSystemRepository) getCsvFileName(name string) string { + return filepath.Join(r.base, fmt.Sprintf("%s.csv", name)) } diff --git a/asset/file_system_repository_test.go b/asset/file_system_repository_test.go index d8793e6..12e3411 100644 --- a/asset/file_system_repository_test.go +++ b/asset/file_system_repository_test.go @@ -5,6 +5,9 @@ package asset_test import ( + "fmt" + "os" + "reflect" "testing" "time" @@ -12,6 +15,29 @@ import ( "github.com/cinar/indicator/helper" ) +func TestFileSystemRepositoryAssets(t *testing.T) { + repository := asset.NewFileSystemRepository("testdata") + expected := []string{"brk-b", "empty"} + + actual, err := repository.Assets() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("actual %v expected %v", actual, expected) + } +} + +func TestFileSystemRepositoryAssetsNonExisting(t *testing.T) { + repository := asset.NewFileSystemRepository("testdata/non_existing") + + _, err := repository.Assets() + if err == nil { + t.Fatal("expected error") + } +} + func TestFileSystemRepositoryGet(t *testing.T) { repository := asset.NewFileSystemRepository("testdata") @@ -64,3 +90,35 @@ func TestFileSystemRepositoryLastDateEmpty(t *testing.T) { t.Fatal("expected error") } } + +func TestFileSystemRepositoryAppend(t *testing.T) { + repository := asset.NewFileSystemRepository("testdata") + + expected, err := repository.Get("brk-b") + if err != nil { + t.Fatal(err) + } + + name := "test_file_system_repository_append" + defer os.Remove(fmt.Sprintf("testdata/%s.csv", name)) + + err = repository.Append(name, expected) + if err != nil { + t.Fatal(err) + } + + expected, err = repository.Get("brk-b") + if err != nil { + t.Fatal(err) + } + + actual, err := repository.Get(name) + if err != nil { + t.Fatal(err) + } + + err = helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} diff --git a/asset/repository.go b/asset/repository.go index 0905b5c..7292123 100644 --- a/asset/repository.go +++ b/asset/repository.go @@ -9,6 +9,9 @@ import "time" // Repository serves as a centralized storage and retrieval // location for asset snapshots. type Repository interface { + // Assets returns the names of all assets in the repository. + Assets() ([]string, error) + // Get attempts to return a channel of snapshots for // the asset with the given name. Get(name string) (<-chan *Snapshot, error) @@ -16,4 +19,8 @@ type Repository interface { // LastDate returns the date of the last snapshot for // the asset with the given name. LastDate(name string) (time.Time, error) + + // Append adds the given snapshows to the asset with the + // given name. + Append(name string, snapshots <-chan *Snapshot) error }