Detailed changes
@@ -29,7 +29,7 @@ func NewBoardCache(b *board.Board, repo repository.ClockedRepo, getUserIdentity
}
}
-func (c *BoardCache) AddItemDraft(columnId entity.Id, title, message string, files []repository.Hash) (entity.CombinedId, *board.AddItemDraftOperation, error) {
+func (c *BoardCache) AddItemDraft(columnId entity.CombinedId, title, message string, files []repository.Hash) (entity.CombinedId, *board.AddItemDraftOperation, error) {
author, err := c.getUserIdentity()
if err != nil {
return entity.UnsetCombinedId, nil, err
@@ -38,9 +38,14 @@ func (c *BoardCache) AddItemDraft(columnId entity.Id, title, message string, fil
return c.AddItemDraftRaw(author, time.Now().Unix(), columnId, title, message, files, nil)
}
-func (c *BoardCache) AddItemDraftRaw(author identity.Interface, unixTime int64, columnId entity.Id, title, message string, files []repository.Hash, metadata map[string]string) (entity.CombinedId, *board.AddItemDraftOperation, error) {
+func (c *BoardCache) AddItemDraftRaw(author identity.Interface, unixTime int64, columnId entity.CombinedId, title, message string, files []repository.Hash, metadata map[string]string) (entity.CombinedId, *board.AddItemDraftOperation, error) {
+ column, err := c.Snapshot().SearchColumn(columnId)
+ if err != nil {
+ return entity.UnsetCombinedId, nil, err
+ }
+
c.mu.Lock()
- itemId, op, err := board.AddItemDraft(c.entity, author, unixTime, columnId, title, message, files, metadata)
+ itemId, op, err := board.AddItemDraft(c.entity, author, unixTime, column.Id, title, message, files, metadata)
c.mu.Unlock()
if err != nil {
return entity.UnsetCombinedId, nil, err
@@ -48,7 +53,7 @@ func (c *BoardCache) AddItemDraftRaw(author identity.Interface, unixTime int64,
return itemId, op, c.notifyUpdated()
}
-func (c *BoardCache) AddItemEntity(columnId entity.Id, e entity.Interface) (entity.CombinedId, *board.AddItemEntityOperation, error) {
+func (c *BoardCache) AddItemEntity(columnId entity.CombinedId, e entity.Interface) (entity.CombinedId, *board.AddItemEntityOperation, error) {
author, err := c.getUserIdentity()
if err != nil {
return entity.UnsetCombinedId, nil, err
@@ -57,9 +62,22 @@ func (c *BoardCache) AddItemEntity(columnId entity.Id, e entity.Interface) (enti
return c.AddItemEntityRaw(author, time.Now().Unix(), columnId, e, nil)
}
-func (c *BoardCache) AddItemEntityRaw(author identity.Interface, unixTime int64, columnId entity.Id, e entity.Interface, metadata map[string]string) (entity.CombinedId, *board.AddItemEntityOperation, error) {
+func (c *BoardCache) AddItemEntityRaw(author identity.Interface, unixTime int64, columnId entity.CombinedId, e entity.Interface, metadata map[string]string) (entity.CombinedId, *board.AddItemEntityOperation, error) {
+ column, err := c.Snapshot().SearchColumn(columnId)
+ if err != nil {
+ return entity.UnsetCombinedId, nil, err
+ }
+
+ var entityType board.ItemEntityType
+ switch e.(type) {
+ case *BugCache:
+ entityType = board.EntityTypeBug
+ default:
+ panic("unknown entity type")
+ }
+
c.mu.Lock()
- itemId, op, err := board.AddItemEntity(c.entity, author, unixTime, columnId, e, metadata)
+ itemId, op, err := board.AddItemEntity(c.entity, author, unixTime, column.Id, entityType, e, metadata)
c.mu.Unlock()
if err != nil {
return entity.UnsetCombinedId, nil, err
@@ -1,6 +1,7 @@
package cache
import (
+ "errors"
"time"
"github.com/git-bug/git-bug/entities/board"
@@ -43,6 +44,50 @@ func NewRepoCacheBoard(repo repository.ClockedRepo,
return &RepoCacheBoard{SubCache: sc}
}
+func (c *RepoCacheBoard) ResolveColumn(prefix string) (*BoardCache, entity.CombinedId, error) {
+ boardPrefix, _ := entity.SeparateIds(prefix)
+ boardCandidate := make([]entity.Id, 0, 5)
+
+ // build a list of possible matching boards
+ c.mu.RLock()
+ for _, excerpt := range c.excerpts {
+ if excerpt.Id().HasPrefix(boardPrefix) {
+ boardCandidate = append(boardCandidate, excerpt.Id())
+ }
+ }
+ c.mu.RUnlock()
+
+ matchingBoardIds := make([]entity.Id, 0, 5)
+ matchingColumnId := entity.UnsetCombinedId
+ var matchingBoard *BoardCache
+
+ // search for matching columns
+ // searching every board candidate allow for some collision with the board prefix only,
+ // before being refined with the full column prefix
+ for _, boardId := range boardCandidate {
+ b, err := c.Resolve(boardId)
+ if err != nil {
+ return nil, entity.UnsetCombinedId, err
+ }
+
+ for _, column := range b.Snapshot().Columns {
+ if column.CombinedId.HasPrefix(prefix) {
+ matchingBoardIds = append(matchingBoardIds, boardId)
+ matchingBoard = b
+ matchingColumnId = column.CombinedId
+ }
+ }
+ }
+
+ if len(matchingBoardIds) > 1 {
+ return nil, entity.UnsetCombinedId, entity.NewErrMultipleMatch("board/column", matchingBoardIds)
+ } else if len(matchingBoardIds) == 0 {
+ return nil, entity.UnsetCombinedId, errors.New("column doesn't exist")
+ }
+
+ return matchingBoard, matchingColumnId, nil
+}
+
func (c *RepoCacheBoard) New(title, description string, columns []string) (*BoardCache, *board.CreateOperation, error) {
author, err := c.getUserIdentity()
if err != nil {
@@ -7,6 +7,9 @@ import (
"strconv"
"sync"
+ "github.com/git-bug/git-bug/entities/board"
+ "github.com/git-bug/git-bug/entities/bug"
+ "github.com/git-bug/git-bug/entities/identity"
"github.com/git-bug/git-bug/entity"
"github.com/git-bug/git-bug/repository"
"github.com/git-bug/git-bug/util/multierr"
@@ -99,12 +102,17 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, chan
c.subcaches = append(c.subcaches, c.boards)
c.resolvers = entity.Resolvers{
- &IdentityCache{}: entity.ResolverFunc[*IdentityCache](c.identities.Resolve),
- &IdentityExcerpt{}: entity.ResolverFunc[*IdentityExcerpt](c.identities.ResolveExcerpt),
- &BugCache{}: entity.ResolverFunc[*BugCache](c.bugs.Resolve),
- &BugExcerpt{}: entity.ResolverFunc[*BugExcerpt](c.bugs.ResolveExcerpt),
- &BoardCache{}: entity.ResolverFunc[*BoardCache](c.boards.Resolve),
- &BoardExcerpt{}: entity.ResolverFunc[*BoardExcerpt](c.boards.ResolveExcerpt),
+ identity.Interface(nil): entity.ResolverFunc[*IdentityCache](c.identities.Resolve),
+ &IdentityCache{}: entity.ResolverFunc[*IdentityCache](c.identities.Resolve),
+ &IdentityExcerpt{}: entity.ResolverFunc[*IdentityExcerpt](c.identities.ResolveExcerpt),
+ bug.Interface(nil): entity.ResolverFunc[*BugCache](c.bugs.Resolve),
+ &bug.Bug{}: entity.ResolverFunc[*BugCache](c.bugs.Resolve),
+ &BugCache{}: entity.ResolverFunc[*BugCache](c.bugs.Resolve),
+ &BugExcerpt{}: entity.ResolverFunc[*BugExcerpt](c.bugs.ResolveExcerpt),
+ board.Interface(nil): entity.ResolverFunc[*BoardCache](c.boards.Resolve),
+ &bug.Bug{}: entity.ResolverFunc[*BoardCache](c.boards.Resolve),
+ &BoardCache{}: entity.ResolverFunc[*BoardCache](c.boards.Resolve),
+ &BoardExcerpt{}: entity.ResolverFunc[*BoardExcerpt](c.boards.ResolveExcerpt),
}
// small buffer so that below functions can emit an event without blocking
@@ -67,6 +67,7 @@ func NewBoardCommand() *cobra.Command {
cmd.AddCommand(newBoardDescriptionCommand())
cmd.AddCommand(newBoardTitleCommand())
cmd.AddCommand(newBoardAddDraftCommand())
+ cmd.AddCommand(newBoardAddBugCommand())
return cmd
}
@@ -0,0 +1,86 @@
+package boardcmd
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/spf13/cobra"
+
+ bugcmd "github.com/git-bug/git-bug/commands/bug"
+ "github.com/git-bug/git-bug/commands/execenv"
+ _select "github.com/git-bug/git-bug/commands/select"
+ "github.com/git-bug/git-bug/entity"
+)
+
+type boardAddBugOptions struct {
+ column string
+}
+
+func newBoardAddBugCommand() *cobra.Command {
+ env := execenv.NewEnv()
+ options := boardAddBugOptions{}
+
+ cmd := &cobra.Command{
+ Use: "add-bug [BOARD_ID] [BUG_ID]",
+ Short: "Add a bug to a board",
+ PreRunE: execenv.LoadBackend(env),
+ RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error {
+ return runBoardAddBug(env, options, args)
+ }),
+ ValidArgsFunction: BoardAndBugCompletion(env),
+ }
+
+ flags := cmd.Flags()
+ flags.SortFlags = false
+
+ flags.StringVarP(&options.column, "column", "c", "1",
+ "The column to add to. Either a column Id or prefix, or the column number starting from 1.")
+ _ = cmd.RegisterFlagCompletionFunc("column", ColumnCompletion(env))
+
+ return cmd
+}
+
+func runBoardAddBug(env *execenv.Env, opts boardAddBugOptions, args []string) error {
+ board, args, err := ResolveSelected(env.Backend, args)
+ if err != nil {
+ return err
+ }
+
+ var columnId entity.CombinedId
+
+ switch {
+ case err == nil:
+ // try to parse as column number
+ index, err := strconv.Atoi(opts.column)
+ if err == nil {
+ if index-1 >= 0 && index-1 < len(board.Snapshot().Columns) {
+ columnId = board.Snapshot().Columns[index-1].CombinedId
+ } else {
+ return fmt.Errorf("invalid column")
+ }
+ }
+ fallthrough // could be an Id
+ case _select.IsErrNoValidId(err):
+ board, columnId, err = env.Backend.Boards().ResolveColumn(opts.column)
+ if err != nil {
+ return err
+ }
+ default:
+ // actual error
+ return err
+ }
+
+ bug, _, err := bugcmd.ResolveSelected(env.Backend, args)
+ if err != nil {
+ return err
+ }
+
+ id, _, err := board.AddItemEntity(columnId, bug)
+ if err != nil {
+ return err
+ }
+
+ env.Out.Printf("%s created\n", id.Human())
+
+ return board.Commit()
+}
@@ -1,12 +1,14 @@
package boardcmd
import (
+ "fmt"
"strconv"
"github.com/spf13/cobra"
buginput "github.com/git-bug/git-bug/commands/bug/input"
"github.com/git-bug/git-bug/commands/execenv"
+ _select "github.com/git-bug/git-bug/commands/select"
"github.com/git-bug/git-bug/entity"
)
@@ -43,7 +45,6 @@ func newBoardAddDraftCommand() *cobra.Command {
"Take the message from the given file. Use - to read the message from the standard input")
flags.StringVarP(&options.column, "column", "c", "1",
"The column to add to. Either a column Id or prefix, or the column number starting from 1.")
- // _ = cmd.MarkFlagRequired("column")
_ = cmd.RegisterFlagCompletionFunc("column", ColumnCompletion(env))
flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input")
@@ -52,18 +53,29 @@ func newBoardAddDraftCommand() *cobra.Command {
func runBoardAddDraft(env *execenv.Env, opts boardAddDraftOptions, args []string) error {
b, args, err := ResolveSelected(env.Backend, args)
- if err != nil {
- return err
- }
- var columnId entity.Id
-
- index, err := strconv.Atoi(opts.column)
- if err == nil && index-1 >= 0 && index-1 < len(b.Snapshot().Columns) {
- columnId = b.Snapshot().Columns[index-1].Id
- } else {
- // TODO: ID or combined ID?
- // TODO: resolve
+ var columnId entity.CombinedId
+
+ switch {
+ case err == nil:
+ // try to parse as column number
+ index, err := strconv.Atoi(opts.column)
+ if err == nil {
+ if index-1 >= 0 && index-1 < len(b.Snapshot().Columns) {
+ columnId = b.Snapshot().Columns[index-1].CombinedId
+ } else {
+ return fmt.Errorf("invalid column")
+ }
+ }
+ fallthrough // could be an Id
+ case _select.IsErrNoValidId(err):
+ b, columnId, err = env.Backend.Boards().ResolveColumn(opts.column)
+ if err != nil {
+ return err
+ }
+ default:
+ // actual error
+ return err
}
if opts.messageFile != "" && opts.message == "" {
@@ -5,8 +5,11 @@ import (
"github.com/spf13/cobra"
+ "github.com/git-bug/git-bug/cache"
+ bugcmd "github.com/git-bug/git-bug/commands/bug"
"github.com/git-bug/git-bug/commands/completion"
"github.com/git-bug/git-bug/commands/execenv"
+ _select "github.com/git-bug/git-bug/commands/select"
)
// BoardCompletion complete a board id
@@ -19,20 +22,25 @@ func BoardCompletion(env *execenv.Env) completion.ValidArgsFunction {
_ = env.Backend.Close()
}()
- for _, id := range env.Backend.Boards().AllIds() {
- if strings.Contains(id.String(), strings.TrimSpace(toComplete)) {
- excerpt, err := env.Backend.Boards().ResolveExcerpt(id)
- if err != nil {
- return completion.HandleError(err)
- }
- completions = append(completions, id.Human()+"\t"+excerpt.Title)
+ return boardWithBackend(env.Backend, toComplete)
+ }
+}
+
+func boardWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
+ for _, id := range backend.Boards().AllIds() {
+ if strings.Contains(id.String(), strings.TrimSpace(toComplete)) {
+ excerpt, err := backend.Boards().ResolveExcerpt(id)
+ if err != nil {
+ return completion.HandleError(err)
}
+ completions = append(completions, id.Human()+"\t"+excerpt.Title)
}
-
- return completions, cobra.ShellCompDirectiveNoFileComp
}
+
+ return completions, cobra.ShellCompDirectiveNoFileComp
}
+// ColumnCompletion complete a board's column id
func ColumnCompletion(env *execenv.Env) completion.ValidArgsFunction {
return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
if err := execenv.LoadBackend(env)(cmd, args); err != nil {
@@ -43,14 +51,39 @@ func ColumnCompletion(env *execenv.Env) completion.ValidArgsFunction {
}()
b, _, err := ResolveSelected(env.Backend, args)
- if err != nil {
+ switch {
+ case _select.IsErrNoValidId(err):
+ // no completion
+ case err == nil:
+ for _, column := range b.Snapshot().Columns {
+ completions = append(completions, column.CombinedId.Human()+"\t"+column.Name)
+ }
+ default:
return completion.HandleError(err)
}
- for _, column := range b.Snapshot().Columns {
- completions = append(completions, column.Id.Human()+"\t"+column.Name)
+ return completions, cobra.ShellCompDirectiveNoFileComp
+ }
+}
+
+func BoardAndBugCompletion(env *execenv.Env) completion.ValidArgsFunction {
+ return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
+ if err := execenv.LoadBackend(env)(cmd, args); err != nil {
+ return completion.HandleError(err)
+ }
+ defer func() {
+ _ = env.Backend.Close()
+ }()
+
+ _, _, err := ResolveSelected(env.Backend, args)
+ switch {
+ case _select.IsErrNoValidId(err):
+ return boardWithBackend(env.Backend, toComplete)
+ case err == nil:
+ return bugcmd.BugWithBackend(env.Backend, toComplete)
+ default:
+ return completion.HandleError(err)
}
- return completions, cobra.ShellCompDirectiveNoFileComp
}
}
@@ -22,11 +22,11 @@ func BugCompletion(env *execenv.Env) completion.ValidArgsFunction {
_ = env.Backend.Close()
}()
- return bugWithBackend(env.Backend, toComplete)
+ return BugWithBackend(env.Backend, toComplete)
}
}
-func bugWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
+func BugWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
for _, id := range backend.Bugs().AllIds() {
if strings.Contains(id.String(), strings.TrimSpace(toComplete)) {
excerpt, err := backend.Bugs().ResolveExcerpt(id)
@@ -53,7 +53,7 @@ func BugAndLabelsCompletion(env *execenv.Env, addOrRemove bool) completion.Valid
b, cleanArgs, err := ResolveSelected(env.Backend, args)
if _select.IsErrNoValidId(err) {
// we need a bug first to complete labels
- return bugWithBackend(env.Backend, toComplete)
+ return BugWithBackend(env.Backend, toComplete)
}
if err != nil {
return completion.HandleError(err)
@@ -49,8 +49,8 @@ type BoardColumn struct {
func NewBoardColumn(column *board.Column) BoardColumn {
jsonColumn := BoardColumn{
- Id: column.Id.String(),
- HumanId: column.Id.Human(),
+ Id: column.CombinedId.String(),
+ HumanId: column.CombinedId.Human(),
Name: column.Name,
}
jsonColumn.Items = make([]any, len(column.Items))
@@ -38,10 +38,10 @@ type Resolver[CacheT cache.CacheEntity] interface {
// line. If it fails, it falls back to the select mechanism.
//
// Returns:
-// - the entity if any
-// - the new list of command line arguments with the entity prefix removed if it
-// has been used
-// - an error if the process failed
+//
+// Contrary to golang convention, the list of args returned is still correct even in
+// case of error, which allows to keep going and decide to handle the failure case more
+// naturally.
func Resolve[CacheT cache.CacheEntity](repo *cache.RepoCache,
typename string, namespace string, resolver Resolver[CacheT],
args []string) (CacheT, []string, error) {
@@ -54,7 +54,7 @@ func Resolve[CacheT cache.CacheEntity](repo *cache.RepoCache,
}
if !entity.IsErrNotFound(err) {
- return *new(CacheT), nil, err
+ return *new(CacheT), args, err
}
}
@@ -67,14 +67,14 @@ func Resolve[CacheT cache.CacheEntity](repo *cache.RepoCache,
// we clear the selected bug
err = Clear(repo, namespace)
if err != nil {
- return *new(CacheT), nil, err
+ return *new(CacheT), args, err
}
- return *new(CacheT), nil, NewErrNoValidId(typename)
+ return *new(CacheT), args, NewErrNoValidId(typename)
}
// another error when reading the entity
if err != nil {
- return *new(CacheT), nil, err
+ return *new(CacheT), args, err
}
// entity is successfully retrieved
@@ -83,7 +83,7 @@ func Resolve[CacheT cache.CacheEntity](repo *cache.RepoCache,
}
// no selected bug and no valid first argument
- return *new(CacheT), nil, NewErrNoValidId(typename)
+ return *new(CacheT), args, NewErrNoValidId(typename)
}
func selectFileName(namespace string) string {
@@ -35,4 +35,4 @@ List boards
.SH SEE ALSO
-\fBgit-bug(1)\fP, \fBgit-bug-board-add-draft(1)\fP, \fBgit-bug-board-description(1)\fP, \fBgit-bug-board-deselect(1)\fP, \fBgit-bug-board-new(1)\fP, \fBgit-bug-board-rm(1)\fP, \fBgit-bug-board-select(1)\fP, \fBgit-bug-board-show(1)\fP, \fBgit-bug-board-title(1)\fP
+\fBgit-bug(1)\fP, \fBgit-bug-board-add-bug(1)\fP, \fBgit-bug-board-add-draft(1)\fP, \fBgit-bug-board-description(1)\fP, \fBgit-bug-board-deselect(1)\fP, \fBgit-bug-board-new(1)\fP, \fBgit-bug-board-rm(1)\fP, \fBgit-bug-board-select(1)\fP, \fBgit-bug-board-show(1)\fP, \fBgit-bug-board-title(1)\fP
@@ -19,6 +19,7 @@ git-bug board [flags]
### SEE ALSO
* [git-bug](git-bug.md) - A bug tracker embedded in Git
+* [git-bug board add-bug](git-bug_board_add-bug.md) - Add a bug to a board
* [git-bug board add-draft](git-bug_board_add-draft.md) - Add a draft item to a board
* [git-bug board description](git-bug_board_description.md) - Display the description of a board
* [git-bug board deselect](git-bug_board_deselect.md) - Clear the implicitly selected board
@@ -59,10 +59,11 @@ func (op *AddItemDraftOperation) Validate() error {
}
func (op *AddItemDraftOperation) Apply(snapshot *Snapshot) {
- snapshot.addParticipant(op.Author())
+ // Recreate the combined Id to match on
+ combinedId := entity.CombineIds(snapshot.Id(), op.ColumnId)
for _, column := range snapshot.Columns {
- if column.Id == op.ColumnId {
+ if column.CombinedId == combinedId {
column.Items = append(column.Items, &Draft{
combinedId: entity.CombineIds(snapshot.id, op.Id()),
Author: op.Author(),
@@ -70,6 +71,8 @@ func (op *AddItemDraftOperation) Apply(snapshot *Snapshot) {
Message: op.Message,
unixTime: timestamp.Timestamp(op.UnixTime),
})
+
+ snapshot.addParticipant(op.Author())
return
}
}
@@ -9,11 +9,11 @@ import (
"github.com/git-bug/git-bug/entity/dag"
)
-// itemEntityType indicate the type of entity board item
-type itemEntityType string
+// ItemEntityType indicate the type of entity board item
+type ItemEntityType string
const (
- entityTypeBug itemEntityType = "bug"
+ EntityTypeBug ItemEntityType = "bug"
)
var _ Operation = &AddItemEntityOperation{}
@@ -21,7 +21,7 @@ var _ Operation = &AddItemEntityOperation{}
type AddItemEntityOperation struct {
dag.OpBase
ColumnId entity.Id `json:"column"`
- EntityType itemEntityType `json:"entity_type"`
+ EntityType ItemEntityType `json:"entity_type"`
EntityId entity.Id `json:"entity_id"`
entity entity.Interface // not serialized
}
@@ -40,7 +40,7 @@ func (op *AddItemEntityOperation) Validate() error {
}
switch op.EntityType {
- case entityTypeBug:
+ case EntityTypeBug:
default:
return fmt.Errorf("unknown entity type")
}
@@ -57,40 +57,39 @@ func (op *AddItemEntityOperation) Apply(snapshot *Snapshot) {
return
}
- snapshot.addParticipant(op.Author())
+ // Recreate the combined Id to match on
+ combinedId := entity.CombineIds(snapshot.Id(), op.ColumnId)
for _, column := range snapshot.Columns {
- if column.Id == op.ColumnId {
- switch e := op.entity.(type) {
- case bug.Interface:
+ if column.CombinedId == combinedId {
+ switch op.EntityType {
+ case EntityTypeBug:
column.Items = append(column.Items, &BugItem{
- combinedId: entity.CombineIds(snapshot.Id(), e.Id()),
- Bug: e,
+ combinedId: entity.CombineIds(snapshot.Id(), op.entity.Id()),
+ Bug: op.entity.(bug.Interface),
})
}
+ snapshot.addParticipant(op.Author())
return
}
}
}
-func NewAddItemEntityOp(author identity.Interface, unixTime int64, columnId entity.Id, e entity.Interface) *AddItemEntityOperation {
- switch e := e.(type) {
- case bug.Interface:
- return &AddItemEntityOperation{
- OpBase: dag.NewOpBase(AddItemEntityOp, author, unixTime),
- ColumnId: columnId,
- EntityType: entityTypeBug,
- EntityId: e.Id(),
- entity: e,
- }
- default:
- panic("invalid entity type")
+func NewAddItemEntityOp(author identity.Interface, unixTime int64, columnId entity.Id, entityType ItemEntityType, e entity.Interface) *AddItemEntityOperation {
+ // Note: due to import cycle we are not able to properly check the type of the entity here;
+ // proceed with caution!
+ return &AddItemEntityOperation{
+ OpBase: dag.NewOpBase(AddItemEntityOp, author, unixTime),
+ ColumnId: columnId,
+ EntityType: entityType,
+ EntityId: e.Id(),
+ entity: e,
}
}
// AddItemEntity is a convenience function to add an entity item to a Board
-func AddItemEntity(b Interface, author identity.Interface, unixTime int64, columnId entity.Id, e entity.Interface, metadata map[string]string) (entity.CombinedId, *AddItemEntityOperation, error) {
- op := NewAddItemEntityOp(author, unixTime, columnId, e)
+func AddItemEntity(b Interface, author identity.Interface, unixTime int64, columnId entity.Id, entityType ItemEntityType, e entity.Interface, metadata map[string]string) (entity.CombinedId, *AddItemEntityOperation, error) {
+ op := NewAddItemEntityOp(author, unixTime, columnId, entityType, e)
for key, val := range metadata {
op.SetMetadata(key, val)
}
@@ -86,13 +86,20 @@ func (op *CreateOperation) Apply(snap *Snapshot) {
snap.CreateTime = op.Time()
for _, name := range op.Columns {
- // we derive a unique Id from the original column name
- id := entity.DeriveId([]byte(name))
+ // we derive a unique ID from:
+ // - the ID of the operation that created the column
+ // - the original column name
+ id := entity.DeriveId(append([]byte(op.Id()), []byte(name)...))
+
+ // we derived the combined ID by interleaving the board ID (the same in
+ // this case).
+ combinedID := entity.CombineIds(snap.id, id)
snap.Columns = append(snap.Columns, &Column{
- Id: id,
- Name: name,
- Items: nil,
+ Id: id,
+ CombinedId: combinedID,
+ Name: name,
+ Items: nil,
})
}
@@ -65,7 +65,7 @@ func operationUnmarshaler(raw json.RawMessage, resolvers entity.Resolvers) (dag.
switch op := op.(type) {
case *AddItemEntityOperation:
switch op.EntityType {
- case entityTypeBug:
+ case EntityTypeBug:
op.entity, err = entity.Resolve[bug.Interface](resolvers, op.EntityId)
default:
return nil, fmt.Errorf("unknown entity type")
@@ -1,6 +1,7 @@
package board
import (
+ "fmt"
"time"
"github.com/git-bug/git-bug/entities/identity"
@@ -9,9 +10,12 @@ import (
)
type Column struct {
- Id entity.Id
- Name string
- Items []Item
+ // id is the identifier of the column within the board context
+ Id entity.Id
+ // CombinedId is the global identifier of the column
+ CombinedId entity.CombinedId
+ Name string
+ Items []Item
}
type Item interface {
@@ -60,6 +64,17 @@ func (snap *Snapshot) EditTime() time.Time {
return snap.Operations[len(snap.Operations)-1].Time()
}
+// SearchColumn will search for a column matching the given id
+func (snap *Snapshot) SearchColumn(id entity.CombinedId) (*Column, error) {
+ for _, column := range snap.Columns {
+ if column.CombinedId == id {
+ return column, nil
+ }
+ }
+
+ return nil, fmt.Errorf("column not found")
+}
+
// append the operation author to the participants list
func (snap *Snapshot) addParticipant(participant identity.Interface) {
for _, p := range snap.Participants {
@@ -24,8 +24,7 @@ type Resolvers map[Resolved]Resolver
func Resolve[T Resolved](rs Resolvers, id Id) (T, error) {
var zero T
for t, resolver := range rs {
- switch t.(type) {
- case T:
+ if _, ok := t.(T); ok {
val, err := resolver.(Resolver).Resolve(id)
if err != nil {
return zero, err