From 2bcdc8eb83fde8faeeb95cfd4fbd8651ce2a04c9 Mon Sep 17 00:00:00 2001 From: Umang Mundhra Date: Wed, 12 Feb 2025 10:23:27 +0530 Subject: [PATCH] En/arango migrations (#1482) --- .../injecting-databases-drivers/page.md | 15 + pkg/gofr/container/datasources.go | 46 +- pkg/gofr/container/mock_container.go | 2 + pkg/gofr/container/mock_datasources.go | 168 +++++++ pkg/gofr/datasource/arangodb/arango.go | 12 + pkg/gofr/datasource/arangodb/arango_db.go | 105 +++++ .../datasource/arangodb/arango_db_test.go | 153 +++++++ .../datasource/arangodb/arango_document.go | 41 +- pkg/gofr/datasource/arangodb/arango_graph.go | 85 +++- pkg/gofr/datasource/arangodb/interface.go | 15 + pkg/gofr/migration/arango.go | 131 ++++++ pkg/gofr/migration/arango_test.go | 116 +++++ pkg/gofr/migration/datasource.go | 1 + pkg/gofr/migration/interface.go | 18 + pkg/gofr/migration/migration.go | 10 + pkg/gofr/migration/migration_test.go | 1 + pkg/gofr/migration/mock_interface.go | 410 +++++++++++++++++- 17 files changed, 1301 insertions(+), 28 deletions(-) create mode 100644 pkg/gofr/datasource/arangodb/arango_db.go create mode 100644 pkg/gofr/datasource/arangodb/arango_db_test.go create mode 100644 pkg/gofr/migration/arango.go create mode 100644 pkg/gofr/migration/arango_test.go diff --git a/docs/advanced-guide/injecting-databases-drivers/page.md b/docs/advanced-guide/injecting-databases-drivers/page.md index 259b43bf2..e78f9b05d 100644 --- a/docs/advanced-guide/injecting-databases-drivers/page.md +++ b/docs/advanced-guide/injecting-databases-drivers/page.md @@ -958,6 +958,21 @@ added using the `app.AddArangoDB()` method, and users can use ArangoDB across th ```go type ArangoDB interface { + // CreateDB creates a new database in ArangoDB. + CreateDB(ctx context.Context, database string) error + // DropDB deletes an existing database in ArangoDB. + DropDB(ctx context.Context, database string) error + + // CreateCollection creates a new collection in a database with specified type. + CreateCollection(ctx context.Context, database, collection string, isEdge bool) error + // DropCollection deletes an existing collection from a database. + DropCollection(ctx context.Context, database, collection string) error + + // CreateGraph creates a new graph in a database. + CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error + // DropGraph deletes an existing graph from a database. + DropGraph(ctx context.Context, database, graph string) error + // CreateDocument creates a new document in the specified collection. CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) // GetDocument retrieves a document by its ID from the specified collection. diff --git a/pkg/gofr/container/datasources.go b/pkg/gofr/container/datasources.go index bb4bd0191..e6de78344 100644 --- a/pkg/gofr/container/datasources.go +++ b/pkg/gofr/container/datasources.go @@ -576,6 +576,28 @@ type ScyllaDBProvider interface { } type ArangoDB interface { + // CreateDB creates a new database in ArangoDB. + CreateDB(ctx context.Context, database string) error + // DropDB deletes an existing database in ArangoDB. + DropDB(ctx context.Context, database string) error + + // CreateCollection creates a new collection in a database with specified type. + CreateCollection(ctx context.Context, database, collection string, isEdge bool) error + // DropCollection deletes an existing collection from a database. + DropCollection(ctx context.Context, database, collection string) error + + // CreateGraph creates a new graph in a database. + // Parameters: + // - ctx: Request context for tracing and cancellation. + // - database: Name of the database where the graph will be created. + // - graph: Name of the graph to be created. + // - edgeDefinitions: Pointer to EdgeDefinition struct containing edge definitions. + // + // Returns an error if the edgeDefinitions parameter is not of type *EdgeDefinition or is nil. + CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error + // DropGraph deletes an existing graph from a database. + DropGraph(ctx context.Context, database, graph string) error + // CreateDocument creates a new document in the specified collection. CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) // GetDocument retrieves a document by its ID from the specified collection. @@ -585,10 +607,30 @@ type ArangoDB interface { // DeleteDocument deletes a document by its ID from the specified collection. DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error - // GetEdges retrieves all the edge documents connected to a specific vertex in an ArangoDB graph. + // GetEdges fetches all edges connected to a given vertex in the specified edge collection. + // + // Parameters: + // - ctx: Request context for tracing and cancellation. + // - dbName: Database name. + // - graphName: Graph name. + // - edgeCollection: Edge collection name. + // - vertexID: Full vertex ID (e.g., "persons/16563"). + // - resp: Pointer to `*EdgeDetails` to store results. + // + // Returns an error if input is invalid, `resp` is of the wrong type, or the query fails. GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error - // Query executes an AQL query and binds the results + // Query executes an AQL query and binds the results. + // + // Parameters: + // - ctx: Request context for tracing and cancellation. + // - dbName: Name of the database where the query will be executed. + // - query: AQL query string to be executed. + // - bindVars: Map of bind variables to be used in the query. + // - result: Pointer to a slice of maps where the query results will be stored. + // + // Returns an error if the database connection fails, the query execution fails, or + // the result parameter is not a pointer to a slice of maps. Query(ctx context.Context, dbName string, query string, bindVars map[string]any, result any) error HealthChecker diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go index c2b04378c..973e9d7a9 100644 --- a/pkg/gofr/container/mock_container.go +++ b/pkg/gofr/container/mock_container.go @@ -24,6 +24,7 @@ type Mocks struct { Mongo *MockMongo KVStore *MockKVStore DGraph *MockDgraph + ArangoDB *MockArangoDBProvider OpenTSDB *MockOpenTSDBProvider File *file.MockFileSystemProvider HTTPService *service.MockHTTP @@ -119,6 +120,7 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { HTTPService: httpMock, DGraph: dgraphMock, OpenTSDB: opentsdbMock, + ArangoDB: arangoMock, Metrics: mockMetrics, } diff --git a/pkg/gofr/container/mock_datasources.go b/pkg/gofr/container/mock_datasources.go index c3f402321..6768b8703 100644 --- a/pkg/gofr/container/mock_datasources.go +++ b/pkg/gofr/container/mock_datasources.go @@ -12056,6 +12056,34 @@ func (m *MockArangoDB) EXPECT() *MockArangoDBMockRecorder { return m.recorder } +// CreateCollection mocks base method. +func (m *MockArangoDB) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, database, collection, isEdge) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockArangoDBMockRecorder) CreateCollection(ctx, database, collection, isEdge any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockArangoDB)(nil).CreateCollection), ctx, database, collection, isEdge) +} + +// CreateDB mocks base method. +func (m *MockArangoDB) CreateDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateDB indicates an expected call of CreateDB. +func (mr *MockArangoDBMockRecorder) CreateDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockArangoDB)(nil).CreateDB), ctx, database) +} + // CreateDocument mocks base method. func (m *MockArangoDB) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { m.ctrl.T.Helper() @@ -12071,6 +12099,20 @@ func (mr *MockArangoDBMockRecorder) CreateDocument(ctx, dbName, collectionName, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockArangoDB)(nil).CreateDocument), ctx, dbName, collectionName, document) } +// CreateGraph mocks base method. +func (m *MockArangoDB) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateGraph", ctx, database, graph, edgeDefinitions) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateGraph indicates an expected call of CreateGraph. +func (mr *MockArangoDBMockRecorder) CreateGraph(ctx, database, graph, edgeDefinitions any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGraph", reflect.TypeOf((*MockArangoDB)(nil).CreateGraph), ctx, database, graph, edgeDefinitions) +} + // DeleteDocument mocks base method. func (m *MockArangoDB) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { m.ctrl.T.Helper() @@ -12085,6 +12127,48 @@ func (mr *MockArangoDBMockRecorder) DeleteDocument(ctx, dbName, collectionName, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockArangoDB)(nil).DeleteDocument), ctx, dbName, collectionName, documentID) } +// DropCollection mocks base method. +func (m *MockArangoDB) DropCollection(ctx context.Context, database, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropCollection", ctx, database, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropCollection indicates an expected call of DropCollection. +func (mr *MockArangoDBMockRecorder) DropCollection(ctx, database, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropCollection", reflect.TypeOf((*MockArangoDB)(nil).DropCollection), ctx, database, collection) +} + +// DropDB mocks base method. +func (m *MockArangoDB) DropDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropDB indicates an expected call of DropDB. +func (mr *MockArangoDBMockRecorder) DropDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDB", reflect.TypeOf((*MockArangoDB)(nil).DropDB), ctx, database) +} + +// DropGraph mocks base method. +func (m *MockArangoDB) DropGraph(ctx context.Context, database, graph string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropGraph", ctx, database, graph) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropGraph indicates an expected call of DropGraph. +func (mr *MockArangoDBMockRecorder) DropGraph(ctx, database, graph any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropGraph", reflect.TypeOf((*MockArangoDB)(nil).DropGraph), ctx, database, graph) +} + // GetDocument mocks base method. func (m *MockArangoDB) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { m.ctrl.T.Helper() @@ -12192,6 +12276,34 @@ func (mr *MockArangoDBProviderMockRecorder) Connect() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockArangoDBProvider)(nil).Connect)) } +// CreateCollection mocks base method. +func (m *MockArangoDBProvider) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, database, collection, isEdge) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockArangoDBProviderMockRecorder) CreateCollection(ctx, database, collection, isEdge any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockArangoDBProvider)(nil).CreateCollection), ctx, database, collection, isEdge) +} + +// CreateDB mocks base method. +func (m *MockArangoDBProvider) CreateDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateDB indicates an expected call of CreateDB. +func (mr *MockArangoDBProviderMockRecorder) CreateDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockArangoDBProvider)(nil).CreateDB), ctx, database) +} + // CreateDocument mocks base method. func (m *MockArangoDBProvider) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { m.ctrl.T.Helper() @@ -12207,6 +12319,20 @@ func (mr *MockArangoDBProviderMockRecorder) CreateDocument(ctx, dbName, collecti return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).CreateDocument), ctx, dbName, collectionName, document) } +// CreateGraph mocks base method. +func (m *MockArangoDBProvider) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateGraph", ctx, database, graph, edgeDefinitions) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateGraph indicates an expected call of CreateGraph. +func (mr *MockArangoDBProviderMockRecorder) CreateGraph(ctx, database, graph, edgeDefinitions any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGraph", reflect.TypeOf((*MockArangoDBProvider)(nil).CreateGraph), ctx, database, graph, edgeDefinitions) +} + // DeleteDocument mocks base method. func (m *MockArangoDBProvider) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { m.ctrl.T.Helper() @@ -12221,6 +12347,48 @@ func (mr *MockArangoDBProviderMockRecorder) DeleteDocument(ctx, dbName, collecti return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).DeleteDocument), ctx, dbName, collectionName, documentID) } +// DropCollection mocks base method. +func (m *MockArangoDBProvider) DropCollection(ctx context.Context, database, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropCollection", ctx, database, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropCollection indicates an expected call of DropCollection. +func (mr *MockArangoDBProviderMockRecorder) DropCollection(ctx, database, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropCollection", reflect.TypeOf((*MockArangoDBProvider)(nil).DropCollection), ctx, database, collection) +} + +// DropDB mocks base method. +func (m *MockArangoDBProvider) DropDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropDB indicates an expected call of DropDB. +func (mr *MockArangoDBProviderMockRecorder) DropDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDB", reflect.TypeOf((*MockArangoDBProvider)(nil).DropDB), ctx, database) +} + +// DropGraph mocks base method. +func (m *MockArangoDBProvider) DropGraph(ctx context.Context, database, graph string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropGraph", ctx, database, graph) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropGraph indicates an expected call of DropGraph. +func (mr *MockArangoDBProviderMockRecorder) DropGraph(ctx, database, graph any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropGraph", reflect.TypeOf((*MockArangoDBProvider)(nil).DropGraph), ctx, database, graph) +} + // GetDocument mocks base method. func (m *MockArangoDBProvider) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { m.ctrl.T.Helper() diff --git a/pkg/gofr/datasource/arangodb/arango.go b/pkg/gofr/datasource/arangodb/arango.go index fe8985a04..df4ed7d5f 100644 --- a/pkg/gofr/datasource/arangodb/arango.go +++ b/pkg/gofr/datasource/arangodb/arango.go @@ -26,6 +26,7 @@ type Client struct { tracer trace.Tracer config *Config endpoint string + *DB *Document *Graph } @@ -59,6 +60,7 @@ func New(c Config) *Client { config: &c, } + client.DB = &DB{client: client} client.Document = &Document{client: client} client.Graph = &Graph{client: client} @@ -149,6 +151,16 @@ func (c *Client) validateConfig() error { } // Query executes an AQL query and binds the results. +// +// Parameters: +// - ctx: Request context for tracing and cancellation. +// - dbName: Name of the database where the query will be executed. +// - query: AQL query string to be executed. +// - bindVars: Map of bind variables to be used in the query. +// - result: Pointer to a slice of maps where the query results will be stored. +// +// Returns an error if the database connection fails, the query execution fails, or the +// result parameter is not a pointer to a slice of maps. func (c *Client) Query(ctx context.Context, dbName, query string, bindVars map[string]any, result any) error { tracerCtx, span := c.addTrace(ctx, "query", map[string]string{"DB": dbName}) startTime := time.Now() diff --git a/pkg/gofr/datasource/arangodb/arango_db.go b/pkg/gofr/datasource/arangodb/arango_db.go new file mode 100644 index 000000000..e278fa3c1 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_db.go @@ -0,0 +1,105 @@ +package arangodb + +import ( + "context" + "time" + + "github.com/arangodb/go-driver/v2/arangodb" +) + +type DB struct { + client *Client +} + +// CreateDB creates a new database in ArangoDB. +func (d *DB) CreateDB(ctx context.Context, database string) error { + tracerCtx, span := d.client.addTrace(ctx, "createDB", map[string]string{"DB": database}) + startTime := time.Now() + + defer d.client.sendOperationStats(&QueryLog{Operation: "createDB", Database: database}, startTime, "createDB", span) + + _, err := d.client.client.CreateDatabase(tracerCtx, database, nil) + + return err +} + +// DropDB deletes a database from ArangoDB. +func (d *DB) DropDB(ctx context.Context, database string) error { + tracerCtx, span := d.client.addTrace(ctx, "dropDB", map[string]string{"DB": database}) + startTime := time.Now() + + defer d.client.sendOperationStats(&QueryLog{Operation: "dropDB", Database: database}, startTime, "dropDB", span) + + db, err := d.client.client.Database(tracerCtx, database) + if err != nil { + return err + } + + err = db.Remove(tracerCtx) + if err != nil { + return err + } + + return err +} + +// CreateCollection creates a new collection in a database with specified type. +func (d *DB) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + tracerCtx, span := d.client.addTrace(ctx, "createCollection", map[string]string{"collection": collection}) + startTime := time.Now() + + defer d.client.sendOperationStats(&QueryLog{Operation: "createCollection", Database: database, + Collection: collection, Filter: isEdge}, startTime, "createCollection", span) + + db, err := d.client.client.Database(tracerCtx, database) + if err != nil { + return err + } + + options := arangodb.CreateCollectionProperties{Type: arangodb.CollectionTypeDocument} + if isEdge { + options.Type = arangodb.CollectionTypeEdge + } + + _, err = db.CreateCollection(tracerCtx, collection, &options) + + return err +} + +// DropCollection deletes an existing collection from a database. +func (d *DB) DropCollection(ctx context.Context, database, collectionName string) error { + return d.handleCollectionOperation(ctx, "dropCollection", database, collectionName, func(collection arangodb.Collection) error { + return collection.Remove(ctx) + }) +} + +func (d *DB) getCollection(ctx context.Context, dbName, collectionName string) (arangodb.Collection, error) { + db, err := d.client.client.Database(ctx, dbName) + if err != nil { + return nil, err + } + + collection, err := db.Collection(ctx, collectionName) + if err != nil { + return nil, err + } + + return collection, nil +} + +// handleCollectionOperation handles common logic for collection operations. +func (d *DB) handleCollectionOperation(ctx context.Context, operation, database, collectionName string, + action func(arangodb.Collection) error) error { + tracerCtx, span := d.client.addTrace(ctx, operation, map[string]string{"collection": collectionName}) + startTime := time.Now() + + defer d.client.sendOperationStats(&QueryLog{Operation: operation, Database: database, + Collection: collectionName}, startTime, operation, span) + + collection, err := d.getCollection(tracerCtx, database, collectionName) + if err != nil { + return err + } + + return action(collection) +} diff --git a/pkg/gofr/datasource/arangodb/arango_db_test.go b/pkg/gofr/datasource/arangodb/arango_db_test.go new file mode 100644 index 000000000..4a97ad51a --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_db_test.go @@ -0,0 +1,153 @@ +package arangodb + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +var errCollectionNotFound = errors.New("collection not found") + +func Test_Client_CreateDB(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + ctx := context.Background() + database := "testDB" + + mockArango.EXPECT().CreateDatabase(gomock.Any(), database, nil).Return(nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.CreateDB(ctx, database) + require.NoError(t, err, "Expected no error while creating the database") +} + +func Test_Client_CreateDB_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + ctx := context.Background() + database := "errorDB" + + mockArango.EXPECT().CreateDatabase(gomock.Any(), database, nil).Return(errDBNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.CreateDB(ctx, database) + require.Error(t, err, "Expected an error while creating the database") + require.Equal(t, "database not found", err.Error()) +} + +func Test_Client_DropDB(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + ctx := context.Background() + database := "testDB" + mockDB := NewMockDatabase(gomock.NewController(t)) + + // Mock the database method to return a mock database instance + mockArango.EXPECT().Database(gomock.Any(), database).Return(mockDB, nil).Times(1) + mockDB.EXPECT().Remove(gomock.Any()).Return(nil).Times(1) // Mock Remove to return no error + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DropDB(ctx, database) + require.NoError(t, err, "Expected no error while dropping the database") +} + +func Test_Client_DropDB_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + ctx := context.Background() + database := "testDB" + + mockArango.EXPECT().Database(gomock.Any(), database).Return(nil, errDBNotFound).Times(1) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DropDB(ctx, database) + require.Error(t, err, "Expected error when trying to drop a non-existent database") + require.Equal(t, "database not found", err.Error()) +} + +func Test_Client_DropDB_RemoveError(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Remove(gomock.Any()).Return(errDBNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DropDB(context.Background(), "testDB") + require.Error(t, err, "Expected error when removing the database") + require.Equal(t, "database not found", err.Error()) +} + +func Test_Client_CreateCollection(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().CreateCollection(gomock.Any(), "testCollection", gomock.Any()).Return(nil, nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.CreateCollection(context.Background(), "testDB", "testCollection", true) + require.NoError(t, err, "Expected no error while creating the collection") +} + +func Test_Client_CreateCollection_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().CreateCollection(gomock.Any(), "testCollection", gomock.Any()).Return(nil, errCollectionNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.CreateCollection(context.Background(), "testDB", "testCollection", false) + require.Error(t, err, "Expected an error while creating the collection") + require.Equal(t, "collection not found", err.Error()) +} + +func Test_Client_DropCollection(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().Remove(gomock.Any()).Return(nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + // Execute + err := client.DropCollection(context.Background(), "testDB", "testCollection") + require.NoError(t, err, "Expected no error while dropping the collection") +} + +func Test_Client_DropCollection_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(nil, errCollectionNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DropCollection(context.Background(), "testDB", "testCollection") + require.Error(t, err, "Expected error when trying to drop a non-existent collection") + require.Equal(t, "collection not found", err.Error()) +} diff --git a/pkg/gofr/datasource/arangodb/arango_document.go b/pkg/gofr/datasource/arangodb/arango_document.go index 02728ac4b..f3515b098 100644 --- a/pkg/gofr/datasource/arangodb/arango_document.go +++ b/pkg/gofr/datasource/arangodb/arango_document.go @@ -21,21 +21,30 @@ type Document struct { // CreateDocument creates a new document in the specified collection. // If the collection is an edge collection, the document must include `_from` and `_to`. +// +// Parameters: +// - ctx: Request context for tracing and cancellation. +// - dbName: Name of the database where the document will be created. +// - collectionName: Name of the collection where the document will be created. +// - document: The document to be created. For edge collections, it must include `_from` and `_to` fields. +// +// Returns the ID of the created document and an error if the document creation fails. +// // Example for creating a regular document: // -// doc := map[string]any{ -// "name": "Alice", -// "age": 30, +// doc := map[string]any{ +// "name": "Alice", +// "age": 30, // } // -// id, err := client.CreateDocument(ctx, "myDB", "users", doc) +// id, err := client.CreateDocument(ctx, "myDB", "users", doc) // // Example for creating an edge document: // // edgeDoc := map[string]any{ -// "_from": "users/123", -// "_to": "orders/456", -// "relation": "purchased", +// "_from": "users/123", +// "_to": "orders/456", +// "relation": "purchased", // } // // id, err := client.CreateDocument(ctx, "myDB", "edges", edgeDoc). @@ -112,7 +121,7 @@ func (d *Document) DeleteDocument(ctx context.Context, dbName, collectionName, d // isEdgeCollection checks if the given collection is an edge collection. func (d *Document) isEdgeCollection(ctx context.Context, dbName, collectionName string) (bool, error) { - collection, err := d.getCollection(ctx, dbName, collectionName) + collection, err := d.client.getCollection(ctx, dbName, collectionName) if err != nil { return false, err } @@ -141,7 +150,7 @@ func executeCollectionOperation(ctx context.Context, d Document, dbName, collect defer d.client.sendOperationStats(ql, startTime, operation, span) - collection, err := d.getCollection(tracerCtx, dbName, collectionName) + collection, err := d.client.getCollection(tracerCtx, dbName, collectionName) if err != nil { return nil, nil, err } @@ -174,17 +183,3 @@ func validateEdgeDocument(document any) error { return nil } - -func (d *Document) getCollection(ctx context.Context, dbName, collectionName string) (arangodb.Collection, error) { - db, err := d.client.client.Database(ctx, dbName) - if err != nil { - return nil, err - } - - collection, err := db.Collection(ctx, collectionName) - if err != nil { - return nil, err - } - - return collection, nil -} diff --git a/pkg/gofr/datasource/arangodb/arango_graph.go b/pkg/gofr/datasource/arangodb/arango_graph.go index e9da26d9e..c63c80034 100644 --- a/pkg/gofr/datasource/arangodb/arango_graph.go +++ b/pkg/gofr/datasource/arangodb/arango_graph.go @@ -10,8 +10,10 @@ import ( ) var ( - errInvalidInput = errors.New("invalid input parameter") - errInvalidResponseType = errors.New("invalid response type") + errInvalidEdgeDefinitionsType = errors.New("edgeDefinitions must be a *EdgeDefinition type") + errNilEdgeDefinitions = errors.New("edgeDefinitions cannot be nil") + errInvalidInput = errors.New("invalid input parameter") + errInvalidResponseType = errors.New("invalid response type") ) type EdgeDetails []arangodb.EdgeDetails @@ -20,6 +22,85 @@ type Graph struct { client *Client } +// CreateGraph creates a new graph in a database. +// Parameters: +// - ctx: Request context for tracing and cancellation. +// - database: Name of the database where the graph will be created. +// - graph: Name of the graph to be created. +// - edgeDefinitions: Pointer to EdgeDefinition struct containing edge definitions. +// +// Returns an error if the edgeDefinitions parameter is not of type *EdgeDefinition or is nil. +func (g *Graph) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + tracerCtx, span := g.client.addTrace(ctx, "createGraph", map[string]string{"graph": graph}) + startTime := time.Now() + + defer g.client.sendOperationStats(&QueryLog{Operation: "createGraph", + Database: database, Collection: graph}, startTime, "createGraph", span) + + db, err := g.client.client.Database(tracerCtx, database) + if err != nil { + return err + } + + edgeDefs, ok := edgeDefinitions.(*EdgeDefinition) + if !ok { + return fmt.Errorf("%w", errInvalidEdgeDefinitionsType) + } + + if edgeDefs == nil { + return fmt.Errorf("%w", errNilEdgeDefinitions) + } + + arangoEdgeDefs := make(EdgeDefinition, 0, len(*edgeDefs)) + for _, ed := range *edgeDefs { + arangoEdgeDefs = append(arangoEdgeDefs, arangodb.EdgeDefinition{ + Collection: ed.Collection, + From: ed.From, + To: ed.To, + }) + } + + options := &arangodb.GraphDefinition{ + EdgeDefinitions: arangoEdgeDefs, + } + + _, err = db.CreateGraph(tracerCtx, graph, options, nil) + + return err +} + +// DropGraph deletes an existing graph from a database. +// Parameters: +// - ctx: Request context for tracing and cancellation. +// - database: Name of the database where the graph exists. +// - graphName: Name of the graph to be deleted. +// +// Returns an error if the graph does not exist or if there is an issue with the database connection. +func (g *Graph) DropGraph(ctx context.Context, database, graphName string) error { + tracerCtx, span := g.client.addTrace(ctx, "dropGraph", map[string]string{"graph": graphName}) + startTime := time.Now() + + defer g.client.sendOperationStats(&QueryLog{Operation: "dropGraph", + Database: database}, startTime, "dropGraph", span) + + db, err := g.client.client.Database(tracerCtx, database) + if err != nil { + return err + } + + graph, err := db.Graph(tracerCtx, graphName, nil) + if err != nil { + return err + } + + err = graph.Remove(tracerCtx, &arangodb.RemoveGraphOptions{DropCollections: true}) + if err != nil { + return err + } + + return err +} + // GetEdges fetches all edges connected to a given vertex in the specified edge collection. // // Parameters: diff --git a/pkg/gofr/datasource/arangodb/interface.go b/pkg/gofr/datasource/arangodb/interface.go index ed8d9695a..0b970c648 100644 --- a/pkg/gofr/datasource/arangodb/interface.go +++ b/pkg/gofr/datasource/arangodb/interface.go @@ -14,6 +14,21 @@ type ArangoDB interface { databases(ctx context.Context) ([]arangodb.Database, error) version(ctx context.Context) (arangodb.VersionInfo, error) + // CreateDB creates a new database in ArangoDB. + CreateDB(ctx context.Context, database string) error + // DropDB deletes an existing database in ArangoDB. + DropDB(ctx context.Context, database string) error + + // CreateCollection creates a new collection in a database with specified type. + CreateCollection(ctx context.Context, database, collection string, isEdge bool) error + // DropCollection deletes an existing collection from a database. + DropCollection(ctx context.Context, database, collection string) error + + // CreateGraph creates a new graph in a database. + CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error + // DropGraph deletes an existing graph from a database. + DropGraph(ctx context.Context, database, graph string) error + CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error diff --git a/pkg/gofr/migration/arango.go b/pkg/gofr/migration/arango.go new file mode 100644 index 000000000..2d05a54f2 --- /dev/null +++ b/pkg/gofr/migration/arango.go @@ -0,0 +1,131 @@ +package migration + +import ( + "context" + "time" + + "gofr.dev/pkg/gofr/container" +) + +// arangoDS is our adapter struct that will implement both interfaces. +type arangoDS struct { + client ArangoDB +} + +// arangoMigrator struct remains the same but uses our adapter. +type arangoMigrator struct { + ArangoDB + migrator +} + +const ( + arangoMigrationDB = "_system" + arangoMigrationCollection = "gofr_migrations" + + getLastArangoMigration = ` + FOR doc IN gofr_migrations + SORT doc.version DESC + LIMIT 1 + RETURN doc.version +` + insertArangoMigrationRecord = ` + INSERT { + version: @version, + method: @method, + start_time: @start_time, + duration: @duration + } INTO gofr_migrations +` +) + +func (ds arangoDS) CreateDB(ctx context.Context, database string) error { + return ds.client.CreateDB(ctx, database) +} + +func (ds arangoDS) DropDB(ctx context.Context, database string) error { + return ds.client.DropDB(ctx, database) +} + +func (ds arangoDS) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + return ds.client.CreateCollection(ctx, database, collection, isEdge) +} + +func (ds arangoDS) DropCollection(ctx context.Context, database, collection string) error { + return ds.client.DropCollection(ctx, database, collection) +} + +func (ds arangoDS) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + return ds.client.CreateGraph(ctx, database, graph, edgeDefinitions) +} + +func (ds arangoDS) DropGraph(ctx context.Context, database, graph string) error { + return ds.client.DropGraph(ctx, database, graph) +} + +func (ds arangoDS) apply(m migrator) migrator { + return arangoMigrator{ + ArangoDB: ds, + migrator: m, + } +} + +func (am arangoMigrator) checkAndCreateMigrationTable(c *container.Container) error { + err := am.CreateCollection(context.Background(), arangoMigrationDB, arangoMigrationCollection, false) + if err != nil { + c.Debug("Migration collection might already exist:", err) + } + + return am.migrator.checkAndCreateMigrationTable(c) +} + +func (am arangoMigrator) getLastMigration(c *container.Container) int64 { + var lastMigrations []int64 + + err := c.ArangoDB.Query(context.Background(), arangoMigrationDB, getLastArangoMigration, nil, &lastMigrations) + if err != nil || len(lastMigrations) == 0 { + return 0 + } + + c.Debugf("ArangoDB last migration fetched value is: %v", lastMigrations[0]) + + lm2 := am.migrator.getLastMigration(c) + if lm2 > lastMigrations[0] { + return lm2 + } + + return lastMigrations[0] +} + +func (am arangoMigrator) beginTransaction(c *container.Container) transactionData { + data := am.migrator.beginTransaction(c) + + c.Debug("ArangoDB migrator begin successfully") + + return data +} + +func (am arangoMigrator) commitMigration(c *container.Container, data transactionData) error { + bindVars := map[string]any{ + "version": data.MigrationNumber, + "method": "UP", + "start_time": data.StartTime, + "duration": time.Since(data.StartTime).Milliseconds(), + } + + var result []map[string]any + + err := c.ArangoDB.Query(context.Background(), arangoMigrationDB, insertArangoMigrationRecord, bindVars, &result) + if err != nil { + return err + } + + c.Debugf("Inserted record for migration %v in ArangoDB gofr_migrations collection", data.MigrationNumber) + + return am.migrator.commitMigration(c, data) +} + +func (am arangoMigrator) rollback(c *container.Container, data transactionData) { + am.migrator.rollback(c, data) + + c.Fatalf("Migration %v failed and rolled back", data.MigrationNumber) +} diff --git a/pkg/gofr/migration/arango_test.go b/pkg/gofr/migration/arango_test.go new file mode 100644 index 000000000..5ae760056 --- /dev/null +++ b/pkg/gofr/migration/arango_test.go @@ -0,0 +1,116 @@ +package migration + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + "gofr.dev/pkg/gofr/container" + "gofr.dev/pkg/gofr/testutil" +) + +func arangoSetup(t *testing.T) (migrator, *container.MockArangoDBProvider, *container.Container) { + t.Helper() + + mockContainer, mocks := container.NewMockContainer(t) + + mockArango := mocks.ArangoDB + + ds := Datasource{ArangoDB: mockContainer.ArangoDB} + + arangoDB := arangoDS{client: mockArango} + migratorWithArango := arangoDB.apply(&ds) + + mockContainer.ArangoDB = mockArango + + return migratorWithArango, mockArango, mockContainer +} + +func Test_ArangoCheckAndCreateMigrationTable(t *testing.T) { + migratorWithArango, mockArango, mockContainer := arangoSetup(t) + + testCases := []struct { + desc string + err error + }{ + {"no error", nil}, + {"collection already exists", nil}, + } + + for i, tc := range testCases { + mockArango.EXPECT().CreateCollection(context.Background(), arangoMigrationDB, arangoMigrationCollection, false).Return(tc.err) + + err := migratorWithArango.checkAndCreateMigrationTable(mockContainer) + + assert.Equal(t, tc.err, err, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} + +func Test_ArangoGetLastMigration(t *testing.T) { + migratorWithArango, mockArango, mockContainer := arangoSetup(t) + + testCases := []struct { + desc string + err error + resp int64 + }{ + {"no error", nil, 0}, + {"query failed", context.DeadlineExceeded, 0}, + } + + var lastMigrations []int64 + + for i, tc := range testCases { + mockArango.EXPECT().Query(context.Background(), arangoMigrationDB, getLastArangoMigration, nil, &lastMigrations).Return(tc.err) + + resp := migratorWithArango.getLastMigration(mockContainer) + + assert.Equal(t, tc.resp, resp, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} + +func Test_ArangoCommitMigration(t *testing.T) { + migratorWithArango, mockArango, mockContainer := arangoSetup(t) + + testCases := []struct { + desc string + err error + }{ + {"no error", nil}, + {"insert failed", context.DeadlineExceeded}, + } + + timeNow := time.Now() + + td := transactionData{ + StartTime: timeNow, + MigrationNumber: 10, + } + + for i, tc := range testCases { + bindVars := map[string]any{ + "version": td.MigrationNumber, + "method": "UP", + "start_time": td.StartTime, + "duration": time.Since(td.StartTime).Milliseconds(), + } + + mockArango.EXPECT().Query(context.Background(), arangoMigrationDB, insertArangoMigrationRecord, bindVars, gomock.Any()).Return(tc.err) + + err := migratorWithArango.commitMigration(mockContainer, td) + + assert.Equal(t, tc.err, err, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} + +func Test_ArangoBeginTransaction(t *testing.T) { + logs := testutil.StdoutOutputForFunc(func() { + migratorWithArango, _, mockContainer := arangoSetup(t) + migratorWithArango.beginTransaction(mockContainer) + }) + + assert.Contains(t, logs, "ArangoDB migrator begin successfully") +} diff --git a/pkg/gofr/migration/datasource.go b/pkg/gofr/migration/datasource.go index 8f855a4d4..d6c404731 100644 --- a/pkg/gofr/migration/datasource.go +++ b/pkg/gofr/migration/datasource.go @@ -13,6 +13,7 @@ type Datasource struct { Clickhouse Clickhouse Cassandra Cassandra Mongo Mongo + ArangoDB ArangoDB } // It is a base implementation for migration manager, on this other database drivers have been wrapped. diff --git a/pkg/gofr/migration/interface.go b/pkg/gofr/migration/interface.go index 78ae74906..6de5a9c96 100644 --- a/pkg/gofr/migration/interface.go +++ b/pkg/gofr/migration/interface.go @@ -63,6 +63,24 @@ type Mongo interface { StartSession() (any, error) } +// ArangoDB is an interface representing an ArangoDB database client with common CRUD operations. +type ArangoDB interface { + // CreateDB creates a new database in ArangoDB. + CreateDB(ctx context.Context, database string) error + // DropDB deletes an existing database in ArangoDB. + DropDB(ctx context.Context, database string) error + + // CreateCollection creates a new collection in a database with specified type. + CreateCollection(ctx context.Context, database, collection string, isEdge bool) error + // DropCollection deletes an existing collection from a database. + DropCollection(ctx context.Context, database, collection string) error + + // CreateGraph creates a new graph in a database. + CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error + // DropGraph deletes an existing graph from a database. + DropGraph(ctx context.Context, database, graph string) error +} + // keeping the migrator interface unexported as, right now it is not being implemented directly, by the externalDB drivers. // keeping the implementations for externalDB at one place such that if any change in migration logic, we would change directly here. type migrator interface { diff --git a/pkg/gofr/migration/migration.go b/pkg/gofr/migration/migration.go index 670c712d4..6bd16af1f 100644 --- a/pkg/gofr/migration/migration.go +++ b/pkg/gofr/migration/migration.go @@ -174,6 +174,16 @@ func getMigrator(c *container.Container) (Datasource, migrator, bool) { c.Debug("initialized data source for Mongo") } + if !isNil(c.ArangoDB) { + ok = true + + ds.ArangoDB = arangoDS{c.ArangoDB} + + mg = arangoDS{c.ArangoDB}.apply(mg) + + c.Debug("initialized data source for ArangoDB") + } + return ds, mg, ok } diff --git a/pkg/gofr/migration/migration_test.go b/pkg/gofr/migration/migration_test.go index c456ee769..b014f9106 100644 --- a/pkg/gofr/migration/migration_test.go +++ b/pkg/gofr/migration/migration_test.go @@ -66,6 +66,7 @@ func initialiseClickHouseRunMocks(t *testing.T) (*MockClickhouse, *container.Con mockContainer.Mongo = nil mockContainer.Cassandra = nil mockContainer.PubSub = nil + mockContainer.ArangoDB = nil mockContainer.Logger = logging.NewMockLogger(logging.DEBUG) mockContainer.Clickhouse = mockClickHouse diff --git a/pkg/gofr/migration/mock_interface.go b/pkg/gofr/migration/mock_interface.go index 7993a4855..0f6c359f6 100644 --- a/pkg/gofr/migration/mock_interface.go +++ b/pkg/gofr/migration/mock_interface.go @@ -17,7 +17,6 @@ import ( redis "github.com/redis/go-redis/v9" gomock "go.uber.org/mock/gomock" - container "gofr.dev/pkg/gofr/container" ) @@ -372,6 +371,415 @@ func (mr *MockClickhouseMockRecorder) Select(ctx, dest, query any, args ...any) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockClickhouse)(nil).Select), varargs...) } +// MockCassandra is a mock of Cassandra interface. +type MockCassandra struct { + ctrl *gomock.Controller + recorder *MockCassandraMockRecorder +} + +// MockCassandraMockRecorder is the mock recorder for MockCassandra. +type MockCassandraMockRecorder struct { + mock *MockCassandra +} + +// NewMockCassandra creates a new mock instance. +func NewMockCassandra(ctrl *gomock.Controller) *MockCassandra { + mock := &MockCassandra{ctrl: ctrl} + mock.recorder = &MockCassandraMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCassandra) EXPECT() *MockCassandraMockRecorder { + return m.recorder +} + +// BatchQuery mocks base method. +func (m *MockCassandra) BatchQuery(name, stmt string, values ...any) error { + m.ctrl.T.Helper() + varargs := []any{name, stmt} + for _, a := range values { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BatchQuery", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// BatchQuery indicates an expected call of BatchQuery. +func (mr *MockCassandraMockRecorder) BatchQuery(name, stmt any, values ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, stmt}, values...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchQuery", reflect.TypeOf((*MockCassandra)(nil).BatchQuery), varargs...) +} + +// Exec mocks base method. +func (m *MockCassandra) Exec(query string, args ...any) error { + m.ctrl.T.Helper() + varargs := []any{query} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Exec", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Exec indicates an expected call of Exec. +func (mr *MockCassandraMockRecorder) Exec(query any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{query}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockCassandra)(nil).Exec), varargs...) +} + +// ExecuteBatch mocks base method. +func (m *MockCassandra) ExecuteBatch(name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteBatch", name) + ret0, _ := ret[0].(error) + return ret0 +} + +// ExecuteBatch indicates an expected call of ExecuteBatch. +func (mr *MockCassandraMockRecorder) ExecuteBatch(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatch", reflect.TypeOf((*MockCassandra)(nil).ExecuteBatch), name) +} + +// HealthCheck mocks base method. +func (m *MockCassandra) HealthCheck(ctx context.Context) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthCheck", ctx) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HealthCheck indicates an expected call of HealthCheck. +func (mr *MockCassandraMockRecorder) HealthCheck(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockCassandra)(nil).HealthCheck), ctx) +} + +// NewBatch mocks base method. +func (m *MockCassandra) NewBatch(name string, batchType int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewBatch", name, batchType) + ret0, _ := ret[0].(error) + return ret0 +} + +// NewBatch indicates an expected call of NewBatch. +func (mr *MockCassandraMockRecorder) NewBatch(name, batchType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBatch", reflect.TypeOf((*MockCassandra)(nil).NewBatch), name, batchType) +} + +// MockMongo is a mock of Mongo interface. +type MockMongo struct { + ctrl *gomock.Controller + recorder *MockMongoMockRecorder +} + +// MockMongoMockRecorder is the mock recorder for MockMongo. +type MockMongoMockRecorder struct { + mock *MockMongo +} + +// NewMockMongo creates a new mock instance. +func NewMockMongo(ctrl *gomock.Controller) *MockMongo { + mock := &MockMongo{ctrl: ctrl} + mock.recorder = &MockMongoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMongo) EXPECT() *MockMongoMockRecorder { + return m.recorder +} + +// CreateCollection mocks base method. +func (m *MockMongo) CreateCollection(ctx context.Context, name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, name) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockMongoMockRecorder) CreateCollection(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockMongo)(nil).CreateCollection), ctx, name) +} + +// DeleteMany mocks base method. +func (m *MockMongo) DeleteMany(ctx context.Context, collection string, filter any) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteMany", ctx, collection, filter) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteMany indicates an expected call of DeleteMany. +func (mr *MockMongoMockRecorder) DeleteMany(ctx, collection, filter any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMany", reflect.TypeOf((*MockMongo)(nil).DeleteMany), ctx, collection, filter) +} + +// DeleteOne mocks base method. +func (m *MockMongo) DeleteOne(ctx context.Context, collection string, filter any) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteOne", ctx, collection, filter) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteOne indicates an expected call of DeleteOne. +func (mr *MockMongoMockRecorder) DeleteOne(ctx, collection, filter any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOne", reflect.TypeOf((*MockMongo)(nil).DeleteOne), ctx, collection, filter) +} + +// Drop mocks base method. +func (m *MockMongo) Drop(ctx context.Context, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Drop", ctx, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// Drop indicates an expected call of Drop. +func (mr *MockMongoMockRecorder) Drop(ctx, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Drop", reflect.TypeOf((*MockMongo)(nil).Drop), ctx, collection) +} + +// Find mocks base method. +func (m *MockMongo) Find(ctx context.Context, collection string, filter, results any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Find", ctx, collection, filter, results) + ret0, _ := ret[0].(error) + return ret0 +} + +// Find indicates an expected call of Find. +func (mr *MockMongoMockRecorder) Find(ctx, collection, filter, results any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockMongo)(nil).Find), ctx, collection, filter, results) +} + +// FindOne mocks base method. +func (m *MockMongo) FindOne(ctx context.Context, collection string, filter, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOne", ctx, collection, filter, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// FindOne indicates an expected call of FindOne. +func (mr *MockMongoMockRecorder) FindOne(ctx, collection, filter, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockMongo)(nil).FindOne), ctx, collection, filter, result) +} + +// InsertMany mocks base method. +func (m *MockMongo) InsertMany(ctx context.Context, collection string, documents []any) ([]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertMany", ctx, collection, documents) + ret0, _ := ret[0].([]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertMany indicates an expected call of InsertMany. +func (mr *MockMongoMockRecorder) InsertMany(ctx, collection, documents any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertMany", reflect.TypeOf((*MockMongo)(nil).InsertMany), ctx, collection, documents) +} + +// InsertOne mocks base method. +func (m *MockMongo) InsertOne(ctx context.Context, collection string, document any) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertOne", ctx, collection, document) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertOne indicates an expected call of InsertOne. +func (mr *MockMongoMockRecorder) InsertOne(ctx, collection, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOne", reflect.TypeOf((*MockMongo)(nil).InsertOne), ctx, collection, document) +} + +// StartSession mocks base method. +func (m *MockMongo) StartSession() (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StartSession") + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StartSession indicates an expected call of StartSession. +func (mr *MockMongoMockRecorder) StartSession() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartSession", reflect.TypeOf((*MockMongo)(nil).StartSession)) +} + +// UpdateByID mocks base method. +func (m *MockMongo) UpdateByID(ctx context.Context, collection string, id, update any) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateByID", ctx, collection, id, update) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateByID indicates an expected call of UpdateByID. +func (mr *MockMongoMockRecorder) UpdateByID(ctx, collection, id, update any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateByID", reflect.TypeOf((*MockMongo)(nil).UpdateByID), ctx, collection, id, update) +} + +// UpdateMany mocks base method. +func (m *MockMongo) UpdateMany(ctx context.Context, collection string, filter, update any) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateMany", ctx, collection, filter, update) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateMany indicates an expected call of UpdateMany. +func (mr *MockMongoMockRecorder) UpdateMany(ctx, collection, filter, update any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMany", reflect.TypeOf((*MockMongo)(nil).UpdateMany), ctx, collection, filter, update) +} + +// UpdateOne mocks base method. +func (m *MockMongo) UpdateOne(ctx context.Context, collection string, filter, update any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateOne", ctx, collection, filter, update) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateOne indicates an expected call of UpdateOne. +func (mr *MockMongoMockRecorder) UpdateOne(ctx, collection, filter, update any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOne", reflect.TypeOf((*MockMongo)(nil).UpdateOne), ctx, collection, filter, update) +} + +// MockArangoDB is a mock of ArangoDB interface. +type MockArangoDB struct { + ctrl *gomock.Controller + recorder *MockArangoDBMockRecorder +} + +// MockArangoDBMockRecorder is the mock recorder for MockArangoDB. +type MockArangoDBMockRecorder struct { + mock *MockArangoDB +} + +// NewMockArangoDB creates a new mock instance. +func NewMockArangoDB(ctrl *gomock.Controller) *MockArangoDB { + mock := &MockArangoDB{ctrl: ctrl} + mock.recorder = &MockArangoDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockArangoDB) EXPECT() *MockArangoDBMockRecorder { + return m.recorder +} + +// CreateCollection mocks base method. +func (m *MockArangoDB) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, database, collection, isEdge) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockArangoDBMockRecorder) CreateCollection(ctx, database, collection, isEdge any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockArangoDB)(nil).CreateCollection), ctx, database, collection, isEdge) +} + +// CreateDB mocks base method. +func (m *MockArangoDB) CreateDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateDB indicates an expected call of CreateDB. +func (mr *MockArangoDBMockRecorder) CreateDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockArangoDB)(nil).CreateDB), ctx, database) +} + +// CreateGraph mocks base method. +func (m *MockArangoDB) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateGraph", ctx, database, graph, edgeDefinitions) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateGraph indicates an expected call of CreateGraph. +func (mr *MockArangoDBMockRecorder) CreateGraph(ctx, database, graph, edgeDefinitions any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGraph", reflect.TypeOf((*MockArangoDB)(nil).CreateGraph), ctx, database, graph, edgeDefinitions) +} + +// DropCollection mocks base method. +func (m *MockArangoDB) DropCollection(ctx context.Context, database, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropCollection", ctx, database, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropCollection indicates an expected call of DropCollection. +func (mr *MockArangoDBMockRecorder) DropCollection(ctx, database, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropCollection", reflect.TypeOf((*MockArangoDB)(nil).DropCollection), ctx, database, collection) +} + +// DropDB mocks base method. +func (m *MockArangoDB) DropDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropDB indicates an expected call of DropDB. +func (mr *MockArangoDBMockRecorder) DropDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDB", reflect.TypeOf((*MockArangoDB)(nil).DropDB), ctx, database) +} + +// DropGraph mocks base method. +func (m *MockArangoDB) DropGraph(ctx context.Context, database, graph string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropGraph", ctx, database, graph) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropGraph indicates an expected call of DropGraph. +func (mr *MockArangoDBMockRecorder) DropGraph(ctx, database, graph any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropGraph", reflect.TypeOf((*MockArangoDB)(nil).DropGraph), ctx, database, graph) +} + // Mockmigrator is a mock of migrator interface. type Mockmigrator struct { ctrl *gomock.Controller