/
todos.go
190 lines (170 loc) · 3.9 KB
/
todos.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package main
import "time"
type TodoListBase struct {
ID TodoListID `db:"todo_list_id"`
Name string `db:"name"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type TodoList struct {
TodoListBase
Todos Todos
}
var todoListCols = TableColumns{"todo_list_id", "name", "created_at", "updated_at"}
func GetAllTodoLists(tx *Tx) ([]TodoListBase, error) {
var tls []TodoListBase
err := tx.Select(&tls, `
SELECT `+todoListCols.OnAlias("tl").String()+`
FROM todo_lists tl
ORDER BY name`, QueryArgs{})
if err != nil {
return nil, err
}
return tls, nil
}
func GetTodoListByID(tx *Tx, tlid TodoListID) (*TodoList, error) {
var tl TodoList
err := tx.Get(&tl, `
SELECT `+todoListCols.OnAlias("tl").String()+`
FROM todo_lists tl
WHERE todo_list_id = :tlid`,
QueryArgs{"tlid": tlid})
if err != nil {
return nil, err
}
err = tl.attachTodos(tx)
if err != nil {
return nil, err
}
return &tl, err
}
func NewTodoList(tx *Tx, name string) (*TodoList, error) {
var tlid TodoListID
err := tx.Get(&tlid, `
INSERT INTO todo_lists (name)
VALUES (:name)
RETURNING todo_list_id`, QueryArgs{
"name": name,
})
if err != nil {
return nil, err
}
return GetTodoListByID(tx, tlid)
}
func UpdateTodoList(tx *Tx, tl TodoList) (*TodoList, error) {
err := tx.UpdateOne(`
UPDATE todo_lists
SET name = :name
, updated_at = NOW()
WHERE todo_list_id = :id`, QueryArgs{
"id": tl.ID,
"name": tl.Name,
})
if err != nil {
return nil, err
}
return GetTodoListByID(tx, tl.ID)
}
func DeleteTodoList(tx *Tx, tlid TodoListID) error {
err := tx.DeleteOne(`
DELETE FROM todo_lists
WHERE todo_list_id = :id`, QueryArgs{
"id": tlid,
})
return err
}
type Todo struct {
ID TodoID `db:"todo_id"`
ListID TodoListID `db:"todo_list_id"`
Description string `db:"description"`
CreatedAt time.Time `db:"created_at"`
Completed bool `db:"completed"`
}
type Todos []Todo
func (ts Todos) FilterByCompleted(completed bool) Todos {
var res Todos
for _, todo := range ts {
if todo.Completed == completed {
res = append(res, todo)
}
}
return res
}
var todoCols = TableColumns{"todo_id", "todo_list_id", "description", "created_at", "completed"}
func (tl *TodoList) attachTodos(tx *Tx) error {
err := tx.Select(&tl.Todos, `
SELECT `+todoCols.OnAlias("t").String()+`
FROM todos t
WHERE t.todo_list_id = :tlid
ORDER BY t.description ASC`, QueryArgs{
"tlid": tl.ID,
})
return err
}
func GetTodoByID(tx *Tx, tid TodoID) (*Todo, error) {
var todo Todo
err := tx.Get(&todo, `
SELECT `+todoCols.OnAlias("t").String()+`
FROM todos t
WHERE todo_id = :tid`, QueryArgs{
"tid": tid,
})
if err != nil {
return nil, err
}
return &todo, nil
}
func NewTodo(tx *Tx, tlid TodoListID, description string) error {
var tid TodoID
err := tx.Get(&tid, `
INSERT INTO todos (todo_list_id, description)
VALUES (:list_id, :description)
RETURNING todo_id`,
QueryArgs{
"list_id": tlid,
"description": description,
})
if err != nil {
return err
}
return touchList(tx, tid)
}
func SetTodoCompleted(tx *Tx, tid TodoID, completed bool) error {
err := tx.UpdateOne(`
UPDATE todos
SET completed = :completed
WHERE todo_id = :id`, QueryArgs{
"id": tid,
"completed": completed,
})
if err != nil {
return err
}
return touchList(tx, tid)
}
func DeleteTodo(tx *Tx, tid TodoID) error {
err := touchList(tx, tid)
if err != nil {
return err
}
err = tx.DeleteOne(`
DELETE FROM todos
WHERE todo_id = :id`, QueryArgs{
"id": tid,
})
return err
}
// touchList bumps the updated_at field on the list this todo is in, forcing a
// new version of the todo list to be stored in the history table.
func touchList(tx *Tx, tid TodoID) error {
err := tx.UpdateOne(`
UPDATE todo_lists
SET updated_at = NOW()
WHERE todo_list_id = (SELECT todo_list_id
FROM todos
WHERE todo_id = :tid)`,
QueryArgs{
"tid": tid,
})
return err
}