2022-01-21 07:23:30 +00:00
package arangodb
import (
2022-01-25 05:27:40 +00:00
"context"
2022-08-02 08:42:36 +00:00
"encoding/json"
2022-01-21 07:23:30 +00:00
"fmt"
2022-08-02 08:42:36 +00:00
"strings"
2022-01-21 07:23:30 +00:00
"time"
arangoDriver "github.com/arangodb/go-driver"
2022-08-02 08:42:36 +00:00
"github.com/google/uuid"
2022-01-21 07:23:30 +00:00
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
2022-01-25 05:27:40 +00:00
"github.com/authorizerdev/authorizer/server/graph/model"
2022-05-29 11:52:46 +00:00
"github.com/authorizerdev/authorizer/server/memorystore"
2022-12-21 17:44:24 +00:00
"github.com/authorizerdev/authorizer/server/refs"
2022-01-21 07:23:30 +00:00
)
// AddUser to save user information in database
2023-07-31 11:12:11 +00:00
func ( p * provider ) AddUser ( ctx context . Context , user * models . User ) ( * models . User , error ) {
2022-01-21 07:23:30 +00:00
if user . ID == "" {
user . ID = uuid . New ( ) . String ( )
2022-08-02 08:42:36 +00:00
user . Key = user . ID
2022-01-21 07:23:30 +00:00
}
if user . Roles == "" {
2022-05-31 02:44:03 +00:00
defaultRoles , err := memorystore . Provider . GetStringStoreEnvVariable ( constants . EnvKeyDefaultRoles )
2022-05-29 11:52:46 +00:00
if err != nil {
return user , err
}
2022-05-31 02:44:03 +00:00
user . Roles = defaultRoles
2022-01-21 07:23:30 +00:00
}
2022-12-21 17:44:24 +00:00
if user . PhoneNumber != nil && strings . TrimSpace ( refs . StringValue ( user . PhoneNumber ) ) != "" {
if u , _ := p . GetUserByPhoneNumber ( ctx , refs . StringValue ( user . PhoneNumber ) ) ; u != nil && u . ID != user . ID {
return user , fmt . Errorf ( "user with given phone number already exists" )
}
}
2022-01-21 07:23:30 +00:00
user . CreatedAt = time . Now ( ) . Unix ( )
user . UpdatedAt = time . Now ( ) . Unix ( )
2022-07-10 16:19:33 +00:00
userCollection , _ := p . db . Collection ( ctx , models . Collections . User )
meta , err := userCollection . CreateDocument ( arangoDriver . WithOverwrite ( ctx ) , user )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
user . Key = meta . Key
user . ID = meta . ID . String ( )
return user , nil
}
// UpdateUser to update user information in database
2023-07-31 11:12:11 +00:00
func ( p * provider ) UpdateUser ( ctx context . Context , user * models . User ) ( * models . User , error ) {
2022-01-21 07:23:30 +00:00
user . UpdatedAt = time . Now ( ) . Unix ( )
2022-12-21 17:44:24 +00:00
2022-07-10 16:19:33 +00:00
collection , _ := p . db . Collection ( ctx , models . Collections . User )
meta , err := collection . UpdateDocument ( ctx , user . Key , user )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
user . Key = meta . Key
user . ID = meta . ID . String ( )
return user , nil
}
// DeleteUser to delete user information from database
2023-07-31 11:12:11 +00:00
func ( p * provider ) DeleteUser ( ctx context . Context , user * models . User ) error {
2022-07-10 16:19:33 +00:00
collection , _ := p . db . Collection ( ctx , models . Collections . User )
_ , err := collection . RemoveDocument ( ctx , user . Key )
2022-01-21 07:23:30 +00:00
if err != nil {
return err
}
2022-07-12 06:18:42 +00:00
query := fmt . Sprintf ( ` FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s ` , models . Collections . Session , models . Collections . Session )
2022-07-12 03:12:32 +00:00
bindVars := map [ string ] interface { } {
2022-08-02 08:42:36 +00:00
"user_id" : user . Key ,
2022-07-12 03:12:32 +00:00
}
cursor , err := p . db . Query ( ctx , query , bindVars )
if err != nil {
return err
}
defer cursor . Close ( )
2022-01-21 07:23:30 +00:00
return nil
}
// ListUsers to get list of users from database
2023-07-31 11:12:11 +00:00
func ( p * provider ) ListUsers ( ctx context . Context , pagination * model . Pagination ) ( * model . Users , error ) {
2022-01-25 05:27:40 +00:00
var users [ ] * model . User
2023-02-06 12:44:19 +00:00
sctx := arangoDriver . WithQueryFullCount ( ctx )
2022-01-21 07:23:30 +00:00
2022-01-25 05:27:40 +00:00
query := fmt . Sprintf ( "FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d" , models . Collections . User , pagination . Offset , pagination . Limit )
2022-07-10 16:19:33 +00:00
cursor , err := p . db . Query ( sctx , query , nil )
2022-01-21 07:23:30 +00:00
if err != nil {
2022-01-25 05:27:40 +00:00
return nil , err
2022-01-21 07:23:30 +00:00
}
defer cursor . Close ( )
2022-01-25 05:27:40 +00:00
paginationClone := pagination
paginationClone . Total = cursor . Statistics ( ) . FullCount ( )
2022-01-21 07:23:30 +00:00
for {
2023-07-31 11:12:11 +00:00
var user * models . User
2022-07-10 16:19:33 +00:00
meta , err := cursor . ReadDocument ( ctx , & user )
2022-01-25 05:27:40 +00:00
if arangoDriver . IsNoMoreDocuments ( err ) {
2022-01-21 07:23:30 +00:00
break
} else if err != nil {
2022-01-25 05:27:40 +00:00
return nil , err
2022-01-21 07:23:30 +00:00
}
if meta . Key != "" {
2022-01-25 05:27:40 +00:00
users = append ( users , user . AsAPIUser ( ) )
2022-01-21 07:23:30 +00:00
}
}
2022-01-25 05:27:40 +00:00
return & model . Users {
2023-07-31 11:12:11 +00:00
Pagination : paginationClone ,
2022-01-25 05:27:40 +00:00
Users : users ,
} , nil
2022-01-21 07:23:30 +00:00
}
// GetUserByEmail to get user information from database using email address
2023-07-31 11:12:11 +00:00
func ( p * provider ) GetUserByEmail ( ctx context . Context , email string ) ( * models . User , error ) {
var user * models . User
2022-01-21 07:23:30 +00:00
query := fmt . Sprintf ( "FOR d in %s FILTER d.email == @email RETURN d" , models . Collections . User )
bindVars := map [ string ] interface { } {
"email" : email ,
}
2022-07-10 16:19:33 +00:00
cursor , err := p . db . Query ( ctx , query , bindVars )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
defer cursor . Close ( )
for {
if ! cursor . HasMore ( ) {
2023-08-01 10:39:17 +00:00
if user == nil {
2022-01-21 07:23:30 +00:00
return user , fmt . Errorf ( "user not found" )
}
break
}
2022-07-10 16:19:33 +00:00
_ , err := cursor . ReadDocument ( ctx , & user )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
}
return user , nil
}
// GetUserByID to get user information from database using user ID
2023-07-31 11:12:11 +00:00
func ( p * provider ) GetUserByID ( ctx context . Context , id string ) ( * models . User , error ) {
var user * models . User
2022-01-21 07:23:30 +00:00
query := fmt . Sprintf ( "FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d" , models . Collections . User )
bindVars := map [ string ] interface { } {
"id" : id ,
}
2022-07-10 16:19:33 +00:00
cursor , err := p . db . Query ( ctx , query , bindVars )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
defer cursor . Close ( )
for {
if ! cursor . HasMore ( ) {
2023-08-01 10:39:17 +00:00
if user == nil {
2022-01-21 07:23:30 +00:00
return user , fmt . Errorf ( "user not found" )
}
break
}
2022-07-10 16:19:33 +00:00
_ , err := cursor . ReadDocument ( ctx , & user )
2022-01-21 07:23:30 +00:00
if err != nil {
return user , err
}
}
return user , nil
}
2022-08-02 08:42:36 +00:00
// UpdateUsers to update multiple users, with parameters of user IDs slice
// If ids set to nil / empty all the users will be updated
func ( p * provider ) UpdateUsers ( ctx context . Context , data map [ string ] interface { } , ids [ ] string ) error {
// set updated_at time for all users
data [ "updated_at" ] = time . Now ( ) . Unix ( )
userInfoBytes , err := json . Marshal ( data )
if err != nil {
return err
}
query := ""
2023-02-06 12:44:19 +00:00
if len ( ids ) > 0 {
2022-08-02 08:42:36 +00:00
keysArray := ""
for _ , id := range ids {
keysArray += fmt . Sprintf ( "'%s', " , id )
}
keysArray = strings . Trim ( keysArray , " " )
keysArray = strings . TrimSuffix ( keysArray , "," )
query = fmt . Sprintf ( "FOR u IN %s FILTER u._id IN [%s] UPDATE u._key with %s IN %s" , models . Collections . User , keysArray , string ( userInfoBytes ) , models . Collections . User )
} else {
query = fmt . Sprintf ( "FOR u IN %s UPDATE u._key with %s IN %s" , models . Collections . User , string ( userInfoBytes ) , models . Collections . User )
}
_ , err = p . db . Query ( ctx , query , nil )
if err != nil {
return err
}
return nil
}
2022-12-21 17:44:24 +00:00
// GetUserByPhoneNumber to get user information from database using phone number
func ( p * provider ) GetUserByPhoneNumber ( ctx context . Context , phoneNumber string ) ( * models . User , error ) {
2023-07-31 11:12:11 +00:00
var user * models . User
2022-12-21 17:44:24 +00:00
query := fmt . Sprintf ( "FOR d in %s FILTER d.phone_number == @phone_number RETURN d" , models . Collections . User )
bindVars := map [ string ] interface { } {
"phone_number" : phoneNumber ,
}
cursor , err := p . db . Query ( ctx , query , bindVars )
if err != nil {
return nil , err
}
defer cursor . Close ( )
for {
if ! cursor . HasMore ( ) {
2023-08-01 10:39:17 +00:00
if user == nil {
2022-12-21 17:44:24 +00:00
return nil , fmt . Errorf ( "user not found" )
}
break
}
_ , err := cursor . ReadDocument ( ctx , & user )
if err != nil {
return nil , err
}
}
2023-07-31 11:12:11 +00:00
return user , nil
2022-12-21 17:44:24 +00:00
}