add in-memory DB for accounts, with tests!
This commit is contained in:
parent
49e14b5bc5
commit
5a540184c9
9 changed files with 534 additions and 45 deletions
|
|
@ -3,12 +3,15 @@ package account
|
|||
import "arimelody-web/model"
|
||||
|
||||
type AccountRepository interface {
|
||||
GetAll() ([]model.Account, error)
|
||||
GetAll() ([]*model.Account, error)
|
||||
GetCount() (int, error)
|
||||
GetByID(id string) (*model.Account, error)
|
||||
GetByUsername(username string) (*model.Account, error)
|
||||
GetByEmail(email string) (*model.Account, error)
|
||||
GetBySession(sessionToken string) (*model.Account, error)
|
||||
|
||||
// Pulled this function: Cross-cutting concerns between accounts and sessions.
|
||||
// Instead, fetch account ID from session and use GetByID()
|
||||
// GetBySession(sessionToken string) (*model.Account, error)
|
||||
|
||||
// Create an account, returning the new account ID.
|
||||
Create(username string, password string, email *string, avatarURL *string) (string, error)
|
||||
|
|
@ -23,11 +26,12 @@ type AccountRepository interface {
|
|||
ChangeAvatarURL(id string, avatarURL string) error
|
||||
RemoveAvatar(id string) error
|
||||
|
||||
Delete(accountID string) error
|
||||
Delete(id string) error
|
||||
|
||||
// Increment the number of account login failure attempts,
|
||||
// returning the current fail count.
|
||||
IncrementFails(accountID string) (int, error)
|
||||
Lock(accountID string) error
|
||||
Unlock(accountID string) error
|
||||
IncrementFails(id string) (int, error)
|
||||
ResetFails(id string) error
|
||||
Lock(id string) error
|
||||
Unlock(id string) error
|
||||
}
|
||||
|
|
|
|||
185
repository/account/memory.go
Normal file
185
repository/account/memory.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"arimelody-web/model"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
AccountRepositoryMemory struct {
|
||||
accounts []*model.Account
|
||||
}
|
||||
)
|
||||
|
||||
var _ AccountRepository = new(AccountRepositoryMemory)
|
||||
|
||||
func NewAccountRepositoryMemory() *AccountRepositoryMemory {
|
||||
return &AccountRepositoryMemory{ accounts: make([]*model.Account, 0) }
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryMemory) GetAll() ([]*model.Account, error) {
|
||||
return repo.accounts, nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) GetCount() (int, error) {
|
||||
return len(repo.accounts), nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) GetByID(id string) (*model.Account, error) {
|
||||
for _, account := range repo.accounts {
|
||||
if account.ID == id { return account, nil }
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) GetByUsername(username string) (*model.Account, error) {
|
||||
for _, account := range repo.accounts {
|
||||
if account.Username == username { return account, nil }
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) GetByEmail(email string) (*model.Account, error) {
|
||||
for _, account := range repo.accounts {
|
||||
if account.Email.Valid && account.Email.String == email {
|
||||
return account, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create an account, returning the new account ID.
|
||||
func (repo *AccountRepositoryMemory) Create(username string, password string, email *string, avatarURL *string) (string, error) {
|
||||
if account, err := repo.GetByUsername(username); err != nil {
|
||||
return "", errors.New("Failed to fetch other acccounts by username")
|
||||
} else if account != nil {
|
||||
return "", errors.New("Account with this username already exists")
|
||||
}
|
||||
|
||||
emailRef := ""
|
||||
if email != nil { emailRef = *email }
|
||||
avatarURLRef := ""
|
||||
if avatarURL != nil { avatarURLRef = *avatarURL }
|
||||
|
||||
id := strconv.Itoa(len(repo.accounts))
|
||||
|
||||
repo.accounts = append(repo.accounts, &model.Account{
|
||||
ID: id,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: sql.NullString{ String: emailRef, Valid: email != nil },
|
||||
AvatarURL: sql.NullString{ String: avatarURLRef, Valid: avatarURL != nil },
|
||||
})
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Intended for large profile updates. For smaller adjusments,
|
||||
// more specialised Change* and Remove* functions should be used.
|
||||
func (repo *AccountRepositoryMemory) Update(id string, username string, password string, email *string, avatarUrl *string) error {
|
||||
if account, err := repo.GetByUsername(username); err != nil {
|
||||
return errors.New("Failed to fetch other acccounts by username")
|
||||
} else if account != nil && account.ID != id {
|
||||
return errors.New("Account with this username already exists")
|
||||
}
|
||||
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
|
||||
account.Username = username
|
||||
account.Password = password
|
||||
account.Email.Valid = email != nil
|
||||
if account.Email.Valid { account.Email.String = *email }
|
||||
account.AvatarURL.Valid = avatarUrl != nil
|
||||
if account.AvatarURL.Valid { account.AvatarURL.String = *avatarUrl }
|
||||
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) ChangeUsername(id string, username string) error {
|
||||
if account, err := repo.GetByUsername(username); err != nil {
|
||||
return errors.New("Failed to fetch other acccounts by username")
|
||||
} else if account != nil && account.ID != id {
|
||||
return errors.New("Account with this username already exists")
|
||||
}
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Username = username
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) ChangePassword(id string, password string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Password = password
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) ChangeEmail(id string, email string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Email.Valid = true
|
||||
account.Email.String = email
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) RemoveEmail(id string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Email.Valid = false
|
||||
account.Email.String = ""
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) ChangeAvatarURL(id string, avatarURL string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.AvatarURL.Valid = true
|
||||
account.AvatarURL.String = avatarURL
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) RemoveAvatar(id string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.AvatarURL.Valid = false
|
||||
account.AvatarURL.String = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryMemory) Delete(id string) error {
|
||||
accountIndex := -1
|
||||
for index, account := range repo.accounts {
|
||||
if account.ID == id {
|
||||
accountIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
if accountIndex == -1 { return nil }
|
||||
|
||||
repo.accounts = append(
|
||||
repo.accounts[:accountIndex],
|
||||
repo.accounts[accountIndex+1:]...,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Increment the number of account login failure attempts,
|
||||
// returning the current fail count.
|
||||
func (repo *AccountRepositoryMemory) IncrementFails(id string) (int, error) {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return 0, err }
|
||||
account.FailAttempts += 1
|
||||
return account.FailAttempts, nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) ResetFails(id string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.FailAttempts = 0
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) Lock(id string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Locked = true
|
||||
return nil
|
||||
}
|
||||
func (repo *AccountRepositoryMemory) Unlock(id string) error {
|
||||
account, err := repo.GetByID(id)
|
||||
if err != nil { return err }
|
||||
account.Locked = false
|
||||
return nil
|
||||
}
|
||||
|
|
@ -20,8 +20,8 @@ func NewAccountRepositoryPostgres(db *sqlx.DB) *AccountRepositoryPostgres {
|
|||
return &AccountRepositoryPostgres{ db: db }
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) GetAll() ([]model.Account, error) {
|
||||
var accounts = []model.Account{}
|
||||
func (repo *AccountRepositoryPostgres) GetAll() ([]*model.Account, error) {
|
||||
var accounts = []*model.Account{}
|
||||
|
||||
err := repo.db.Select(&accounts, "SELECT * FROM account ORDER BY created_at ASC")
|
||||
if err != nil {
|
||||
|
|
@ -79,22 +79,6 @@ func (repo *AccountRepositoryPostgres) GetByEmail(email string) (*model.Account,
|
|||
return &account, nil
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) GetBySession(sessionToken string) (*model.Account, error) {
|
||||
if sessionToken == "" { return nil, nil }
|
||||
|
||||
account := model.Account{}
|
||||
|
||||
err := repo.db.Get(&account, "SELECT account.* FROM account JOIN token ON id=account WHERE token=$1", sessionToken)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) Create(
|
||||
username string,
|
||||
password string,
|
||||
|
|
@ -175,25 +159,30 @@ func (repo *AccountRepositoryPostgres) RemoveAvatar(id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) Delete(accountID string) error {
|
||||
_, err := repo.db.Exec("DELETE FROM account WHERE id=$1", accountID)
|
||||
func (repo *AccountRepositoryPostgres) Delete(id string) error {
|
||||
_, err := repo.db.Exec("DELETE FROM account WHERE id=$1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
// Increment the number of account login failure attempts,
|
||||
// returning the current fail count.
|
||||
func (repo *AccountRepositoryPostgres) IncrementFails(accountID string) (int, error) {
|
||||
func (repo *AccountRepositoryPostgres) IncrementFails(id string) (int, error) {
|
||||
failAttempts := 0
|
||||
err := repo.db.Get(&failAttempts, "UPDATE account SET fail_attempts = fail_attempts + 1 WHERE id=$1 RETURNING fail_attempts", accountID)
|
||||
err := repo.db.Get(&failAttempts, "UPDATE account SET fail_attempts = fail_attempts + 1 WHERE id=$1 RETURNING fail_attempts", id)
|
||||
return failAttempts, err
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) Lock(accountID string) error {
|
||||
_, err := repo.db.Exec("UPDATE account SET locked = true WHERE id=$1", accountID)
|
||||
func (repo *AccountRepositoryPostgres) ResetFails(id string) error {
|
||||
_, err := repo.db.Exec("UPDATE account SET fail_attempts = 0 WHERE id=$1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) Unlock(accountID string) error {
|
||||
_, err := repo.db.Exec("UPDATE account SET locked = false, fail_attempts = 0 WHERE id=$1", accountID)
|
||||
func (repo *AccountRepositoryPostgres) Lock(id string) error {
|
||||
_, err := repo.db.Exec("UPDATE account SET locked = true WHERE id=$1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *AccountRepositoryPostgres) Unlock(id string) error {
|
||||
_, err := repo.db.Exec("UPDATE account SET locked = false, fail_attempts = 0 WHERE id=$1", id)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue