feat: add mongodb support (#82)

* feat: add mongodb enum

* fix: isMongodb var condition

* feat: add init mongodb connection

* feat: add mongodb operations for various db methods

* fix: error message
This commit is contained in:
Lakhan Samani
2021-12-20 23:21:27 +05:30
committed by GitHub
parent 2342f7c5c6
commit 65478296cb
10 changed files with 335 additions and 77 deletions

View File

@@ -23,7 +23,7 @@ func initArangodb() (arangoDriver.Database, error) {
return nil, err
}
client, err := arangoDriver.NewClient(arangoDriver.ClientConfig{
arangoClient, err := arangoDriver.NewClient(arangoDriver.ClientConfig{
Connection: conn,
})
if err != nil {
@@ -32,19 +32,19 @@ func initArangodb() (arangoDriver.Database, error) {
var arangodb driver.Database
arangodb_exists, err := client.DatabaseExists(nil, constants.DATABASE_NAME)
arangodb_exists, err := arangoClient.DatabaseExists(nil, constants.DATABASE_NAME)
if arangodb_exists {
log.Println(constants.DATABASE_NAME + " db exists already")
arangodb, err = client.Database(nil, constants.DATABASE_NAME)
arangodb, err = arangoClient.Database(nil, constants.DATABASE_NAME)
if err != nil {
return nil, err
}
} else {
arangodb, err = client.CreateDatabase(nil, constants.DATABASE_NAME, nil)
arangodb, err = arangoClient.CreateDatabase(nil, constants.DATABASE_NAME, nil)
if err != nil {
return nil, err
@@ -89,7 +89,6 @@ func initArangodb() (arangoDriver.Database, error) {
Sparse: true,
})
verificationRequestCollection.EnsureHashIndex(ctx, []string{"token"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})

View File

@@ -6,6 +6,7 @@ import (
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"go.mongodb.org/mongo-driver/mongo"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
@@ -32,6 +33,7 @@ type Manager interface {
type manager struct {
sqlDB *gorm.DB
arangodb arangoDriver.Database
mongodb *mongo.Database
}
// mainly used by nosql dbs
@@ -42,11 +44,12 @@ type CollectionList struct {
}
var (
IsSQL bool
IsArangoDB bool
Mgr Manager
Prefix = "authorizer_"
Collections = CollectionList{
IsORMSupported bool
IsArangoDB bool
IsMongoDB bool
Mgr Manager
Prefix = "authorizer_"
Collections = CollectionList{
User: Prefix + "users",
VerificationRequest: Prefix + "verification_requests",
Session: Prefix + "sessions",
@@ -57,8 +60,9 @@ func InitDB() {
var sqlDB *gorm.DB
var err error
IsSQL = constants.DATABASE_TYPE != enum.Arangodb.String()
IsORMSupported = constants.DATABASE_TYPE != enum.Arangodb.String() && constants.DATABASE_TYPE != enum.Mongodb.String()
IsArangoDB = constants.DATABASE_TYPE == enum.Arangodb.String()
IsMongoDB = constants.DATABASE_TYPE == enum.Mongodb.String()
// sql db orm config
ormConfig := &gorm.Config{
@@ -85,19 +89,31 @@ func InitDB() {
case enum.Arangodb.String():
arangodb, err := initArangodb()
if err != nil {
log.Fatal("error initing arangodb:", err)
log.Fatal("error initializing arangodb:", err)
}
Mgr = &manager{
sqlDB: nil,
mongodb: nil,
arangodb: arangodb,
}
break
case enum.Mongodb.String():
mongodb, err := initMongodb()
if err != nil {
log.Fatal("error initializing mongodb connection:", err)
}
Mgr = &manager{
sqlDB: nil,
arangodb: nil,
mongodb: mongodb,
}
}
// common for all sql dbs that are configured via gorm
if IsSQL {
// common for all sql dbs that are configured via go-orm
if IsORMSupported {
if err != nil {
log.Fatal("Failed to init sqlDB:", err)
} else {
@@ -106,6 +122,7 @@ func InitDB() {
Mgr = &manager{
sqlDB: sqlDB,
arangodb: nil,
mongodb: nil,
}
}
}

81
server/db/mongodb.go Normal file
View File

@@ -0,0 +1,81 @@
package db
import (
"context"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
func initMongodb() (*mongo.Database, error) {
mongodbOptions := options.Client().ApplyURI(constants.DATABASE_URL)
maxWait := time.Duration(5 * time.Second)
mongodbOptions.ConnectTimeout = &maxWait
mongoClient, err := mongo.NewClient(mongodbOptions)
if err != nil {
return nil, err
}
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
err = mongoClient.Connect(ctx)
if err != nil {
return nil, err
}
err = mongoClient.Ping(ctx, readpref.Primary())
if err != nil {
return nil, err
}
mongodb := mongoClient.Database(constants.DATABASE_NAME, options.Database())
mongodb.CreateCollection(ctx, Collections.User, options.CreateCollection())
userCollection := mongodb.Collection(Collections.User, options.Collection())
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"email": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
mongodb.CreateCollection(ctx, Collections.VerificationRequest, options.CreateCollection())
verificationRequestCollection := mongodb.Collection(Collections.VerificationRequest, options.Collection())
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"email": 1, "identifier": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"token": 1},
Options: options.Index().SetSparse(true),
},
}, options.CreateIndexes())
mongodb.CreateCollection(ctx, Collections.Session, options.CreateCollection())
sessionCollection := mongodb.Collection(Collections.Session, options.Collection())
sessionCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
return mongodb, nil
}

View File

@@ -5,19 +5,20 @@ import (
"time"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/mongo/options"
"gorm.io/gorm/clause"
)
type Session struct {
Key string `json:"_key,omitempty"` // for arangodb
ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id"`
UserID string `gorm:"type:char(36)" json:"user_id"`
User User `json:"-"`
UserAgent string `json:"user_agent"`
IP string `json:"ip"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"`
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id"`
User User `json:"-" bson:"-"`
UserAgent string `json:"user_agent" bson:"user_agent"`
IP string `json:"ip" bson:"ip"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
}
// AddSession function to save user sessiosn
@@ -26,16 +27,7 @@ func (mgr *manager) AddSession(session Session) error {
session.ID = uuid.New().String()
}
if session.CreatedAt == 0 {
session.CreatedAt = time.Now().Unix()
}
if session.UpdatedAt == 0 {
session.CreatedAt = time.Now().Unix()
}
if IsSQL {
// copy id as value for fields required for mongodb & arangodb
if IsORMSupported {
session.Key = session.ID
session.ObjectID = session.ID
res := mgr.sqlDB.Clauses(
@@ -49,7 +41,6 @@ func (mgr *manager) AddSession(session Session) error {
}
if IsArangoDB {
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
sessionCollection, _ := mgr.arangodb.Collection(nil, Collections.Session)
@@ -60,5 +51,18 @@ func (mgr *manager) AddSession(session Session) error {
}
}
if IsMongoDB {
session.Key = session.ID
session.ObjectID = session.ID
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
sessionCollection := mgr.mongodb.Collection(Collections.Session, options.Collection())
_, err := sessionCollection.InsertOne(nil, session)
if err != nil {
log.Println(`error saving session`, err)
return err
}
}
return nil
}

View File

@@ -1,7 +1,6 @@
package db
import (
"context"
"fmt"
"log"
"time"
@@ -9,23 +8,25 @@ import (
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"gorm.io/gorm/clause"
)
type User struct {
Key string `json:"_key,omitempty"` // for arangodb
ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `gorm:"unique" json:"email"`
Password string `gorm:"type:text" json:"password"`
SignupMethod string `json:"signup_method"`
EmailVerifiedAt int64 `json:"email_verified_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"`
Image string `gorm:"type:text" json:"image"`
Roles string `json:"roles"`
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
FirstName string `json:"first_name" bson:"first_name"`
LastName string `json:"last_name" bson:"last_name"`
Email string `gorm:"unique" json:"email" bson:"email"`
Password string `gorm:"type:text" json:"password" bson:"password"`
SignupMethod string `json:"signup_method" bson:"signup_method"`
EmailVerifiedAt int64 `json:"email_verified_at" bson:"email_verified_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
Image string `gorm:"type:text" json:"image" bson:"image"`
Roles string `json:"roles" bson:"roles"`
}
// AddUser function to add user even with email conflict
@@ -34,7 +35,7 @@ func (mgr *manager) AddUser(user User) (User, error) {
user.ID = uuid.New().String()
}
if IsSQL {
if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb
user.Key = user.ID
user.ObjectID = user.ID
@@ -53,9 +54,8 @@ func (mgr *manager) AddUser(user User) (User, error) {
if IsArangoDB {
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
ctx := context.Background()
userCollection, _ := mgr.arangodb.Collection(nil, Collections.User)
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user)
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(nil), user)
if err != nil {
log.Println("error adding user:", err)
return user, err
@@ -63,6 +63,20 @@ func (mgr *manager) AddUser(user User) (User, error) {
user.Key = meta.Key
user.ObjectID = meta.ID.String()
}
if IsMongoDB {
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
user.Key = user.ID
user.ObjectID = user.ID
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.InsertOne(nil, user)
if err != nil {
log.Println("error adding user:", err)
return user, err
}
}
return user, nil
}
@@ -70,7 +84,7 @@ func (mgr *manager) AddUser(user User) (User, error) {
func (mgr *manager) UpdateUser(user User) (User, error) {
user.UpdatedAt = time.Now().Unix()
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Save(&user)
if result.Error != nil {
@@ -90,6 +104,16 @@ func (mgr *manager) UpdateUser(user User) (User, error) {
user.Key = meta.Key
user.ObjectID = meta.ID.String()
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.UpdateOne(nil, bson.M{"id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
if err != nil {
log.Println("error updating user:", err)
return user, err
}
}
return user, nil
}
@@ -97,7 +121,7 @@ func (mgr *manager) UpdateUser(user User) (User, error) {
func (mgr *manager) GetUsers() ([]User, error) {
var users []User
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Find(&users)
if result.Error != nil {
log.Println("error getting users:", result.Error)
@@ -125,20 +149,37 @@ func (mgr *manager) GetUsers() ([]User, error) {
}
if meta.Key != "" {
user.Key = meta.Key
user.ObjectID = meta.ID.String()
users = append(users, user)
}
}
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
cursor, err := userCollection.Find(nil, bson.M{}, options.Find())
if err != nil {
log.Println("error getting users:", err)
return users, err
}
defer cursor.Close(nil)
for cursor.Next(nil) {
var user User
err := cursor.Decode(&user)
if err != nil {
return users, err
}
users = append(users, user)
}
}
return users, nil
}
func (mgr *manager) GetUserByEmail(email string) (User, error) {
var user User
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Where("email = ?", email).First(&user)
if result.Error != nil {
@@ -172,13 +213,21 @@ func (mgr *manager) GetUserByEmail(email string) (User, error) {
}
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
err := userCollection.FindOne(nil, bson.M{"email": email}).Decode(&user)
if err != nil {
return user, err
}
}
return user, nil
}
func (mgr *manager) GetUserByID(id string) (User, error) {
var user User
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Where("id = ?", id).First(&user)
if result.Error != nil {
@@ -212,11 +261,19 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
}
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
err := userCollection.FindOne(nil, bson.M{"id": id}).Decode(&user)
if err != nil {
return user, err
}
}
return user, nil
}
func (mgr *manager) DeleteUser(user User) error {
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Delete(&user)
if result.Error != nil {
@@ -234,5 +291,14 @@ func (mgr *manager) DeleteUser(user User) error {
}
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.DeleteOne(nil, bson.M{"id": user.ID}, options.Delete())
if err != nil {
log.Println("error deleting user:", err)
return err
}
}
return nil
}

View File

@@ -3,22 +3,25 @@ package db
import (
"fmt"
"log"
"time"
"github.com/arangodb/go-driver"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"gorm.io/gorm/clause"
)
type VerificationRequest struct {
Key string `json:"_key,omitempty"` // for arangodb
ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id"`
Token string `gorm:"type:text" json:"token"`
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier"`
ExpiresAt int64 `json:"expires_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email"`
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
Token string `gorm:"type:text" json:"token" bson:"token"`
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
}
// AddVerification function to add verification record
@@ -26,7 +29,7 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
if verification.ID == "" {
verification.ID = uuid.New().String()
}
if IsSQL {
if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb
verification.Key = verification.ID
verification.ObjectID = verification.ID
@@ -42,14 +45,31 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
}
if IsArangoDB {
verification.CreatedAt = time.Now().Unix()
verification.UpdatedAt = time.Now().Unix()
verificationRequestCollection, _ := mgr.arangodb.Collection(nil, Collections.VerificationRequest)
meta, err := verificationRequestCollection.CreateDocument(nil, verification)
if err != nil {
log.Println("error saving verification record:", err)
return verification, err
}
verification.Key = meta.Key
verification.ObjectID = meta.ID.String()
}
if IsMongoDB {
verification.CreatedAt = time.Now().Unix()
verification.UpdatedAt = time.Now().Unix()
verification.Key = verification.ID
verification.ObjectID = verification.ID
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.InsertOne(nil, verification)
if err != nil {
log.Println("error saving verification record:", err)
return verification, err
}
}
return verification, nil
}
@@ -57,7 +77,7 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) {
var verificationRequests []VerificationRequest
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Find(&verificationRequests)
if result.Error != nil {
log.Println("error getting verification requests:", result.Error)
@@ -85,20 +105,38 @@ func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) {
}
if meta.Key != "" {
verificationRequest.Key = meta.Key
verificationRequest.ObjectID = meta.ID.String()
verificationRequests = append(verificationRequests, verificationRequest)
}
}
}
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, options.Find())
if err != nil {
log.Println("error getting verification requests:", err)
return verificationRequests, err
}
defer cursor.Close(nil)
for cursor.Next(nil) {
var verificationRequest VerificationRequest
err := cursor.Decode(&verificationRequest)
if err != nil {
return verificationRequests, err
}
verificationRequests = append(verificationRequests, verificationRequest)
}
}
return verificationRequests, nil
}
func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, error) {
var verification VerificationRequest
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Where("token = ?", token).First(&verification)
if result.Error != nil {
@@ -133,12 +171,20 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e
}
}
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
err := verificationRequestCollection.FindOne(nil, bson.M{"token": token}).Decode(&verification)
if err != nil {
return verification, err
}
}
return verification, nil
}
func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) {
var verification VerificationRequest
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Where("email = ?", email).First(&verification)
if result.Error != nil {
@@ -173,11 +219,19 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e
}
}
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
err := verificationRequestCollection.FindOne(nil, bson.M{"email": email}).Decode(&verification)
if err != nil {
return verification, err
}
}
return verification, nil
}
func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRequest) error {
if IsSQL {
if IsORMSupported {
result := mgr.sqlDB.Delete(&verificationRequest)
if result.Error != nil {
@@ -195,5 +249,14 @@ func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRe
}
}
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"id": verificationRequest.ID}, options.Delete())
if err != nil {
log.Println("error deleting verification request::", err)
return err
}
}
return nil
}