feat: delete cards #6
7 changed files with 130 additions and 130 deletions
|
@ -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{¤tStatus, &nextStatus}
|
boltItems := []db.BoltItem{¤tStatus, &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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package database
|
package db
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
Loading…
Reference in a new issue