Extend PostgreSQL implementation and unit tests on MongoDB storage
This commit is contained in:
@@ -9,17 +9,17 @@ type Account struct {
|
||||
}
|
||||
|
||||
type AccountAuth struct {
|
||||
Local LocalAuth
|
||||
Local *LocalAuth `bson:"local,omitempty"`
|
||||
//TODO handle SSO
|
||||
}
|
||||
|
||||
type LocalAuth struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
EmailValidation Validation `json:"email_validation" bson:"email_validation"`
|
||||
PhoneNumber string `json:"phone_number" bson:"phone_number"`
|
||||
PhoneNumberValidation Validation `json:"phone_number_validation" bson:"phone_number_validation"`
|
||||
Username *string `json:"username" bson:"username,omitempty"`
|
||||
Password string `json:"password" bson:"password"`
|
||||
Email *string `json:"email" bson:"email,omitempty"`
|
||||
EmailValidation *Validation `json:"email_validation" bson:"email_validation,omitempty"`
|
||||
PhoneNumber *string `json:"phone_number" bson:"phone_number,omitempty"`
|
||||
PhoneNumberValidation *Validation `json:"phone_number_validation" bson:"phone_number_validation,omitempty"`
|
||||
}
|
||||
|
||||
type Validation struct {
|
||||
|
||||
@@ -65,20 +65,20 @@ func NewMongoDBStorage(cfg *viper.Viper) (MongoDBStorage, error) {
|
||||
// If username, is provided (not an empty string), it will search by username only
|
||||
// If username is an empty string and email is provided, it will search by email
|
||||
// If both username and email are empty strings, phone_number must be provided and it will search by phone number
|
||||
func (s MongoDBStorage) LocalAuthentication(namespace string, username string, email string, phone_number string) (*Account, error) {
|
||||
func (s MongoDBStorage) LocalAuthentication(namespace string, username *string, email *string, phone_number *string) (*Account, error) {
|
||||
collection := s.Client.Database(s.DbName).Collection(s.Collections["users"])
|
||||
|
||||
account := &Account{}
|
||||
|
||||
if username != "" {
|
||||
if username != nil {
|
||||
if err := collection.FindOne(context.TODO(), bson.M{"namespace": namespace, "authentication.local.username": username}).Decode(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if email != "" {
|
||||
} else if email != nil {
|
||||
if err := collection.FindOne(context.TODO(), bson.M{"namespace": namespace, "authentication.local.email": email}).Decode(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if phone_number != "" {
|
||||
} else if phone_number != nil {
|
||||
if err := collection.FindOne(context.TODO(), bson.M{"namespace": namespace, "authentication.local.phone_number": phone_number}).Decode(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -191,3 +191,8 @@ func (s MongoDBStorage) UpdateAccount(account Account) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s MongoDBStorage) Migrate() error {
|
||||
fmt.Println("no migration")
|
||||
return nil
|
||||
}
|
||||
|
||||
118
storage/mongodb_test.go
Normal file
118
storage/mongodb_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var cfg2 *viper.Viper
|
||||
|
||||
func init() {
|
||||
cfg2 = viper.New()
|
||||
cfg2.SetDefault("storage.db.mongodb.host", "localhost")
|
||||
cfg2.SetDefault("storage.db.mongodb.port", "27017")
|
||||
cfg2.SetDefault("storage.db.mongodb.user", "mongodb")
|
||||
cfg2.SetDefault("storage.db.mongodb.db_name", "mobilityaccounts_tests")
|
||||
cfg2.SetDefault("storage.db.mongodb.sslmode", "disable")
|
||||
cfg2.SetDefault("storage.db.mongodb.collections.users", "users")
|
||||
cfg2.SetConfigName("config") // Define values in config.yaml
|
||||
cfg2.AddConfigPath(".")
|
||||
cfg2.ReadInConfig()
|
||||
}
|
||||
|
||||
func TestMongoDBStorage_CreateAndGetAccount(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
err = db.CreateAccount(account1)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := db.GetAccount(account1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, &account1, result)
|
||||
}
|
||||
func TestMongoDBStorage_CreateAndGetAccountNoAuth(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.CreateAccount(account3)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := db.GetAccount(account3.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, &account3, result)
|
||||
}
|
||||
|
||||
func TestMongoDBStorage_UpdateAccount(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.CreateAccount(account2)
|
||||
require.NoError(t, err)
|
||||
|
||||
modified := account2
|
||||
modified.Authentication.Local.Email = Ptr("modifiedtest@test.com")
|
||||
modified.Data["key1"] = "modeifiedvalue"
|
||||
modified.Data["addedkey"] = "addedvalue"
|
||||
modified.Metadata["addedmetadatakey"] = "addedmetadatavalue"
|
||||
err = db.UpdateAccount(modified)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := db.GetAccount(account2.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, &modified, result)
|
||||
}
|
||||
|
||||
func TestMongoDBStorage_LocalAuthentication(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.LocalAuthentication(account1.Namespace, account1.Authentication.Local.Username, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.LocalAuthentication(account1.Namespace, nil, account1.Authentication.Local.Email, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.LocalAuthentication(account1.Namespace, nil, nil, account1.Authentication.Local.PhoneNumber)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestMongoDBStorage_GetAccounts(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
accounts, err := db.GetAccounts([]string{account1.Namespace, account3.Namespace})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, account := range accounts {
|
||||
require.Contains(t, []string{account1.Namespace, account3.Namespace}, account.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMongoDBsqlStorage_GetAccountsByIds(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
accounts, err := db.GetAccountsByIds([]string{account2.ID, account3.ID})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, account := range accounts {
|
||||
require.Contains(t, []string{account2.ID, account3.ID}, account.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMongoDBStorage_CreateAlreadyExistingCredentials(t *testing.T) {
|
||||
db, err := NewMongoDBStorage(cfg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
modified := account1
|
||||
modified.ID = uuid.NewString() // Change the ID to make as if it was a new one
|
||||
|
||||
err = db.CreateAccount(modified)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -1,39 +1,40 @@
|
||||
/*
|
||||
___ ___ ___ _ __ __ _ ___
|
||||
/ __/ _ \ / _ \| '_ \ / _` |/ _ \
|
||||
| (_| (_) | (_) | |_) | (_| | (_) |
|
||||
\___\___/ \___/| .__/ \__, |\___/
|
||||
| | __/ |
|
||||
|_| |___/
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ariga.io/atlas/sql/postgres"
|
||||
"ariga.io/atlas/sql/schema"
|
||||
"github.com/lib/pq"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type PostgresqlStorage struct {
|
||||
DbConnection *sql.DB
|
||||
Tables map[string]string
|
||||
}
|
||||
|
||||
func NewPostgresqlStorage(cfg *viper.Viper) (PostgresqlStorage, error) {
|
||||
var (
|
||||
host = cfg.GetString("storage.db.psql.host")
|
||||
port = cfg.GetString("storage.db.psql.port")
|
||||
user = cfg.GetString("storage.db.psql.user")
|
||||
password = cfg.GetString("storage.db.psql.password")
|
||||
dbname = cfg.GetString("storage.db.psql.dbname")
|
||||
host = cfg.GetString("storage.db.psql.host")
|
||||
port = cfg.GetString("storage.db.psql.port")
|
||||
user = cfg.GetString("storage.db.psql.user")
|
||||
password = cfg.GetString("storage.db.psql.password")
|
||||
dbname = cfg.GetString("storage.db.psql.dbname")
|
||||
sslmode = cfg.GetString("storage.db.psql.sslmode")
|
||||
pg_schema = cfg.GetString("storage.db.psql.schema")
|
||||
pgtables_accounts = cfg.GetString("storage.db.psql.tables.accounts")
|
||||
pgtables_accounts_auth_local = cfg.GetString("storage.db.psql.tables.accounts_auth_local")
|
||||
)
|
||||
portInt, _ := strconv.Atoi(port)
|
||||
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, portInt,
|
||||
user, password, dbname)
|
||||
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", host, portInt, user, password, dbname, sslmode)
|
||||
db, err := sql.Open("postgres", psqlconn)
|
||||
if err != nil {
|
||||
fmt.Println("error", err)
|
||||
@@ -41,10 +42,15 @@ func NewPostgresqlStorage(cfg *viper.Viper) (PostgresqlStorage, error) {
|
||||
}
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return PostgresqlStorage{}, fmt.Errorf("connection to postgresql database failed")
|
||||
}
|
||||
return PostgresqlStorage{
|
||||
DbConnection: db,
|
||||
Tables: map[string]string{
|
||||
"accounts": fmt.Sprintf("%s.%s", pg_schema, pgtables_accounts),
|
||||
"accounts_auth_local": fmt.Sprintf("%s.%s", pg_schema, pgtables_accounts_auth_local),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -53,29 +59,91 @@ func (psql PostgresqlStorage) GetAccount(id string) (*Account, error) {
|
||||
data, metadata, emailValidation, phoneValidation []byte
|
||||
)
|
||||
account := &Account{}
|
||||
stmtAccounts, err := psql.DbConnection.Prepare("" +
|
||||
"SELECT id, namespace, data, " +
|
||||
"metadata, username, password, email, email_validation, " +
|
||||
"phone_number, phone_number_validation FROM accounts a JOIN " +
|
||||
"account_auth auth ON id = account_id WHERE id = $1")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("psql connection failed")
|
||||
}
|
||||
defer stmtAccounts.Close()
|
||||
err = stmtAccounts.QueryRow(id).Scan(&account.ID,
|
||||
|
||||
var username, password, email, phonenumber *string
|
||||
|
||||
req := fmt.Sprintf(`SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number, phone_number_validation
|
||||
FROM %s a
|
||||
LEFT JOIN %s auth ON id = account_id WHERE id = $1`, psql.Tables["accounts"], psql.Tables["accounts_auth_local"])
|
||||
err := psql.DbConnection.QueryRow(req, id).Scan(&account.ID,
|
||||
&account.Namespace,
|
||||
&data,
|
||||
&metadata,
|
||||
&username,
|
||||
&password,
|
||||
&email,
|
||||
&emailValidation,
|
||||
&phonenumber,
|
||||
&phoneValidation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("psql select account query failed : %s", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(metadata, &account.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if password != nil || username != nil || email != nil || phonenumber != nil {
|
||||
account.Authentication.Local = &LocalAuth{
|
||||
Username: username,
|
||||
Password: *password,
|
||||
Email: email,
|
||||
PhoneNumber: phonenumber,
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) LocalAuthentication(namespace string, username *string, email *string, phone_number *string) (*Account, error) {
|
||||
account := &Account{}
|
||||
var (
|
||||
data, metadata, emailValidation, phoneValidation []byte
|
||||
)
|
||||
|
||||
requested_field := ""
|
||||
requested_value := ""
|
||||
if username != nil {
|
||||
requested_field = "username"
|
||||
requested_value = *username
|
||||
} else if email != nil {
|
||||
requested_field = "email"
|
||||
requested_value = *email
|
||||
} else if phone_number != nil {
|
||||
requested_field = "phone_number"
|
||||
requested_value = *phone_number
|
||||
} else {
|
||||
return nil, fmt.Errorf("localauthentication func error PSQL")
|
||||
}
|
||||
|
||||
req := fmt.Sprintf(`SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number, phone_number_validation
|
||||
FROM %s INNER JOIN %s ON id = account_id
|
||||
WHERE account_namespace = $1 AND %s = $2;`, psql.Tables["accounts"], psql.Tables["accounts_auth_local"], requested_field)
|
||||
|
||||
account.Authentication.Local = &LocalAuth{}
|
||||
err := psql.DbConnection.QueryRow(req, namespace, requested_value).Scan(
|
||||
&account.ID,
|
||||
&account.Namespace, &data, &metadata,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&account.Authentication.Local.Email,
|
||||
&emailValidation,
|
||||
&account.Authentication.Local.PhoneNumber,
|
||||
&phoneValidation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("psql select account query failed")
|
||||
}
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,6 +151,10 @@ func (psql PostgresqlStorage) GetAccount(id string) (*Account, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -94,142 +166,15 @@ func (psql PostgresqlStorage) GetAccount(id string) (*Account, error) {
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) LocalAuthentication(namespace string, username string, email string,
|
||||
phone_number string) (*Account, error) {
|
||||
account := &Account{}
|
||||
var (
|
||||
data, metadata, emailValidation, phoneValidation []byte
|
||||
)
|
||||
if username != "" {
|
||||
usernameStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata, username, " +
|
||||
"password, email, email_validation, phone_number, phone_number_validation " +
|
||||
"FROM accounts INNER JOIN account_auth ON accounts.id = account_auth.account_id WHERE " +
|
||||
"namespace = $1 AND username = $2;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = usernameStmt.QueryRow(namespace, username).Scan(
|
||||
&account.ID,
|
||||
&account.Namespace, &data, &metadata,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&account.Authentication.Local.Email,
|
||||
&emailValidation, &account.Authentication.Local.PhoneNumber,
|
||||
&phoneValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(metadata, &account.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return account, nil
|
||||
} else if email != "" {
|
||||
account.Authentication.Local.Email = email
|
||||
emailStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata, username, " +
|
||||
"password, email_validation, phone_number, phone_number_validation " +
|
||||
"FROM accounts INNER JOIN account_auth ON " +
|
||||
"accounts.id = account_auth.account_id WHERE namespace = $1 AND email = $2;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = emailStmt.QueryRow(namespace, email).Scan(
|
||||
&account.ID,
|
||||
&account.Namespace,
|
||||
&data, &metadata,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&emailValidation,
|
||||
&account.Authentication.Local.PhoneNumber,
|
||||
&phoneValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(metadata, &account.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return account, nil
|
||||
} else if phone_number != "" {
|
||||
account.Authentication.Local.PhoneNumber = phone_number
|
||||
phoneStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, " +
|
||||
"data, metadata, username, password, email, " +
|
||||
"email_validation, phone_number_validation FROM accounts " +
|
||||
"INNER JOIN account_auth ON accounts.id = account_auth.account_id WHERE " +
|
||||
"namespace = $1 AND phone_number = $2;")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = phoneStmt.QueryRow(namespace, phone_number).Scan(&account.ID,
|
||||
&account.Namespace,
|
||||
&data,
|
||||
&metadata,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&account.Authentication.Local.Email,
|
||||
&emailValidation,
|
||||
&phoneValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(metadata, &account.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &account.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
return nil, fmt.Errorf("localauthentication func error PSQL")
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) GetAccounts(namespaces []string) ([]Account, error) {
|
||||
var accounts []Account
|
||||
namespacesStr := "'" + strings.Join(namespaces, "', '") + "'"
|
||||
query := `
|
||||
SELECT accounts.id, accounts.namespace, accounts.data, accounts.metadata,
|
||||
account_auth.username, account_auth.password,
|
||||
account_auth.email, account_auth.email_validation,
|
||||
account_auth.phone_number, account_auth.phone_number_validation
|
||||
FROM accounts
|
||||
INNER JOIN account_auth ON accounts.id = account_auth.account_id
|
||||
WHERE accounts.namespace IN (%s)
|
||||
SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number, phone_number_validation
|
||||
FROM %s LEFT JOIN %s ON id = account_id
|
||||
WHERE namespace IN (%s)
|
||||
`
|
||||
query = fmt.Sprintf(query, namespacesStr)
|
||||
query = fmt.Sprintf(query, psql.Tables["accounts"], psql.Tables["accounts_auth_local"], namespacesStr)
|
||||
rows, err := psql.DbConnection.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -239,19 +184,20 @@ func (psql PostgresqlStorage) GetAccounts(namespaces []string) ([]Account, error
|
||||
var account Account
|
||||
var dataBytes []byte
|
||||
var metadataBytes []byte
|
||||
var emailValidationBytes []byte
|
||||
var phoneNumberValidationBytes []byte
|
||||
var emailValidation []byte
|
||||
var phoneValidation []byte
|
||||
var username, password, email, phonenumber *string
|
||||
err := rows.Scan(
|
||||
&account.ID,
|
||||
&account.Namespace,
|
||||
&dataBytes,
|
||||
&metadataBytes,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&account.Authentication.Local.Email,
|
||||
&emailValidationBytes,
|
||||
&account.Authentication.Local.PhoneNumber,
|
||||
&phoneNumberValidationBytes,
|
||||
&username,
|
||||
&password,
|
||||
&email,
|
||||
&emailValidation,
|
||||
&phonenumber,
|
||||
&phoneValidation,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -266,14 +212,22 @@ func (psql PostgresqlStorage) GetAccounts(namespaces []string) ([]Account, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(emailValidationBytes, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(phoneNumberValidationBytes, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if password != nil || username != nil || email != nil || phonenumber != nil {
|
||||
account.Authentication.Local = &LocalAuth{
|
||||
Username: username,
|
||||
Password: *password,
|
||||
Email: email,
|
||||
PhoneNumber: phonenumber,
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
@@ -282,18 +236,15 @@ func (psql PostgresqlStorage) GetAccounts(namespaces []string) ([]Account, error
|
||||
|
||||
func (psql PostgresqlStorage) GetAccountsByIds(accountids []string) ([]Account, error) {
|
||||
var accounts []Account
|
||||
accountIdsStr := "'" + strings.Join(accountids, "', '") + "'"
|
||||
|
||||
query := `
|
||||
SELECT accounts.id, accounts.namespace, accounts.data, accounts.metadata,
|
||||
account_auth.username, account_auth.password,
|
||||
account_auth.email, account_auth.email_validation,
|
||||
account_auth.phone_number, account_auth.phone_number_validation
|
||||
FROM accounts
|
||||
INNER JOIN account_auth ON accounts.id = account_auth.account_id
|
||||
WHERE accounts.id IN (%s)
|
||||
SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number, phone_number_validation
|
||||
FROM %s
|
||||
LEFT JOIN %s ON id = account_id
|
||||
WHERE id = ANY ($1)
|
||||
`
|
||||
query = fmt.Sprintf(query, accountIdsStr)
|
||||
rows, err := psql.DbConnection.Query(query)
|
||||
query = fmt.Sprintf(query, psql.Tables["accounts"], psql.Tables["accounts_auth_local"]) //, accountIdsStr)
|
||||
rows, err := psql.DbConnection.Query(query, pq.Array(accountids))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -302,19 +253,21 @@ func (psql PostgresqlStorage) GetAccountsByIds(accountids []string) ([]Account,
|
||||
var account Account
|
||||
var dataBytes []byte
|
||||
var metadataBytes []byte
|
||||
var emailValidationBytes []byte
|
||||
var phoneNumberValidationBytes []byte
|
||||
var emailValidation []byte
|
||||
var phoneValidation []byte
|
||||
var username, password, email, phonenumber *string
|
||||
err := rows.Scan(
|
||||
&account.ID,
|
||||
&account.Namespace,
|
||||
&dataBytes,
|
||||
&metadataBytes,
|
||||
&account.Authentication.Local.Username,
|
||||
&account.Authentication.Local.Password,
|
||||
&account.Authentication.Local.Email,
|
||||
&emailValidationBytes,
|
||||
&account.Authentication.Local.PhoneNumber,
|
||||
&phoneNumberValidationBytes)
|
||||
&username,
|
||||
&password,
|
||||
&email,
|
||||
&emailValidation,
|
||||
&phonenumber,
|
||||
&phoneValidation,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -329,14 +282,21 @@ func (psql PostgresqlStorage) GetAccountsByIds(accountids []string) ([]Account,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(emailValidationBytes, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(phoneNumberValidationBytes, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if password != nil || username != nil || email != nil || phonenumber != nil {
|
||||
account.Authentication.Local = &LocalAuth{
|
||||
Username: username,
|
||||
Password: *password,
|
||||
Email: email,
|
||||
PhoneNumber: phonenumber,
|
||||
}
|
||||
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
@@ -344,11 +304,6 @@ func (psql PostgresqlStorage) GetAccountsByIds(accountids []string) ([]Account,
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) CreateAccount(account Account) error {
|
||||
insertAccountStmt, err := psql.DbConnection.Prepare("INSERT INTO accounts (id, namespace, data, metadata)" +
|
||||
" VALUES ($1, $2, $3, $4)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataAccountJson, err := json.Marshal(account.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -357,43 +312,67 @@ func (psql PostgresqlStorage) CreateAccount(account Account) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := psql.DbConnection.BeginTx(context.Background(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
insertAccountStmt, err := tx.Prepare(fmt.Sprintf("INSERT INTO %s (id, namespace, data, metadata) VALUES ($1, $2, $3, $4)", psql.Tables["accounts"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer insertAccountStmt.Close()
|
||||
_, err = insertAccountStmt.Exec(account.ID, account.Namespace, dataAccountJson, metadataAccountJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insertAccountAuthStmt, err := psql.DbConnection.Prepare("INSERT INTO account_auth (account_id, username," +
|
||||
" password, email, email_validation,phone_number,phone_number_validation) " +
|
||||
"values($1, $2, $3, $4, $5, $6, $7)")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if account.Authentication.Local != nil {
|
||||
|
||||
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insertAccountAuthStmt, err := tx.Prepare(fmt.Sprintf(`INSERT INTO %s (account_id, account_namespace, username, password, email, email_validation, phone_number, phone_number_validation)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8);`, psql.Tables["accounts_auth_local"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer insertAccountAuthStmt.Close()
|
||||
_, err = insertAccountAuthStmt.Exec(account.ID,
|
||||
account.Namespace,
|
||||
account.Authentication.Local.Username,
|
||||
account.Authentication.Local.Password,
|
||||
account.Authentication.Local.Email,
|
||||
emailValidationJson,
|
||||
account.Authentication.Local.PhoneNumber,
|
||||
phoneValidationJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = insertAccountAuthStmt.Exec(account.ID,
|
||||
account.Authentication.Local.Username,
|
||||
account.Authentication.Local.Password,
|
||||
account.Authentication.Local.Email,
|
||||
emailValidationJson,
|
||||
account.Authentication.Local.PhoneNumber,
|
||||
phoneValidationJson)
|
||||
if err != nil {
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) UpdateAccount(account Account) error {
|
||||
updateAccountStmt, err := psql.DbConnection.Prepare("update accounts set namespace=$1, data=$2, " +
|
||||
" metadata=$3 where id= $4")
|
||||
|
||||
tx, err := psql.DbConnection.BeginTx(context.Background(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
dataAccountJson, err := json.Marshal(account.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -402,33 +381,77 @@ func (psql PostgresqlStorage) UpdateAccount(account Account) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = updateAccountStmt.Exec(account.Namespace, dataAccountJson, metadataAccountJson, account.ID)
|
||||
|
||||
req := fmt.Sprintf("UPDATE %s SET namespace=$1, data=$2, metadata=$3 where id= $4", psql.Tables["accounts"])
|
||||
_, err = tx.Exec(req, account.Namespace, dataAccountJson, metadataAccountJson, account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateAccountAuthStmt, err := psql.DbConnection.Prepare("update account_auth set username = $1," +
|
||||
" password = $2," +
|
||||
" email = $3, email_validation = $4 ," +
|
||||
"phone_number = $5,phone_number_validation = $6 where account_id = $7")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = updateAccountAuthStmt.Exec(account.Authentication.Local.Username,
|
||||
account.Authentication.Local.Password,
|
||||
account.Authentication.Local.Email,
|
||||
emailValidationJson,
|
||||
account.Authentication.Local.PhoneNumber,
|
||||
phoneValidationJson, account.ID)
|
||||
if err != nil {
|
||||
|
||||
if account.Authentication.Local != nil {
|
||||
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req = fmt.Sprintf(`UPDATE %s
|
||||
SET username = $1, password = $2, email = $3, email_validation = $4,
|
||||
phone_number = $5,phone_number_validation = $6 where account_id = $7`, psql.Tables["accounts_auth_local"])
|
||||
_, err = tx.Exec(req, account.Authentication.Local.Username,
|
||||
account.Authentication.Local.Password,
|
||||
account.Authentication.Local.Email,
|
||||
emailValidationJson,
|
||||
account.Authentication.Local.PhoneNumber,
|
||||
phoneValidationJson, account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (psql PostgresqlStorage) Migrate() error {
|
||||
ctx := context.Background()
|
||||
driver, err := postgres.Open(psql.DbConnection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing, err := driver.InspectRealm(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var desired schema.Realm
|
||||
|
||||
hcl, err := os.ReadFile("postgresql/schema.hcl")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = postgres.EvalHCLBytes(hcl, &desired, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
diff, err := driver.RealmDiff(existing, &desired)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = driver.ApplyChanges(ctx, diff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
81
storage/postgresql/schema.hcl
Normal file
81
storage/postgresql/schema.hcl
Normal file
@@ -0,0 +1,81 @@
|
||||
table "accounts" {
|
||||
schema = schema.mobilityaccounts
|
||||
column "id" {
|
||||
null = false
|
||||
type = uuid
|
||||
}
|
||||
column "namespace" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "data" {
|
||||
null = true
|
||||
type = jsonb
|
||||
}
|
||||
column "metadata" {
|
||||
null = true
|
||||
type = jsonb
|
||||
}
|
||||
primary_key {
|
||||
columns = [column.id]
|
||||
}
|
||||
}
|
||||
table "accounts_auth_local" {
|
||||
schema = schema.mobilityaccounts
|
||||
column "account_id" {
|
||||
null = true
|
||||
type = uuid
|
||||
}
|
||||
column "account_namespace" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "username" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "password" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "email" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "email_validation" {
|
||||
null = true
|
||||
type = jsonb
|
||||
}
|
||||
column "phone_number" {
|
||||
null = true
|
||||
type = text
|
||||
}
|
||||
column "phone_number_validation" {
|
||||
null = true
|
||||
type = jsonb
|
||||
}
|
||||
foreign_key "accounts_auth_local_account_id_fkey" {
|
||||
columns = [column.account_id]
|
||||
ref_columns = [table.accounts.column.id]
|
||||
on_update = NO_ACTION
|
||||
on_delete = NO_ACTION
|
||||
}
|
||||
index "accounts_auth_local_account_id_key" {
|
||||
unique = true
|
||||
columns = [column.account_id]
|
||||
}
|
||||
index "accounts_auth_local_idx_email" {
|
||||
unique = true
|
||||
columns = [column.account_namespace, column.email]
|
||||
}
|
||||
index "accounts_auth_local_idx_phone_number" {
|
||||
unique = true
|
||||
columns = [column.account_namespace, column.phone_number]
|
||||
}
|
||||
index "accounts_auth_local_idx_username" {
|
||||
unique = true
|
||||
columns = [column.account_namespace, column.username]
|
||||
}
|
||||
}
|
||||
schema "mobilityaccounts" {
|
||||
}
|
||||
@@ -1,279 +1,141 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Tests must be run in order
|
||||
// Table creation:
|
||||
/* #####################################################################################################################
|
||||
CREATE TABLE accounts (
|
||||
id TEXT PRIMARY KEY,
|
||||
namespace TEXT,
|
||||
data JSONB,
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
CREATE TABLE account_auth (
|
||||
account_id TEXT PRIMARY KEY,
|
||||
username TEXT,
|
||||
password TEXT,
|
||||
email TEXT,
|
||||
email_validation JSONB,
|
||||
phone_number TEXT,
|
||||
phone_number_validation JSONB,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id)
|
||||
);
|
||||
#######################################################################################################################
|
||||
*/
|
||||
/* Inserting an Account: (Password = test)
|
||||
#######################################################################################################################
|
||||
INSERT INTO accounts (id, namespace, data, metadata)
|
||||
VALUES ('2faa137b-27be-476f-b98c-8b7eed6f1f3a', 'parcoursmob', '{"email": "salimbouaram12@gmail.com", "gender": "9",
|
||||
"groups": ["483280d0-db2d-4f06-b361-02e4be5012d2", "483280d0-db2d-4f06-b361-02e4be5012d2:admin"], "last_name": "salim",
|
||||
"first_name": "salim", "display_name": "salim salim", "phone_number": ""}', '{"created": "2023-04-24T09:29:18.262+00:00"}');
|
||||
#######################################################################################################################
|
||||
INSERT INTO account_auth (account_id, username, password, email, email_validation, phone_number, phone_number_validation)
|
||||
VALUES ('2faa137b-27be-476f-b98c-8b7eed6f1f3a', 'salim-amine.bou-aram@coopgo.fr',
|
||||
'$2a$10$j9LwkGYT6HhLpWxUvpEniOJ3nBKEhwAn52G.t4QYMgje4HnJuWqHK', 'salim-amine.bou-aram@coopgo.fr',
|
||||
'{"validated": false, "validation_code": ""}', '0749331953', '{"validated": false, "validation_code": ""}');
|
||||
#######################################################################################################################
|
||||
*/
|
||||
|
||||
var cfg *viper.Viper
|
||||
|
||||
func init() {
|
||||
cfg = viper.New()
|
||||
cfg.Set("storage.db.psql.host", "localhost")
|
||||
cfg.Set("storage.db.psql.port", "5432")
|
||||
cfg.Set("storage.db.psql.user", "postgres")
|
||||
cfg.Set("storage.db.psql.password", "postgres")
|
||||
cfg.Set("storage.db.psql.dbname", "mobilityaccounts")
|
||||
cfg.SetDefault("storage.db.psql.host", "localhost")
|
||||
cfg.SetDefault("storage.db.psql.port", "5432")
|
||||
cfg.SetDefault("storage.db.psql.user", "postgres")
|
||||
cfg.SetDefault("storage.db.psql.password", "postgres")
|
||||
cfg.SetDefault("storage.db.psql.dbname", "mobilityaccounts_tests")
|
||||
cfg.SetDefault("storage.db.psql.sslmode", "disable")
|
||||
cfg.SetDefault("storage.db.psql.schema", "mobilityaccounts")
|
||||
cfg.SetDefault("storage.db.psql.tables.accounts", "accounts")
|
||||
cfg.SetDefault("storage.db.psql.tables.accounts_auth_local", "accounts_auth_local")
|
||||
cfg.SetConfigName("config") // Override default values in a config.yaml file within this directory
|
||||
cfg.AddConfigPath(".")
|
||||
cfg.ReadInConfig()
|
||||
}
|
||||
|
||||
func TestNewPostgresqlStorage(t *testing.T) {
|
||||
func TestPostgresqlStorage_Initialize(t *testing.T) {
|
||||
storage, err := NewPostgresqlStorage(cfg)
|
||||
storage.DbConnection.Exec("Delete from account_auth ;")
|
||||
storage.DbConnection.Exec("Delete from accounts ;")
|
||||
if err != nil {
|
||||
t.Errorf("error creating new PostgreSQL storage: %v", err)
|
||||
}
|
||||
defer storage.DbConnection.Close()
|
||||
}
|
||||
|
||||
func generateUUIDv4() string {
|
||||
uuid := make([]byte, 16)
|
||||
_, err := rand.Read(uuid)
|
||||
err = storage.Migrate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
t.Errorf("database migration issue: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := storage.DbConnection.BeginTx(context.Background(), nil)
|
||||
if err != nil {
|
||||
t.Errorf("transaction issue: %v", err)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s;", storage.Tables["accounts_auth_local"]))
|
||||
if err != nil {
|
||||
t.Errorf("delete accounts table issue: %v", err)
|
||||
return
|
||||
}
|
||||
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s;", storage.Tables["accounts"]))
|
||||
if err != nil {
|
||||
t.Errorf("delete accounts table issue: %v", err)
|
||||
return
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
t.Errorf("commit transaction issue: %v", err)
|
||||
return
|
||||
}
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40
|
||||
uuid[8] = (uuid[8] & 0xbf) | 0x80
|
||||
return hex.EncodeToString(uuid[:4]) + "-" + hex.EncodeToString(uuid[4:6]) + "-" +
|
||||
hex.EncodeToString(uuid[6:8]) + "-" + hex.EncodeToString(uuid[8:10]) + "-" +
|
||||
hex.EncodeToString(uuid[10:])
|
||||
}
|
||||
|
||||
func TestGetAccount(t *testing.T) {
|
||||
// Open a database connection
|
||||
func TestPostgresqlStorage_CreateAndGetAccount(t *testing.T) {
|
||||
db, err := NewPostgresqlStorage(cfg)
|
||||
Id := generateUUIDv4()
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
}
|
||||
// Insert data into accounts table
|
||||
accountData := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
accountMetadata := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
account := Account{
|
||||
ID: Id,
|
||||
Namespace: "test_namespace",
|
||||
Data: accountData,
|
||||
Metadata: accountMetadata,
|
||||
}
|
||||
dataJSON, err := json.Marshal(map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
})
|
||||
err = db.CreateAccount(account1)
|
||||
if err != nil {
|
||||
t.Errorf("error account data and metdata")
|
||||
t.Errorf("Failed to create account : %s", err)
|
||||
return
|
||||
}
|
||||
_, err = db.DbConnection.Exec("INSERT INTO accounts (id, namespace, data, metadata) "+
|
||||
"VALUES ($1, $2, $3, $4)", account.ID, account.Namespace, dataJSON, dataJSON)
|
||||
|
||||
result, err := db.GetAccount(account1.ID)
|
||||
if err != nil {
|
||||
t.Errorf("error in inserting a new account")
|
||||
t.Errorf("Failed to get account : %s", err)
|
||||
return
|
||||
}
|
||||
// Insert data into account_auth table
|
||||
emailValidation := Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "code",
|
||||
}
|
||||
localAuth := LocalAuth{
|
||||
Username: "testuser",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
EmailValidation: emailValidation,
|
||||
PhoneNumber: "1234567890",
|
||||
PhoneNumberValidation: emailValidation,
|
||||
}
|
||||
localAuthJSON, err := json.Marshal(emailValidation)
|
||||
if err != nil {
|
||||
t.Errorf("error account_auth localAuth")
|
||||
}
|
||||
_, err = db.DbConnection.Exec("INSERT INTO account_auth (account_id, username, password, "+
|
||||
"email, email_validation, "+
|
||||
"phone_number,phone_number_validation) VALUES ($1, $2, $3, $4, $5, $6, $7)", account.ID,
|
||||
localAuth.Username, localAuth.Password, localAuth.Email, localAuthJSON, localAuth.PhoneNumber, localAuthJSON)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.Errorf("error in iserting a new account in account_auth")
|
||||
}
|
||||
account_, err := db.GetAccount(Id)
|
||||
if err != nil {
|
||||
t.Errorf("failed")
|
||||
fmt.Println(err)
|
||||
}
|
||||
expectedAccount := &Account{
|
||||
ID: Id,
|
||||
Namespace: "test_namespace",
|
||||
Data: accountData,
|
||||
Metadata: accountMetadata,
|
||||
Authentication: AccountAuth{
|
||||
Local: localAuth,
|
||||
},
|
||||
}
|
||||
if reflect.DeepEqual(account_, expectedAccount) {
|
||||
fmt.Println("PASS")
|
||||
} else {
|
||||
t.Errorf("The received account is not the same as expected")
|
||||
|
||||
if !reflect.DeepEqual(&account1, result) {
|
||||
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &account1, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresqlStorage_CreateAccount(t *testing.T) {
|
||||
func TestPostgresqlStorage_CreateAndGetAccountNoAuth(t *testing.T) {
|
||||
db, err := NewPostgresqlStorage(cfg)
|
||||
Id := generateUUIDv4()
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
}
|
||||
emailValidation := Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "code",
|
||||
}
|
||||
localAuth := LocalAuth{
|
||||
Username: "salim",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
EmailValidation: emailValidation,
|
||||
PhoneNumber: "1234567890",
|
||||
PhoneNumberValidation: emailValidation,
|
||||
}
|
||||
accountData := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
accountMetadata := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
account := Account{
|
||||
ID: Id,
|
||||
Namespace: "namespace",
|
||||
Authentication: AccountAuth{
|
||||
Local: localAuth,
|
||||
},
|
||||
Data: accountData,
|
||||
Metadata: accountMetadata,
|
||||
}
|
||||
err = db.CreateAccount(account)
|
||||
err = db.CreateAccount(account3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.Errorf("Failed to create account")
|
||||
t.Errorf("Failed to create account : %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := db.GetAccount(account3.ID)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get account : %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(&account3, result) {
|
||||
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &account3, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresqlStorage_UpdateAccount(t *testing.T) {
|
||||
db, err := NewPostgresqlStorage(cfg)
|
||||
Id := generateUUIDv4()
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
}
|
||||
emailValidation := Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "code",
|
||||
}
|
||||
localAuth := LocalAuth{
|
||||
Username: "salim",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
EmailValidation: emailValidation,
|
||||
PhoneNumber: "1234567890",
|
||||
PhoneNumberValidation: emailValidation,
|
||||
}
|
||||
accountData := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
accountMetadata := map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
}
|
||||
account := Account{
|
||||
ID: Id,
|
||||
Namespace: "test_namespace",
|
||||
Authentication: AccountAuth{
|
||||
Local: localAuth,
|
||||
},
|
||||
Data: accountData,
|
||||
Metadata: accountMetadata,
|
||||
}
|
||||
err = db.CreateAccount(account)
|
||||
err = db.CreateAccount(account2)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.Errorf("Failed to create account")
|
||||
t.Errorf("Failed to create account : %s", err)
|
||||
return
|
||||
}
|
||||
account2 := Account{
|
||||
ID: Id,
|
||||
Namespace: "salim",
|
||||
Authentication: AccountAuth{
|
||||
Local: LocalAuth{
|
||||
Username: "salim",
|
||||
Password: "salim",
|
||||
Email: "salim@test.com",
|
||||
EmailValidation: Validation{
|
||||
Validated: false,
|
||||
ValidationCode: "123",
|
||||
},
|
||||
PhoneNumber: "12345",
|
||||
PhoneNumberValidation: Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "1233",
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: map[string]any{
|
||||
"key1": "salim1",
|
||||
"key2": "salim2",
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"key1": "salim1",
|
||||
"key2": "salim2",
|
||||
},
|
||||
}
|
||||
err = db.UpdateAccount(account2)
|
||||
modified := account2
|
||||
modified.Authentication.Local.Email = Ptr("modifiedtest@test.com")
|
||||
modified.Data["key1"] = "modeifiedvalue"
|
||||
modified.Data["addedkey"] = "addedvalue"
|
||||
modified.Metadata["addedmetadatakey"] = "addedmetadatavalue"
|
||||
err = db.UpdateAccount(modified)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.Errorf("failed")
|
||||
t.Errorf("failed updating account : %s", err)
|
||||
}
|
||||
|
||||
result, err := db.GetAccount(account2.ID)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get account : %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(&modified, result) {
|
||||
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &modified, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,30 +144,18 @@ func TestPostgresqlStorage_LocalAuthentication(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
}
|
||||
accountByUsername, err := db.LocalAuthentication("test_namespace", "testuser",
|
||||
"", "")
|
||||
_, err = db.LocalAuthentication(account1.Namespace, account1.Authentication.Local.Username, nil, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed LocalAuthentication based on username and namespace")
|
||||
t.Errorf("Failed LocalAuthentication based on username and namespace : %s", err)
|
||||
}
|
||||
fmt.Println(accountByUsername)
|
||||
accountByEmail, err := db.LocalAuthentication("test_namespace", "",
|
||||
"test@test.com", "")
|
||||
_, err = db.LocalAuthentication(account1.Namespace, nil, account1.Authentication.Local.Email, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed LocalAuthentication based on username and namespace")
|
||||
t.Errorf("Failed LocalAuthentication based on email and namespace :\n Namespace: %s\n Email: %s\nError: %s", account1.Namespace, *account1.Authentication.Local.Email, err)
|
||||
}
|
||||
fmt.Println(accountByEmail)
|
||||
accountByPhone, err := db.LocalAuthentication("test_namespace", "",
|
||||
"", "1234567890")
|
||||
_, err = db.LocalAuthentication(account1.Namespace, nil, nil, account1.Authentication.Local.PhoneNumber)
|
||||
if err != nil {
|
||||
t.Errorf("Failed LocalAuthentication based on username and namespace")
|
||||
t.Errorf("Failed LocalAuthentication based on phone number and namespace :\n Namespace: %s\n Phone number: %s\nError: %s", account1.Namespace, *account1.Authentication.Local.PhoneNumber, err)
|
||||
}
|
||||
fmt.Println(accountByPhone)
|
||||
accountByAll, err := db.LocalAuthentication("test_namespace", "testuser",
|
||||
"test@test.com", "1234567890")
|
||||
if err != nil {
|
||||
t.Errorf("Failed LocalAuthentication based on username and namespace")
|
||||
}
|
||||
fmt.Println(accountByAll)
|
||||
}
|
||||
|
||||
func TestPostgresqlStorage_GetAccounts(t *testing.T) {
|
||||
@@ -313,12 +163,15 @@ func TestPostgresqlStorage_GetAccounts(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
}
|
||||
accounts, err := db.GetAccounts([]string{"test_namespace", "salim", "namespace"})
|
||||
accounts, err := db.GetAccounts([]string{account1.Namespace, account3.Namespace})
|
||||
if err != nil {
|
||||
t.Errorf("Failed")
|
||||
t.Errorf("Failed : %s", err)
|
||||
return
|
||||
}
|
||||
for _, account := range accounts {
|
||||
fmt.Println(account)
|
||||
if account.Namespace != account1.Namespace && account.Namespace != account3.Namespace {
|
||||
t.Errorf("This namespace was not requested : %s", account.Namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,44 +179,47 @@ func TestPostgresqlStorage_GetAccountsByIds(t *testing.T) {
|
||||
db, err := NewPostgresqlStorage(cfg)
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
return
|
||||
}
|
||||
account := Account{
|
||||
ID: "772315f1-8113-486a-90c7-9073410065bd",
|
||||
Namespace: "oo",
|
||||
Authentication: AccountAuth{
|
||||
Local: LocalAuth{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
Email: "salim@test.com",
|
||||
EmailValidation: Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "123",
|
||||
},
|
||||
PhoneNumber: "12345",
|
||||
PhoneNumberValidation: Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "1233",
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: map[string]any{
|
||||
"key1": "salim1",
|
||||
"key2": "salim2",
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"key1": "salim1",
|
||||
"key2": "salim2",
|
||||
},
|
||||
}
|
||||
err = db.CreateAccount(account)
|
||||
|
||||
accounts, err := db.GetAccountsByIds([]string{account2.ID, account3.ID})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create account")
|
||||
t.Errorf("Failed to get account by ID : %s", err)
|
||||
return
|
||||
}
|
||||
accounts, err := db.GetAccountsByIds([]string{"772315f1-8113-486a-90c7-9073410065bd"})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get account by ID")
|
||||
|
||||
found2 := false
|
||||
found3 := false
|
||||
for _, account := range accounts {
|
||||
if account.ID == account2.ID {
|
||||
found2 = true
|
||||
} else if account.ID == account3.ID {
|
||||
found3 = true
|
||||
} else {
|
||||
t.Errorf("This id was not requested : %s", account.ID)
|
||||
}
|
||||
}
|
||||
for _, acc := range accounts {
|
||||
fmt.Println(acc)
|
||||
if !found2 {
|
||||
t.Errorf("account id not found for account2 : %s", account2.ID)
|
||||
}
|
||||
if !found3 {
|
||||
t.Errorf("account id not found for account3 : %s", account3.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresqlStorage_CreateAlreadyExistingCredentials(t *testing.T) {
|
||||
db, err := NewPostgresqlStorage(cfg)
|
||||
if err != nil {
|
||||
t.Errorf("failed to create new psql connection")
|
||||
return
|
||||
}
|
||||
|
||||
modified := account1
|
||||
modified.ID = uuid.NewString() // Change the ID to make as if it was a new one
|
||||
|
||||
err = db.CreateAccount(modified)
|
||||
if err == nil {
|
||||
t.Errorf("account should not be created : unique index violated !")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,15 @@ func NewStorage(cfg *viper.Viper) (Storage, error) {
|
||||
|
||||
type DBStorage interface {
|
||||
GetAccount(id string) (*Account, error)
|
||||
LocalAuthentication(namespace string, username string, email string, phone_number string) (*Account, error)
|
||||
LocalAuthentication(namespace string, username *string, email *string, phone_number *string) (*Account, error)
|
||||
GetAccounts(namespaces []string) ([]Account, error)
|
||||
GetAccountsByIds(accountids []string) ([]Account, error)
|
||||
CreateAccount(account Account) error
|
||||
|
||||
//TODO : remove UpdateAccount, implement UpdateAccountData and UpdateAccountLocalAuthentication
|
||||
UpdateAccount(account Account) error
|
||||
|
||||
Migrate() error
|
||||
}
|
||||
|
||||
func NewDBStorage(cfg *viper.Viper) (DBStorage, error) {
|
||||
@@ -66,3 +70,7 @@ func NewKVStore(cfg *viper.Viper) (KVStore, error) {
|
||||
kv, err := NewEtcdKVStore(cfg)
|
||||
return kv, err
|
||||
}
|
||||
|
||||
func Ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
67
storage/storage_test.go
Normal file
67
storage/storage_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package storage
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
var account1 = Account{
|
||||
ID: uuid.NewString(),
|
||||
Namespace: "namespace",
|
||||
Authentication: AccountAuth{
|
||||
Local: &LocalAuth{
|
||||
Username: Ptr("test"),
|
||||
Password: "hashedpassword",
|
||||
Email: Ptr("test@test.com"),
|
||||
EmailValidation: &Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "",
|
||||
},
|
||||
PhoneNumber: Ptr("+3312345678"),
|
||||
PhoneNumberValidation: &Validation{
|
||||
Validated: true,
|
||||
ValidationCode: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
}
|
||||
|
||||
var account2 = Account{
|
||||
ID: uuid.NewString(),
|
||||
Namespace: "test",
|
||||
Authentication: AccountAuth{
|
||||
Local: &LocalAuth{
|
||||
Username: Ptr("test2"),
|
||||
Password: "hashedpassword",
|
||||
},
|
||||
},
|
||||
Data: map[string]any{
|
||||
"key1": "value3",
|
||||
"key2": "value4",
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"key1": "value5",
|
||||
"key2": "value6",
|
||||
},
|
||||
}
|
||||
|
||||
var account3 = Account{
|
||||
ID: uuid.NewString(),
|
||||
Namespace: "other_namespace",
|
||||
Authentication: AccountAuth{
|
||||
Local: nil,
|
||||
},
|
||||
Data: map[string]any{
|
||||
"key1": "value3",
|
||||
"key2": "value4",
|
||||
},
|
||||
Metadata: map[string]any{
|
||||
"key1": "value5",
|
||||
"key2": "value6",
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user