1package board
2
3import (
4 "fmt"
5
6 "github.com/git-bug/git-bug/entities/identity"
7
8 "github.com/git-bug/git-bug/entity"
9 "github.com/git-bug/git-bug/entity/dag"
10 "github.com/git-bug/git-bug/util/text"
11)
12
13var DefaultColumns = []string{"To Do", "In Progress", "Done"}
14
15var _ dag.Operation = &CreateOperation{}
16
17type CreateOperation struct {
18 dag.OpBase
19 Title string `json:"title"`
20 Description string `json:"description"`
21 Columns []string `json:"columns"`
22}
23
24func NewCreateOp(author identity.Interface, unixTime int64, title string, description string, columns []string) *CreateOperation {
25 return &CreateOperation{
26 OpBase: dag.NewOpBase(CreateOp, author, unixTime),
27 Title: title,
28 Description: description,
29 Columns: columns,
30 }
31}
32
33func (op *CreateOperation) Id() entity.Id {
34 return dag.IdOperation(op, &op.OpBase)
35}
36
37func (op *CreateOperation) Validate() error {
38 if err := op.OpBase.Validate(op, CreateOp); err != nil {
39 return err
40 }
41
42 if text.Empty(op.Title) {
43 return fmt.Errorf("title is empty")
44 }
45 if !text.SafeOneLine(op.Title) {
46 return fmt.Errorf("title has unsafe characters")
47 }
48
49 if !text.SafeOneLine(op.Description) {
50 return fmt.Errorf("description has unsafe characters")
51 }
52
53 if len(op.Columns) <= 0 {
54 return fmt.Errorf("no columns")
55 }
56 for _, column := range op.Columns {
57 if !text.SafeOneLine(column) {
58 return fmt.Errorf("a columns has unsafe characters")
59 }
60 if len(column) > 100 {
61 return fmt.Errorf("a columns is too long")
62 }
63 }
64
65 set := make(map[string]struct{})
66 for _, column := range op.Columns {
67 set[column] = struct{}{}
68 }
69 if len(set) != len(op.Columns) {
70 return fmt.Errorf("non unique column name")
71 }
72
73 return nil
74}
75
76func (op *CreateOperation) Apply(snap *Snapshot) {
77 // sanity check: will fail when adding a second Create
78 if snap.id != "" && snap.id != entity.UnsetId && snap.id != op.Id() {
79 return
80 }
81
82 snap.id = op.Id()
83
84 snap.Title = op.Title
85 snap.Description = op.Description
86 snap.CreateTime = op.Time()
87
88 for _, name := range op.Columns {
89 // we derive a unique Id from the original column name
90 id := entity.DeriveId([]byte(name))
91
92 snap.Columns = append(snap.Columns, &Column{
93 Id: id,
94 Name: name,
95 Items: nil,
96 })
97 }
98
99 snap.addParticipant(op.Author())
100}
101
102// CreateDefaultColumns is a convenience function to create a board with the default columns
103func CreateDefaultColumns(author identity.Interface, unixTime int64, title, description string, metadata map[string]string) (*Board, *CreateOperation, error) {
104 return Create(author, unixTime, title, description, DefaultColumns, metadata)
105}
106
107// Create is a convenience function to create a board
108func Create(author identity.Interface, unixTime int64, title, description string, columns []string, metadata map[string]string) (*Board, *CreateOperation, error) {
109 b := NewBoard()
110 op := NewCreateOp(author, unixTime, title, description, columns)
111 for key, val := range metadata {
112 op.SetMetadata(key, val)
113 }
114 if err := op.Validate(); err != nil {
115 return nil, op, err
116 }
117 b.Append(op)
118 return b, op, nil
119}