mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
The server module named github.com/hanzoai/team/server/v8 — a path that resolves to the retired workspace's repository, not this one — while server/public still published github.com/mattermost/mattermost/server/public. Both are github.com/hanzoteam/server/... now, which is where this code actually lives. That second half is what made the binary unbootable. Upstream's public path stayed claimed by upstream, mattermost-plugin-ai imported it, and the build linked public/model twice; public/plugin's gob.Register then ran twice under one name and panicked at init, before main. Reverting the rename traded the brand for a working binary. Rebranding the plugin alongside it needs no such trade: the agents fork is github.com/hanzoteam/agents/v2 against our public module, so no module named mattermost/mattermost is in the graph at all. Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and it is ours.
277 lines
7.3 KiB
Go
277 lines
7.3 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package sqlstore
|
|
|
|
import (
|
|
"database/sql"
|
|
"slices"
|
|
"strconv"
|
|
|
|
sq "github.com/mattermost/squirrel"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/hanzoteam/server/server/public/model"
|
|
"github.com/hanzoteam/server/server/v8/channels/store"
|
|
"github.com/hanzoteam/server/server/v8/channels/utils"
|
|
)
|
|
|
|
type SqlViewStore struct {
|
|
*SqlStore
|
|
viewSelect sq.SelectBuilder
|
|
}
|
|
|
|
func newSqlViewStore(sqlStore *SqlStore) store.ViewStore {
|
|
s := &SqlViewStore{
|
|
SqlStore: sqlStore,
|
|
}
|
|
|
|
s.viewSelect = s.getQueryBuilder().
|
|
Select(viewColumns()...).
|
|
From("Views")
|
|
|
|
return s
|
|
}
|
|
|
|
func viewColumns() []string {
|
|
return []string{
|
|
"Id", "ChannelId", "Type", "CreatorId", "Title",
|
|
"Description", "SortOrder", "Props",
|
|
"CreateAt", "UpdateAt", "DeleteAt",
|
|
}
|
|
}
|
|
|
|
func (s *SqlViewStore) Save(view *model.View) (*model.View, error) {
|
|
if err := s.saveViewT(s.GetMaster(), view); err != nil {
|
|
return nil, err
|
|
}
|
|
return view, nil
|
|
}
|
|
|
|
// saveViewT inserts a view row using the provided executor (either a
|
|
// transaction or a regular master DB handle). The caller is responsible for
|
|
// setting view.ChannelId before calling. PreSave + IsValid are run here so
|
|
// callers can't forget to validate.
|
|
func (s *SqlViewStore) saveViewT(ex sqlxExecutor, view *model.View) error {
|
|
view.PreSave()
|
|
if err := view.IsValid(); err != nil {
|
|
return err
|
|
}
|
|
|
|
builder := s.getQueryBuilder().
|
|
Insert("Views").
|
|
Columns(viewColumns()...).
|
|
Values(
|
|
view.Id, view.ChannelId, view.Type, view.CreatorId, view.Title,
|
|
view.Description, view.SortOrder, view.Props,
|
|
view.CreateAt, view.UpdateAt, view.DeleteAt,
|
|
)
|
|
|
|
if _, err := ex.ExecBuilder(builder); err != nil {
|
|
return errors.Wrap(err, "failed to save view")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SqlViewStore) Get(id string) (*model.View, error) {
|
|
builder := s.viewSelect.Where(sq.Eq{"Id": id, "DeleteAt": 0})
|
|
|
|
var view model.View
|
|
if err := s.GetReplica().GetBuilder(&view, builder); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, store.NewErrNotFound("View", id)
|
|
}
|
|
return nil, errors.Wrapf(err, "failed to get view with id=%s", id)
|
|
}
|
|
|
|
return &view, nil
|
|
}
|
|
|
|
func (s *SqlViewStore) GetForChannel(channelID string, opts model.ViewQueryOpts) ([]*model.View, error) {
|
|
if channelID == "" {
|
|
return nil, store.NewErrInvalidInput("View", "channelID", channelID)
|
|
}
|
|
|
|
if opts.PerPage <= 0 {
|
|
opts.PerPage = model.ViewQueryDefaultPerPage
|
|
} else if opts.PerPage > model.ViewQueryMaxPerPage {
|
|
opts.PerPage = model.ViewQueryMaxPerPage
|
|
}
|
|
|
|
if opts.Page < 0 {
|
|
opts.Page = 0
|
|
}
|
|
|
|
builder := s.viewSelect.
|
|
Where(sq.Eq{"ChannelId": channelID, "DeleteAt": 0}).
|
|
OrderBy("SortOrder ASC", "CreateAt ASC", "Id ASC").
|
|
Limit(uint64(opts.PerPage)).
|
|
Offset(uint64(opts.Page * opts.PerPage))
|
|
|
|
var rows []model.View
|
|
if err := s.GetReplica().SelectBuilder(&rows, builder); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to get views for channel %s", channelID)
|
|
}
|
|
|
|
views := make([]*model.View, len(rows))
|
|
for i := range rows {
|
|
views[i] = &rows[i]
|
|
}
|
|
|
|
return views, nil
|
|
}
|
|
|
|
func (s *SqlViewStore) CountForChannel(channelID string, opts model.ViewQueryOpts) (int64, error) {
|
|
if channelID == "" {
|
|
return 0, store.NewErrInvalidInput("View", "channelID", channelID)
|
|
}
|
|
|
|
builder := s.getQueryBuilder().
|
|
Select("COUNT(*)").
|
|
From("Views").
|
|
Where(sq.Eq{"ChannelId": channelID, "DeleteAt": 0})
|
|
|
|
var count int64
|
|
if err := s.GetReplica().GetBuilder(&count, builder); err != nil {
|
|
return 0, errors.Wrapf(err, "failed to count views for channel %s", channelID)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlViewStore) Update(view *model.View) (*model.View, error) {
|
|
view.PreUpdate()
|
|
if err := view.IsValid(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
builder := s.getQueryBuilder().
|
|
Update("Views").
|
|
Set("Title", view.Title).
|
|
Set("Description", view.Description).
|
|
Set("SortOrder", view.SortOrder).
|
|
Set("Props", view.Props).
|
|
Set("UpdateAt", view.UpdateAt).
|
|
Where(sq.Eq{"Id": view.Id, "DeleteAt": 0})
|
|
|
|
res, err := s.GetMaster().ExecBuilder(builder)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to update view with id=%s", view.Id)
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to get affected rows after updating view with id=%s", view.Id)
|
|
}
|
|
if rowsAffected == 0 {
|
|
return nil, store.NewErrNotFound("View", view.Id)
|
|
}
|
|
|
|
return view, nil
|
|
}
|
|
|
|
func (s *SqlViewStore) Delete(viewID string, deleteAt int64) error {
|
|
builder := s.getQueryBuilder().
|
|
Update("Views").
|
|
Set("DeleteAt", deleteAt).
|
|
Set("UpdateAt", deleteAt).
|
|
Where(sq.Eq{"Id": viewID, "DeleteAt": 0})
|
|
|
|
res, err := s.GetMaster().ExecBuilder(builder)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to delete view with id=%s", viewID)
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to get affected rows after deleting view with id=%s", viewID)
|
|
}
|
|
if rowsAffected == 0 {
|
|
return store.NewErrNotFound("View", viewID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SqlViewStore) UpdateSortOrder(viewID, channelID string, newIndex int64) ([]*model.View, error) {
|
|
if channelID == "" {
|
|
return nil, store.NewErrInvalidInput("View", "channelID", channelID)
|
|
}
|
|
if viewID == "" {
|
|
return nil, store.NewErrInvalidInput("View", "viewID", viewID)
|
|
}
|
|
if newIndex < 0 {
|
|
return nil, store.NewErrInvalidInput("View", "SortOrder", newIndex)
|
|
}
|
|
|
|
now := model.GetMillis()
|
|
transaction, err := s.GetMaster().Begin()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to begin transaction for UpdateSortOrder")
|
|
}
|
|
defer finalizeTransactionX(transaction, &err)
|
|
|
|
builder := s.viewSelect.
|
|
Where(sq.Eq{"ChannelId": channelID, "DeleteAt": 0}).
|
|
OrderBy("SortOrder ASC", "CreateAt ASC", "Id ASC")
|
|
|
|
var rows []model.View
|
|
if err = transaction.SelectBuilder(&rows, builder); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to get views for channel %s", channelID)
|
|
}
|
|
|
|
views := make([]*model.View, len(rows))
|
|
for i := range rows {
|
|
views[i] = &rows[i]
|
|
}
|
|
|
|
if len(views) == 0 || int(newIndex) > len(views)-1 {
|
|
return nil, store.NewErrInvalidInput("View", "SortOrder", newIndex)
|
|
}
|
|
|
|
currentIndex := -1
|
|
var current *model.View
|
|
for index, v := range views {
|
|
if v.Id == viewID {
|
|
currentIndex = index
|
|
current = v
|
|
break
|
|
}
|
|
}
|
|
|
|
if currentIndex == -1 {
|
|
return nil, store.NewErrNotFound("View", viewID)
|
|
}
|
|
|
|
views = utils.RemoveElementFromSliceAtIndex(views, currentIndex)
|
|
views = slices.Insert(views, int(newIndex), current)
|
|
|
|
caseStmt := sq.Case()
|
|
query := s.getQueryBuilder().Update("Views")
|
|
ids := make([]string, 0, len(views))
|
|
for index, v := range views {
|
|
v.SortOrder = index
|
|
v.UpdateAt = now
|
|
caseStmt = caseStmt.When(sq.Eq{"Id": v.Id}, strconv.FormatInt(int64(index), 10))
|
|
ids = append(ids, v.Id)
|
|
}
|
|
query = query.Set("SortOrder", caseStmt)
|
|
query = query.Set("UpdateAt", now)
|
|
query = query.Where(sq.Eq{"Id": ids})
|
|
|
|
queryStr, args, queryErr := query.ToSql()
|
|
if queryErr != nil {
|
|
return nil, errors.Wrap(queryErr, "failed to build sort order update query for views")
|
|
}
|
|
|
|
if _, execErr := transaction.Exec(queryStr, args...); execErr != nil {
|
|
return nil, errors.Wrapf(execErr, "failed to update sort order for views in channel %s", channelID)
|
|
}
|
|
|
|
if err = transaction.Commit(); err != nil {
|
|
return nil, errors.Wrap(err, "failed to commit sort order update for views")
|
|
}
|
|
return views, nil
|
|
}
|