Skip to content

Commit

Permalink
[GUILD-436] Add a new method to find using an index (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcarriere committed Mar 19, 2019
1 parent fd8e9b2 commit e5583d0
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 35 deletions.
2 changes: 2 additions & 0 deletions Makefile
Expand Up @@ -6,6 +6,7 @@ test:

test_docker:
docker-compose run --rm golang make test
docker-compose down
.PHONY: test_docker

cover:
Expand All @@ -14,6 +15,7 @@ cover:

cover_docker:
docker-compose run --rm golang make cover
docker-compose down
.PHONY: cover_docker

publish_cover: cover_docker
Expand Down
4 changes: 1 addition & 3 deletions docker-compose.yml
Expand Up @@ -14,6 +14,4 @@ services:
working_dir: /eventhorizon

dynamodb:
image: amazon/dynamodb-local:latest
ports:
- "8000:8000"
image: amazon/dynamodb-local:latest
36 changes: 36 additions & 0 deletions repo.go
Expand Up @@ -146,6 +146,33 @@ func (r *Repo) FindWithFilter(ctx context.Context, expr string, args ...interfac
return result, nil
}

// FindWithFilterUsingIndex allows to find entities with a filter using an index
func (r *Repo) FindWithFilterUsingIndex(ctx context.Context, indexInput IndexInput, filterQuery string, filterArgs ...interface{}) ([]eh.Entity, error) {
if r.factoryFn == nil {
return nil, eh.RepoError{
Err: ErrModelNotSet,
Namespace: eh.NamespaceFromContext(ctx),
}
}

table := r.service.Table(r.config.TableName)

iter := table.Get(indexInput.PartitionKey, indexInput.PartitionKeyValue).
Range(indexInput.SortKey, dynamo.Equal, indexInput.SortKeyValue).
Index(indexInput.IndexName).
Filter(filterQuery, filterArgs...).
Iter()

result := []eh.Entity{}
entity := r.factoryFn()
for iter.Next(entity) {
result = append(result, entity)
entity = r.factoryFn()
}

return result, nil
}

// Save implements the Save method of the eventhorizon.WriteRepo interface.
func (r *Repo) Save(ctx context.Context, entity eh.Entity) error {
table := r.service.Table(r.config.TableName)
Expand Down Expand Up @@ -188,3 +215,12 @@ func (r *Repo) Remove(ctx context.Context, id uuid.UUID) error {
func (r *Repo) SetEntityFactory(f func() eh.Entity) {
r.factoryFn = f
}

// IndexInput is all the params we need to filter on an index
type IndexInput struct {
IndexName string
PartitionKey string
PartitionKeyValue interface{}
SortKey string
SortKeyValue interface{}
}
95 changes: 63 additions & 32 deletions repo_test.go
Expand Up @@ -23,7 +23,7 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"

"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/stretchr/testify/suite"

"github.com/stretchr/testify/assert"
Expand All @@ -40,14 +40,14 @@ type RepoTestSuite struct {
repo *Repo
}

// SetupSuite will be run by testify once, at the very
// start of the testing suite, before any tests are run.
// SetupSuite will be run once, at the very start of the testing suite
func (suite *RepoTestSuite) SetupSuite() {
conf := suite.getRepoConfig()
db := suite.getDynamoDB(conf)

suite.conf = conf
suite.db = db
suite.repo = suite.getRepo(suite.conf)
}

func (suite *RepoTestSuite) getDynamoDB(conf *RepoConfig) *dynamo.DB {
Expand All @@ -63,11 +63,16 @@ func (suite *RepoTestSuite) getDynamoDB(conf *RepoConfig) *dynamo.DB {
}

func (suite *RepoTestSuite) getRepo(conf *RepoConfig) *Repo {
testModel := &TestModel{}

if err := suite.db.CreateTable(suite.conf.TableName, testModel).OnDemand(true).Run(); err != nil {
suite.T().Fatal("could not create table:", err)
}

repo, err := NewRepo(conf)
if err != nil || repo == nil {
suite.T().Fatal("error creating repo:", err)
}
repo.SetEntityFactory(func() eh.Entity { return &TestModel{} })
return repo
}

Expand All @@ -77,18 +82,16 @@ func (suite *RepoTestSuite) getRepoConfig() *RepoConfig {
Endpoint: os.Getenv("DYNAMODB_HOST"),
}
}
func (suite *RepoTestSuite) BeforeTest(suiteName, testName string) {
testModel := &TestModel{}
if err := suite.db.CreateTable(suite.conf.TableName, testModel).Run(); err != nil {
suite.T().Fatal("could not create table:", err)
}

suite.repo = suite.getRepo(suite.conf)
func (suite *RepoTestSuite) BeforeTest(suiteName, testName string) {
suite.repo.SetEntityFactory(func() eh.Entity { return &TestModel{} })
}

func (suite *RepoTestSuite) AfterTest(suiteName, testName string) {
if err := suite.db.Table(suite.conf.TableName).DeleteTable().Run(); err != nil {
suite.T().Fatal("could not delete table: ", err)
// Emptying the table
entities, _ := suite.repo.FindAll(context.Background())
for _, entity := range entities {
_ = suite.repo.Remove(context.Background(), entity.EntityID())
}
}

Expand All @@ -108,11 +111,8 @@ func (suite *RepoTestSuite) TestSaveAndFind() {
}

func (suite *RepoTestSuite) TestSaveAndFindAll() {
testModel := &TestModel{ID: uuid.New(), Content: "test"}
testModel2 := &TestModel{ID: uuid.New(), Content: "test2"}

err := suite.repo.Save(context.Background(), testModel)
suite.repo.Save(context.Background(), testModel2)
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "test"})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "test2"})

results, err := suite.repo.FindAll(context.Background())
if err != nil {
Expand All @@ -122,25 +122,57 @@ func (suite *RepoTestSuite) TestSaveAndFindAll() {
}

func (suite *RepoTestSuite) TestSaveAndFindWithFilter() {
testModel := &TestModel{ID: uuid.New(), Content: "test", FilterableID: 123}
testModel2 := &TestModel{ID: uuid.New(), Content: "test2", FilterableID: 123}
testModel3 := &TestModel{ID: uuid.New(), Content: "test3", FilterableID: 456}

err := suite.repo.Save(context.Background(), testModel)
suite.repo.Save(context.Background(), testModel2)
suite.repo.Save(context.Background(), testModel3)
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "test", FilterableID: 123})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "test2", FilterableID: 123})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "test3", FilterableID: 456})

results, err := suite.repo.FindWithFilter(context.Background(), "FilterableID = ?", 123)
if err != nil {
suite.T().Fatal("error finding entity:", err)
suite.T().Fatal("error finding entities:", err)
}
assert.Equal(suite.T(), 2, len(results))
}

func (suite *RepoTestSuite) TestSaveAndFindUsingIndex() {
index := dynamo.Index{
Name: "testIndex",
HashKey: "FilterableID",
HashKeyType: dynamo.NumberType,
RangeKey: "FilterableSortKey",
RangeKeyType: dynamo.StringType,
ProjectionType: dynamodb.ProjectionTypeAll,
}
if _, err := suite.db.Table(suite.conf.TableName).UpdateTable().CreateIndex(index).OnDemand(true).Run(); err != nil {
suite.T().Fatal("could not create index:", err)
}
defer suite.db.Table(suite.conf.TableName).UpdateTable().DeleteIndex(index.Name).Run()

_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "testContent", FilterableID: 123, FilterableSortKey: "test"})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "testContent", FilterableID: 123, FilterableSortKey: "test"})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "testContent2", FilterableID: 123, FilterableSortKey: "test"})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "testContent", FilterableID: 456, FilterableSortKey: "test"})
_ = suite.repo.Save(context.Background(), &TestModel{ID: uuid.New(), Content: "testContent", FilterableID: 123, FilterableSortKey: "test2"})

indexInput := IndexInput{
IndexName: index.Name,
PartitionKey: index.HashKey,
PartitionKeyValue: 123,
SortKey: index.RangeKey,
SortKeyValue: "test",
}

results, err := suite.repo.FindWithFilterUsingIndex(context.Background(), indexInput, "Content = ?", "testContent")
if err != nil {
suite.T().Fatal("error finding entities:", err)
}
assert.Equal(suite.T(), 2, len(results))

}

func (suite *RepoTestSuite) TestRemove() {
testModel := &TestModel{ID: uuid.New(), Content: "test"}

suite.repo.Save(context.Background(), testModel)
_ = suite.repo.Save(context.Background(), testModel)
err := suite.repo.Remove(context.Background(), testModel.ID)
if err != nil {
suite.T().Fatal("failed to remove entity:", err)
Expand All @@ -166,9 +198,7 @@ func (suite *RepoTestSuite) TestNoFactoryFn() {
}

func (suite *RepoTestSuite) TestEmptyUUID() {
testModel := &TestModel{Content: "test"}

err := suite.repo.Save(context.Background(), testModel)
err := suite.repo.Save(context.Background(), &TestModel{Content: "test"})
assert.EqualError(suite.T(), err, "could not save entity: missing entity ID (default)")
}

Expand All @@ -178,9 +208,10 @@ func (suite *RepoTestSuite) TestParent() {
}

type TestModel struct {
ID uuid.UUID `dynamo:",hash"`
Content string
FilterableID int
ID uuid.UUID `dynamo:",hash"`
Content string
FilterableID int
FilterableSortKey string
}

// EntityID implements the EntityID method of the eventhorizon.Entity interface.
Expand Down

0 comments on commit e5583d0

Please sign in to comment.