Add book list support in the database
This commit is contained in:
parent
f2b393a453
commit
c0a70a18e1
5 changed files with 298 additions and 1 deletions
|
@ -36,6 +36,8 @@ type DB interface {
|
||||||
UpdateSubmissionComment(submissionID, bookID, comment string) error
|
UpdateSubmissionComment(submissionID, bookID, comment string) error
|
||||||
GetSubmission(submissionID string) (submission []Submission, err error)
|
GetSubmission(submissionID string) (submission []Submission, err error)
|
||||||
GetComment(bookID string) (string, error)
|
GetComment(bookID string) (string, error)
|
||||||
|
|
||||||
|
BookLister
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -101,7 +103,7 @@ func (db pgDB) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db pgDB) create() error {
|
func (db pgDB) create() error {
|
||||||
models := []interface{}{&Book{}, &New{}, &User{}, &Visit{}, &Submission{}}
|
models := []interface{}{&Book{}, &New{}, &User{}, &Visit{}, &Submission{}, &BookList{}, &BookListEntry{}}
|
||||||
for _, model := range models {
|
for _, model := range models {
|
||||||
options := &orm.CreateTableOptions{
|
options := &orm.CreateTableOptions{
|
||||||
IfNotExists: true,
|
IfNotExists: true,
|
||||||
|
@ -230,4 +232,8 @@ ALTER TABLE ONLY submissions
|
||||||
DROP CONSTRAINT IF EXISTS submissions_book_id_fkey;
|
DROP CONSTRAINT IF EXISTS submissions_book_id_fkey;
|
||||||
ALTER TABLE ONLY submissions
|
ALTER TABLE ONLY submissions
|
||||||
ADD CONSTRAINT submissions_book_id_fkey FOREIGN KEY (book_id) REFERENCES books(id) ON DELETE SET NULL;
|
ADD CONSTRAINT submissions_book_id_fkey FOREIGN KEY (book_id) REFERENCES books(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- BookLists indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS book_lists_list_id_idx on book_lists(list_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS book_lists_user_idx on book_lists(user_id);
|
||||||
`
|
`
|
||||||
|
|
132
lib/database/list.go
Normal file
132
lib/database/list.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-pg/pg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BookLister interface {
|
||||||
|
NewBookList(listID, title, username string, description []string) error
|
||||||
|
AddBookToList(listID, bookID string) error
|
||||||
|
DeleteBookFromList(listID, bookID string) error
|
||||||
|
UpdateBookList(listID, title string, description []string) error
|
||||||
|
GetBookList(listID string) (*BookList, error)
|
||||||
|
GetListsByUser(username string) ([]BookList, error)
|
||||||
|
GetListsByBook(bookID string) ([]BookList, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BookList struct {
|
||||||
|
ID int `sql:"type:serial"`
|
||||||
|
ListID string `sql:"type:varchar(16)"`
|
||||||
|
Title string
|
||||||
|
Description []string `sql:"description" pg:",array"`
|
||||||
|
UserID int `sql:"type:integer"`
|
||||||
|
User *User
|
||||||
|
Books []Book `pg:"many2many:book_list_entries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BookListEntry struct {
|
||||||
|
ID int `sql:"type:serial"`
|
||||||
|
BookListID int `sql:"type:integer"`
|
||||||
|
BookList *BookList
|
||||||
|
BookID string `sql:"type:varchar(16)"`
|
||||||
|
Book *Book
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) NewBookList(listID, title, username string, description []string) error {
|
||||||
|
user, err := db.getUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return db.sql.Insert(&BookList{
|
||||||
|
ListID: listID,
|
||||||
|
Title: title,
|
||||||
|
Description: description,
|
||||||
|
UserID: user.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) AddBookToList(listID, bookID string) error {
|
||||||
|
list, err := db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.sql.Insert(&BookListEntry{
|
||||||
|
BookListID: list.ID,
|
||||||
|
BookID: bookID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) DeleteBookFromList(listID, bookID string) error {
|
||||||
|
list, err := db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.sql.Model(&BookListEntry{}).
|
||||||
|
Where("book_list_id = ? AND book_id = ?", list.ID, bookID).
|
||||||
|
Delete()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) UpdateBookList(listID, title string, description []string) error {
|
||||||
|
_, err := db.sql.Model(&BookList{}).
|
||||||
|
Set("title = ?, description = ?", title, pg.Array(description)).
|
||||||
|
Where("list_id = ?", listID).
|
||||||
|
Update()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) GetBookList(listID string) (*BookList, error) {
|
||||||
|
var bookList BookList
|
||||||
|
err := db.sql.Model(&bookList).
|
||||||
|
Column("Books").
|
||||||
|
Where("list_id = ?", listID).
|
||||||
|
Select()
|
||||||
|
return &bookList, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) GetListsByUser(username string) ([]BookList, error) {
|
||||||
|
var bookLists []BookList
|
||||||
|
|
||||||
|
user, err := db.getUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return bookLists, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.sql.Model(&bookLists).
|
||||||
|
Column("Books").
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
Select()
|
||||||
|
return bookLists, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) GetListsByBook(bookID string) ([]BookList, error) {
|
||||||
|
var bookLists []BookList
|
||||||
|
var bookListEntries []BookListEntry
|
||||||
|
|
||||||
|
err := db.sql.Model(&bookListEntries).
|
||||||
|
Where("book_id = ?", bookID).
|
||||||
|
Select()
|
||||||
|
if err != nil {
|
||||||
|
return bookLists, err
|
||||||
|
}
|
||||||
|
|
||||||
|
whereQuery := "id IN ("
|
||||||
|
listIDs := make([]interface{}, len(bookListEntries))
|
||||||
|
for i, entry := range bookListEntries {
|
||||||
|
whereQuery += "?"
|
||||||
|
if i < len(bookListEntries)-1 {
|
||||||
|
whereQuery += ", "
|
||||||
|
} else {
|
||||||
|
whereQuery += ")"
|
||||||
|
}
|
||||||
|
listIDs[i] = entry.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.sql.Model(&bookLists).
|
||||||
|
Column("Books").
|
||||||
|
Where(whereQuery, listIDs...).
|
||||||
|
Select()
|
||||||
|
return bookLists, err
|
||||||
|
}
|
123
lib/database/list_test.go
Normal file
123
lib/database/list_test.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
const (
|
||||||
|
listID = "1234567890"
|
||||||
|
title = "My title"
|
||||||
|
username = "username"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewBookList(t *testing.T) {
|
||||||
|
db, dbclose := testDbInit(t)
|
||||||
|
defer dbclose()
|
||||||
|
testCreateList(t, db)
|
||||||
|
|
||||||
|
list, err := db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
if list.ListID != listID {
|
||||||
|
t.Fatal("list id doesn't match: ", list.ListID, " <=> ", listID)
|
||||||
|
}
|
||||||
|
if list.Title != title {
|
||||||
|
t.Fatal("title doesn't match: ", list.Title, " <=> ", title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBookListAddDelBooks(t *testing.T) {
|
||||||
|
db, dbclose := testDbInit(t)
|
||||||
|
defer dbclose()
|
||||||
|
|
||||||
|
testCreateList(t, db)
|
||||||
|
testAddBook(t, db)
|
||||||
|
|
||||||
|
err := db.AddBookToList(listID, book.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.AddBookToList() return an error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
if len(list.Books) != 1 {
|
||||||
|
t.Fatal("We got a un expected number of books: ", list.Books)
|
||||||
|
}
|
||||||
|
if list.Books[0].ID != book.ID {
|
||||||
|
t.Fatal("book id doesn't match: ", list.Books[0].ID, " <=> ", book.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
lists, err := db.GetListsByBook(book.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetListsByBook() return an error: ", err)
|
||||||
|
}
|
||||||
|
if len(lists) != 1 {
|
||||||
|
t.Fatal("We got a un expected number of lists: ", lists)
|
||||||
|
}
|
||||||
|
if lists[0].Books[0].Title != book.Title {
|
||||||
|
t.Fatal("book id doesn't match: ", lists[0].Books[0].Title, " <=> ", book.Title)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeleteBookFromList(listID, book.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.DeleteBookFromList() return an error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err = db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
if len(list.Books) != 0 {
|
||||||
|
t.Fatal("We got a un expected number of books: ", list.Books)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBookListByUser(t *testing.T) {
|
||||||
|
db, dbclose := testDbInit(t)
|
||||||
|
defer dbclose()
|
||||||
|
testCreateList(t, db)
|
||||||
|
|
||||||
|
lists, err := db.GetListsByUser(username)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetBookListsByUser() return an error: ", err)
|
||||||
|
}
|
||||||
|
if len(lists) != 1 {
|
||||||
|
t.Fatal("We got a un expected number of lists: ", lists)
|
||||||
|
}
|
||||||
|
if lists[0].ListID != listID {
|
||||||
|
t.Fatal("list id doesn't match: ", lists[0].ListID, " <=> ", listID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBookListUpdate(t *testing.T) {
|
||||||
|
const otherTitle = "other title"
|
||||||
|
|
||||||
|
db, dbclose := testDbInit(t)
|
||||||
|
defer dbclose()
|
||||||
|
testCreateList(t, db)
|
||||||
|
|
||||||
|
err := db.UpdateBookList(listID, otherTitle, []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.UpdateBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := db.GetBookList(listID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.GetBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
if list.Title != otherTitle {
|
||||||
|
t.Fatal("title doesn't match: ", list.Title, " <=> ", otherTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCreateList(t *testing.T, db DB) {
|
||||||
|
err := db.AddUser(username, "pass")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.AddUser() return an error: ", err)
|
||||||
|
}
|
||||||
|
err = db.NewBookList(listID, title, username, []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("db.NewBookList() return an error: ", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,3 +120,31 @@ func (db *roDB) GetComment(bookID string) (string, error) {
|
||||||
func (db *roDB) GetSubmission(submissionID string) (submission []Submission, err error) {
|
func (db *roDB) GetSubmission(submissionID string) (submission []Submission, err error) {
|
||||||
return db.db.GetSubmission(submissionID)
|
return db.db.GetSubmission(submissionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *roDB) NewBookList(listID, title, username string, description []string) error {
|
||||||
|
return errors.New("RO database")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) AddBookToList(listID, bookID string) error {
|
||||||
|
return errors.New("RO database")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) DeleteBookFromList(listID, bookID string) error {
|
||||||
|
return errors.New("RO database")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) UpdateBookList(listID, title string, description []string) error {
|
||||||
|
return errors.New("RO database")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) GetBookList(listID string) (*BookList, error) {
|
||||||
|
return db.db.GetBookList(listID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) GetListsByUser(username string) ([]BookList, error) {
|
||||||
|
return db.db.GetListsByUser(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *roDB) GetListsByBook(bookID string) ([]BookList, error) {
|
||||||
|
return db.db.GetListsByBook(bookID)
|
||||||
|
}
|
||||||
|
|
|
@ -109,6 +109,14 @@ func (db *pgDB) ListUsers() ([]User, error) {
|
||||||
return users, err
|
return users, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *pgDB) getUser(name string) (User, error) {
|
||||||
|
var user User
|
||||||
|
err := db.sql.Model(&user).
|
||||||
|
Where("username = ?", name).
|
||||||
|
Select()
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
func validUserName(name string) bool {
|
func validUserName(name string) bool {
|
||||||
switch name {
|
switch name {
|
||||||
case "", "admin", "webmaster", "postmaster", "info", "root", "news":
|
case "", "admin", "webmaster", "postmaster", "info", "root", "news":
|
||||||
|
|
Reference in a new issue