From 18f8e4aea78374d4367313a01e79f739a96accda Mon Sep 17 00:00:00 2001 From: Prabhjot Singh Sethi Date: Wed, 9 Apr 2025 08:04:40 +0000 Subject: [PATCH] Enable functionality to handle add and remove of entry from mongo Signed-off-by: Prabhjot Singh Sethi --- db/mongo.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++-- db/mongo_test.go | 33 +++++++++++++++++- db/store.go | 18 ++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) diff --git a/db/mongo.go b/db/mongo.go index 69432dc..841d821 100644 --- a/db/mongo.go +++ b/db/mongo.go @@ -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 @@ -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) } diff --git a/db/mongo_test.go b/db/mongo_test.go index 549cb1b..7b9aa75 100644 --- a/db/mongo_test.go +++ b/db/mongo_test.go @@ -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{ @@ -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) { diff --git a/db/store.go b/db/store.go index c498cb3..b670aa9 100644 --- a/db/store.go +++ b/db/store.go @@ -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