package dbengine import ( "context" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "nilfm.cc/git/felt/models" "time" ) const errNoCollection string = "collection not found: felt.%s" const errNoDocument string = "document with name/id '%s' doesn't exist in collection: %s" const errNotAString string = "document property is not a string: %s.%s" const errNotAnArray string = "doccument property is not an array: %s.%s" type DbAdapter interface { Init(mongoUri string) error CreateTable(table models.TableKey) error DestroyTable(table models.TableKey) error CheckTable(table models.TableKey) boolean InsertDiceRoll(table models.TableKey, diceRoll models.DiceRoll) error GetDiceRolls(table models.TableKey) ([]models.DiceRoll, error) SetMapImageUrl(table models.TableKey, url string) error GetMapImageUrl(table models.TableKey) (string, error) AddToken(table models.TableKey, token models.Token, active boolean) error RemoveToken(table models.TableKey, tokenId string, active boolean) error ModifyToken(table models.TableKey, token models.Token, active boolean) error GetTokens(table models.TableKey, active boolean) ([]models.Token, error) } type DbEngine struct { client mongo.Client db mongo.Database } func (self *DbEngine) mkCtx(timeoutSec int) context.Context { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) return ctx } func (self *DbEngine) Init(mongoUri string) error { client, err := mongo.NewClient(options.Client().ApplyURI(mongoUri)) if err != nil { return err } self.client = client ctx := self.mkCtx(10) err = client.Connect(ctx) if err != nil { return err } db := client.Database("felt") self.db = db err = self.ensureCollections(db) return err } func (self *DbEngine) ensureCollections(db mongo.Database) error { tables := db.Collection("tables") if tables == nil { createCmd := bson.D{ {"create", "tables"}, {"clusteredIndex", { {"key", {"name"}}, {"unique", true}, {"name", "idx_tables_unique_names"}, }}, } var createResult bson.M err := db.RunCommand( self.mkCtx(10), createCmd).Decode(&createResult) if err != nil { return err } } return nil } func (self *DbEngine) CreateTable(table models.TableKey) error { tables := self.db.Collection("tables") if tables != nil { _, err := tables.InsertOne(self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, {"mapImageUrl", ""}, {"diceRolls", bson.A{}}, {"tokens", bson.A{}}, {"availableTokens", bson.A{}}, }) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) DestroyTable(table models.TableKey) error { tables := self.db.Collection("tables") if tables != nil { _, err := tables.DeleteOne(self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) CheckTable(table models.TableKey) boolean { tables := self.db.Collection("tables") if tables != nil { _, err := tables.FindOne(self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }) if err != nil { return false } else { return true } } return false } func (self *DbEngine) InsertDiceRoll(table models.TableKey, diceRoll models.DiceRoll) error { tables := self.db.Collection("tables") if tables != nil { var result models.Table err := tables.FindOneAndUpdate( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }, bson.D{ {"$push", bson.D{ "diceRolls", bson.D{ {"$each", []models.DiceRoll{diceRoll}}, {"$slice", 1000}, }, }}, }, ).Decode(&result) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) GetDiceRolls(table models.TableKey) ([]models.DiceRoll, error) { tables := self.db.Colletion("tables") if tables != nil { var result models.Table err := tables.findOne( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }).Decode(&result) if err == nil { return result.DiceRolls, nil } else { return "", errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables")) } } return "", errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) SetMapImageUrl(table models.TableKey, url string) error { tables := self.db.Collection("tables") if tables != nil { var result models.Table err := tables.FindOneAndUpdate( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }, bson.D{ {"mapImageUrl", url}, }, ).Decode(&result) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) GetMapImageUrl(table models.TableKey) (string, error) { tables := self.db.Collection("tables") if tables != nil { var result models.Table err := tables.FindOne( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }).Decode(&result) if err == nil { return result.MapImageUrl, nil } else { return "", errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables")) } } return "", errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) AddToken(table models.TableKey, token models.Token, active boolean) error { tables := self.db.Collection("tables") if tables != nil { tokenArrKey := "tokens" if !active { tokenArrKey = "availableTokens" } var result models.Table err := tables.FindOneAndUpdate( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }, bson.D{ {"$push", bson.D{tokenArrKey, token}}, }, ).Decode(&result) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) RemoveToken(table models.TableKey, tokenId string, active boolean) error { tables := self.db.Collection("tables") if tables != nil { tokenArrKey := "tokens" if !active { tokenArrKey = "availableTokens" } var result models.Table err := tables.FindOneAndUpdate( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }, bson.D{ {"$pull", bson.D{tokenArrKey, bson.D{"_id", tokenId}}}, }, ).Decode(&result) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) ModifyToken(table models.TableKey, token models.Token, active boolean) error { tables := self.db.Collection("tables") if tables != nil { tokenArrKey := "tokens" if !active { tokenArrKey = "availableTokens" } var result models.Table err := tables.FindOneAndUpdate( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, {tokenArrKey, bson.D{"_id", tokenId}}, }, bson.D{ {"$set", bson.D{tokenArrKey + ".$", bson.D{ {"name", token.Name}, {"spriteUri", token.SpriteUri}, {"x", token.X}, {"y", token.Y}, }}}, }, ).Decode(&result) return err } return errors.New(fmt.Sprintf(errNoCollection, "tables")) } func (self *DbEngine) GetTokens(table models.TableKey, active boolean) ([]models.Token, error) { tables := self.db.Collection("tables") if tables != nil { var result models.Table err := tables.FindOne( self.mkCtx(10), bson.D{ {"name", table.Name}, {"passcode", table.Passcode}, }).Decode(&result) if err == nil { if active { return result.Tokens } else { return result.AvailableTokens } } else { return nil, errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables")) } } return nil, errors.New(fmt.Sprintf(errNoCollection, "tables")) }