fix: add restrictions when setting IDs. #31

Manually merged
dananglin merged 1 commit from set-card-id-enhancement into main 2024-01-21 17:34:29 +00:00
4 changed files with 108 additions and 61 deletions

View file

@ -1,15 +1,5 @@
package board package board
import "fmt"
type CardNotExistError struct {
ID int
}
func (e CardNotExistError) Error() string {
return fmt.Sprintf("card ID '%d' does not exist in the database", e.ID)
}
// Card represents a card on a Kanban board. // Card represents a card on a Kanban board.
type Card struct { type Card struct {
ID int ID int
@ -18,12 +8,23 @@ type Card struct {
Created string Created string
} }
// UpdateId updates the ID of the Card value. // SetID updates the ID of the Card value only if
func (c *Card) UpdateId(id int) { // the ID is < 1 (i.e. unset).
c.ID = id func (c *Card) SetID(id int) error {
if id < 1 {
return InvalidIDError{id}
} }
// Id returns the ID of the Card value. if c.ID > 0 {
func (c *Card) Id() int { return IDAlreadySetError{}
}
c.ID = id
return nil
}
// GetID returns the ID of the Card value.
func (c *Card) GetID() int {
return c.ID return c.ID
} }

53
internal/board/errors.go Normal file
View file

@ -0,0 +1,53 @@
package board
import "fmt"
// CardNotExistError is returned when a card does not exist in the database.
type CardNotExistError struct {
ID int
}
func (e CardNotExistError) Error() string {
return fmt.Sprintf("card ID '%d' does not exist in the database", e.ID)
}
// InvalidIDError is an error for when an attempt to set an ID lower than 1 is made.
type InvalidIDError struct {
ID int
}
func ( e InvalidIDError) Error() string {
return fmt.Sprintf("'%d' is an invalid ID", e.ID)
}
// IDAlreadySetError is an error for when an attempt is made to set an ID that has already been set.
type IDAlreadySetError struct {}
func (e IDAlreadySetError) Error() string {
return "the item's ID is already set"
}
// StatusListEmptyError is an error for unexpected empty list of statuses.
type StatusListEmptyError struct{}
func (e StatusListEmptyError) Error() string {
return "the status list must not be empty"
}
// StatusNotExistError is an error for when a status cannot be found in the database.
type StatusNotExistError struct {
ID int
}
func (e StatusNotExistError) Error() string {
return fmt.Sprintf("status ID '%d' does not exist in the database", e.ID)
}
// StatusNotEmptyError is an error for when an attempt is made to delete non-empty statuses.
type StatusNotEmptyError struct {
ID int
}
func (e StatusNotEmptyError) Error() string {
return fmt.Sprintf("status ID '%d' must contain no cards before deletion", e.ID)
}

View file

@ -1,32 +1,9 @@
package board package board
import ( import (
"fmt"
"sort" "sort"
) )
type StatusListEmptyError struct{}
func (e StatusListEmptyError) Error() string {
return "the status list must not be empty"
}
type StatusNotExistError struct {
ID int
}
func (e StatusNotExistError) Error() string {
return fmt.Sprintf("status ID '%d' does not exist in the database", e.ID)
}
type StatusNotEmptyError struct {
ID int
}
func (e StatusNotEmptyError) Error() string {
return fmt.Sprintf("status ID '%d' must contain no cards before deletion", e.ID)
}
// Status represents the status of the Kanban board. // Status represents the status of the Kanban board.
type Status struct { type Status struct {
ID int ID int
@ -36,12 +13,22 @@ type Status struct {
} }
// 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) SetID(id int) error {
s.ID = id if id < 1 {
return InvalidIDError{id}
} }
// Id returns the ID of the Status value. if s.ID > 0 {
func (s *Status) Id() int { return IDAlreadySetError{}
}
s.ID = id
return nil
}
// GetID returns the ID of the Status value.
func (s *Status) GetID() int {
return s.ID return s.ID
} }

View file

@ -18,8 +18,8 @@ const (
) )
type BoltItem interface { type BoltItem interface {
UpdateId(int) SetID(int) error
Id() int GetID() int
} }
// OpenDatabase opens the database, at a given path, for reading and writing. // OpenDatabase opens the database, at a given path, for reading and writing.
@ -65,7 +65,7 @@ func Read(db *bolt.DB, bucketName string, itemID int) ([]byte, error) {
return nil return nil
}); err != nil { }); err != nil {
return nil, fmt.Errorf("error while reading the Bolt item from the database, %w", err) return nil, fmt.Errorf("error while reading the Bolt item from the database; %w", err)
} }
return data, nil return data, nil
@ -116,13 +116,13 @@ func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) {
return nil return nil
}); err != nil { }); err != nil {
return fmt.Errorf("unable to load status, %w", err) return fmt.Errorf("unable to load the Bolt item; %w", err)
} }
return nil return nil
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("error while loading statuses from the database, %w", err) return nil, fmt.Errorf("error while loading the Bolt items from the database; %w", err)
} }
return output, nil return output, nil
@ -140,12 +140,15 @@ func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
return bucketNotExistError{bucket: string(bucketNameBytes)} return bucketNotExistError{bucket: string(bucketNameBytes)}
} }
if item.Id() < 1 { if item.GetID() < 1 {
var id uint64 var id uint64
if id, err = bucket.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 Bolt item; %w", err)
}
if err = item.SetID(int(id)); err != nil {
return fmt.Errorf("unable to set the generated ID to the Bolt item; %w", err)
} }
item.UpdateId(int(id))
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -154,17 +157,17 @@ 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 = bucket.Put([]byte(strconv.Itoa(item.Id())), buf.Bytes()); err != nil { if err = bucket.Put([]byte(strconv.Itoa(item.GetID())), buf.Bytes()); err != nil {
return fmt.Errorf("unable to write the card to the bucket, %w", err) return fmt.Errorf("unable to write the Bolt item to the bucket; %w", err)
} }
return nil return nil
}) })
if err != nil { if err != nil {
return 0, fmt.Errorf("error while saving the card to the database, %w", err) return 0, fmt.Errorf("error while saving the Bolt item to the database; %w", err)
} }
return item.Id(), nil return item.GetID(), nil
} }
// WriteMany saves one or more Bolt items to the status bucket. // WriteMany saves one or more Bolt items to the status bucket.
@ -187,12 +190,15 @@ func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, e
for ind, item := range items { for ind, item := range items {
var err error var err error
if item.Id() < 1 { if item.GetID() < 1 {
var id uint64 var id uint64
if id, err = bucket.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)
} }
item.UpdateId(int(id))
if err = item.SetID(int(id)); err != nil {
return fmt.Errorf("unable to set the generated ID to the Bolt item; %w", err)
}
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -201,11 +207,11 @@ func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, e
return fmt.Errorf("unable to encode data, %w", err) return fmt.Errorf("unable to encode data, %w", err)
} }
if err = bucket.Put([]byte(strconv.Itoa(item.Id())), buf.Bytes()); err != nil { if err = bucket.Put([]byte(strconv.Itoa(item.GetID())), 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[ind] = item.Id() ids[ind] = item.GetID()
} }
return nil return nil
@ -229,12 +235,12 @@ func Delete(db *bolt.DB, bucketName string, itemID int) error {
} }
if err := bucket.Delete([]byte(strconv.Itoa(itemID))); err != nil { if err := bucket.Delete([]byte(strconv.Itoa(itemID))); err != nil {
return fmt.Errorf("an error occurred when deleting Bolt item '%d', %w", itemID, err) return fmt.Errorf("an error occurred when deleting Bolt item '%d'; %w", itemID, err)
} }
return nil return nil
}); err != nil { }); err != nil {
return fmt.Errorf("error deleting data from the '%s' bucket, %w", bucketName, err) return fmt.Errorf("error deleting data from the '%s' bucket; %w", bucketName, err)
} }
return nil return nil
@ -245,7 +251,7 @@ func mkDataDir(path string) error {
dir := filepath.Dir(path) dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0o700); err != nil { if err := os.MkdirAll(dir, 0o700); err != nil {
return fmt.Errorf("error while making directory %s, %w", dir, err) return fmt.Errorf("error while making directory %s; %w", dir, err)
} }
return nil return nil
@ -258,14 +264,14 @@ func ensureBuckets(db *bolt.DB) error {
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
for _, v := range buckets { for _, v := range buckets {
if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil { if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil {
return fmt.Errorf("unable to ensure that %s bucket is created in the database, %w", v, err) return fmt.Errorf("unable to ensure that %s bucket is created in the database; %w", v, err)
} }
} }
return nil return nil
}) })
if err != nil { if err != nil {
return fmt.Errorf("error while ensuring buckets exist in the database, %w", err) return fmt.Errorf("error while ensuring buckets exist in the database; %w", err)
} }
return nil return nil