feat: delete cards #6

Closed
dananglin wants to merge 35 commits from delete-cards into main
7 changed files with 130 additions and 130 deletions
Showing only changes of commit e13d3e2085 - Show all commits

View file

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"sort" "sort"
"codeflow.dananglin.me.uk/apollo/canal/internal/database" "codeflow.dananglin.me.uk/apollo/canal/internal/db"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
) )
@ -14,16 +14,16 @@ type Board struct {
db *bolt.DB db *bolt.DB
} }
// Open reads the board from the database. // Open reads the board from the db.
// If no board exists then a new one will be created. // If no board exists then a new one will be created.
func Open(path string) (Board, error) { func Open(path string) (Board, error) {
db, err := database.OpenDatabase(path) database, err := db.OpenDatabase(path)
if err != nil { if err != nil {
return Board{}, fmt.Errorf("unable to open the database, %w", err) return Board{}, fmt.Errorf("unable to open the db. %w", err)
} }
board := Board{ board := Board{
db: db, db: database,
} }
statusList, err := board.StatusList() statusList, err := board.StatusList()
@ -34,14 +34,14 @@ func Open(path string) (Board, error) {
if len(statusList) == 0 { if len(statusList) == 0 {
newStatusList := defaultStatusList() newStatusList := defaultStatusList()
boltItems := make([]database.BoltItem, len(newStatusList)) boltItems := make([]db.BoltItem, len(newStatusList))
for i := range newStatusList { for i := range newStatusList {
boltItems[i] = &newStatusList[i] boltItems[i] = &newStatusList[i]
} }
if _, err := database.WriteMany(db, database.StatusBucket, boltItems); err != nil { if _, err := db.WriteMany(database, db.StatusBucket, boltItems); err != nil {
return Board{}, fmt.Errorf("unable to save the default status list to the database, %w", err) return Board{}, fmt.Errorf("unable to save the default status list to the db. %w", err)
} }
} }
@ -55,15 +55,15 @@ func (b *Board) Close() error {
} }
if err := b.db.Close(); err != nil { if err := b.db.Close(); err != nil {
return fmt.Errorf("error closing the database, %w", err) return fmt.Errorf("error closing the db. %w", err)
} }
return nil return nil
} }
// StatusList returns the ordered list of statuses from the database. // StatusList returns the ordered list of statuses from the db.
func (b *Board) StatusList() ([]Status, error) { func (b *Board) StatusList() ([]Status, error) {
data, err := database.ReadAll(b.db, database.StatusBucket) data, err := db.ReadAll(b.db, db.StatusBucket)
if err != nil { if err != nil {
return []Status{}, fmt.Errorf("unable to read the status list, %w", err) return []Status{}, fmt.Errorf("unable to read the status list, %w", err)
} }
@ -89,11 +89,11 @@ func (b *Board) StatusList() ([]Status, error) {
return statuses, nil return statuses, nil
} }
// Status returns a single status from the database. // Status returns a single status from the db.
func (b *Board) Status(id int) (Status, error) { func (b *Board) Status(id int) (Status, error) {
data, err := database.Read(b.db, database.StatusBucket, id) data, err := db.Read(b.db, db.StatusBucket, id)
if err != nil { if err != nil {
return Status{}, fmt.Errorf("unable to read status [%d] from the database, %w", id, err) return Status{}, fmt.Errorf("unable to read status [%d] from the db. %w", id, err)
} }
var status Status var status Status
@ -114,7 +114,7 @@ type StatusArgs struct {
Order int Order int
} }
// CreateStatus creates a status in the database. // CreateStatus creates a status in the db.
func (b *Board) CreateStatus(args StatusArgs) error { func (b *Board) CreateStatus(args StatusArgs) error {
status := Status{ status := Status{
ID: -1, ID: -1,
@ -123,8 +123,8 @@ func (b *Board) CreateStatus(args StatusArgs) error {
CardIds: nil, CardIds: nil,
} }
if _, err := database.Write(b.db, database.StatusBucket, &status); err != nil { if _, err := db.Write(b.db, db.StatusBucket, &status); err != nil {
return fmt.Errorf("unable to write the status to the database, %w", err) return fmt.Errorf("unable to write the status to the db. %w", err)
} }
return nil return nil
@ -135,11 +135,11 @@ type UpdateStatusArgs struct {
StatusArgs StatusArgs
} }
// UpdateStatus modifies an existing status in the database. // UpdateStatus modifies an existing status in the db.
func (b *Board) UpdateStatus(args UpdateStatusArgs) error { func (b *Board) UpdateStatus(args UpdateStatusArgs) error {
status, err := b.Status(args.StatusID) status, err := b.Status(args.StatusID)
if err != nil { if err != nil {
return fmt.Errorf("unable to retrieve the status from the database, %w", err) return fmt.Errorf("unable to retrieve the status from the db. %w", err)
} }
if len(args.Name) > 0 { if len(args.Name) > 0 {
@ -150,8 +150,8 @@ func (b *Board) UpdateStatus(args UpdateStatusArgs) error {
status.Order = args.Order status.Order = args.Order
} }
if _, err := database.Write(b.db, database.StatusBucket, &status); err != nil { if _, err := db.Write(b.db, db.StatusBucket, &status); err != nil {
return fmt.Errorf("unable to write the status to the database, %w", err) return fmt.Errorf("unable to write the status to the db. %w", err)
} }
return nil return nil
@ -183,10 +183,10 @@ func (b *Board) MoveToStatus(args MoveToStatusArgs) error {
nextStatus.AddCardID(args.CardID) nextStatus.AddCardID(args.CardID)
currentStatus.RemoveCardID(args.CardID) currentStatus.RemoveCardID(args.CardID)
boltItems := []database.BoltItem{&currentStatus, &nextStatus} boltItems := []db.BoltItem{&currentStatus, &nextStatus}
if _, err := database.WriteMany(b.db, database.StatusBucket, boltItems); err != nil { if _, err := db.WriteMany(b.db, db.StatusBucket, boltItems); err != nil {
return fmt.Errorf("unable to update the statuses in the database, %w", err) return fmt.Errorf("unable to update the statuses in the db. %w", err)
} }
return nil return nil
@ -197,7 +197,7 @@ type CardArgs struct {
NewContent string NewContent string
} }
// CreateCard creates a card in the database. // CreateCard creates a card in the db.
func (b *Board) CreateCard(args CardArgs) (int, error) { func (b *Board) CreateCard(args CardArgs) (int, error) {
statusList, err := b.StatusList() statusList, err := b.StatusList()
if err != nil { if err != nil {
@ -214,28 +214,28 @@ func (b *Board) CreateCard(args CardArgs) (int, error) {
Content: args.NewContent, Content: args.NewContent,
} }
cardID, err := database.Write(b.db, database.CardBucket, &card) cardID, err := db.Write(b.db, db.CardBucket, &card)
if err != nil { if err != nil {
return 0, fmt.Errorf("unable to write card to the database, %w", err) return 0, fmt.Errorf("unable to write card to the db. %w", err)
} }
initialStatus := statusList[0] initialStatus := statusList[0]
initialStatus.AddCardID(cardID) initialStatus.AddCardID(cardID)
id, err := database.Write(b.db, database.StatusBucket, &initialStatus) id, err := db.Write(b.db, db.StatusBucket, &initialStatus)
if err != nil { if err != nil {
return 0, fmt.Errorf("unable to write the %s status to the database, %w", initialStatus.Name, err) return 0, fmt.Errorf("unable to write the %s status to the db. %w", initialStatus.Name, err)
} }
return id, nil return id, nil
} }
// Card returns a Card value from the database. // Card returns a Card value from the db.
func (b *Board) Card(id int) (Card, error) { func (b *Board) Card(id int) (Card, error) {
data, err := database.Read(b.db, database.CardBucket, id) data, err := db.Read(b.db, db.CardBucket, id)
if err != nil { if err != nil {
return Card{}, fmt.Errorf("unable to read card [%d] from the database, %w", id, err) return Card{}, fmt.Errorf("unable to read card [%d] from the db. %w", id, err)
} }
var card Card var card Card
@ -251,12 +251,12 @@ func (b *Board) Card(id int) (Card, error) {
return card, nil return card, nil
} }
// CardList returns a list of Card values from the database. // CardList returns a list of Card values from the db.
// TODO: function needs testing. // TODO: function needs testing.
func (b *Board) CardList(ids []int) ([]Card, error) { func (b *Board) CardList(ids []int) ([]Card, error) {
data, err := database.ReadMany(b.db, database.CardBucket, ids) data, err := db.ReadMany(b.db, db.CardBucket, ids)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read card list from the database, %w", err) return nil, fmt.Errorf("unable to read card list from the db. %w", err)
} }
cards := make([]Card, len(data)) cards := make([]Card, len(data))
@ -283,7 +283,7 @@ type UpdateCardArgs struct {
CardArgs CardArgs
} }
// UpdateCard modifies an existing card in the database. // UpdateCard modifies an existing card in the db.
func (b *Board) UpdateCard(args UpdateCardArgs) error { func (b *Board) UpdateCard(args UpdateCardArgs) error {
card, err := b.Card(args.CardID) card, err := b.Card(args.CardID)
if err != nil { if err != nil {
@ -298,14 +298,14 @@ func (b *Board) UpdateCard(args UpdateCardArgs) error {
card.Content = args.NewContent card.Content = args.NewContent
} }
if _, err := database.Write(b.db, database.CardBucket, &card); err != nil { if _, err := db.Write(b.db, db.CardBucket, &card); err != nil {
return fmt.Errorf("unable to write card to the database, %w", err) return fmt.Errorf("unable to write card to the db. %w", err)
} }
return nil return nil
} }
// DeleteCard deletes a card from the database. // DeleteCard deletes a card from the db.
// TODO: finish implementation. // TODO: finish implementation.
//func (b *Board) DeleteCard(id int) error { //func (b *Board) DeleteCard(id int) error {
// return nil // return nil

View file

@ -20,34 +20,34 @@ func TestCardLifecycle(t *testing.T) {
testDBPath := filepath.Join(projectDir, "test", "databases", "Board_TestCardLifecycle.db") testDBPath := filepath.Join(projectDir, "test", "databases", "Board_TestCardLifecycle.db")
os.Remove(testDBPath) os.Remove(testDBPath)
b, err := board.Open(testDBPath) kanban, err := board.Open(testDBPath)
if err != nil { if err != nil {
t.Fatalf("Unable to open the test Kanban board, %s.", err) t.Fatalf("Unable to open the test Kanban board, %s.", err)
} }
defer func() { defer func() {
_ = b.Close() _ = kanban.Close()
}() }()
initialCardTitle := "A test card." initialCardTitle := "A test card."
initialCardContent := "Ensure that this card is safely stored in the database." initialCardContent := "Ensure that this card is safely stored in the database."
expectedCardID := 1 expectedCardID := 1
t.Run("Test Create Card", testCreateCard(b, initialCardTitle, initialCardContent, expectedCardID)) t.Run("Test Create Card", testCreateCard(kanban, initialCardTitle, initialCardContent, expectedCardID))
t.Run("Test Read Card", testReadCard(b, expectedCardID, initialCardTitle, initialCardContent)) t.Run("Test Read Card", testReadCard(kanban, expectedCardID, initialCardTitle, initialCardContent))
modifiedCardTitle := "Test card updated." modifiedCardTitle := "Test card updated."
modifiedCardContent1 := "Ensure that this card is safely updated in the database." modifiedCardContent1 := "Ensure that this card is safely updated in the database."
t.Run("Test Update Card", testUpdateCard(b, expectedCardID, modifiedCardTitle, modifiedCardContent1)) t.Run("Test Update Card", testUpdateCard(kanban, expectedCardID, modifiedCardTitle, modifiedCardContent1))
modifiedCardContent2 := "Updated card content only." modifiedCardContent2 := "Updated card content only."
t.Run("Test Update Card Content", testUpdateCardContent(b, expectedCardID, modifiedCardTitle, modifiedCardContent2)) t.Run("Test Update Card Content", testUpdateCardContent(kanban, expectedCardID, modifiedCardTitle, modifiedCardContent2))
} }
func testCreateCard(b board.Board, title, content string, wantID int) func(t *testing.T) { func testCreateCard(kanban board.Board, title, content string, wantID int) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When the card is created and saved to the database.") t.Log("When the card is created and saved to the database.")
@ -56,11 +56,11 @@ func testCreateCard(b board.Board, title, content string, wantID int) func(t *te
NewContent: content, NewContent: content,
} }
if _, err := b.CreateCard(args); err != nil { if _, err := kanban.CreateCard(args); err != nil {
t.Fatalf("ERROR: Unable to create the test card, %s.", err) t.Fatalf("ERROR: Unable to create the test card, %s.", err)
} }
statusList, err := b.StatusList() statusList, err := kanban.StatusList()
if err != nil { if err != nil {
t.Fatalf("ERROR: Unable to run `ReadStatusList`, %s.", err) t.Fatalf("ERROR: Unable to run `ReadStatusList`, %s.", err)
} }
@ -83,11 +83,11 @@ func testCreateCard(b board.Board, title, content string, wantID int) func(t *te
} }
} }
func testReadCard(b board.Board, cardID int, wantTitle, wantContent string) func(t *testing.T) { func testReadCard(kanban board.Board, cardID int, wantTitle, wantContent string) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When a card is read from the database.") t.Log("When a card is read from the database.")
card, err := b.Card(cardID) card, err := kanban.Card(cardID)
if err != nil { if err != nil {
t.Fatalf("ERROR: Unable to read test card, %s.", err) t.Fatalf("ERROR: Unable to read test card, %s.", err)
} }
@ -106,7 +106,7 @@ func testReadCard(b board.Board, cardID int, wantTitle, wantContent string) func
} }
} }
func testUpdateCard(b board.Board, cardID int, newTitle, newContent string) func(t *testing.T) { func testUpdateCard(kanban board.Board, cardID int, newTitle, newContent string) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When a card is updated in the database.") t.Log("When a card is updated in the database.")
@ -118,11 +118,11 @@ func testUpdateCard(b board.Board, cardID int, newTitle, newContent string) func
}, },
} }
if err := b.UpdateCard(args); err != nil { if err := kanban.UpdateCard(args); err != nil {
t.Fatalf("ERROR: Unable to update the test card, %s", err) t.Fatalf("ERROR: Unable to update the test card, %s", err)
} }
got, err := b.Card(cardID) got, err := kanban.Card(cardID)
if err != nil { if err != nil {
t.Fatalf("ERROR: Unable to read the modified test card, %s", err) t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
} }
@ -141,7 +141,7 @@ func testUpdateCard(b board.Board, cardID int, newTitle, newContent string) func
} }
} }
func testUpdateCardContent(b board.Board, cardID int, expectedTitle, newContent string) func(t *testing.T) { func testUpdateCardContent(kanban board.Board, cardID int, expectedTitle, newContent string) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When (and only when) a card's content is updated in the database.") t.Log("When (and only when) a card's content is updated in the database.")
@ -153,11 +153,11 @@ func testUpdateCardContent(b board.Board, cardID int, expectedTitle, newContent
}, },
} }
if err := b.UpdateCard(args); err != nil { if err := kanban.UpdateCard(args); err != nil {
t.Fatalf("ERROR: Unable to update the test card, %s", err) t.Fatalf("ERROR: Unable to update the test card, %s", err)
} }
got, err := b.Card(cardID) got, err := kanban.Card(cardID)
if err != nil { if err != nil {
t.Fatalf("ERROR: Unable to read the modified test card, %s", err) t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
} }

View file

@ -12,7 +12,7 @@ type Status struct {
Order int Order int
} }
// UpdateId updates the ID of the Status value. // UpdateID updates the ID of the Status value.
func (s *Status) UpdateId(id int) { func (s *Status) UpdateId(id int) {
s.ID = id s.ID = id
} }
@ -41,7 +41,7 @@ func (s *Status) AddCardID(cardID int) {
// Return if it already exists in the list // Return if it already exists in the list
ind := sort.SearchInts(s.CardIds, cardID) ind := sort.SearchInts(s.CardIds, cardID)
if ind <= len(s.CardIds) && cardID == s.CardIds[ind] { if ind < len(s.CardIds) && cardID == s.CardIds[ind] {
return return
} }

View file

@ -44,7 +44,7 @@ func TestStatusLifecycle(t *testing.T) {
t.Run("Test Move Card To Status", testMoveCardToStatus(kanban)) t.Run("Test Move Card To Status", testMoveCardToStatus(kanban))
} }
func testCreateStatus(b board.Board, name string, order int) func(t *testing.T) { func testCreateStatus(kanban board.Board, name string, order int) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When the status is created and saved to the database.") t.Log("When the status is created and saved to the database.")
@ -53,7 +53,7 @@ func testCreateStatus(b board.Board, name string, order int) func(t *testing.T)
Order: order, Order: order,
} }
if err := b.CreateStatus(args); err != nil { if err := kanban.CreateStatus(args); err != nil {
t.Fatalf("ERROR: Unable to create the new status, %v.", err) t.Fatalf("ERROR: Unable to create the new status, %v.", err)
} }
@ -61,11 +61,11 @@ func testCreateStatus(b board.Board, name string, order int) func(t *testing.T)
} }
} }
func testReadStatus(b board.Board, statusID int, wantName string) func(t *testing.T) { func testReadStatus(kanban board.Board, statusID int, wantName string) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Log("When the status is read from the database.") t.Log("When the status is read from the database.")
status, err := b.Status(statusID) status, err := kanban.Status(statusID)
if err != nil { if err != nil {
t.Fatalf("ERROR: Unable to read the test status, %v", err) t.Fatalf("ERROR: Unable to read the test status, %v", err)
} }

View file

@ -1,4 +1,4 @@
package database package db
import ( import (
"bytes" "bytes"
@ -38,33 +38,33 @@ func OpenDatabase(path string) (*bolt.DB, error) {
Timeout: 1 * time.Second, Timeout: 1 * time.Second,
} }
var db *bolt.DB var database *bolt.DB
if db, err = bolt.Open(path, 0o600, &opts); err != nil { if database, err = bolt.Open(path, 0o600, &opts); err != nil {
return nil, fmt.Errorf("unable to open database at %s, %w", path, err) return nil, fmt.Errorf("unable to open database at %s, %w", path, err)
} }
if err = ensureBuckets(db); err != nil { if err = ensureBuckets(database); err != nil {
return nil, fmt.Errorf("unable to ensure the required buckets are in the database, %w", err) return nil, fmt.Errorf("unable to ensure the required buckets are in the database, %w", err)
} }
return db, nil return database, nil
} }
// Read retrieves a Bolt item from a specified bucket and returns the data in bytes. // Read retrieves a Bolt item from a specified bucket and returns the data in bytes.
func Read(db *bolt.DB, bucketName string, id int) ([]byte, error) { func Read(db *bolt.DB, bucketName string, itemID int) ([]byte, error) {
bucket := []byte(bucketName) bucketNameBytes := []byte(bucketName)
var data []byte var data []byte
if err := db.View(func(tx *bolt.Tx) error { if err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) bucket := tx.Bucket(bucketNameBytes)
if b == nil { if bucket == nil {
return bucketNotExistError{bucket: string(bucket)} return bucketNotExistError{bucket: string(bucketNameBytes)}
} }
data = b.Get([]byte(strconv.Itoa(id))) data = bucket.Get([]byte(strconv.Itoa(itemID)))
return nil return nil
}); err != nil { }); err != nil {
@ -76,19 +76,19 @@ func Read(db *bolt.DB, bucketName string, id int) ([]byte, error) {
// ReadMany reads one or more Bolt items from the specified bucket. // ReadMany reads one or more Bolt items from the specified bucket.
func ReadMany(db *bolt.DB, bucketName string, ids []int) ([][]byte, error) { func ReadMany(db *bolt.DB, bucketName string, ids []int) ([][]byte, error) {
bucket := []byte(bucketName) bucketNameBytes := []byte(bucketName)
var output [][]byte var output [][]byte
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) bucket := tx.Bucket(bucketNameBytes)
if b == nil { if bucket == nil {
return bucketNotExistError{bucket: bucketName} return bucketNotExistError{bucket: bucketName}
} }
for _, v := range ids { for _, v := range ids {
data := b.Get([]byte(strconv.Itoa(v))) data := bucket.Get([]byte(strconv.Itoa(v)))
output = append(output, data) output = append(output, data)
} }
@ -103,18 +103,18 @@ func ReadMany(db *bolt.DB, bucketName string, ids []int) ([][]byte, error) {
// ReadAll retrieves all the Bolt Items from the specified bucket. // ReadAll retrieves all the Bolt Items from the specified bucket.
func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) { func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) {
bucket := []byte(bucketName) bucketNameBytes := []byte(bucketName)
var output [][]byte var output [][]byte
err := db.View(func(tx *bolt.Tx) error { err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) bucket := tx.Bucket(bucketNameBytes)
if b == nil { if bucket == nil {
return bucketNotExistError{bucket: bucketName} return bucketNotExistError{bucket: bucketName}
} }
if err := b.ForEach(func(_, v []byte) error { if err := bucket.ForEach(func(_, v []byte) error {
output = append(output, v) output = append(output, v)
return nil return nil
@ -133,19 +133,19 @@ func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) {
// Write creates or updates a Bolt item to a specified bucket. // Write creates or updates a Bolt item to a specified bucket.
func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) { func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
bucket := []byte(bucketName) bucketNameBytes := []byte(bucketName)
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
var err error var err error
b := tx.Bucket(bucket) bucket := tx.Bucket(bucketNameBytes)
if b == nil { if bucket == nil {
return bucketNotExistError{bucket: string(bucket)} return bucketNotExistError{bucket: string(bucketNameBytes)}
} }
if item.Id() < 1 { if item.Id() < 1 {
var id uint64 var id uint64
if id, err = b.NextSequence(); err != nil { if id, err = bucket.NextSequence(); err != nil {
return fmt.Errorf("unable to generate an ID for the card, %w", err) return fmt.Errorf("unable to generate an ID for the card, %w", err)
} }
item.UpdateId(int(id)) item.UpdateId(int(id))
@ -157,7 +157,7 @@ func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
return fmt.Errorf("unable to encode data, %w", err) return fmt.Errorf("unable to encode data, %w", err)
} }
if err = b.Put([]byte(strconv.Itoa(item.Id())), buf.Bytes()); err != nil { if err = bucket.Put([]byte(strconv.Itoa(item.Id())), buf.Bytes()); err != nil {
return fmt.Errorf("unable to write the card to the bucket, %w", err) return fmt.Errorf("unable to write the card to the bucket, %w", err)
} }
@ -171,44 +171,44 @@ func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
} }
// WriteMany saves one or more Bolt items to the status bucket. // WriteMany saves one or more Bolt items to the status bucket.
func WriteMany(db *bolt.DB, bucketName string, items []BoltItem) ([]int, error) { func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, error) {
if len(items) == 0 { if len(items) == 0 {
return []int{}, nil return []int{}, nil
} }
ids := make([]int, len(items)) ids := make([]int, len(items))
bucket := []byte(bucketName) bucketNameBytes := []byte(bucketName)
err := db.Update(func(tx *bolt.Tx) error { err := database.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(bucket) bucket := tx.Bucket(bucketNameBytes)
if b == nil { if bucket == nil {
return bucketNotExistError{bucket: string(bucket)} return bucketNotExistError{bucket: string(bucketNameBytes)}
} }
for i, v := range items { for ind, item := range items {
var err error var err error
if v.Id() < 1 { if item.Id() < 1 {
var id uint64 var id uint64
if id, err = b.NextSequence(); err != nil { if id, err = bucket.NextSequence(); err != nil {
return fmt.Errorf("unable to generate ID, %w", err) return fmt.Errorf("unable to generate ID, %w", err)
} }
v.UpdateId(int(id)) item.UpdateId(int(id))
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
encoder := gob.NewEncoder(buf) encoder := gob.NewEncoder(buf)
if err = encoder.Encode(v); err != nil { if err = encoder.Encode(item); err != nil {
return fmt.Errorf("unable to encode data, %w", err) return fmt.Errorf("unable to encode data, %w", err)
} }
if err = b.Put([]byte(strconv.Itoa(v.Id())), buf.Bytes()); err != nil { if err = bucket.Put([]byte(strconv.Itoa(item.Id())), buf.Bytes()); err != nil {
return fmt.Errorf("unable to add the Bolt Item to the %s bucket, %w", bucketName, err) return fmt.Errorf("unable to add the Bolt Item to the %s bucket, %w", bucketName, err)
} }
ids[i] = v.Id() ids[ind] = item.Id()
} }
return nil return nil

View file

@ -1,4 +1,4 @@
package database_test package db_test
import ( import (
"bytes" "bytes"
@ -10,7 +10,7 @@ import (
"testing" "testing"
"codeflow.dananglin.me.uk/apollo/canal/internal/board" "codeflow.dananglin.me.uk/apollo/canal/internal/board"
"codeflow.dananglin.me.uk/apollo/canal/internal/database" "codeflow.dananglin.me.uk/apollo/canal/internal/db"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
) )
@ -33,7 +33,7 @@ func TestOpenDataBaseXDGDataDir(t *testing.T) {
_ = os.Unsetenv("XDG_DATA_HOME") _ = os.Unsetenv("XDG_DATA_HOME")
}() }()
db, err := database.OpenDatabase("") db, err := db.OpenDatabase("")
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst opening the test database, %s.", err) t.Fatalf("An error occurred whilst opening the test database, %s.", err)
} }
@ -52,7 +52,7 @@ func TestOpenDataBaseXDGDataDir(t *testing.T) {
func TestWriteAndReadStatusList(t *testing.T) { func TestWriteAndReadStatusList(t *testing.T) {
t.Parallel() t.Parallel()
var db *bolt.DB var database *bolt.DB
var err error var err error
@ -64,19 +64,19 @@ func TestWriteAndReadStatusList(t *testing.T) {
testDB := filepath.Join(projectDir, "test", "databases", "Database_TestWriteAndReadStatusList.db") testDB := filepath.Join(projectDir, "test", "databases", "Database_TestWriteAndReadStatusList.db")
os.Remove(testDB) os.Remove(testDB)
if db, err = database.OpenDatabase(testDB); err != nil { if database, err = db.OpenDatabase(testDB); err != nil {
t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err) t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err)
} }
defer func() { defer func() {
_ = db.Close() _ = database.Close()
}() }()
testWriteStatusList(t, db) testWriteStatusList(t, database)
testReadStatusList(t, db) testReadStatusList(t, database)
} }
func testWriteStatusList(t *testing.T, db *bolt.DB) { func testWriteStatusList(t *testing.T, database *bolt.DB) {
t.Helper() t.Helper()
newStatusList := []board.Status{ newStatusList := []board.Status{
@ -106,21 +106,21 @@ func testWriteStatusList(t *testing.T, db *bolt.DB) {
}, },
} }
boltItems := make([]database.BoltItem, len(newStatusList)) boltItems := make([]db.BoltItem, len(newStatusList))
for i := range newStatusList { for i := range newStatusList {
boltItems[i] = &newStatusList[i] boltItems[i] = &newStatusList[i]
} }
if _, err := database.WriteMany(db, database.StatusBucket, boltItems); err != nil { if _, err := db.WriteMany(database, db.StatusBucket, boltItems); err != nil {
t.Fatalf("An error occurred whilst writing the initial status list to the database, %s", err) t.Fatalf("An error occurred whilst writing the initial status list to the database, %s", err)
} }
} }
func testReadStatusList(t *testing.T, db *bolt.DB) { func testReadStatusList(t *testing.T, database *bolt.DB) {
t.Helper() t.Helper()
data, err := database.ReadAll(db, database.StatusBucket) data, err := db.ReadAll(database, db.StatusBucket)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err) t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err)
} }
@ -178,7 +178,7 @@ func testReadStatusList(t *testing.T, db *bolt.DB) {
func TestReadAndWriteCards(t *testing.T) { func TestReadAndWriteCards(t *testing.T) {
t.Parallel() t.Parallel()
var db *bolt.DB var database *bolt.DB
var err error var err error
@ -190,12 +190,12 @@ func TestReadAndWriteCards(t *testing.T) {
testDB := filepath.Join(projectDir, "test", "databases", "Database_TestReadWriteCard.db") testDB := filepath.Join(projectDir, "test", "databases", "Database_TestReadWriteCard.db")
os.Remove(testDB) os.Remove(testDB)
if db, err = database.OpenDatabase(testDB); err != nil { if database, err = db.OpenDatabase(testDB); err != nil {
t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err) t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err)
} }
defer func() { defer func() {
_ = db.Close() _ = database.Close()
}() }()
singleCard := board.Card{ singleCard := board.Card{
@ -204,8 +204,8 @@ func TestReadAndWriteCards(t *testing.T) {
Content: "This task should be completed.", Content: "This task should be completed.",
} }
singleCardID := testWriteOneCard(t, db, singleCard) singleCardID := testWriteOneCard(t, database, singleCard)
testReadOneCard(t, db, singleCardID) testReadOneCard(t, database, singleCardID)
manyCards := []board.Card{ manyCards := []board.Card{
{ {
@ -225,14 +225,14 @@ func TestReadAndWriteCards(t *testing.T) {
}, },
} }
manyCardIDs := testWriteManyCard(t, db, manyCards) manyCardIDs := testWriteManyCard(t, database, manyCards)
testReadManyCards(t, db, manyCardIDs) testReadManyCards(t, database, manyCardIDs)
} }
func testWriteOneCard(t *testing.T, db *bolt.DB, card board.Card) int { func testWriteOneCard(t *testing.T, database *bolt.DB, card board.Card) int {
t.Helper() t.Helper()
cardID, err := database.Write(db, database.CardBucket, &card) cardID, err := db.Write(database, db.CardBucket, &card)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst writing the card to the database, %s", err) t.Fatalf("An error occurred whilst writing the card to the database, %s", err)
} }
@ -240,10 +240,10 @@ func testWriteOneCard(t *testing.T, db *bolt.DB, card board.Card) int {
return cardID return cardID
} }
func testReadOneCard(t *testing.T, db *bolt.DB, cardID int) { func testReadOneCard(t *testing.T, database *bolt.DB, cardID int) {
t.Helper() t.Helper()
data, err := database.Read(db, database.CardBucket, cardID) data, err := db.Read(database, db.CardBucket, cardID)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst loading the modified from the database, %s", err) t.Fatalf("An error occurred whilst loading the modified from the database, %s", err)
} }
@ -271,16 +271,16 @@ func testReadOneCard(t *testing.T, db *bolt.DB, cardID int) {
} }
} }
func testWriteManyCard(t *testing.T, db *bolt.DB, cards []board.Card) []int { func testWriteManyCard(t *testing.T, database *bolt.DB, cards []board.Card) []int {
t.Helper() t.Helper()
boltItems := make([]database.BoltItem, len(cards)) boltItems := make([]db.BoltItem, len(cards))
for i := range cards { for i := range cards {
boltItems[i] = &cards[i] boltItems[i] = &cards[i]
} }
ids, err := database.WriteMany(db, database.CardBucket, boltItems) ids, err := db.WriteMany(database, db.CardBucket, boltItems)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst writing many cards to the database, %s", err) t.Fatalf("An error occurred whilst writing many cards to the database, %s", err)
} }
@ -288,10 +288,10 @@ func testWriteManyCard(t *testing.T, db *bolt.DB, cards []board.Card) []int {
return ids return ids
} }
func testReadManyCards(t *testing.T, db *bolt.DB, cardIDs []int) { func testReadManyCards(t *testing.T, database *bolt.DB, cardIDs []int) {
t.Helper() t.Helper()
data, err := database.ReadMany(db, database.CardBucket, cardIDs) data, err := db.ReadMany(database, db.CardBucket, cardIDs)
if err != nil { if err != nil {
t.Fatalf("An error occurred whilst reading the data from the database, %s", err) t.Fatalf("An error occurred whilst reading the data from the database, %s", err)
} }

View file

@ -1,4 +1,4 @@
package database package db
import "fmt" import "fmt"