-
Notifications
You must be signed in to change notification settings - Fork 2
/
user.go
141 lines (118 loc) · 3.01 KB
/
user.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
package gokit_realworld
import (
"encoding/json"
"errors"
"golang.org/x/crypto/bcrypt"
"strings"
)
var (
ErrResourceNotFound = Error{ENotFound, errors.New("resource not found")}
ErrUserNotFound = Error{ENotFound, errors.New("user not found")}
ErrUserAlreadyExists = Error{EConflict, errors.New("user already exists")}
ErrIncorrectPasswordError = Error{EIncorrectPassword, errors.New("incorrect password")}
)
type Bio struct {
Value string
Valid bool
}
func (b *Bio) UnmarshalJSON(bytes []byte) error {
bio := string(bytes)
bio = strings.Trim(bio, `"`)
if strings.ToLower(bio) == "null" {
b.Valid = false
return nil
}
b.Valid = true
b.Value = bio
return nil
}
func (b Bio) MarshalJSON() ([]byte, error) {
if b.Valid {
return json.Marshal(b.Value)
}
return json.Marshal(nil)
}
type Image struct {
Value string
Valid bool
}
// TODO: check: if we can extract these methods to a generic methods/type and attach these to the type
func (i *Image) UnmarshalJSON(bytes []byte) error {
image := string(bytes)
image = strings.Trim(image, `"`)
if strings.ToLower(image) == "null" {
i.Valid = false
return nil
}
i.Valid = true
i.Value = image
return nil
}
func (i Image) MarshalJSON() ([]byte, error) {
if i.Valid {
return json.Marshal(i.Value)
}
return json.Marshal(nil)
}
type FollowRequest struct {
Followee string
Follower int64
}
type Follows map[int64]struct{}
func (ff Follows) List() (l []int64) {
l = make([]int64, 0)
for f, _ := range ff {
l = append(l, f)
}
return
}
// TODO: where should we put the validation? Should we have separate validation per domain model and transports?
type User struct {
ID int64
Username string
Email string
Password string
Bio Bio
Image Image
Followers Follows
Followings Follows
}
func (u *User) HashPassword(plain string) (string, error) {
if len(plain) == 0 {
return "", errors.New("password should not be empty")
}
h, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
return string(h), err
}
func (u *User) CheckPassword(plain string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(plain))
return err == nil
}
func (u *User) IsFollower(follower *User) bool {
if u.Followers == nil {
return false
}
if _, ok := u.Followers[follower.ID]; ok {
return true
}
return false
}
type UserService interface {
Register(user User) (*User, error)
Login(user User) (*User, error)
Get(user User) (*User, error)
Update(user User) (*User, error)
GetProfile(user User) (*User, error)
Follow(req FollowRequest) (*User, error)
Unfollow(req FollowRequest) (*User, error)
}
type UserRepo interface {
// TODO: should this return user? What if we assume this should only be a **write** command
Create(u User) (*User, error)
Update(u User) (*User, error)
Get(e string) (*User, error)
GetByID(id int64) (*User, error)
GetByUsername(u string) (*User, error)
AddFollower(follower, followee int64) (*User, error)
RemoveFollower(follower, followee int64) (*User, error)
}