2023-01-17 20:08:00 +00:00
package couchbase
import (
"context"
"errors"
"fmt"
"reflect"
2023-02-02 07:13:17 +00:00
"strconv"
2023-02-02 06:58:52 +00:00
"strings"
"time"
2023-01-17 20:08:00 +00:00
2023-01-24 23:49:01 +00:00
"github.com/couchbase/gocb/v2"
2023-01-17 20:08:00 +00:00
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/memorystore"
)
2023-01-24 23:49:01 +00:00
const (
defaultBucketName = "authorizer"
defaultScope = "_default"
)
2023-01-17 20:08:00 +00:00
type provider struct {
db * gocb . Scope
scopeName string
}
2023-01-24 23:49:01 +00:00
// NewProvider returns a new Couchbase provider
2023-01-17 20:08:00 +00:00
func NewProvider ( ) ( * provider , error ) {
2023-01-24 23:49:01 +00:00
bucketName := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . CouchbaseBucket
scopeName := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . CouchbaseScope
2023-01-17 20:08:00 +00:00
dbURL := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . DatabaseURL
userName := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . DatabaseUsername
password := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . DatabasePassword
opts := gocb . ClusterOptions {
Username : userName ,
Password : password ,
}
2023-01-24 23:49:01 +00:00
if bucketName == "" {
bucketName = defaultBucketName
}
if scopeName == "" {
scopeName = defaultScope
}
2023-01-17 20:08:00 +00:00
cluster , err := gocb . Connect ( dbURL , opts )
if err != nil {
return nil , err
}
// To create the bucket and scope if not exist
bucket , err := CreateBucketAndScope ( cluster , bucketName , scopeName )
if err != nil {
return nil , err
}
scope := bucket . Scope ( scopeName )
scopeIdentifier := fmt . Sprintf ( "%s.%s" , bucketName , scopeName )
v := reflect . ValueOf ( models . Collections )
for i := 0 ; i < v . NumField ( ) ; i ++ {
2023-01-31 05:48:20 +00:00
collectionName := v . Field ( i )
2023-01-17 20:08:00 +00:00
user := gocb . CollectionSpec {
2023-01-31 05:48:20 +00:00
Name : collectionName . String ( ) ,
2023-01-17 20:08:00 +00:00
ScopeName : scopeName ,
}
collectionOpts := gocb . CreateCollectionOptions {
Context : context . TODO ( ) ,
}
2023-01-24 23:49:01 +00:00
err = bucket . Collections ( ) . CreateCollection ( user , & collectionOpts )
if err != nil && ! errors . Is ( err , gocb . ErrCollectionExists ) {
return nil , err
}
2023-02-02 06:58:52 +00:00
// TODO: find how to fix this sleep time.
// Add wait time for successful collection creation
time . Sleep ( 5 * time . Second )
2023-01-31 05:48:20 +00:00
indexQuery := fmt . Sprintf ( "CREATE PRIMARY INDEX ON %s.%s" , scopeIdentifier , collectionName . String ( ) )
_ , err = scope . Query ( indexQuery , nil )
2023-02-02 06:58:52 +00:00
if err != nil && ! strings . Contains ( err . Error ( ) , "The index #primary already exists" ) {
return nil , err
2023-01-31 05:48:20 +00:00
}
2023-01-17 20:08:00 +00:00
}
indices := GetIndex ( scopeIdentifier )
for i := 0 ; i < v . NumField ( ) ; i ++ {
field := v . Field ( i )
for _ , indexQuery := range indices [ field . String ( ) ] {
scope . Query ( indexQuery , nil )
}
}
return & provider {
db : scope ,
scopeName : scopeIdentifier ,
} , nil
}
func CreateBucketAndScope ( cluster * gocb . Cluster , bucketName string , scopeName string ) ( * gocb . Bucket , error ) {
2023-02-02 07:13:17 +00:00
bucketRAMQuotaMB := memorystore . RequiredEnvStoreObj . GetRequiredEnv ( ) . CouchbaseBucketRAMQuotaMB
if bucketRAMQuotaMB == "" {
bucketRAMQuotaMB = "1000"
}
bucketRAMQuota , err := strconv . ParseInt ( bucketRAMQuotaMB , 10 , 64 )
if err != nil {
return nil , err
}
2023-01-17 20:08:00 +00:00
settings := gocb . BucketSettings {
Name : bucketName ,
2023-02-02 07:13:17 +00:00
RAMQuotaMB : uint64 ( bucketRAMQuota ) ,
2023-01-17 20:08:00 +00:00
BucketType : gocb . CouchbaseBucketType ,
EvictionPolicy : gocb . EvictionPolicyTypeValueOnly ,
FlushEnabled : true ,
CompressionMode : gocb . CompressionModeActive ,
}
2023-02-05 05:31:20 +00:00
shouldCreateBucket := false
// check if bucket exists
_ , err = cluster . Buckets ( ) . GetBucket ( bucketName , nil )
if err != nil {
// bucket not found
shouldCreateBucket = true
}
if shouldCreateBucket {
err = cluster . Buckets ( ) . CreateBucket ( gocb . CreateBucketSettings {
BucketSettings : settings ,
ConflictResolutionType : gocb . ConflictResolutionTypeSequenceNumber ,
} , nil )
2023-02-05 05:33:26 +00:00
if err != nil {
2023-02-05 05:31:20 +00:00
return nil , err
}
2023-01-17 20:08:00 +00:00
}
2023-02-05 05:31:20 +00:00
bucket := cluster . Bucket ( bucketName )
2023-01-24 23:49:01 +00:00
if scopeName != defaultScope {
err = bucket . Collections ( ) . CreateScope ( scopeName , nil )
if err != nil && ! errors . Is ( err , gocb . ErrScopeExists ) {
return bucket , err
}
2023-01-17 20:08:00 +00:00
}
return bucket , nil
}
func GetIndex ( scopeName string ) map [ string ] [ ] string {
indices := make ( map [ string ] [ ] string )
// User Index
userIndex1 := fmt . Sprintf ( "CREATE INDEX userEmailIndex ON %s.%s(email)" , scopeName , models . Collections . User )
userIndex2 := fmt . Sprintf ( "CREATE INDEX userPhoneIndex ON %s.%s(phone_number)" , scopeName , models . Collections . User )
indices [ models . Collections . User ] = [ ] string { userIndex1 , userIndex2 }
// VerificationRequest
verificationIndex1 := fmt . Sprintf ( "CREATE INDEX verificationRequestTokenIndex ON %s.%s(token)" , scopeName , models . Collections . VerificationRequest )
verificationIndex2 := fmt . Sprintf ( "CREATE INDEX verificationRequestEmailAndIdentifierIndex ON %s.%s(email,identifier)" , scopeName , models . Collections . VerificationRequest )
indices [ models . Collections . VerificationRequest ] = [ ] string { verificationIndex1 , verificationIndex2 }
// Session index
sessionIndex1 := fmt . Sprintf ( "CREATE INDEX SessionUserIdIndex ON %s.%s(user_id)" , scopeName , models . Collections . Session )
indices [ models . Collections . Session ] = [ ] string { sessionIndex1 }
// Webhook index
webhookIndex1 := fmt . Sprintf ( "CREATE INDEX webhookEventNameIndex ON %s.%s(event_name)" , scopeName , models . Collections . Webhook )
indices [ models . Collections . Webhook ] = [ ] string { webhookIndex1 }
// WebhookLog index
webhookLogIndex1 := fmt . Sprintf ( "CREATE INDEX webhookLogIdIndex ON %s.%s(webhook_id)" , scopeName , models . Collections . WebhookLog )
indices [ models . Collections . Webhook ] = [ ] string { webhookLogIndex1 }
// WebhookLog index
emailTempIndex1 := fmt . Sprintf ( "CREATE INDEX EmailTemplateEventNameIndex ON %s.%s(event_name)" , scopeName , models . Collections . EmailTemplate )
indices [ models . Collections . EmailTemplate ] = [ ] string { emailTempIndex1 }
// OTP index
otpIndex1 := fmt . Sprintf ( "CREATE INDEX OTPEmailIndex ON %s.%s(email)" , scopeName , models . Collections . OTP )
indices [ models . Collections . OTP ] = [ ] string { otpIndex1 }
2023-07-13 06:09:22 +00:00
// OTP index
otpIndex2 := fmt . Sprintf ( "CREATE INDEX OTPPhoneNumberIndex ON %s.%s(phone_number)" , scopeName , models . Collections . OTP )
indices [ models . Collections . OTP ] = [ ] string { otpIndex2 }
2023-01-17 20:08:00 +00:00
return indices
}