fix: add restrictions when setting IDs.
Main change: Add restrictions when adding IDs for cards and statuses. Errors are returned when an attempt is made to set an ID lower than 1, or an attempt is made to set an ID that has already been set. Additional changes: - Error types in the board packages are now defined in errors.go. - Small formatting to error messages.
This commit is contained in:
parent
d532c86475
commit
4e7eb77583
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