mongoifc

package module
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2022 License: MIT Imports: 9 Imported by: 1

README

mongoifc

Code Analysis Go Reference codecov GitHub tag (latest SemVer)

The Interfaces for the MongoDB driver

Versioning Policy

The mongoifc code is stabilized, so now the version will match the version of the MongoDB driver since v1.8.0.

In case of need for bug fixes in mongoifc, the version will be in this format v1.8.1+N, where v1.8.1 is the version of MongoDB driver and N is a patch of mongoifc. The new version for changes in README.md, tests, examples, GitHub workflows is not required.

Important

It is not a simple drop in replacement because of the limitations in Go. You should rewrite your code to use this library instead of mongo driver.

conn := mongoifc.Connect(...)

instead of

conn := mongo.Connect(...)

or if you have a special code that returns the mongo object then you need to use one of Wrap functions to wrap the mongo.Client or mongo.Database or mongo.Collection or mongo.Session objects:

orig := mongo.Connect(...)

...

conn := mongoifc.WrapClient(orig)

or

func GetTenantDB(ctx context.Context, tenantID, dbName string) (*mongo.Database, error) {
// a code that returns a special database for a given tenant and database name
}

...

orig, err := GetTenantDB(ctx, tenant, "users")
if err != nil {
...
}
db = mongoifc.WrapDatabase(orig)

Now let's dig a bit into the limitations. Assume that you have a function to return a list of admin users, and you rewrote it using mongoifc:

package users

// Original: func GetAdmins(ctx context.Context, db *mongo.Database) ([]*User, error)
func GetAdmins(ctx context.Context, db mongoifc.Database) ([]*User, error) {
	var users []*User
	cur, err := db.Collection(UsersCollection).Find(ctx, User{
		Active:  true,
		IsAdmin: true,
	})
	if err != nil {
		return nil, err
	}
	if err := cur.All(ctx, &users); err != nil {
		return nil, err
	}
	return users, err
}

and if you pass an object of *mongo.Database type instead of mongoifc.Database

conn, _ := mongo.Connect(context.Background(), ...)
db := conn.Database(...)

users.GetAdmins(context.Background(), db)

then compilation fails with such error:

 cannot use db (type *mongo.Database) as type mongoifc.Database in argument to simple.GetAdmins:
     *mongo.Database does not implement mongoifc.Database (wrong type for Aggregate method)
         have Aggregate(context.Context, interface {}, ...*"go.mongodb.org/mongo-driver/mongo/options".AggregateOptions) (*mongo.Cursor, error)
         want Aggregate(context.Context, interface {}, ...*"go.mongodb.org/mongo-driver/mongo/options".AggregateOptions) (mongoifc.Cursor, error)

This is the main reason of wrapping the original objects and using the mongoifc instead.

Wrapped Interfaces

Mocks

The mocks folder contains the mocks generated by mockery and gomock tools.

The examples of how to use the mocks can be found in the examples folder or check any of the *_test.go files as well.

Example

Here you can find the examples of how to use and mock mongodb functions

package main

import (
	"context"
	"encoding/json"
	"os"

	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo/options"

	"github.com/sv-tools/mongoifc"
)

const (
	UsersCollection = "users"
)

type User struct {
	ID      string `json:"id,omitempty" bson:"_id,omitempty"`
	Name    string `json:"name,omitempty" bson:"name,omitempty"`
	Email   string `json:"email,omitempty" bson:"email,omitempty"`
	Active  bool   `json:"active,omitempty" bson:"active,omitempty"`
	IsAdmin bool   `json:"is_admin,omitempty" bson:"is_admin,omitempty"`
}

func GetAdmins(ctx context.Context, db mongoifc.Database) ([]*User, error) {
	var users []*User
	cur, err := db.Collection(UsersCollection).Find(ctx, User{
		Active:  true,
		IsAdmin: true,
	})
	if err != nil {
		return nil, err
	}
	if err := cur.All(ctx, &users); err != nil {
		return nil, err
	}
	return users, err
}

// go run main.go "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/?authSource=admin" "${DATABASE}"
func main() {
	uri, database := os.Args[1], os.Args[2]
	opt := options.Client().ApplyURI(uri)
	cl, err := mongoifc.NewClient(opt)
	if err != nil {
		panic(err)
	}

	if err = cl.Connect(context.Background()); err != nil {
		panic(err)
	}
	defer cl.Disconnect(context.Background())

	db := cl.Database(database)
	users, err := GetAdmins(context.Background(), db)
	if err != nil {
		panic(err)
	}
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	if err := enc.Encode(users); err != nil {
		panic(err)
	}
}
How to mock
package main

import (
	"context"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"go.mongodb.org/mongo-driver/mongo/options"

	"github.com/sv-tools/mongoifc"
	gomockMocks "github.com/sv-tools/mongoifc/mocks/gomock"
	mockeryMocks "github.com/sv-tools/mongoifc/mocks/mockery"
)

func TestGetAdmins(t *testing.T) {
	t.Parallel()

	expectedUsers := []*User{
		{Name: "foo", Active: true, IsAdmin: true},
		{Name: "bar", Active: true, IsAdmin: true},
	}
	ctx := context.Background()

	t.Run("mockery", func(t *testing.T) {
		t.Parallel()

		cur := &mockeryMocks.Cursor{}
		cur.On("All", ctx, mock.Anything).Run(func(args mock.Arguments) {
			users := args[1].(*[]*User)
			*users = append(*users, expectedUsers...)
		}).Return(nil)

		col := &mockeryMocks.Collection{}
		col.On("Find", ctx, mock.AnythingOfType("User")).Return(cur, nil)

		db := &mockeryMocks.Database{}
		db.On("Collection", UsersCollection).Return(col)

		users, err := GetAdmins(ctx, db)
		require.NoError(t, err)
		require.Equal(t, expectedUsers, users)
	})

	t.Run("gomock", func(t *testing.T) {
		t.Parallel()

		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		cur := gomockMocks.NewMockCursor(ctrl)
		cur.EXPECT().All(ctx, gomock.Any()).Do(func(ctx context.Context, arg interface{}) {
			users := arg.(*[]*User)
			*users = append(*users, expectedUsers...)
		}).Return(nil)

		col := gomockMocks.NewMockCollection(ctrl)
		col.EXPECT().Find(ctx, gomock.Any()).Return(cur, nil)

		db := gomockMocks.NewMockDatabase(ctrl)
		db.EXPECT().Collection(UsersCollection).Return(col)

		users, err := GetAdmins(ctx, db)
		require.NoError(t, err)
		require.Equal(t, expectedUsers, users)
	})
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func UnWrapClient added in v1.0.0

func UnWrapClient(cl Client) *mongo.Client

UnWrapClient returns original mongo.Client

func UnWrapCollection added in v1.8.2

func UnWrapCollection(co Collection) *mongo.Collection

UnWrapCollection returns original mongo.Collection

func UnWrapDatabase added in v1.8.2

func UnWrapDatabase(db Database) *mongo.Database

UnWrapDatabase returns original mongo.Database

func UnWrapSession added in v1.8.2

func UnWrapSession(ss Session) mongo.Session

UnWrapSession returns original mongo.Session

func WithSession

func WithSession(ctx context.Context, sess Session, fn func(sc SessionContext) error) error

WithSession is a wrapper for `mongo.WithSession` function to call then `mongo.WithSession` function Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#WithSession

Types

type ChangeStream

type ChangeStream interface {
	Current() bson.Raw
	Close(ctx context.Context) error
	Decode(val interface{}) error
	Err() error
	ID() int64
	Next(ctx context.Context) bool
	ResumeToken() bson.Raw
	TryNext(ctx context.Context) bool
}

ChangeStream is an interface for `mongo.ChangeStream` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#ChangeStream

type Client

type Client interface {
	Connect(ctx context.Context) error
	Database(name string, opts ...*options.DatabaseOptions) Database
	Disconnect(ctx context.Context) error
	ListDatabaseNames(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) ([]string, error)
	ListDatabases(
		ctx context.Context,
		filter interface{},
		opts ...*options.ListDatabasesOptions,
	) (mongo.ListDatabasesResult, error)
	NumberSessionsInProgress() int
	Ping(ctx context.Context, rp *readpref.ReadPref) error
	StartSession(opts ...*options.SessionOptions) (Session, error)
	UseSession(ctx context.Context, fn func(sc SessionContext) error) error
	UseSessionWithOptions(ctx context.Context, opts *options.SessionOptions, fn func(sc SessionContext) error) error
	Watch(
		ctx context.Context,
		pipeline interface{},
		opts ...*options.ChangeStreamOptions,
	) (ChangeStream, error)
}

Client is an interface for `mongo.Client` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Client

func Connect

func Connect(ctx context.Context, opts ...*options.ClientOptions) (Client, error)

Connect is a wrapper for `mongo.Connect` function to return the object as `Client` interface Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect

func NewClient

func NewClient(opts ...*options.ClientOptions) (Client, error)

NewClient is a wrapper for `mongo.NewClient` function to return the object as `Client` interface Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#NewClient

func WrapClient

func WrapClient(cl *mongo.Client) Client

WrapClient returns an instance of Client interface for given mongo.Client object

type Collection

type Collection interface {
	Aggregate(ctx context.Context, pipeline interface{}, opts ...*options.AggregateOptions) (Cursor, error)
	BulkWrite(
		ctx context.Context,
		models []mongo.WriteModel,
		opts ...*options.BulkWriteOptions,
	) (*mongo.BulkWriteResult, error)
	Clone(opts ...*options.CollectionOptions) (Collection, error)
	CountDocuments(ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error)
	Database() Database
	DeleteMany(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
	DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
	Distinct(
		ctx context.Context,
		fieldName string,
		filter interface{},
		opts ...*options.DistinctOptions,
	) ([]interface{}, error)
	Drop(ctx context.Context) error
	EstimatedDocumentCount(ctx context.Context, opts ...*options.EstimatedDocumentCountOptions) (int64, error)
	Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) (Cursor, error)
	FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) SingleResult
	FindOneAndDelete(ctx context.Context, filter interface{}, opts ...*options.FindOneAndDeleteOptions) SingleResult
	FindOneAndReplace(
		ctx context.Context,
		filter interface{},
		replacement interface{},
		opts ...*options.FindOneAndReplaceOptions,
	) SingleResult
	FindOneAndUpdate(
		ctx context.Context,
		filter interface{},
		update interface{},
		opts ...*options.FindOneAndUpdateOptions,
	) SingleResult
	Indexes() IndexView
	InsertMany(
		ctx context.Context,
		documents []interface{},
		opts ...*options.InsertManyOptions,
	) (*mongo.InsertManyResult, error)
	InsertOne(
		ctx context.Context,
		document interface{},
		opts ...*options.InsertOneOptions,
	) (*mongo.InsertOneResult, error)
	Name() string
	ReplaceOne(
		ctx context.Context,
		filter interface{},
		replacement interface{},
		opts ...*options.ReplaceOptions,
	) (*mongo.UpdateResult, error)
	UpdateByID(
		ctx context.Context,
		id interface{},
		update interface{},
		opts ...*options.UpdateOptions,
	) (*mongo.UpdateResult, error)
	UpdateMany(
		ctx context.Context,
		filter interface{},
		update interface{},
		opts ...*options.UpdateOptions,
	) (*mongo.UpdateResult, error)
	UpdateOne(
		ctx context.Context,
		filter interface{},
		update interface{},
		opts ...*options.UpdateOptions,
	) (*mongo.UpdateResult, error)
	Watch(ctx context.Context, pipeline interface{}, opts ...*options.ChangeStreamOptions) (ChangeStream, error)
}

Collection is an interface for `mongo.Collection` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Collection

func WrapCollection added in v1.8.2

func WrapCollection(co *mongo.Collection) Collection

WrapCollection returns an instance of Collection interface for given mongo.Collection object

type Cursor

type Cursor interface {
	Current() bson.Raw
	All(ctx context.Context, results interface{}) error
	Close(ctx context.Context) error
	Decode(val interface{}) error
	Err() error
	ID() int64
	Next(ctx context.Context) bool
	RemainingBatchLength() int
	TryNext(ctx context.Context) bool
}

Cursor is an interface for `mongo.Cursor` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Cursor

func NewCursorFromDocuments added in v1.9.0

func NewCursorFromDocuments(documents []interface{}, err error, registry *bsoncodec.Registry) (Cursor, error)

NewCursorFromDocuments is a wrapper for NewCursorFromDocuments function of the mongodb to return Cursor https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#NewCursorFromDocuments

type Database

type Database interface {
	Aggregate(ctx context.Context, pipeline interface{}, opts ...*options.AggregateOptions) (Cursor, error)
	Client() Client
	Collection(name string, opts ...*options.CollectionOptions) Collection
	CreateCollection(ctx context.Context, name string, opts ...*options.CreateCollectionOptions) error
	CreateView(
		ctx context.Context,
		viewName, viewOn string,
		pipeline interface{},
		opts ...*options.CreateViewOptions,
	) error
	Drop(ctx context.Context) error
	ListCollectionNames(
		ctx context.Context,
		filter interface{},
		opts ...*options.ListCollectionsOptions,
	) ([]string, error)
	ListCollections(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) (Cursor, error)
	ListCollectionSpecifications(
		ctx context.Context,
		filter interface{},
		opts ...*options.ListCollectionsOptions,
	) ([]*mongo.CollectionSpecification, error)
	Name() string
	ReadConcern() *readconcern.ReadConcern
	ReadPreference() *readpref.ReadPref
	RunCommand(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) SingleResult
	RunCommandCursor(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) (Cursor, error)
	Watch(
		ctx context.Context,
		pipeline interface{},
		opts ...*options.ChangeStreamOptions,
	) (ChangeStream, error)
	WriteConcern() *writeconcern.WriteConcern
}

Database is an interface for `mongo.Database` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Database

func WrapDatabase added in v1.8.2

func WrapDatabase(db *mongo.Database) Database

WrapDatabase returns an instance of Database interface for given mongo.Database object

type IndexView

type IndexView interface {
	CreateMany(ctx context.Context, models []mongo.IndexModel, opts ...*options.CreateIndexesOptions) ([]string, error)
	CreateOne(ctx context.Context, model mongo.IndexModel, opts ...*options.CreateIndexesOptions) (string, error)
	DropAll(ctx context.Context, opts ...*options.DropIndexesOptions) (bson.Raw, error)
	DropOne(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error)
	List(ctx context.Context, opts ...*options.ListIndexesOptions) (Cursor, error)
	ListSpecifications(ctx context.Context, opts ...*options.ListIndexesOptions) ([]*mongo.IndexSpecification, error)
}

IndexView is an interface for `mongo.IndexView` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#IndexView

type Session

type Session interface {
	StartTransaction(opts ...*options.TransactionOptions) error
	AbortTransaction(ctx context.Context) error
	CommitTransaction(ctx context.Context) error
	WithTransaction(
		ctx context.Context,
		fn func(sc SessionContext) (interface{}, error),
		opts ...*options.TransactionOptions,
	) (interface{}, error)
	EndSession(ctx context.Context)
	ClusterTime() bson.Raw
	OperationTime() *primitive.Timestamp
	Client() Client
	ID() bson.Raw
	AdvanceClusterTime(bson.Raw) error
	AdvanceOperationTime(*primitive.Timestamp) error
}

Session is an interface for `mongo.Session` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Session

func WrapSession added in v1.8.2

func WrapSession(ss mongo.Session) Session

WrapSession returns an instance of Session interface for given mongo.Session object

type SessionContext added in v1.0.0

type SessionContext interface {
	context.Context
	Session
}

type SingleResult

type SingleResult interface {
	Decode(v interface{}) error
	DecodeBytes() (bson.Raw, error)
	Err() error
}

SingleResult is an interface for `mongo.SingleResult` structure Documentation: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#SingleResult

func NewSingleResultFromDocument added in v1.9.0

func NewSingleResultFromDocument(document interface{}, err error, registry *bsoncodec.Registry) SingleResult

NewSingleResultFromDocument is a wrapper for NewSingleResultFromDocument function of the mongodb to return SingleResult https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#NewSingleResultFromDocument

Directories

Path Synopsis
examples
mocks
gomock
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL