/
account.go
195 lines (175 loc) · 6.41 KB
/
account.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
191
192
193
194
195
package db
import (
"errors"
"fmt"
"database/sql"
"github.com/TetAlius/GoSyncMyCalendars/customErrors"
log "github.com/TetAlius/GoSyncMyCalendars/logger"
"github.com/google/uuid"
"github.com/lib/pq"
)
// Account mapped from db
type Account struct {
// User that account belongs to
User *User
// TokenType of the account
TokenType string
// Refresh token of the account
RefreshToken string
// Email associated to the account
Email string
// Kind of the account
Kind int
// AccessToken of the account
AccessToken string
// InternalID of the account
ID int
// Whether if the account is the principal one
Principal bool
// List of all calendars associated to the account on DB
Calendars []Calendar
}
// Gets accounts given a user UUID
func (data Database) getAccountsByUser(userUUID uuid.UUID) (principalAccount Account, accounts []Account, err error) {
rows, err := data.client.Query("SELECT accounts.token_type, accounts.refresh_token,accounts.email,accounts.kind,accounts.access_token,accounts.id, accounts.principal FROM accounts where user_uuid = $1 order by accounts.principal DESC, lower(accounts.email) ASC", userUUID)
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("could not query select: %s", err.Error())
return
}
defer rows.Close()
for rows.Next() {
var email string
var kind int
var id int
var tokenType string
var refreshToken string
var accessToken string
var principal bool
var account Account
err = rows.Scan(&tokenType, &refreshToken, &email, &kind, &accessToken, &id, &principal)
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error retrieving accounts for user: %s", userUUID)
return account, nil, err
}
account = Account{
TokenType: tokenType,
RefreshToken: refreshToken,
Email: email,
AccessToken: accessToken,
Kind: kind,
ID: id,
Principal: principal,
}
err = data.findCalendars(&account)
if err != nil {
log.Errorf("error adding calendars: %s", account.ID)
return
}
if principal {
principalAccount = account
} else {
accounts = append(accounts, account)
}
}
return
}
// Gets calendars from an account
func (data Database) FindCalendars(account *Account) (err error) {
return data.findCalendars(account)
}
// Saves an account to the db
func (data Database) save(account Account) (id int, err error) {
err = data.client.QueryRow("insert into accounts(user_uuid,token_type,refresh_token,email,kind,access_token, principal) values ($1,$2,$3,$4,$5,$6,$7) RETURNING id",
account.User.UUID, account.TokenType, account.RefreshToken, account.Email, account.Kind, account.AccessToken, account.Principal).Scan(&id)
if pgerr, ok := err.(*pq.Error); ok && pgerr.Code == uniqueViolationError {
log.Warningf("account already used: %s", account.Email)
return 0, &customErrors.AccountAlreadyUsed{Mail: account.Email}
}
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error executing query: %s", err.Error())
return
}
return
}
// Finds an Account from a db
func (data Database) findAccount(account *Account) (err error) {
var email string
var kind int
var id int
var principal bool
err = data.client.QueryRow("SELECT accounts.email,accounts.kind,accounts.id, accounts.principal FROM accounts where user_uuid = $1 and id = $2", account.User.UUID, account.ID).Scan(&email, &kind, &id, &principal)
switch {
case err == sql.ErrNoRows:
log.Debugf("No account with that id: %d.", id)
return &customErrors.NotFoundError{Message: fmt.Sprintf("No account with that id: %d.", id)}
case err != nil:
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error looking for account with id: %d", id)
return
}
account.Email = email
account.Kind = kind
account.ID = id
account.Principal = principal
return
}
// Stores a calendar from an account on db
func (data Database) addCalendar(account Account, calendar Calendar) (err error) {
stmt, err := data.client.Prepare("insert into calendars(uuid, account_email, name, id) values ($1,$2,$3,$4);")
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error preparing query: %s", err.Error())
return
}
defer stmt.Close()
calendar.UUID = uuid.New()
res, err := stmt.Exec(calendar.UUID, account.Email, calendar.Name, calendar.ID)
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error executing query: %s", err.Error())
return
}
affect, err := res.RowsAffected()
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error retrieving rows affected: %s", err.Error())
return
}
if affect != 1 {
data.sentry.CaptureErrorAndWait(errors.New(fmt.Sprintf("could not create new calendar with id: %s and name: %s", calendar.ID, calendar.Name)), map[string]string{"database": "frontend"})
return errors.New(fmt.Sprintf("could not create new calendar with id: %s and name: %s", calendar.ID, calendar.Name))
}
return
}
// Updates an account from a given user
func (data Database) updateAccountFromUser(account Account, user *User) (id int, err error) {
stmt, err := data.client.Prepare("update accounts set (token_type,refresh_token,access_token) = ($1,$2,$3) where accounts.email = $4 and accounts.user_uuid =$5;")
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error preparing query: %s", err.Error())
return
}
defer stmt.Close()
res, err := stmt.Exec(account.TokenType, account.RefreshToken, account.AccessToken, account.Email, user.UUID)
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error executing query: %s", err.Error())
return
}
affect, err := res.RowsAffected()
if err != nil {
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
log.Errorf("error retrieving rows affected: %s", err.Error())
return
}
if affect != 1 {
err = fmt.Errorf("could not associate the account with mail: %s", account.Email)
data.sentry.CaptureErrorAndWait(err, map[string]string{"database": "frontend"})
return
}
data.client.QueryRow("select id from accounts where accounts.email = $1 and accounts.user_uuid =$2").Scan(&id)
return
}