Categorygithub.com/ddspog/mongo
modulepackage
1.3.0
Repository: https://github.com/ddspog/mongo.git
Documentation: pkg.go.dev

# README

mongo GoDoc Go Report Card Commitizen friendly Travis CI

by ddspog

Package mongo helps you mask the connection to MongoDB using mgo package.

License

You are free to copy, modify and distribute mongo package with attribution under the terms of the MIT license. See the LICENSE file for details.

Installation

Install mongo package with:

go get github.com/ddspog/mongo

How to use

This package mask the connection to MongoDB using mgo package.

This is made with function Connect, that saves Session and Mongo object which will be used later from other packages. Also, I've embedded the Collection, Database and Query types, to allow mocking via interfaces. The embedded was necessary for the functions to use the interfaces as return values, that way, the code can use the original, or generate a mock of them for testing purposes.

The package can be used like this:

// To connect with MongoDB database.
mongo.Connect()
defer mongo.Disconnect()

// You can use mgo known functions with mongo.CurrentSession() or
// mongo.Mongo(). If you want to use only the Database object to
// handle the operations on MongoDB with a handler, use:
mongo.ConsumeDatabaseOnSession(func(db elements.Databaser) {
    // Make db object available on handlers.
    p := handler.NewProductHandler()
    p.Link(db)

    // ... Do other operations.
})

Other option of usage is through the use of mongo.DatabaseSocket:

// To connect with MongoDB database.
mongo.Connect()
defer mongo.Disconnect()

// Create socket
s := mongo.NewSocket()
defer s.Close()

// Make db object available on handlers.
p := handler.NewProductHandler()
p.Link(s.DB())

// ... Do other operations.

Or even through the concept of LinkedHandlers, as described later:

// To connect with MongoDB database.
mongo.Connect()
defer mongo.Disconnect()

// Create a linked handler
p, _ := handler.NewLinkedProductHandler()

// ... Do other operations.

Further usage it's the same way mgo package is used. Look into mgo docs page: https://godoc.org/github.com/globalsign/mgo

The Connect function tries to connect to a MONGODB_URL environment variable, but when it's not defined, it uses a default URL:

mongodb://localhost:27017/test

Mocking

You can mock some functions of this package, by mocking the mgo called functions mgo.ParseURL and mgo.Dial. Use the MockMongoSetup presented on this package (only in test environment), like:

create, _ := mongo.NewMockMongoSetup(t)
defer create.Finish()

create.ParseURL().Returns(db, nil)
create.Dial().Returns(info, nil)

// Call any preparations on connection ...
if err := mongo.Connect(); err != nil {
    t.fail()
}

Documenter

Mongo package also contain utility functions to help modeling documents.

The package contains a interface Documenter which contain getters for important attributes to any document on MongoDB: _id, created_on and updated_on. It also contains functions that generates correctly the created_on and updated_on attributes.

The Documenter can be used like this:

// Create a type representing the Document type
type Product struct {
    IDV ObjectId   `json:"_id,omitempty" bson:"_id,omitempty"`
    CreatedOnV  int64   `json:"created_on,omitempty" bson:"created_on,omitempty"`
    UpdatedOnV  int64   `json:"updated_on,omitempty" bson:"updated_on,omitempty"`
    NameV   string  `json:"name" form:"name" binding:"required" bson:"name"`
    PriceV  float32 `json:"price" form:"price" binding:"required" bson:"price"`
}

// Implement the Documenter interface.
func (p *Product) ID() (id ObjectId) {
    id = p.IDV
    return
}

func (p *Product) CreatedOn() (t int64) {
    t = p.CreatedOnV
    return
}

func (p *Product) UpdatedOn() (t int64) {
    t = p.UpdatedOnV
    return
}

func (p *Product) New() (doc mongo.Documenter) {
    doc = &Product{}
    return
}

// On these methods, you can use the functions implemented mongo
// package.
func (p *Product) Map() (out M, err error) {
    out, err = mongo.MapDocumenter(p)
    return
}

func (p *Product) Init(in M) (err error) {
    var doc mongo.Documenter = p
    err = mongo.InitDocumenter(in, &doc)
    return
}

func (p *Product) GenerateID() {
    p.IDV = mongo.NewID()
}

func (p *Product) CalculateCreatedOn() {
    p.CreatedOnV = mongo.NowInMilli()
}

func (p *Product) CalculateUpdatedOn() {
    p.UpdatedOnV = mongo.NowInMilli()
}

// Create a product variable, and try its methods.
p := Product{}
p.CalculateCreatedOn()
t := p.CreatedOn()

You can also mock some other functions of this package, by mocking some called functions time.Now and NewObjectId. Use the MockModelSetup presented on this package (only in test environment), like:

create, _ := mongo.NewMockModelSetup(t)
defer create.Finish()

create.Now().Returns(time.Parse("02-01-2006", "22/12/2006"))
create.NewID().Returns(ObjectIdHex("anyID"))

var d mongo.Documenter
// Call any needed methods ...
d.GenerateID()
d.CalculateCreatedOn()

Handle

Mongo package also enable creation of Handle, a type that connects to database collections and do some operations.

The Handle were made to be imported on embedding type, and through overriding of some methods, to implement an adequate Handler for a desired type of Document. The Handle type assumes to operate on a Documenter type, that will contain information about the operation to made with Handle.

The package should be used to create new types. Use the Handler type for creating embedding types.

type ProductHandle struct {
    *mongo.Handle
    DocumentV *product.Product
}

For each new type, a constructor may be needed, and for that Handler has a basic constructor.

func New() (p *ProductHandle) {
    p = &ProductHandle{
        Handle: mongo.NewHandle(),
        DocumentV: product.New(),
    }
    return
}

func NewLinked() (p *ProductHandle, err error) {
    p = &ProductHandle{
        DocumentV: product.New(),
    }
    p.Handle, err = mongo.NewLinkedHandle("products")
}

All functions were made to be overridden and rewrite. First thing to do it's creating the Link method, as it follows:

func (p *ProductHandle) Link(db mongo.Databaser) (err error) {
    err = p.Handle.Link(db)
    return
}

The creation of Insert, Remove and RemoveAll are trivial. Call it with a Document getter function defined like:

func (p *ProductHandle) Document() (d *product.Product) {
    d = p.DocumentV
    return
}

func (p *ProductHandle) Insert() (err error) {
    err = p.Handle.Insert(p.Document())
    return
}

The Clean function is simple and helps a lot:

func (p *ProductHandle) Clean() {
    p.Handle.Clean()
    p.DocumentV = product.New()
}

The Update function uses an id as an argument:

func (p *ProductHandle) Update(id ObjectId) (err error) {
    err = p.Handle.Update(id, p.Document())
    return
}

The complicated functions are Find and FindAll which requires casting for the Document type:

func (p *ProductHandle) Find() (prod *product.Product, err error) {
    var doc mongo.Documenter = product.New()
    err = p.Handle.Find(p.Document(), doc)
    prod = doc.(*product.Product)
    return
}

func (p *ProductHandle) FindAll() (proda []*product.Product, err error) {
    var da []mongo.Documenter
    err = p.Handle.FindAll(p.Document(), &da)
    proda = make([]*product.Product, len(da))
    for i := range da {
        //noinspection GoNilContainerIndexing
        proda[i] = da[i].(*product.Product)
    }
    return
}

For all functions written, verification it's advisable.

Testing

This package contains a nice coverage with the unit tests, within the objectives of the project.

The elements, embedded and mocks sub-packages have low coverage because they fulfill a need to mock mgo elements. These packages only embedded mgo objects to mock, and by this a lot of unused functions were created to fulfill interface requisites.

On the other hand, model, handler and mongo package have full coverage, being the focus of this project.

The project also contains a set of acceptance tests. I've have set the test-acceptance task with the commands to run it. These tests requires a mongo test database to be available. It creates, search and remove elements from it, being reusable without broking the database.

Contribution

This package has some objectives from now:

  • Being incorporate on mgo package (possible fork) on possible future.
  • Incorporate any new ideas about possible improvements.

Any interest in help is much appreciated.

# Packages

No description provided by the author

# Functions

Connect to MongoDB of server.
ConsumeDatabaseOnSession clones a session and use it to creates a Databaser object to be consumed in f function.
CurrentSession return connected mongo session.
Disconnect undo the connection made.
InitDocumenter translates a M received, to the Documenter structure received as a pointer.
MapDocumenter translates a Documenter in whatever structure it has, to a M object, more easily read by mgo.Collection methods.
Mongo return the MongoDB connection string information.
NewHandle creates a new Handle to be embedded onto handle for other types.
NewID generates a new id for documents.
NewLinkedHandle creates a new linked Handle to be embedded onto handle for other types.
NewMockMGOSetup returns a new MockMGOSetup, already configuring mock environment for the mgo classes mocked with gomock.
NewMockModelSetup returns a new MockModelSetup, already configuring a FakeNow function, and a FakeNewID function on the setup.
NewMockMongoSetup returns a new MockMongoSetup, already configuring a FakeParseURL function, and a FakeDial function on the setup.
NewSocket creates a new DatabaseSocket, initializing channel values, supporting the DB calls.
NowInMilli returns the actual time, in a int64 value in Millisecond unit, used by the updaters of created_on and updated_on.
ObjectIdHex returns an ObjectId from the provided hex representation.

# Constants

DBUrl is the default MongoDB url that will be used to connect to the database.

# Variables

ErrDBNotDefined it's an error received when an DB is nil or undefined.
ErrHandlerNotLinked it's an error received when the Handler isn't linked to any collection.
ErrIDNotDefined it's an error received when an ID isn't defined.
ErrTryRelinkWithNoSocket it's an error received when the Handler tries a second Link with no socket defined on SetSocket().

# Structs

DatabaseSocket it's a socket connection with a specified MongoDB database, that can be closed after using it.
Handle it's a type implementing the Handler interface, responsible of taking documents and using them to manipulate collections.
MockModelSetup it's a setup type for configuring mocking for util functions.
MockMongoSetup it's a setup type for configuring mocking for connect functions.

# Interfaces

Documenter it's an interface that could be common to any documents types used to store values on a MongoDB.
FakeDialer it's a function mocking object, needed for mock purposes.
FakeNewIDer it's a function mocking object, needed for mock purposes.
FakeNower it's a function mocking object, needed for mock purposes.
FakeParseURLer it's a function mocking object, needed for mock purposes.

# Type aliases

M is a convenient alias for a map[string]interface{} map, useful for dealing with BSON in a native way.
MockCollectioner is a mock of Collectioner interface.
MockDatabaser is a mock of Databaser interface.
MockMGOSetup it's a setup type for configuring mocking of mgo package.
ObjectId is a unique ID identifying a BSON value.