felt/mongodb/adapter.go

428 lines
10 KiB
Go

package mongodb
import (
"context"
"errors"
"fmt"
"forge.lightcrystal.systems/nilix/felt/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"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<key=%s>.%s"
const errNotAnArray string = "document property is not an array: %s<key=%s>.%s"
const ErrNotFound string = "this token doesn't exist at this table; forget about it"
type DbAdapter interface {
Init(mongoUri string) error
CreateTable(table models.TableKey) error
DestroyTable(table models.TableKey) error
CheckTable(table models.TableKey) bool
GetProtocols() ([]string, error)
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)
SetAuxMessage(table models.TableKey, message string) error
GetAuxMessage(table models.TableKey) (string, error)
CheckToken(table models.TableKey, tokenId string) (bool, bool)
CreateToken(table models.TableKey, token models.Token) (string, error)
ActivateToken(table models.TableKey, tokenId string, active bool) error
MoveToken(table models.TableKey, token models.Token) error
DestroyToken(table models.TableKey, tokenId string) error
GetTokens(table models.TableKey, activeOnly bool) ([]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", bson.D{
{"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) bool {
tables := self.db.Collection("tables")
if tables != nil {
res := tables.FindOne(self.mkCtx(10), bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
})
return res != nil
}
return false
}
func (self *DbEngine) GetProtocols() ([]string, error) {
tables := self.db.Collection("tables")
if tables != nil {
var results []models.Table
cursor, err := tables.Find(self.mkCtx(10), bson.D{})
if err != nil {
return []string{}, err
}
if err = cursor.All(self.mkCtx(10), &results); err != nil {
return []string{}, err
}
var protocols []string
for _, t := range results {
protocols = append(protocols, fmt.Sprintf("%s.%s", t.Name, t.Passcode))
}
return protocols, nil
}
return []string{}, errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
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.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.DiceRolls, nil
} else {
return nil, errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables"))
}
}
return nil, 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{
{"$set", 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) SetAuxMessage(table models.TableKey, message 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{
{"$set", bson.D{{"auxMessage", message}}},
},
).Decode(&result)
return err
}
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) GetAuxMessage(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.AuxMessage, nil
} else {
return "", errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables"))
}
}
return "", errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) CheckToken(table models.TableKey, tokenId string) (bool, bool) {
tables := self.db.Collection("tables")
if tables != nil {
result := models.Table{}
err := tables.FindOne(self.mkCtx(10), bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
{"tokens._id", tokenId},
}).Decode(&result)
if err != nil {
fmt.Printf("%v", err)
return false, false
} else {
active := false
for _, t := range result.Tokens {
if t.Id != nil && *t.Id == tokenId && t.Active {
active = true
}
}
return true, active
}
}
return false, false
}
func (self *DbEngine) CreateToken(table models.TableKey, token models.Token) (string, error) {
tables := self.db.Collection("tables")
id := primitive.NewObjectID().Hex()
token.Id = &id
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{{"tokens", token}}},
},
).Decode(&result)
if err == nil {
return id, err
} else {
return "", err
}
}
return "", errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) ActivateToken(table models.TableKey, tokenId string, active bool) 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},
{"tokens._id", tokenId},
},
bson.D{
{"$set", bson.D{{"tokens.$.active", active}}},
},
).Decode(&result)
return err
}
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) MoveToken(table models.TableKey, token models.Token) 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},
{"tokens._id", token.Id},
},
bson.D{
{"$set", bson.D{{"tokens.$.x", token.X}, {"tokens.$.y", token.Y}}},
},
).Decode(&result)
return err
}
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) DestroyToken(table models.TableKey, tokenId 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{
{"$pull", bson.D{{"tokens", bson.D{{"_id", tokenId}}}}},
},
).Decode(&result)
return err
}
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) GetTokens(table models.TableKey, activeOnly bool) ([]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 {
tokens := []models.Token{}
for _, t := range result.Tokens {
if !activeOnly || t.Active {
tokens = append(tokens, t)
}
}
return tokens, nil
} else {
return nil, errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables"))
}
}
return nil, errors.New(fmt.Sprintf(errNoCollection, "tables"))
}