heruvym/dal/dal.go

398 lines
7.2 KiB
Go
Raw Normal View History

2021-04-10 18:46:51 +00:00
package dal
import (
2021-05-02 22:25:12 +00:00
"bitbucket.org/skeris/heruvym/model"
2021-04-10 18:46:51 +00:00
"context"
"github.com/rs/xid"
"github.com/themakers/hlog"
2021-05-01 12:36:22 +00:00
"go.mongodb.org/mongo-driver/bson"
2021-04-10 18:46:51 +00:00
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"time"
)
2021-04-11 09:48:15 +00:00
const (
collMessages = "messages"
collTickets = "tickets"
)
2021-04-10 18:46:51 +00:00
type DAL struct {
2021-04-11 09:48:15 +00:00
logger hlog.Logger
colMsg, colTck *mongo.Collection
client *mongo.Client
2021-04-10 18:46:51 +00:00
}
type ErrorConnectToDB struct {
Err error
MongoURI string
}
type ErrorPingDB struct {
Err error
MongoURI string
}
type InfoPing struct {
MongoURI string
2021-04-11 09:48:15 +00:00
Nanoseconds int64
2021-04-10 18:46:51 +00:00
}
func New(
ctx context.Context,
mongoURI, database string,
log hlog.Logger,
) (*DAL, error) {
client, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
if err != nil {
log.Emit(ErrorConnectToDB{
Err: err,
MongoURI: mongoURI,
})
return nil, err
}
2021-04-11 09:48:15 +00:00
before := time.Now().Unix()
2021-04-10 18:46:51 +00:00
if err := client.Ping(ctx, readpref.PrimaryPreferred()); err != nil {
log.Emit(ErrorPingDB{
Err: err,
MongoURI: mongoURI,
})
return nil, err
}
log.Emit(InfoPing{
MongoURI: mongoURI,
2021-04-11 09:48:15 +00:00
Nanoseconds: time.Now().Unix() - before,
2021-04-10 18:46:51 +00:00
})
return &DAL{
client: client,
2021-04-11 09:48:15 +00:00
colMsg: client.Database(database).Collection(collMessages),
colTck: client.Database(database).Collection(collTickets),
2021-04-10 18:46:51 +00:00
logger: log.Module("DAL"),
}, nil
}
type ErrorInsert struct {
2021-04-11 09:48:15 +00:00
Err error
2021-04-10 18:46:51 +00:00
UserID, SessionID string
}
func (d *DAL) PutMessage(
ctx context.Context,
2021-04-11 09:48:15 +00:00
message, userID, sessionID, ticketID string,
2021-04-10 18:46:51 +00:00
files []string,
2021-05-02 22:25:12 +00:00
) (*model.Message, error) {
insertable := model.Message{
2021-04-10 18:46:51 +00:00
ID: xid.New().String(),
UserID: userID,
SessionID: sessionID,
2021-04-11 09:48:15 +00:00
TicketID: ticketID,
2021-04-10 18:46:51 +00:00
Message: message,
Files: files,
2021-05-02 22:25:12 +00:00
Shown: map[string]int{},
2021-04-10 18:46:51 +00:00
CreatedAt: time.Now(),
2021-05-02 22:25:12 +00:00
}
if _, err := d.colMsg.InsertOne(ctx, &insertable); err != nil {
2021-04-10 18:46:51 +00:00
d.logger.Emit(ErrorInsert{
Err: err,
UserID: userID,
SessionID: sessionID,
})
2021-05-02 22:25:12 +00:00
return nil, err
2021-04-10 18:46:51 +00:00
}
2021-05-02 22:25:12 +00:00
return &insertable, nil
2021-04-10 18:46:51 +00:00
}
2021-04-11 09:48:15 +00:00
func (d *DAL) CreateTicket(
ctx context.Context,
userID,
sessionID,
2021-05-02 22:25:12 +00:00
title, message string,
files []string,
2021-04-11 09:48:15 +00:00
) (string, error) {
2021-05-02 22:25:12 +00:00
ticketID := xid.New().String()
2021-04-11 09:48:15 +00:00
if _, err := d.colTck.InsertOne(ctx, &model.Ticket{
2021-05-02 22:25:12 +00:00
ID: ticketID,
2021-04-11 09:48:15 +00:00
UserID: userID,
SessionID: sessionID,
Title: title,
2021-05-02 22:25:12 +00:00
State: model.StateOpen,
2021-04-11 09:53:26 +00:00
CreatedAt: time.Now(),
2021-05-02 22:25:12 +00:00
TopMessage: model.Message{
ID: xid.New().String(),
UserID: userID,
SessionID: sessionID,
TicketID: ticketID,
Message: message,
Files: files,
CreatedAt: time.Now(),
},
2021-04-11 09:48:15 +00:00
}); err != nil {
d.logger.Emit(ErrorInsert{
Err: err,
UserID: userID,
SessionID: sessionID,
})
return "", err
}
2021-05-02 22:25:12 +00:00
return ticketID, nil
2021-04-11 09:48:15 +00:00
}
2021-05-01 12:36:22 +00:00
func (d *DAL) GetTickets4Sess(ctx context.Context, sessID string) ([]model.Ticket, error) {
cur, err := d.colTck.Find(ctx, bson.M{
"SessionID": sessID,
})
if err != nil {
return nil, err
}
var result []model.Ticket
if err := cur.All(ctx, &result); err != nil {
return nil, err
}
return result, nil
}
2021-05-02 22:25:12 +00:00
func (d *DAL) GetTicket4Sess(
ctx context.Context,
ticketID,
sessID string) (*model.Ticket, error) {
var result model.Ticket
if err := d.colTck.FindOne(ctx, bson.M{
"_id": ticketID,
"SessionID": sessID,
}).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
func (d *DAL) GetTicket4User(
ctx context.Context,
ticketID,
userID string) (*model.Ticket, error) {
var result model.Ticket
if err := d.colTck.FindOne(ctx, bson.M{
"_id": ticketID,
"UserID": userID,
}).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
func (d *DAL) WatchTickets(
ctx context.Context,
userID string,
yield func(ticket model.Ticket) error) error {
operationTypes := []bson.D{{{"operationType", "insert"}},
{{"operationType", "update"}}}
matchStage := bson.D{
{
"$and", bson.D{
{"$or", operationTypes},
{"fullDocument.UserID", userID},
}},
}
matchPipeline := bson.D{
{"$match", matchStage},
}
cs, err := d.colTck.Watch(ctx, matchPipeline,
options.ChangeStream().SetFullDocument(options.Default))
if err != nil {
return err
}
var piece model.Ticket
for cs.Next(ctx) {
if err := cs.Decode(&piece); err != nil {
return err
}
if err := yield(piece); err != nil {
return err
}
}
return nil
}
func (d *DAL) YieldUserTickets(
ctx context.Context,
userID string,
yield func(ticket model.Ticket) error) error {
cursor, err := d.colTck.Find(ctx, bson.M{
"UserID": userID,
})
if err != nil {
return err
}
var piece model.Ticket
for cursor.Next(ctx) {
if err := cursor.Decode(&piece); err != nil {
return err
}
if err := yield(piece); err != nil {
return err
}
}
return nil
}
func (d *DAL) WatchActiveTickets(ctx context.Context, yield func(ticket model.Ticket) error) error {
operationTypes := []bson.D{{{"operationType", "insert"}},
{{"operationType", "update"}}}
matchStage := bson.D{{"$and", []bson.D{
{
{"$or", operationTypes},
},
{
{
"fullDocument.State", bson.D{{
"$ne", model.StateClose,
}},
},
},
}}}
matchPipeline := bson.D{
{"$match", matchStage},
}
cs, err := d.colTck.Watch(ctx, matchPipeline,
options.ChangeStream().SetFullDocument(options.Default))
if err != nil {
return err
}
var piece model.Ticket
for cs.Next(ctx) {
if err := cs.Decode(&piece); err != nil {
return err
}
if err := yield(piece); err != nil {
return err
}
}
return nil
}
func (d *DAL) UpdateTopMessage(ctx context.Context, ticketID string, msg *model.Message) error {
if err := d.colTck.FindOneAndUpdate(ctx, bson.M{
"_id": ticketID,
}, bson.M{
"$set": bson.M{
"UpdatedAt": time.Now(),
"TopMessage": msg,
},
}).Decode(&model.Ticket{}); err != nil {
return err
}
return nil
}
func (d *DAL) YieldMessages(
ctx context.Context,
ticketID string,
yield func(ticket model.Message) error) error {
cursor, err := d.colTck.Find(ctx, bson.M{
"TicketID": ticketID,
})
if err != nil {
return err
}
var piece model.Message
for cursor.Next(ctx) {
if err := cursor.Decode(&piece); err != nil {
return err
}
if err := yield(piece); err != nil {
return err
}
}
return nil
}
func (d *DAL) WatchMessages(
ctx context.Context, ticketID string, yield func(ticket model.Message) error) error {
operationTypes := []bson.D{{{"operationType", "insert"}},
{{"operationType", "update"}}}
matchStage := bson.D{{"$and", []bson.D{
{
{"$or", operationTypes},
},
{
{
"fullDocument.TicketID", ticketID,
},
},
}}}
matchPipeline := bson.D{
{"$match", matchStage},
}
cs, err := d.colMsg.Watch(ctx, matchPipeline,
options.ChangeStream().SetFullDocument(options.Default))
if err != nil {
return err
}
var piece model.Message
for cs.Next(ctx) {
if err := cs.Decode(&piece); err != nil {
return err
}
if err := yield(piece); err != nil {
return err
}
}
return nil
}
func (d *DAL) SetShown(ctx context.Context, messageID, userID string) error {
if err := d.colMsg.FindOneAndUpdate(ctx, bson.M{
"_id": messageID,
}, bson.M{
"$set": bson.M{
userID: 1,
},
}).Decode(&model.Message{}); err != nil {
return err
}
return nil
}