Implement Read Only mode

This commit is contained in:
Las Zenow 2017-05-21 10:16:16 +00:00
parent 6464d92dd4
commit e1bd235785
19 changed files with 544 additions and 335 deletions

View file

@ -10,7 +10,7 @@ var book = map[string]interface{}{
func TestAddBook(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
tAddBook(t, db)
@ -31,7 +31,7 @@ func TestAddBook(t *testing.T) {
func TestActiveBook(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
tAddBook(t, db)
books, _, _ := db.GetNewBooks("", 1, 0)
@ -53,7 +53,7 @@ func TestActiveBook(t *testing.T) {
func TestFlag(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
tAddBook(t, db)
id, _ := book["id"].(string)
@ -97,7 +97,7 @@ func TestFlag(t *testing.T) {
}
}
func tAddBook(t *testing.T, db *DB) {
func tAddBook(t *testing.T, db DB) {
err := db.AddBook(book)
if err != nil {
t.Error("db.AddBook(", book, ") return an error:", err)

View file

@ -3,22 +3,42 @@ package database
import (
log "github.com/cihub/seelog"
"errors"
"os"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
const (
visited_coll = "visited"
downloaded_coll = "downloaded"
tags_coll = "tags"
)
type DB struct {
session *mgo.Session
name string
type DB interface {
Close()
Copy() DB
AddBook(book map[string]interface{}) error
GetBooks(query string, length int, start int) (books []Book, num int, err error)
GetBooksIter() Iter
GetNewBooks(query string, length int, start int) (books []Book, num int, err error)
GetBookId(id string) (Book, error)
DeleteBook(id string) error
UpdateBook(id string, data map[string]interface{}) error
FlagBadQuality(id string, user string) error
ActiveBook(id string) error
IsBookActive(id string) bool
User(name string) *User
AddUser(name string, pass string) error
AddNews(text string) error
GetNews(num int, days int) (news []News, err error)
AddStats(stats interface{}) error
GetVisitedBooks() (books []Book, err error)
UpdateMostVisited() error
GetDownloadedBooks() (books []Book, err error)
UpdateDownloadedBooks() error
GetTags() ([]string, error)
UpdateTags() error
GetVisits(visitType VisitType) ([]Visits, error)
UpdateHourVisits() error
UpdateDayVisits() error
UpdateMonthVisits() error
UpdateHourDownloads() error
UpdateDayDownloads() error
UpdateMonthDownloads() error
}
type Iter interface {
@ -26,9 +46,9 @@ type Iter interface {
Next(interface{}) bool
}
func Init(host string, name string) *DB {
func Init(host string, name string) DB {
var err error
db := new(DB)
db := new(mgoDB)
db.session, err = mgo.Dial(host)
if err != nil {
log.Critical(err)
@ -39,230 +59,6 @@ func Init(host string, name string) *DB {
return db
}
func (db *DB) initIndexes() {
dbCopy := db.session.Copy()
booksColl := dbCopy.DB(db.name).C(books_coll)
go indexBooks(booksColl)
statsColl := dbCopy.DB(db.name).C(stats_coll)
go indexStats(statsColl)
newsColl := dbCopy.DB(db.name).C(news_coll)
go indexNews(newsColl)
}
func (db *DB) Close() {
db.session.Close()
}
func (db *DB) Copy() *DB {
dbCopy := new(DB)
dbCopy.session = db.session.Copy()
dbCopy.name = db.name
return dbCopy
}
func (db *DB) AddBook(book map[string]interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return addBook(booksColl, book)
}
func (db *DB) GetBooks(query string, length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooks(booksColl, query, length, start)
}
func (db *DB) GetBooksIter() Iter {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooksIter(booksColl)
}
func (db *DB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getNewBooks(booksColl, query, length, start)
}
func (db *DB) GetBookId(id string) (Book, error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBookId(booksColl, id)
}
func (db *DB) DeleteBook(id string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return deleteBook(booksColl, id)
}
func (db *DB) UpdateBook(id string, data map[string]interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return updateBook(booksColl, id, data)
}
func (db *DB) FlagBadQuality(id string, user string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return flagBadQuality(booksColl, id, user)
}
func (db *DB) ActiveBook(id string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return activeBook(booksColl, id)
}
func (db *DB) IsBookActive(id string) bool {
booksColl := db.session.DB(db.name).C(books_coll)
return isBookActive(booksColl, id)
}
func (db *DB) User(name string) *User {
userColl := db.session.DB(db.name).C(user_coll)
return getUser(userColl, name)
}
func (db *DB) AddUser(name string, pass string) error {
userColl := db.session.DB(db.name).C(user_coll)
return addUser(userColl, name, pass)
}
func (db *DB) AddNews(text string) error {
newsColl := db.session.DB(db.name).C(news_coll)
return addNews(newsColl, text)
}
func (db *DB) GetNews(num int, days int) (news []News, err error) {
newsColl := db.session.DB(db.name).C(news_coll)
return getNews(newsColl, num, days)
}
// TODO: split code in files
func (db *DB) AddStats(stats interface{}) error {
statsColl := db.session.DB(db.name).C(stats_coll)
return statsColl.Insert(stats)
}
/* Get the most visited books
*/
func (db *DB) GetVisitedBooks() (books []Book, err error) {
visitedColl := db.session.DB(db.name).C(visited_coll)
bookId, err := GetBooksVisited(visitedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.session.DB(db.name).C(books_coll)
booksColl.Find(bson.M{"_id": id}).One(&books[i])
books[i].Id = bson.ObjectId(books[i].Id).Hex()
}
return
}
func (db *DB) UpdateMostVisited() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(visited_coll)
return u.UpdateMostBooks("book")
}
/* Get the most downloaded books
*/
func (db *DB) GetDownloadedBooks() (books []Book, err error) {
downloadedColl := db.session.DB(db.name).C(downloaded_coll)
bookId, err := GetBooksVisited(downloadedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.session.DB(db.name).C(books_coll)
booksColl.Find(bson.M{"_id": id}).One(&books[i])
books[i].Id = bson.ObjectId(books[i].Id).Hex()
}
return
}
func (db *DB) UpdateDownloadedBooks() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(downloaded_coll)
return u.UpdateMostBooks("download")
}
func (db *DB) GetTags() ([]string, error) {
tagsColl := db.session.DB(db.name).C(tags_coll)
return GetTags(tagsColl)
}
func (db *DB) UpdateTags() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(books_coll)
u.dst = db.session.DB(db.name).C(tags_coll)
return u.UpdateTags()
}
func (db *DB) GetVisits(visitType VisitType) ([]Visits, error) {
var coll *mgo.Collection
switch visitType {
case Hourly_visits:
coll = db.session.DB(db.name).C(hourly_visits_coll)
case Daily_visits:
coll = db.session.DB(db.name).C(daily_visits_coll)
case Monthly_visits:
coll = db.session.DB(db.name).C(monthly_visits_coll)
case Hourly_downloads:
coll = db.session.DB(db.name).C(hourly_downloads_coll)
case Daily_downloads:
coll = db.session.DB(db.name).C(daily_downloads_coll)
case Monthly_downloads:
coll = db.session.DB(db.name).C(monthly_downloads_coll)
default:
return nil, errors.New("Not valid VisitType")
}
return GetVisits(coll)
}
func (db *DB) UpdateHourVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_visits_coll)
return u.UpdateHourVisits(false)
}
func (db *DB) UpdateDayVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_visits_coll)
return u.UpdateDayVisits(false)
}
func (db *DB) UpdateMonthVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_visits_coll)
return u.UpdateMonthVisits(false)
}
func (db *DB) UpdateHourDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_downloads_coll)
return u.UpdateHourVisits(true)
}
func (db *DB) UpdateDayDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_downloads_coll)
return u.UpdateDayVisits(true)
}
func (db *DB) UpdateMonthDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_downloads_coll)
return u.UpdateMonthVisits(true)
}
// function defined for the tests
func (db *DB) del() {
defer db.Close()
db.session.DB(db.name).DropDatabase()
func RO(db DB) DB {
return &roDB{db}
}

View file

@ -1,6 +1,10 @@
package database
import "testing"
import (
"testing"
mgo "gopkg.in/mgo.v2"
)
const (
test_coll = "test_trantor"
@ -12,29 +16,9 @@ func TestInit(t *testing.T) {
defer db.Close()
}
func TestCopy(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
db2 := db.Copy()
if db.name != db2.name {
t.Errorf("Names don't match")
}
names1, err := db.session.DatabaseNames()
if err != nil {
t.Errorf("Error on db1: ", err)
}
names2, err := db2.session.DatabaseNames()
if err != nil {
t.Errorf("Error on db1: ", err)
}
if len(names1) != len(names2) {
t.Errorf("len(names) don't match")
}
for i, _ := range names1 {
if names1[i] != names2[i] {
t.Errorf("Names don't match")
}
}
func del(db DB) {
db.Close()
session, _ := mgo.Dial(test_host)
defer session.Close()
session.DB(test_coll).DropDatabase()
}

241
lib/database/mgo.go Normal file
View file

@ -0,0 +1,241 @@
package database
import (
"errors"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
const (
visited_coll = "visited"
downloaded_coll = "downloaded"
tags_coll = "tags"
)
type mgoDB struct {
session *mgo.Session
name string
}
func (db *mgoDB) initIndexes() {
dbCopy := db.session.Copy()
booksColl := dbCopy.DB(db.name).C(books_coll)
go indexBooks(booksColl)
statsColl := dbCopy.DB(db.name).C(stats_coll)
go indexStats(statsColl)
newsColl := dbCopy.DB(db.name).C(news_coll)
go indexNews(newsColl)
}
func (db *mgoDB) Close() {
db.session.Close()
}
func (db *mgoDB) Copy() DB {
dbCopy := new(mgoDB)
dbCopy.session = db.session.Copy()
dbCopy.name = db.name
return dbCopy
}
func (db *mgoDB) AddBook(book map[string]interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return addBook(booksColl, book)
}
func (db *mgoDB) GetBooks(query string, length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooks(booksColl, query, length, start)
}
func (db *mgoDB) GetBooksIter() Iter {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooksIter(booksColl)
}
func (db *mgoDB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getNewBooks(booksColl, query, length, start)
}
func (db *mgoDB) GetBookId(id string) (Book, error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBookId(booksColl, id)
}
func (db *mgoDB) DeleteBook(id string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return deleteBook(booksColl, id)
}
func (db *mgoDB) UpdateBook(id string, data map[string]interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return updateBook(booksColl, id, data)
}
func (db *mgoDB) FlagBadQuality(id string, user string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return flagBadQuality(booksColl, id, user)
}
func (db *mgoDB) ActiveBook(id string) error {
booksColl := db.session.DB(db.name).C(books_coll)
return activeBook(booksColl, id)
}
func (db *mgoDB) IsBookActive(id string) bool {
booksColl := db.session.DB(db.name).C(books_coll)
return isBookActive(booksColl, id)
}
func (db *mgoDB) User(name string) *User {
userColl := db.session.DB(db.name).C(user_coll)
return getUser(userColl, name)
}
func (db *mgoDB) AddUser(name string, pass string) error {
userColl := db.session.DB(db.name).C(user_coll)
return addUser(userColl, name, pass)
}
func (db *mgoDB) AddNews(text string) error {
newsColl := db.session.DB(db.name).C(news_coll)
return addNews(newsColl, text)
}
func (db *mgoDB) GetNews(num int, days int) (news []News, err error) {
newsColl := db.session.DB(db.name).C(news_coll)
return getNews(newsColl, num, days)
}
// TODO: split code in files
func (db *mgoDB) AddStats(stats interface{}) error {
statsColl := db.session.DB(db.name).C(stats_coll)
return statsColl.Insert(stats)
}
/* Get the most visited books
*/
func (db *mgoDB) GetVisitedBooks() (books []Book, err error) {
visitedColl := db.session.DB(db.name).C(visited_coll)
bookId, err := GetBooksVisited(visitedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.session.DB(db.name).C(books_coll)
booksColl.Find(bson.M{"_id": id}).One(&books[i])
books[i].Id = bson.ObjectId(books[i].Id).Hex()
}
return
}
func (db *mgoDB) UpdateMostVisited() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(visited_coll)
return u.UpdateMostBooks("book")
}
/* Get the most downloaded books
*/
func (db *mgoDB) GetDownloadedBooks() (books []Book, err error) {
downloadedColl := db.session.DB(db.name).C(downloaded_coll)
bookId, err := GetBooksVisited(downloadedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.session.DB(db.name).C(books_coll)
booksColl.Find(bson.M{"_id": id}).One(&books[i])
books[i].Id = bson.ObjectId(books[i].Id).Hex()
}
return
}
func (db *mgoDB) UpdateDownloadedBooks() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(downloaded_coll)
return u.UpdateMostBooks("download")
}
func (db *mgoDB) GetTags() ([]string, error) {
tagsColl := db.session.DB(db.name).C(tags_coll)
return GetTags(tagsColl)
}
func (db *mgoDB) UpdateTags() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(books_coll)
u.dst = db.session.DB(db.name).C(tags_coll)
return u.UpdateTags()
}
func (db *mgoDB) GetVisits(visitType VisitType) ([]Visits, error) {
var coll *mgo.Collection
switch visitType {
case Hourly_visits:
coll = db.session.DB(db.name).C(hourly_visits_coll)
case Daily_visits:
coll = db.session.DB(db.name).C(daily_visits_coll)
case Monthly_visits:
coll = db.session.DB(db.name).C(monthly_visits_coll)
case Hourly_downloads:
coll = db.session.DB(db.name).C(hourly_downloads_coll)
case Daily_downloads:
coll = db.session.DB(db.name).C(daily_downloads_coll)
case Monthly_downloads:
coll = db.session.DB(db.name).C(monthly_downloads_coll)
default:
return nil, errors.New("Not valid VisitType")
}
return GetVisits(coll)
}
func (db *mgoDB) UpdateHourVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_visits_coll)
return u.UpdateHourVisits(false)
}
func (db *mgoDB) UpdateDayVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_visits_coll)
return u.UpdateDayVisits(false)
}
func (db *mgoDB) UpdateMonthVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_visits_coll)
return u.UpdateMonthVisits(false)
}
func (db *mgoDB) UpdateHourDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_downloads_coll)
return u.UpdateHourVisits(true)
}
func (db *mgoDB) UpdateDayDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_downloads_coll)
return u.UpdateDayVisits(true)
}
func (db *mgoDB) UpdateMonthDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_downloads_coll)
return u.UpdateMonthVisits(true)
}

View file

@ -6,7 +6,7 @@ func TestNews(t *testing.T) {
const text = "Some news text"
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
err := db.AddNews(text)
if err != nil {

129
lib/database/ro.go Normal file
View file

@ -0,0 +1,129 @@
package database
import (
"errors"
)
type roDB struct {
db DB
}
func (db *roDB) Close() {
db.db.Close()
}
func (db *roDB) Copy() DB {
return &roDB{db.db.Copy()}
}
func (db *roDB) AddBook(book map[string]interface{}) error {
return errors.New("RO database")
}
func (db *roDB) GetBooks(query string, length int, start int) (books []Book, num int, err error) {
return db.db.GetBooks(query, length, start)
}
func (db *roDB) GetBooksIter() Iter {
return db.db.GetBooksIter()
}
func (db *roDB) GetNewBooks(query string, length int, start int) (books []Book, num int, err error) {
return db.db.GetNewBooks(query, length, start)
}
func (db *roDB) GetBookId(id string) (Book, error) {
return db.db.GetBookId(id)
}
func (db *roDB) DeleteBook(id string) error {
return errors.New("RO database")
}
func (db *roDB) UpdateBook(id string, data map[string]interface{}) error {
return errors.New("RO database")
}
func (db *roDB) FlagBadQuality(id string, user string) error {
return errors.New("RO database")
}
func (db *roDB) ActiveBook(id string) error {
return errors.New("RO database")
}
func (db *roDB) IsBookActive(id string) bool {
return db.db.IsBookActive(id)
}
func (db *roDB) User(name string) *User {
return db.db.User(name)
}
func (db *roDB) AddUser(name string, pass string) error {
return errors.New("RO database")
}
func (db *roDB) AddNews(text string) error {
return errors.New("RO database")
}
func (db *roDB) GetNews(num int, days int) (news []News, err error) {
return db.db.GetNews(num, days)
}
func (db *roDB) AddStats(stats interface{}) error {
return nil
}
func (db *roDB) GetVisitedBooks() (books []Book, err error) {
return db.db.GetVisitedBooks()
}
func (db *roDB) UpdateMostVisited() error {
return errors.New("RO database")
}
func (db *roDB) GetDownloadedBooks() (books []Book, err error) {
return db.db.GetDownloadedBooks()
}
func (db *roDB) UpdateDownloadedBooks() error {
return errors.New("RO database")
}
func (db *roDB) GetTags() ([]string, error) {
return db.db.GetTags()
}
func (db *roDB) UpdateTags() error {
return errors.New("RO database")
}
func (db *roDB) GetVisits(visitType VisitType) ([]Visits, error) {
return db.db.GetVisits(visitType)
}
func (db *roDB) UpdateHourVisits() error {
return errors.New("RO database")
}
func (db *roDB) UpdateDayVisits() error {
return errors.New("RO database")
}
func (db *roDB) UpdateMonthVisits() error {
return errors.New("RO database")
}
func (db *roDB) UpdateHourDownloads() error {
return errors.New("RO database")
}
func (db *roDB) UpdateDayDownloads() error {
return errors.New("RO database")
}
func (db *roDB) UpdateMonthDownloads() error {
return errors.New("RO database")
}

View file

@ -8,7 +8,7 @@ const (
func TestUserEmpty(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
if db.User("").Valid("") {
t.Errorf("user.Valid() with an empty password return true")
@ -17,7 +17,7 @@ func TestUserEmpty(t *testing.T) {
func TestAddUser(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
tAddUser(t, db)
if !db.User(name).Valid(pass) {
@ -27,7 +27,7 @@ func TestAddUser(t *testing.T) {
func TestEmptyUsername(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
defer del(db)
tAddUser(t, db)
if db.User("").Valid(pass) {
@ -35,7 +35,7 @@ func TestEmptyUsername(t *testing.T) {
}
}
func tAddUser(t *testing.T, db *DB) {
func tAddUser(t *testing.T, db DB) {
err := db.AddUser(name, pass)
if err != nil {
t.Errorf("db.Adduser(", name, ", ", pass, ") return an error: ", err)