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
}