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
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.
type Card struct {
ID int
@ -18,12 +8,23 @@ type Card struct {
Created string
}
// UpdateId updates the ID of the Card value.
func (c *Card) UpdateId(id int) {
// SetID updates the ID of the Card value only if
// the ID is < 1 (i.e. unset).
func (c *Card) SetID(id int) error {
if id < 1 {
return InvalidIDError{id}
}
if c.ID > 0 {
return IDAlreadySetError{}
}
c.ID = id
return nil
}
// Id returns the ID of the Card value.
func (c *Card) Id() int {
// GetID returns the ID of the Card value.
func (c *Card) GetID() int {
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
import (
"fmt"
"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.
type Status struct {
ID int
@ -36,12 +13,22 @@ type Status struct {
}
// UpdateID updates the ID of the Status value.
func (s *Status) UpdateId(id int) {
func (s *Status) SetID(id int) error {
if id < 1 {
return InvalidIDError{id}
}
if s.ID > 0 {
return IDAlreadySetError{}
}
s.ID = id
return nil
}
// Id returns the ID of the Status value.
func (s *Status) Id() int {
// GetID returns the ID of the Status value.
func (s *Status) GetID() int {
return s.ID
}

View file

@ -18,8 +18,8 @@ const (
)
type BoltItem interface {
UpdateId(int)
Id() int
SetID(int) error
GetID() int
}
// 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
}); 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
@ -116,13 +116,13 @@ func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) {
return 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
})
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
@ -140,12 +140,15 @@ func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
return bucketNotExistError{bucket: string(bucketNameBytes)}
}
if item.Id() < 1 {
if item.GetID() < 1 {
var id uint64
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)
@ -154,17 +157,17 @@ func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
return fmt.Errorf("unable to encode data, %w", err)
}
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)
if err = bucket.Put([]byte(strconv.Itoa(item.GetID())), buf.Bytes()); err != nil {
return fmt.Errorf("unable to write the Bolt item to the bucket; %w", err)
}
return 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.
@ -187,12 +190,15 @@ func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, e
for ind, item := range items {
var err error
if item.Id() < 1 {
if item.GetID() < 1 {
var id uint64
if id, err = bucket.NextSequence(); err != nil {
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)
@ -201,11 +207,11 @@ func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, e
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)
}
ids[ind] = item.Id()
ids[ind] = item.GetID()
}
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 {
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
}); 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
@ -245,7 +251,7 @@ func mkDataDir(path string) error {
dir := filepath.Dir(path)
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
@ -258,14 +264,14 @@ func ensureBuckets(db *bolt.DB) error {
err := db.Update(func(tx *bolt.Tx) error {
for _, v := range buckets {
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
})
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