Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 89 additions & 2 deletions db/mongo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,98 @@ import (
"net"
"strconv"

"github.com/Prabhjot-Sethi/core/errors"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo"

"github.com/Prabhjot-Sethi/core/errors"
)

type mongoCollection struct {
StoreCollection
parent *mongoStore // handler for the parent mongo DB object
colName string // name of the collection this collection object is working with
col *mongo.Collection
}

// inserts one entry with given key and data to the collection
// returns errors if entry already exists or if there is a connection
// error with the database server
func (c *mongoCollection) InsertOne(ctx context.Context, key interface{}, data interface{}) error {
if data == nil {
return errors.Wrap(errors.InvalidArgument, "db Insert error: No data to store")
}
if key == nil {
return errors.Wrap(errors.InvalidArgument, "db Insert error: No Key specified to store")
}

// convert data to bson document for transacting with mongo db library
marshaledData, err := bson.Marshal(data)
if err != nil {
// return if any error occured
return err
}

bd := bson.D{}
err = bson.Unmarshal(marshaledData, &bd)
if err != nil {
// return if any error occured
return err
}

// key is already nil checked
// set the primary key to specified key.
//
// TODO(prabhjot) check if we want to allow nil key at some point
// Typically mongodb allows inserts with key not specified
// where it auto allocates the key to random id
primKey := bson.E{
Key: "_id", // set primary key
Value: key,
}
bd = append(bd, primKey)

_, err = c.col.InsertOne(ctx, bd)
if err != nil {
// TODO(prabhjot) we may need to identify and differentiate
// Already Exist error here.
return err
}
return nil
}

// remove one entry from the collection matching the given key
func (c *mongoCollection) DeleteOne(ctx context.Context, key interface{}) error {
resp, err := c.col.DeleteOne(ctx, bson.M{"_id": key})
if err != nil {
// TODO(prabhjot) we may need to identify and differentiate
// Not found error here
return err
}
if resp.DeletedCount == 0 {
return errors.Wrap(errors.NotFound, "No Document found")
}

return nil
}

type mongoStore struct {
Store
db *mongo.Database
}

func (s *mongoStore) GetCollection(name string) StoreCollection {
handle := s.db.Collection(name)
c := &mongoCollection{
parent: s,
colName: name,
col: handle,
}

return c
}

type mongoClient struct {
StoreClient
client *mongo.Client
Expand Down Expand Up @@ -95,6 +175,13 @@ func (c *mongoClient) GetDataStore(dbName string) Store {
return mongoStore
}

// gets Mongo DB collection for given collection name
// inside a database specified with db name
func (c *mongoClient) GetCollection(dbName, col string) StoreCollection {
s := c.GetDataStore(dbName)
return s.GetCollection(col)
}

func (c *mongoClient) HealthCheck(ctx context.Context) error {
return c.client.Ping(ctx, nil)
}
33 changes: 32 additions & 1 deletion db/mongo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import (
"testing"
)

type MyKey struct {
Name string
}

type MyData struct {
Desc string
}

func Test_ClientConnection(t *testing.T) {
t.Run("Valid_Auth_Config", func(t *testing.T) {
config := &MongoConfig{
Expand All @@ -29,7 +37,30 @@ func Test_ClientConnection(t *testing.T) {
t.Errorf("failed to perform Health check with DB Error: %s", err)
}

_ = client.GetDataStore("test")
s := client.GetDataStore("test")

col := s.GetCollection("collection1")

key := &MyKey{
Name: "test-key",
}
data := &MyData{
Desc: "sample-description",
}
err = col.InsertOne(context.Background(), key, data)
if err != nil {
t.Errorf("failed to insert an entry to collection Error: %s", err)
}

err = col.DeleteOne(context.Background(), key)
if err != nil {
t.Errorf("failed to delete entry using key Error: %s", err)
}

err = col.DeleteOne(context.Background(), key)
if err == nil {
t.Errorf("attemptting delete on already deleted entry, but didn't receive expected error")
}
})

t.Run("InValid_Port", func(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions db/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,31 @@ import (
"context"
)

// interface definition for a collection in store
type StoreCollection interface {
// insert one entry to the collection for the given key and data
InsertOne(ctx context.Context, key interface{}, data interface{}) error

// remove one entry from the collection matching the given key
DeleteOne(ctx context.Context, key interface{}) error
}

// interface definition for a store, responsible for holding group
// of collections
type Store interface {
// Gets collection corresponding to the collection name
GetCollection(col string) StoreCollection
}

// interface definition for Client corresponding to a store and
type StoreClient interface {
// Get the Data Store interface given the client interface
GetDataStore(dbName string) Store

// Gets collection corresponding to the collection name inside
// the requested database name
GetCollection(dbName, col string) StoreCollection

// Health Check, if the Store is connectable and healthy
// returns the status of health of the server by means of
// error if error is nil the health of the DB store can be
Expand Down