-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
store.go
171 lines (145 loc) · 3.82 KB
/
store.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
package course
import (
"context"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/jatolentino/tutorialspoint/database"
)
// Create inserts a new course.
func Create(ctx context.Context, db sqlx.ExtContext, course Course) error {
const q = `
INSERT INTO courses
(course_id, name, description, price, image_url, created_at, updated_at)
VALUES
(:course_id, :name, :description, :price, :image_url, :created_at, :updated_at)`
if err := database.NamedExecContext(ctx, db, q, course); err != nil {
return fmt.Errorf("inserting course: %w", err)
}
return nil
}
// Update updates the details of a specific course.
// It relies on optimistic lock to deal with data races.
func Update(ctx context.Context, db sqlx.ExtContext, course Course) (Course, error) {
const q = `
UPDATE courses
SET
name = :name,
description = :description,
price = :price,
image_url = :image_url,
updated_at = :updated_at,
version = version + 1
WHERE
course_id = :course_id AND
version = :version
RETURNING version`
v := struct {
Version int `db:"version"`
}{}
if err := database.NamedQueryStruct(ctx, db, q, course, &v); err != nil {
if errors.Is(err, database.ErrDBNotFound) {
return Course{}, fmt.Errorf("updating course[%s]: version conflict", course.ID)
}
return Course{}, fmt.Errorf("updating course[%s]: %w", course.ID, err)
}
course.Version = v.Version
return course, nil
}
// Fetch returns information of a specific course.
func Fetch(ctx context.Context, db sqlx.ExtContext, id string) (Course, error) {
in := struct {
ID string `db:"course_id"`
}{
ID: id,
}
const q = `
SELECT
*
FROM
courses
WHERE
course_id = :course_id`
var course Course
if err := database.NamedQueryStruct(ctx, db, q, in, &course); err != nil {
return Course{}, fmt.Errorf("selecting course[%s]: %w", id, err)
}
return course, nil
}
// FetchAll returns all courses.
func FetchAll(ctx context.Context, db sqlx.ExtContext) ([]Course, error) {
const q = `
SELECT
*
FROM
courses
ORDER BY
course_id`
cs := []Course{}
if err := database.NamedQuerySlice(ctx, db, q, struct{}{}, &cs); err != nil {
return nil, fmt.Errorf("selecting all courses: %w", err)
}
return cs, nil
}
// FetchByOwner returns all the courses owned by the passed user.
func FetchByOwner(ctx context.Context, db sqlx.ExtContext, userID string) ([]Course, error) {
in := struct {
ID string `db:"user_id"`
Status string `db:"status"`
}{
ID: userID,
// TODO: Use a const instead of this magic value.
// This seems a good reason to move all the handlers in the api package.
// Or just create a models package with all the struct and const.
Status: "success",
}
const q = `
SELECT
c.*
FROM
orders AS o
INNER JOIN
order_items AS i ON i.order_id = o.order_id
INNER JOIN
courses AS c ON i.course_id = c.course_id
WHERE
o.status = :status AND
o.user_id = :user_id
ORDER BY
c.course_id`
cs := []Course{}
if err := database.NamedQuerySlice(ctx, db, q, in, &cs); err != nil {
return nil, fmt.Errorf("selecting all courses: %w", err)
}
return cs, nil
}
// FetchOwned returns the specified course if the passed user owns it.
func FetchOwned(ctx context.Context, db sqlx.ExtContext, courseID string, userID string) (Course, error) {
in := struct {
UserID string `db:"user_id"`
CourseID string `db:"course_id"`
Status string `db:"status"`
}{
UserID: userID,
CourseID: courseID,
Status: "success",
}
const q = `
SELECT
c.*
FROM
orders AS o
INNER JOIN
order_items AS i ON i.order_id = o.order_id
INNER JOIN
courses AS c ON i.course_id = c.course_id
WHERE
o.status = :status AND
o.user_id = :user_id AND
c.course_id = :course_id`
var cs Course
if err := database.NamedQueryStruct(ctx, db, q, in, &cs); err != nil {
return Course{}, fmt.Errorf("selecting owned course: %w", err)
}
return cs, nil
}