V3-664 Extend User repository. Reimplemented with dbx (#591)
* V3-664 Extend User repository. Reimplemented with dbx * structure updated * redundant packages removed, structure simplified * fixing goimports * removed constuctor from user struct * separated types declarations from database file * test file renamed * fixes according to review * fixing goimports * fixing goimports
This commit is contained in:
parent
07ed38c930
commit
54cd9491c6
2
go.mod
2
go.mod
@ -69,7 +69,7 @@ require (
|
||||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 // indirect
|
||||
github.com/rs/cors v1.5.0 // indirect
|
||||
github.com/shirou/gopsutil v2.17.12+incompatible
|
||||
github.com/skyrings/skyring-common v0.0.0-20160929130248-d1c0bb1cbd5e // indirect
|
||||
github.com/skyrings/skyring-common v0.0.0-20160929130248-d1c0bb1cbd5e
|
||||
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/pflag v1.0.3
|
||||
|
15
pkg/satellite/database.go
Normal file
15
pkg/satellite/database.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellite
|
||||
|
||||
// DB contains access to different satellite databases
|
||||
type DB interface {
|
||||
// Users is getter for Users repository
|
||||
Users() Users
|
||||
|
||||
// CreateTables is a method for creating all tables for satellitedb
|
||||
CreateTables() error
|
||||
// Close is used to close db connection
|
||||
Close() error
|
||||
}
|
47
pkg/satellite/satellitedb/db.go
Normal file
47
pkg/satellite/satellitedb/db.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellitedb
|
||||
|
||||
import (
|
||||
"storj.io/storj/pkg/satellite"
|
||||
|
||||
"storj.io/storj/pkg/satellite/satellitedb/dbx"
|
||||
)
|
||||
|
||||
// Database contains access to different satellite databases
|
||||
type Database struct {
|
||||
db *dbx.DB
|
||||
}
|
||||
|
||||
// New - constructor for DB
|
||||
func New(driver, source string) (satellite.DB, error) {
|
||||
db, err := dbx.Open(driver, source)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
database := &Database{
|
||||
db: db,
|
||||
}
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
// Users is getter for Users repository
|
||||
func (db *Database) Users() satellite.Users {
|
||||
return &users{db.db}
|
||||
}
|
||||
|
||||
// CreateTables is a method for creating all tables for satellitedb
|
||||
func (db *Database) CreateTables() error {
|
||||
_, err := db.db.Exec(db.db.Schema())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Close is used to close db connection
|
||||
func (db *Database) Close() error {
|
||||
return db.db.Close()
|
||||
}
|
0
pkg/satellite/satellitedb/db/.keep
Normal file
0
pkg/satellite/satellitedb/db/.keep
Normal file
35
pkg/satellite/satellitedb/dbx/accountdb.dbx
Normal file
35
pkg/satellite/satellitedb/dbx/accountdb.dbx
Normal file
@ -0,0 +1,35 @@
|
||||
// dbx.v1 golang satellitedb.dbx .
|
||||
|
||||
model user (
|
||||
key id
|
||||
unique email
|
||||
|
||||
field id text
|
||||
field first_name text ( updatable )
|
||||
field last_name text ( updatable )
|
||||
field email text ( updatable )
|
||||
field password_hash blob ( updatable )
|
||||
|
||||
field created_at timestamp ( autoinsert )
|
||||
)
|
||||
|
||||
read one (
|
||||
select user
|
||||
where user.email = ?
|
||||
where user.password_hash = ?
|
||||
)
|
||||
read one (
|
||||
select user
|
||||
where user.id = ?
|
||||
)
|
||||
create user ( )
|
||||
update user ( where user.id = ? )
|
||||
delete user ( where user.id = ? )
|
||||
|
||||
//TODO: this entity will be used and updated in the next commit
|
||||
model company (
|
||||
key id
|
||||
|
||||
field id blob
|
||||
field userId user.id cascade ( updatable )
|
||||
)
|
1098
pkg/satellite/satellitedb/dbx/accountdb.dbx.go
Normal file
1098
pkg/satellite/satellitedb/dbx/accountdb.dbx.go
Normal file
File diff suppressed because it is too large
Load Diff
17
pkg/satellite/satellitedb/dbx/accountdb.dbx.sqlite3.sql
Normal file
17
pkg/satellite/satellitedb/dbx/accountdb.dbx.sqlite3.sql
Normal file
@ -0,0 +1,17 @@
|
||||
-- AUTOGENERATED BY gopkg.in/spacemonkeygo/dbx.v1
|
||||
-- DO NOT EDIT
|
||||
CREATE TABLE users (
|
||||
id TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
password_hash BLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
PRIMARY KEY ( id ),
|
||||
UNIQUE ( email )
|
||||
);
|
||||
CREATE TABLE companies (
|
||||
id BLOB NOT NULL,
|
||||
userId TEXT NOT NULL REFERENCES users( id ) ON DELETE CASCADE,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
7
pkg/satellite/satellitedb/dbx/gen.go
Normal file
7
pkg/satellite/satellitedb/dbx/gen.go
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package dbx
|
||||
|
||||
//go:generate dbx.v1 golang -d sqlite3 satellitedb.dbx .
|
||||
//go:generate dbx.v1 schema -d sqlite3 satellitedb.dbx .
|
107
pkg/satellite/satellitedb/users.go
Normal file
107
pkg/satellite/satellitedb/users.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellitedb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/pkg/satellite"
|
||||
|
||||
"github.com/skyrings/skyring-common/tools/uuid"
|
||||
"storj.io/storj/pkg/satellite/satellitedb/dbx"
|
||||
)
|
||||
|
||||
// implementation of User interface repository using spacemonkeygo/dbx orm
|
||||
type users struct {
|
||||
db *dbx.DB
|
||||
}
|
||||
|
||||
// Get is a method for querying user from the database by id
|
||||
func (users *users) Get(ctx context.Context, id uuid.UUID) (*satellite.User, error) {
|
||||
|
||||
userID := dbx.User_Id(id.String())
|
||||
|
||||
user, err := users.db.Get_User_By_Id(ctx, userID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return userFromDBX(user)
|
||||
}
|
||||
|
||||
// GetByCredentials is a method for querying user by credentials from the database.
|
||||
func (users *users) GetByCredentials(ctx context.Context, password []byte, email string) (*satellite.User, error) {
|
||||
|
||||
userEmail := dbx.User_Email(email)
|
||||
userPassword := dbx.User_PasswordHash(password)
|
||||
|
||||
user, err := users.db.Get_User_By_Email_And_PasswordHash(ctx, userEmail, userPassword)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return userFromDBX(user)
|
||||
}
|
||||
|
||||
// Insert is a method for inserting user into the database
|
||||
func (users *users) Insert(ctx context.Context, user *satellite.User) error {
|
||||
_, err := users.db.Create_User(ctx,
|
||||
dbx.User_Id(user.ID.String()),
|
||||
dbx.User_FirstName(user.FirstName),
|
||||
dbx.User_LastName(user.LastName),
|
||||
dbx.User_Email(user.Email),
|
||||
dbx.User_PasswordHash(user.PasswordHash))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete is a method for deleting user by Id from the database.
|
||||
func (users *users) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := users.db.Delete_User_By_Id(ctx, dbx.User_Id(id.String()))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update is a method for updating user entity
|
||||
func (users *users) Update(ctx context.Context, user *satellite.User) error {
|
||||
_, err := users.db.Update_User_By_Id(ctx,
|
||||
dbx.User_Id(user.ID.String()),
|
||||
dbx.User_Update_Fields{
|
||||
FirstName: dbx.User_FirstName(user.FirstName),
|
||||
LastName: dbx.User_LastName(user.LastName),
|
||||
Email: dbx.User_Email(user.Email),
|
||||
PasswordHash: dbx.User_PasswordHash(user.PasswordHash),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// userFromDBX is used for creating User entity from autogenerated dbx.User struct
|
||||
// TODO: move error strings to better place
|
||||
func userFromDBX(user *dbx.User) (*satellite.User, error) {
|
||||
if user == nil {
|
||||
return nil, errs.New("user parameter is nil")
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(user.Id)
|
||||
|
||||
if err != nil {
|
||||
return nil, errs.New("Id in not valid UUID string")
|
||||
}
|
||||
|
||||
u := &satellite.User{}
|
||||
|
||||
u.ID = *id
|
||||
u.FirstName = user.FirstName
|
||||
u.LastName = user.LastName
|
||||
u.Email = user.Email
|
||||
u.PasswordHash = user.PasswordHash
|
||||
u.CreatedAt = user.CreatedAt
|
||||
|
||||
return u, nil
|
||||
}
|
199
pkg/satellite/satellitedb/users_test.go
Normal file
199
pkg/satellite/satellitedb/users_test.go
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellitedb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/pkg/satellite/satellitedb/dbx"
|
||||
|
||||
"storj.io/storj/pkg/satellite"
|
||||
|
||||
"github.com/skyrings/skyring-common/tools/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepository(t *testing.T) {
|
||||
|
||||
//testing constants
|
||||
const (
|
||||
lastName = "lastName"
|
||||
email = "email@ukr.net"
|
||||
passValid = "123456"
|
||||
name = "name"
|
||||
newName = "newName"
|
||||
newLastName = "newLastName"
|
||||
newEmail = "newEmail@ukr.net"
|
||||
newPass = "newPass"
|
||||
)
|
||||
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
// to test with real db3 file use this connection string - "../db/accountdb.db3"
|
||||
db, err := New("sqlite3", "file::memory:?mode=memory&cache=shared")
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
defer ctx.Check(db.Close)
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
repository := db.Users()
|
||||
|
||||
t.Run("User insertion success", func(t *testing.T) {
|
||||
|
||||
id, err := uuid.New()
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
user := &satellite.User{
|
||||
ID: *id,
|
||||
FirstName: name,
|
||||
LastName: lastName,
|
||||
Email: email,
|
||||
PasswordHash: []byte(passValid),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = repository.Insert(ctx, user)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Can't insert user with same email twice", func(t *testing.T) {
|
||||
|
||||
id, err := uuid.New()
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
user := &satellite.User{
|
||||
ID: *id,
|
||||
FirstName: name,
|
||||
LastName: lastName,
|
||||
Email: email,
|
||||
PasswordHash: []byte(passValid),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = repository.Insert(ctx, user)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Get user success", func(t *testing.T) {
|
||||
userByCreds, err := repository.GetByCredentials(ctx, []byte(passValid), email)
|
||||
|
||||
assert.Equal(t, userByCreds.FirstName, name)
|
||||
assert.Equal(t, userByCreds.LastName, lastName)
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
userByID, err := repository.GetByCredentials(ctx, []byte(passValid), email)
|
||||
|
||||
assert.Equal(t, userByID.FirstName, name)
|
||||
assert.Equal(t, userByID.LastName, lastName)
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, userByID.ID, userByCreds.ID)
|
||||
assert.Equal(t, userByID.FirstName, userByCreds.FirstName)
|
||||
assert.Equal(t, userByID.LastName, userByCreds.LastName)
|
||||
assert.Equal(t, userByID.Email, userByCreds.Email)
|
||||
assert.Equal(t, userByID.PasswordHash, userByCreds.PasswordHash)
|
||||
assert.Equal(t, userByID.CreatedAt, userByCreds.CreatedAt)
|
||||
})
|
||||
|
||||
t.Run("Update user success", func(t *testing.T) {
|
||||
oldUser, err := repository.GetByCredentials(ctx, []byte(passValid), email)
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
newUser := &satellite.User{
|
||||
ID: oldUser.ID,
|
||||
FirstName: newName,
|
||||
LastName: newLastName,
|
||||
Email: newEmail,
|
||||
PasswordHash: []byte(newPass),
|
||||
CreatedAt: oldUser.CreatedAt,
|
||||
}
|
||||
|
||||
err = repository.Update(ctx, newUser)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
newUser, err = repository.Get(ctx, oldUser.ID)
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, newUser.ID, oldUser.ID)
|
||||
assert.Equal(t, newUser.FirstName, newName)
|
||||
assert.Equal(t, newUser.LastName, newLastName)
|
||||
assert.Equal(t, newUser.Email, newEmail)
|
||||
assert.Equal(t, newUser.PasswordHash, []byte(newPass))
|
||||
assert.Equal(t, newUser.CreatedAt, oldUser.CreatedAt)
|
||||
})
|
||||
|
||||
t.Run("Delete user success", func(t *testing.T) {
|
||||
oldUser, err := repository.GetByCredentials(ctx, []byte(newPass), newEmail)
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
err = repository.Delete(ctx, oldUser.ID)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = repository.Get(ctx, oldUser.ID)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserDboFromDbx(t *testing.T) {
|
||||
|
||||
t.Run("can't create dbo from nil dbx model", func(t *testing.T) {
|
||||
user, err := userFromDBX(nil)
|
||||
|
||||
assert.Nil(t, user)
|
||||
assert.NotNil(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("can't create dbo from dbx model with invalid Id", func(t *testing.T) {
|
||||
dbxUser := dbx.User{
|
||||
Id: "qweqwe",
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
Email: "email@ukr.net",
|
||||
PasswordHash: []byte("ihqerfgnu238723huagsd"),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
user, err := userFromDBX(&dbxUser)
|
||||
|
||||
assert.Nil(t, user)
|
||||
assert.NotNil(t, err)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
37
pkg/satellite/users.go
Normal file
37
pkg/satellite/users.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/skyrings/skyring-common/tools/uuid"
|
||||
)
|
||||
|
||||
// Users exposes methods to manage User table in database.
|
||||
type Users interface {
|
||||
// GetByCredentials is a method for querying user by credentials from the database.
|
||||
GetByCredentials(ctx context.Context, password []byte, email string) (*User, error)
|
||||
// Get is a method for querying user from the database by id
|
||||
Get(ctx context.Context, id uuid.UUID) (*User, error)
|
||||
// Insert is a method for inserting user into the database
|
||||
Insert(ctx context.Context, user *User) error
|
||||
// Delete is a method for deleting user by Id from the database.
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
// Update is a method for updating user entity
|
||||
Update(ctx context.Context, user *User) error
|
||||
}
|
||||
|
||||
// User is a database object that describes User entity
|
||||
type User struct {
|
||||
ID uuid.UUID
|
||||
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
PasswordHash []byte
|
||||
|
||||
CreatedAt time.Time
|
||||
}
|
Loading…
Reference in New Issue
Block a user