/
postgres_user_repository.go
115 lines (92 loc) · 3.19 KB
/
postgres_user_repository.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
package repository
import (
"context"
"github.com/dolong2110/memorization-apps/account/model"
"github.com/dolong2110/memorization-apps/account/model/apperrors"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"log"
)
// pGUserRepository is data/repository implementation
// of service layer UserRepository
type pGUserRepository struct {
DB *sqlx.DB
}
// NewUserRepository is a factory for initializing User Repositories
func NewUserRepository(db *sqlx.DB) model.UserRepository {
return &pGUserRepository{
DB: db,
}
}
// Create reaches out to database SQLX api
func (r *pGUserRepository) Create(ctx context.Context, user *model.User) error {
query := "INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *"
if err := r.DB.GetContext(ctx, user, query, user.Email, user.Password); err != nil {
// check unique constraint
if err, ok := err.(*pq.Error); ok && err.Code.Name() == "unique_violation" {
log.Printf("Could not create a user with email: %v. Reason: %v\n", user.Email, err.Code.Name())
return apperrors.NewConflict("email", user.Email)
}
log.Printf("Could not create a user with email: %v. Reason: %v\n", user.Email, err)
return apperrors.NewInternal()
}
return nil
}
// FindByEmail retrieves user row by email address
func (r *pGUserRepository) FindByEmail(ctx context.Context, email string) (*model.User, error) {
user := &model.User{}
query := "SELECT * FROM users WHERE email=$1"
if err := r.DB.GetContext(ctx, user, query, email); err != nil {
log.Printf("Unable to get user with email address: %v. Err: %v\n", email, err)
return user, apperrors.NewNotFound("email", email)
}
return user, nil
}
// FindByID fetches user by id
func (r *pGUserRepository) FindByID(ctx context.Context, uid uuid.UUID) (*model.User, error) {
user := &model.User{}
query := "SELECT * FROM users WHERE uid=$1"
// we need to actually check errors as it could be something other than not found
if err := r.DB.GetContext(ctx, user, query, uid); err != nil {
return user, apperrors.NewNotFound("uid", uid.String())
}
return user, nil
}
// Update updates a user's properties
func (r *pGUserRepository) Update(ctx context.Context, user *model.User) error {
query := `
UPDATE users
SET name=:name, email=:email, website=:website
WHERE uid=:uid
RETURNING *;
`
nstmt, err := r.DB.PrepareNamedContext(ctx, query)
if err != nil {
log.Printf("Unable to prepare user update query: %v\n", err)
return apperrors.NewInternal()
}
if err := nstmt.GetContext(ctx, user, user); err != nil {
log.Printf("Failed to update details for user: %v\n", user)
return apperrors.NewInternal()
}
return nil
}
// UpdateImage is used to separately update a user's image separate from
// other account details
func (r *pGUserRepository) UpdateImage(ctx context.Context, uid uuid.UUID, imageURL string) (*model.User, error) {
query := `
UPDATE users
SET image_url=$2
WHERE uid=$1
RETURNING *;
`
// must be instantiated to scan into ref using `GetContext`
user := &model.User{}
err := r.DB.GetContext(ctx, user, query, uid, imageURL)
if err != nil {
log.Printf("Error updating image_url in database: %v\n", err)
return nil, apperrors.NewInternal()
}
return user, nil
}