diff --git a/.travis.yml b/.travis.yml index 4b980dd..091355f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ language: go go: - - 1.6 + - 1.15 - tip diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d058a9b --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/jay-dee7/storage + +go 1.15 + +require ( + github.com/mailhog/data v1.0.1 // indirect + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..94febb9 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/mailhog/data v1.0.1 h1:7I+opBvVdi4EMJaihXavM98jp/ovt4o6mz47446RAW8= +github.com/mailhog/data v1.0.1/go.mod h1:tjR/iXRhbSUKHzAAMd99RygVaDB5rIDC/bmWc363MzU= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= diff --git a/mongodb.go b/mongodb.go index 6e506fa..d12ca93 100644 --- a/mongodb.go +++ b/mongodb.go @@ -1,10 +1,10 @@ package storage import ( - "log" "github.com/mailhog/data" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" + "log" ) // MongoDB represents MongoDB backed storage backend diff --git a/mongodb_multi_tenant.go b/mongodb_multi_tenant.go new file mode 100644 index 0000000..02caabf --- /dev/null +++ b/mongodb_multi_tenant.go @@ -0,0 +1,120 @@ +package storage + +import ( + "github.com/mailhog/data" + "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" + "log" +) + +// MongoDB represents MongoDB backed storage backend +type MultiTenantMongoDB struct { + database *mgo.Database +} + +// CreateMongoDB creates a MongoDB backed storage backend +func CreateMultiTenantMongoDB(uri, db, coll string) *MultiTenantMongoDB { + log.Printf("Connecting to MongoDB: %s\n", uri) + session, err := mgo.Dial(uri) + if err != nil { + log.Printf("Error connecting to MongoDB: %s", err) + return nil + } + err = session.DB(db).C(coll).EnsureIndexKey("created") + if err != nil { + log.Printf("Failed creating index: %s", err) + return nil + } + return &MultiTenantMongoDB{ + database: session.DB(db), + } +} + +// Store stores a message in MongoDB and returns its storage ID +func (m *MultiTenantMongoDB) Store(msg *data.Message, tenant string) (string, error) { + err := m.database.C(tenant).Insert(m) + if err != nil { + log.Printf("Error inserting message: %s", err) + return "", err + } + return string(msg.ID), nil +} + +// Count returns the number of stored messages +func (m *MultiTenantMongoDB) Count(tenant string) int { + c, _ := m.database.C(tenant).Count() + return c +} + +// Search finds messages matching the query +func (m *MultiTenantMongoDB) Search(kind, query string, start, limit int, tenant string) (*data.Messages, int, error) { + messages := &data.Messages{} + var count = 0 + var field = "raw.data" + switch kind { + case "to": + field = "raw.to" + case "from": + field = "raw.from" + } + err := m.database.C(tenant).Find(bson.M{field: bson.RegEx{Pattern: query, Options: "i"}}).Skip(start).Limit(limit).Sort("-created").Select(bson.M{ + "id": 1, + "_id": 1, + "from": 1, + "to": 1, + "content.headers": 1, + "content.size": 1, + "created": 1, + "raw": 1, + }).All(messages) + if err != nil { + log.Printf("Error loading messages: %s", err) + return nil, 0, err + } + count, _ = m.database.C(tenant).Find(bson.M{field: bson.RegEx{Pattern: query, Options: "i"}}).Count() + + return messages, count, nil +} + +// List returns a list of messages by index +func (m *MultiTenantMongoDB) List(start int, limit int, tenant string) (*data.Messages, error) { + messages := &data.Messages{} + err := m.database.C(tenant).Find(bson.M{}).Skip(start).Limit(limit).Sort("-created").Select(bson.M{ + "id": 1, + "_id": 1, + "from": 1, + "to": 1, + "content.headers": 1, + "content.size": 1, + "created": 1, + "raw": 1, + }).All(messages) + if err != nil { + log.Printf("Error loading messages: %s", err) + return nil, err + } + return messages, nil +} + +// DeleteOne deletes an individual message by storage ID +func (m *MultiTenantMongoDB) DeleteOne(id, tenant string) error { + _, err := m.database.C(tenant).RemoveAll(bson.M{"id": id}) + return err +} + +// DeleteAll deletes all messages stored in MongoDB +func (m *MultiTenantMongoDB) DeleteAll(tenant string) error { + _, err := m.database.C(tenant).RemoveAll(bson.M{}) + return err +} + +// Load loads an individual message by storage ID +func (m *MultiTenantMongoDB) Load(id, tenant string) (*data.Message, error) { + result := &data.Message{} + err := m.database.C(tenant).Find(bson.M{"id": id}).One(&result) + if err != nil { + log.Printf("Error loading message: %s", err) + return nil, err + } + return result, nil +} diff --git a/storage.go b/storage.go index 80966f0..59c0be9 100644 --- a/storage.go +++ b/storage.go @@ -12,3 +12,14 @@ type Storage interface { DeleteAll() error Load(id string) (*data.Message, error) } + +// Storage represents a storage backend +type MultiTenantStorage interface { + Store(m *data.Message, tenant string) (string, error) + List(start, limit int, tenant string) (*data.Messages, error) + Search(kind, query string, start, limit int, tenant string) (*data.Messages, int, error) + Count(tenant string) int + DeleteOne(id, tenant string) error + DeleteAll(tenant string) error + Load(id, tenant string) (*data.Message, error) +}