Commit b3ed9e2c authored by Michel Roy's avatar Michel Roy Committed by Kevin Di Lallo
Browse files

added couch package

parent 1efc1127
Loading
Loading
Loading
Loading
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019  InterDigital Communications, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package couchdb

import (
	"context"

	"github.com/flimzy/kivik"
	_ "github.com/go-kivik/couchdb"

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
)

var client *Client

// Client - Implements a couchDB client
type Client struct {
	addr     string
	dbClient *kivik.Client
}

// Connector - Implements a CouchDB connector
type Connector struct {
	dbName   string
	dbHandle *kivik.DB
}

// NewConnector - Creates and initialize a CouchDB connector to a database
func NewConnector(addr string, dbName string) (rc *Connector, err error) {
	rc = new(Connector)

	// Connect to CouchDB
	if client == nil {
		log.Debug("Establish new couchDB client connection")
		c := new(Client)
		c.addr = addr
		c.dbClient, err = kivik.New(context.TODO(), "couch", addr)
		if err != nil {
			return nil, err
		}
		client = c
	}

	rc.dbName = dbName
	// Create DB if not exist
	exists, err := client.dbClient.DBExists(context.TODO(), rc.dbName)
	if err != nil {
		return nil, err
	}
	if !exists {
		log.Debug("Create DB: " + dbName)
		err = client.dbClient.CreateDB(context.TODO(), dbName)
		if err != nil {
			return nil, err
		}
	}
	// Open DB
	log.Debug("Open DB: " + dbName)
	rc.dbHandle, err = client.dbClient.DB(context.TODO(), dbName)
	if err != nil {
		return nil, err
	}
	return rc, nil
}

// getDocument - Get document from DB
func (dbCon *Connector) getDoc(returnNilOnNotFound bool, docName string) (doc []byte, err error) {
	log.Debug("Get document from DB: " + docName)
	row, err := dbCon.dbHandle.Get(context.TODO(), docName)
	if err != nil {
		// that's a call to the couch DB.. in order not to return nil, we override it
		if returnNilOnNotFound {
			// specifically for the case where there is nothing.. so the document object will be empty
			return nil, nil
		}
		return nil, err
	}
	// Decode JSON-encoded document
	err = row.ScanDoc(&doc)
	return doc, err
}

// getDocList - Get document list from DB
func (dbCon *Connector) getDocList() (docList [][]byte, err error) {
	log.Debug("Get all docs from DB")
	rows, err := dbCon.dbHandle.AllDocs(context.TODO())
	if err != nil {
		return nil, err
	}

	// Loop through docs and populate doc list to return
	log.Debug("Loop through docs")
	for rows.Next() {
		var doc []byte
		doc, err = dbCon.getDoc(false, rows.ID())
		if err == nil {
			// Append to list
			docList = append(docList, doc)
		}
	}

	return docList, nil
}

// addDoc - Add scenario to DB
func (dbCon *Connector) addDoc(docName string, doc []byte) (string, error) {
	log.Debug("Add new doc to DB: " + docName)
	rev, err := dbCon.dbHandle.Put(context.TODO(), docName, doc)
	if err != nil {
		return "", err
	}

	return rev, nil
}

// updateDoc - Update a document in DB
func (dbCon *Connector) updateDoc(docName string, doc []byte) (string, error) {
	log.Debug("Update doc from DB: " + docName)
	// Remove previous version
	err := dbCon.deleteDoc(docName)
	if err != nil {
		return "", err
	}

	// Add updated version
	rev, err := dbCon.addDoc(docName, doc)
	if err != nil {
		return "", err
	}

	return rev, nil
}

// deleteDoc - Remove a document from DB
func (dbCon *Connector) deleteDoc(docName string) error {
	log.Debug("Delete doc from DB: " + docName)
	// Get latest Rev of stored document
	rev, err := dbCon.dbHandle.Rev(context.TODO(), docName)
	if err != nil {
		return err
	}

	// Remove doc from couchDB
	_, err = dbCon.dbHandle.Delete(context.TODO(), docName, rev)
	if err != nil {
		return err
	}

	return nil
}

// deleteAllDocs - Remove all documents from DB
func (dbCon *Connector) deleteAllDocs() error {
	log.Debug("Delete all docs from DB")
	// Retrieve all scenarios from DB
	rows, err := dbCon.dbHandle.AllDocs(context.TODO())
	if err != nil {
		return err
	}

	// Loop through docs and remove each one
	for rows.Next() {
		_ = dbCon.deleteDoc(rows.ID())
	}

	return nil
}
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019  InterDigital Communications, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package couchdb

import (
	"fmt"
	"testing"

	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
)

var couchDBAddr = "http://localhost:30985/"
var dbName1 = "unit-test1"
var dbName2 = "unit-test2"

func TestCouchDB(t *testing.T) {
	fmt.Println("--- ", t.Name())
	log.MeepTextLogInit(t.Name())

	con1, err := NewConnector(couchDBAddr, dbName1)
	if err != nil {
		log.Debug(err)
		t.Errorf("Error creating connector")
	} else if con1 == nil {
		t.Errorf("Received a nil connector")
	}
	con2, err := NewConnector(couchDBAddr, dbName2)
	if err != nil {
		log.Debug(err)
		t.Errorf("Error creating connector")
	} else if con2 == nil {
		t.Errorf("Received a nil connector")
	}

	testDb(t, con1)

	testDb(t, con2)

	err = con1.deleteAllDocs()
	if err != nil {
		t.Errorf("Deleting all docs returned an error (con1)")
	}
	err = con2.deleteAllDocs()
	if err != nil {
		t.Errorf("Deleting all docs returned an error (con2)")
	}

}

func testDb(t *testing.T, c *Connector) {
	//Empty DB
	err := c.deleteAllDocs()
	if err != nil {
		log.Debug(err)
		t.Errorf("deleteAllDocs shouls not return an error")
	}

	// Get inexistent doc
	doc, err := c.getDoc(false, "not-a-document")
	if err == nil {
		t.Errorf("getDoc should return an error (inexistent doc)")
	} else if doc != nil {
		t.Errorf("getDoc should return nil for inexistent doc")
	}

	// Get inexistent doc
	doc, err = c.getDoc(true, "not-a-document")
	if err != nil {
		t.Errorf("getDoc error should be suppressed (inexistent doc)")
	} else if doc != nil {
		t.Errorf("getDoc should return nil for inexistent doc")
	}

	// Get doc list
	docList, err := c.getDocList()
	if err != nil {
		t.Errorf("getDocList should not return an error (empty doc list)")
	} else if len(docList) != 0 {
		t.Errorf("getDocList should return an empty list (empty doc list)")
	}

	doc1 := []byte(`{"data":"This is document #1"}`)
	doc1Update := []byte(`{"data":"This is document #1 update"}`)
	doc2 := []byte(`{"data":"This is document #2"}`)
	doc3 := []byte(`{"data":"This is document #3"}`)

	rev1, err := c.addDoc("doc1", doc1)
	if err != nil {
		log.Debug(err)
		t.Errorf("addDoc returned an error")
	}
	log.Debug(rev1)
	rev2, err := c.addDoc("doc2", doc2)
	if err != nil {
		log.Debug(err)
		t.Errorf("addDoc returned an error")
	}
	log.Debug(rev2)
	rev3, err := c.addDoc("doc3", doc3)
	if err != nil {
		log.Debug(err)
		t.Errorf("addDoc returned an error")
	}
	log.Debug(rev3)

	// Get doc list
	docList, err = c.getDocList()
	if err != nil {
		t.Errorf("getDocList should not return an error (3 doc list)")
	} else if len(docList) != 3 {
		t.Errorf("getDocList should return a 3 document list (3 doc list)")
	}

	rev1, err = c.updateDoc("doc1", doc1Update)
	if err != nil {
		log.Debug(err)
		t.Errorf("updateDoc returned an error")
	}
	log.Debug(rev1)

	// Get doc list
	docList, err = c.getDocList()
	if err != nil {
		t.Errorf("getDocList should not return an error (3 doc list)")
	} else if len(docList) != 3 {
		t.Errorf("getDocList should return a 3 document list (3 doc list)")
	}

}
+15 −0
Original line number Diff line number Diff line
module github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-couch

go 1.12

require (
	github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger v0.0.0
	github.com/flimzy/kivik v1.8.1
	github.com/go-kivik/couchdb v1.8.1
	github.com/go-kivik/kivik v1.8.1
	github.com/imdario/mergo v0.3.8 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
)

replace github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger => ../../go-packages/meep-logger
+25 −0
Original line number Diff line number Diff line
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/flimzy/kivik v1.8.1 h1:URl7e0OnfSvAu3ZHQ5BkvzRZlCmyYuDyWUCcPWIHlU0=
github.com/flimzy/kivik v1.8.1/go.mod h1:S2aPycbG0eDFll4wgXt9uacSNkXISPufutnc9sv+mdA=
github.com/go-kivik/couchdb v1.8.1 h1:2yjmysS48JYpyWTkx2E3c7ASZP8Kh0eABWnkKlV8bbw=
github.com/go-kivik/couchdb v1.8.1/go.mod h1:5XJRkAMpBlEVA4q0ktIZjUPYBjoBmRoiWvwUBzP3BOQ=
github.com/go-kivik/kivik v1.8.1 h1:GScP1mS5wP2km2awszvKzPEjC21lYjQGr3GY+4a/o2U=
github.com/go-kivik/kivik v1.8.1/go.mod h1:nIuJ8z4ikBrVUSk3Ua8NoDqYKULPNjuddjqRvlSUyyQ=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ GOPKGS=(
    meep-net-char-mgr
    meep-metric-store
    meep-watchdog
    meep-couch
)

echo ""
Loading