fix: add restrictions when setting IDs. #31
4 changed files with 108 additions and 61 deletions
|
@ -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).
|
||||||
|
func (c *Card) SetID(id int) error {
|
||||||
|
if id < 1 {
|
||||||
|
return InvalidIDError{id}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ID > 0 {
|
||||||
|
return IDAlreadySetError{}
|
||||||
|
}
|
||||||
|
|
||||||
c.ID = id
|
c.ID = id
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id returns the ID of the Card value.
|
// GetID returns the ID of the Card value.
|
||||||
func (c *Card) Id() int {
|
func (c *Card) GetID() int {
|
||||||
return c.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
53
internal/board/errors.go
Normal file
53
internal/board/errors.go
Normal 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)
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
if id < 1 {
|
||||||
|
return InvalidIDError{id}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ID > 0 {
|
||||||
|
return IDAlreadySetError{}
|
||||||
|
}
|
||||||
|
|
||||||
s.ID = id
|
s.ID = id
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id returns the ID of the Status value.
|
// GetID returns the ID of the Status value.
|
||||||
func (s *Status) Id() int {
|
func (s *Status) GetID() int {
|
||||||
return s.ID
|
return s.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue