2023-07-03 06:23:32 +00:00
|
|
|
package adapter
|
2022-10-03 05:19:33 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-10-09 08:19:31 +00:00
|
|
|
"database/sql"
|
|
|
|
"errors"
|
2022-10-03 05:19:33 +00:00
|
|
|
"fmt"
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
"gitea.dwysokinski.me/twhelp/dcbot/internal/adapter/internal/bunmodel"
|
2022-10-03 05:19:33 +00:00
|
|
|
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
|
2022-10-10 05:16:40 +00:00
|
|
|
"github.com/google/uuid"
|
2023-06-18 06:47:51 +00:00
|
|
|
"github.com/jackc/pgerrcode"
|
2022-10-03 05:19:33 +00:00
|
|
|
"github.com/uptrace/bun"
|
2023-06-18 06:47:51 +00:00
|
|
|
"github.com/uptrace/bun/driver/pgdriver"
|
2022-10-03 05:19:33 +00:00
|
|
|
)
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
type GroupBun struct {
|
2022-10-03 05:19:33 +00:00
|
|
|
db *bun.DB
|
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func NewGroupBun(db *bun.DB) *GroupBun {
|
|
|
|
return &GroupBun{db: db}
|
2022-10-03 05:19:33 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) Create(ctx context.Context, params domain.CreateGroupParams) (domain.GroupWithMonitors, error) {
|
|
|
|
group := bunmodel.Group{
|
2022-10-11 05:05:35 +00:00
|
|
|
ServerID: params.ServerID(),
|
|
|
|
VersionCode: params.VersionCode(),
|
|
|
|
ChannelGains: params.ChannelGains(),
|
|
|
|
ChannelLosses: params.ChannelLosses(),
|
|
|
|
ServerKey: params.ServerKey(),
|
2022-10-28 11:59:20 +00:00
|
|
|
Barbarians: params.Barbarians(),
|
|
|
|
Internals: params.Internals(),
|
2023-06-30 05:09:52 +00:00
|
|
|
LanguageTag: params.LanguageTag(),
|
2022-10-03 05:19:33 +00:00
|
|
|
}
|
2022-10-09 08:19:31 +00:00
|
|
|
|
2022-10-03 05:19:33 +00:00
|
|
|
if _, err := g.db.NewInsert().
|
|
|
|
Model(&group).
|
|
|
|
Returning("*").
|
|
|
|
Exec(ctx); err != nil {
|
2023-06-18 06:47:51 +00:00
|
|
|
return domain.GroupWithMonitors{}, fmt.Errorf("something went wrong while inserting group into the db: %w", err)
|
2022-10-03 05:19:33 +00:00
|
|
|
}
|
2022-10-09 08:19:31 +00:00
|
|
|
|
2022-10-03 05:19:33 +00:00
|
|
|
return group.ToDomain(), nil
|
|
|
|
}
|
2022-10-09 08:19:31 +00:00
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) Update(ctx context.Context, id string, params domain.UpdateGroupParams) (domain.GroupWithMonitors, error) {
|
2022-10-10 05:16:40 +00:00
|
|
|
if params.IsZero() {
|
2023-06-18 06:47:51 +00:00
|
|
|
return domain.GroupWithMonitors{}, domain.ErrNothingToUpdate
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := uuid.Parse(id); err != nil {
|
2023-06-18 06:47:51 +00:00
|
|
|
return domain.GroupWithMonitors{}, domain.GroupNotFoundError{ID: id}
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
var group bunmodel.Group
|
2022-10-10 05:16:40 +00:00
|
|
|
|
|
|
|
res, err := g.db.NewUpdate().
|
|
|
|
Model(&group).
|
2023-06-18 06:47:51 +00:00
|
|
|
Returning("NULL").
|
2022-10-10 05:16:40 +00:00
|
|
|
Where("id = ?", id).
|
|
|
|
Apply(updateGroupsParamsApplier{params}.apply).
|
|
|
|
Exec(ctx)
|
|
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
2023-06-18 06:47:51 +00:00
|
|
|
return domain.GroupWithMonitors{}, fmt.Errorf("couldn't update group (id=%s): %w", id, err)
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
if affected, _ := res.RowsAffected(); affected == 0 {
|
2023-06-18 06:47:51 +00:00
|
|
|
return domain.GroupWithMonitors{}, domain.GroupNotFoundError{ID: id}
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
2023-06-18 06:47:51 +00:00
|
|
|
return g.Get(ctx, id)
|
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) AddMonitor(ctx context.Context, id string, tribeID int64) (domain.GroupWithMonitors, error) {
|
2023-06-18 06:47:51 +00:00
|
|
|
parsedID, err := uuid.Parse(id)
|
|
|
|
if err != nil {
|
|
|
|
return domain.GroupWithMonitors{}, domain.GroupDoesNotExistError{ID: id}
|
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
monitor := bunmodel.Monitor{
|
2023-06-18 06:47:51 +00:00
|
|
|
GroupID: parsedID,
|
|
|
|
TribeID: tribeID,
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = g.db.NewInsert().
|
|
|
|
Model(&monitor).
|
|
|
|
Returning("NULL").
|
|
|
|
Exec(ctx); err != nil {
|
|
|
|
|
|
|
|
return domain.GroupWithMonitors{}, fmt.Errorf(
|
|
|
|
"something went wrong while inserting monitor into the db: %w",
|
|
|
|
mapAddMonitorError(err, id, tribeID),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return g.Get(ctx, id)
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) DeleteMonitors(ctx context.Context, id string, monitorIDs ...string) (domain.GroupWithMonitors, error) {
|
2023-06-18 06:47:51 +00:00
|
|
|
if _, err := uuid.Parse(id); err != nil {
|
|
|
|
return domain.GroupWithMonitors{}, domain.GroupNotFoundError{ID: id}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, monitorID := range monitorIDs {
|
|
|
|
if _, err := uuid.Parse(monitorID); err != nil {
|
|
|
|
return domain.GroupWithMonitors{}, domain.MonitorNotFoundError{ID: monitorID}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := g.db.NewDelete().
|
2023-07-03 06:23:32 +00:00
|
|
|
Model(&bunmodel.Monitor{}).
|
2023-06-18 06:47:51 +00:00
|
|
|
Returning("NULL").
|
|
|
|
Where("id IN (?)", bun.In(monitorIDs)).
|
|
|
|
Where("group_id = ?", id).
|
|
|
|
Exec(ctx)
|
|
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return domain.GroupWithMonitors{}, fmt.Errorf("couldn't delete monitors: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return g.Get(ctx, id)
|
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) List(ctx context.Context, params domain.ListGroupsParams) ([]domain.GroupWithMonitors, error) {
|
|
|
|
var groups []bunmodel.Group
|
2022-10-09 08:19:31 +00:00
|
|
|
|
|
|
|
if err := g.db.NewSelect().
|
|
|
|
Model(&groups).
|
|
|
|
Order("created_at ASC").
|
2023-06-18 06:47:51 +00:00
|
|
|
Relation("Monitors", func(q *bun.SelectQuery) *bun.SelectQuery {
|
|
|
|
return q.Order("created_at ASC")
|
|
|
|
}).
|
2022-10-09 08:19:31 +00:00
|
|
|
Apply(listGroupsParamsApplier{params}.apply).
|
|
|
|
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return nil, fmt.Errorf("couldn't select groups from the db: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-06-18 06:47:51 +00:00
|
|
|
result := make([]domain.GroupWithMonitors, 0, len(groups))
|
2022-10-23 06:20:48 +00:00
|
|
|
for _, group := range groups {
|
|
|
|
result = append(result, group.ToDomain())
|
2022-10-09 08:19:31 +00:00
|
|
|
}
|
2022-10-23 06:20:48 +00:00
|
|
|
|
2022-10-09 08:19:31 +00:00
|
|
|
return result, nil
|
2022-10-12 05:19:07 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) Get(ctx context.Context, id string) (domain.GroupWithMonitors, error) {
|
2023-06-18 06:47:51 +00:00
|
|
|
group, err := g.get(ctx, id, true)
|
|
|
|
if err != nil {
|
|
|
|
return domain.GroupWithMonitors{}, err
|
|
|
|
}
|
|
|
|
return group.ToDomain(), nil
|
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) get(ctx context.Context, id string, withMonitors bool) (bunmodel.Group, error) {
|
2022-10-23 06:20:48 +00:00
|
|
|
if _, err := uuid.Parse(id); err != nil {
|
2023-07-03 06:23:32 +00:00
|
|
|
return bunmodel.Group{}, domain.GroupNotFoundError{ID: id}
|
2022-10-23 06:20:48 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
var group bunmodel.Group
|
2022-10-23 06:20:48 +00:00
|
|
|
|
2023-06-18 06:47:51 +00:00
|
|
|
q := g.db.NewSelect().
|
2022-10-23 06:20:48 +00:00
|
|
|
Model(&group).
|
2023-06-18 06:47:51 +00:00
|
|
|
Where("id = ?", id)
|
|
|
|
if withMonitors {
|
|
|
|
q = q.Relation("Monitors", func(q *bun.SelectQuery) *bun.SelectQuery {
|
|
|
|
return q.Order("created_at ASC")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := q.Scan(ctx); err != nil {
|
2022-10-23 06:20:48 +00:00
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
2023-07-03 06:23:32 +00:00
|
|
|
return bunmodel.Group{}, domain.GroupNotFoundError{ID: id}
|
2022-10-23 06:20:48 +00:00
|
|
|
}
|
2023-07-03 06:23:32 +00:00
|
|
|
return bunmodel.Group{}, fmt.Errorf("couldn't select group (id=%s) from the db: %w", id, err)
|
2022-10-23 06:20:48 +00:00
|
|
|
}
|
|
|
|
|
2023-06-18 06:47:51 +00:00
|
|
|
return group, nil
|
2022-10-23 06:20:48 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) Delete(ctx context.Context, id string) error {
|
2022-10-12 05:19:07 +00:00
|
|
|
if _, err := uuid.Parse(id); err != nil {
|
|
|
|
return domain.GroupNotFoundError{ID: id}
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := g.db.NewDelete().
|
2023-07-03 06:23:32 +00:00
|
|
|
Model(&bunmodel.Group{}).
|
2022-10-12 05:19:07 +00:00
|
|
|
Returning("NULL").
|
|
|
|
Where("id = ?", id).
|
|
|
|
Exec(ctx)
|
|
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
2023-06-18 06:47:51 +00:00
|
|
|
return fmt.Errorf("couldn't delete group (id=%s): %w", id, err)
|
2022-10-12 05:19:07 +00:00
|
|
|
}
|
|
|
|
if affected, _ := res.RowsAffected(); affected == 0 {
|
|
|
|
return domain.GroupNotFoundError{ID: id}
|
|
|
|
}
|
2023-06-18 06:47:51 +00:00
|
|
|
|
2022-10-12 05:19:07 +00:00
|
|
|
return nil
|
2022-10-09 08:19:31 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 06:23:32 +00:00
|
|
|
func (g *GroupBun) DeleteMany(ctx context.Context, ids ...string) error {
|
2022-10-31 05:52:20 +00:00
|
|
|
if len(ids) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := g.db.NewDelete().
|
2023-07-03 06:23:32 +00:00
|
|
|
Model(&bunmodel.Group{}).
|
2022-10-31 05:52:20 +00:00
|
|
|
Returning("NULL").
|
|
|
|
Where("id IN (?)", bun.In(ids)).
|
|
|
|
Exec(ctx)
|
|
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return fmt.Errorf("couldn't delete groups: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-10 05:16:40 +00:00
|
|
|
type updateGroupsParamsApplier struct {
|
|
|
|
params domain.UpdateGroupParams
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u updateGroupsParamsApplier) apply(q *bun.UpdateQuery) *bun.UpdateQuery {
|
2022-10-11 05:05:35 +00:00
|
|
|
if u.params.ChannelGains.Valid {
|
2022-10-11 05:29:42 +00:00
|
|
|
if u.params.ChannelGains.String != "" {
|
|
|
|
q = q.Set("channel_gains = ?", u.params.ChannelGains.String)
|
|
|
|
} else {
|
|
|
|
q = q.Set("channel_gains = NULL")
|
|
|
|
}
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
2022-10-11 05:05:35 +00:00
|
|
|
if u.params.ChannelLosses.Valid {
|
2022-10-11 05:29:42 +00:00
|
|
|
if u.params.ChannelLosses.String != "" {
|
|
|
|
q = q.Set("channel_losses = ?", u.params.ChannelLosses.String)
|
|
|
|
} else {
|
|
|
|
q = q.Set("channel_losses = NULL")
|
|
|
|
}
|
2022-10-10 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 05:09:52 +00:00
|
|
|
if u.params.LanguageTag.Valid {
|
|
|
|
if u.params.LanguageTag.String != "" {
|
|
|
|
q = q.Set("language_tag = ?", u.params.LanguageTag.String)
|
|
|
|
} else {
|
|
|
|
q = q.Set("language_tag = NULL")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-28 11:59:20 +00:00
|
|
|
if u.params.Barbarians.Valid {
|
|
|
|
q = q.Set("barbarians = ?", u.params.Barbarians.Bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
if u.params.Internals.Valid {
|
|
|
|
q = q.Set("internals = ?", u.params.Internals.Bool)
|
|
|
|
}
|
|
|
|
|
2022-10-10 05:16:40 +00:00
|
|
|
return q
|
|
|
|
}
|
|
|
|
|
2022-10-09 08:19:31 +00:00
|
|
|
type listGroupsParamsApplier struct {
|
|
|
|
params domain.ListGroupsParams
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l listGroupsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
2023-06-18 06:47:51 +00:00
|
|
|
if len(l.params.ServerIDs) > 0 {
|
2022-10-09 08:19:31 +00:00
|
|
|
q = q.Where("server_id IN (?)", bun.In(l.params.ServerIDs))
|
|
|
|
}
|
|
|
|
|
2022-10-29 05:24:52 +00:00
|
|
|
if l.params.EnabledNotifications.Valid {
|
|
|
|
if l.params.EnabledNotifications.Bool {
|
|
|
|
q = q.WhereGroup(" AND ", func(query *bun.SelectQuery) *bun.SelectQuery {
|
|
|
|
return q.WhereOr("channel_gains IS NOT NULL").
|
|
|
|
WhereOr("channel_losses IS NOT NULL")
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
q = q.Where("channel_gains IS NULL").Where("channel_losses IS NULL")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-31 05:52:20 +00:00
|
|
|
if l.params.VersionCode.Valid {
|
|
|
|
q = q.Where("version_code = ?", l.params.VersionCode.String)
|
|
|
|
}
|
|
|
|
|
2023-06-18 06:47:51 +00:00
|
|
|
if len(l.params.ServerKeys) > 0 {
|
2022-10-31 05:52:20 +00:00
|
|
|
q = q.Where("server_key IN (?)", bun.In(l.params.ServerKeys))
|
|
|
|
}
|
|
|
|
|
|
|
|
if !l.params.CreatedAtLTE.IsZero() {
|
|
|
|
q = q.Where("created_at <= ?", l.params.CreatedAtLTE)
|
|
|
|
}
|
|
|
|
|
2022-10-09 08:19:31 +00:00
|
|
|
return q
|
|
|
|
}
|
2023-06-18 06:47:51 +00:00
|
|
|
|
|
|
|
func mapAddMonitorError(err error, groupID string, tribeID int64) error {
|
|
|
|
var pgError pgdriver.Error
|
|
|
|
if !errors.As(err, &pgError) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
code := pgError.Field('C')
|
|
|
|
constraint := pgError.Field('n')
|
|
|
|
switch {
|
|
|
|
case code == pgerrcode.ForeignKeyViolation && constraint == "monitors_group_id_fkey":
|
|
|
|
return domain.GroupDoesNotExistError{
|
|
|
|
ID: groupID,
|
|
|
|
}
|
|
|
|
case code == pgerrcode.UniqueViolation && constraint == "monitors_group_id_tribe_id_key":
|
|
|
|
return domain.MonitorAlreadyExistsError{
|
|
|
|
TribeID: tribeID,
|
|
|
|
GroupID: groupID,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|