Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
297 lines
8.5 KiB
Go
297 lines
8.5 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package sqlstore
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sq "github.com/mattermost/squirrel"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
)
|
|
|
|
type SqlPropertyFieldStore struct {
|
|
*SqlStore
|
|
|
|
tableSelectQuery sq.SelectBuilder
|
|
}
|
|
|
|
func newPropertyFieldStore(sqlStore *SqlStore) store.PropertyFieldStore {
|
|
s := SqlPropertyFieldStore{SqlStore: sqlStore}
|
|
|
|
s.tableSelectQuery = s.getQueryBuilder().
|
|
Select("ID", "GroupID", "Name", "Type", "Attrs", "TargetID", "TargetType", "CreateAt", "UpdateAt", "DeleteAt").
|
|
From("PropertyFields")
|
|
|
|
return &s
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
|
|
if field.ID != "" {
|
|
return nil, store.NewErrInvalidInput("PropertyField", "id", field.ID)
|
|
}
|
|
|
|
field.PreSave()
|
|
|
|
if err := field.IsValid(); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_create_isvalid")
|
|
}
|
|
|
|
builder := s.getQueryBuilder().
|
|
Insert("PropertyFields").
|
|
Columns("ID", "GroupID", "Name", "Type", "Attrs", "TargetID", "TargetType", "CreateAt", "UpdateAt", "DeleteAt").
|
|
Values(field.ID, field.GroupID, field.Name, field.Type, field.Attrs, field.TargetID, field.TargetType, field.CreateAt, field.UpdateAt, field.DeleteAt)
|
|
|
|
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_create_insert")
|
|
}
|
|
|
|
return field, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) Get(groupID, id string) (*model.PropertyField, error) {
|
|
builder := s.tableSelectQuery.Where(sq.Eq{"id": id})
|
|
|
|
if groupID != "" {
|
|
builder = builder.Where(sq.Eq{"GroupID": groupID})
|
|
}
|
|
|
|
var field model.PropertyField
|
|
if err := s.GetReplica().GetBuilder(&field, builder); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_get_select")
|
|
}
|
|
|
|
return &field, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) GetFieldByName(groupID, targetID, name string) (*model.PropertyField, error) {
|
|
builder := s.tableSelectQuery.
|
|
Where(sq.Eq{"GroupID": groupID}).
|
|
Where(sq.Eq{"TargetID": targetID}).
|
|
Where(sq.Eq{"Name": name}).
|
|
Where(sq.Eq{"DeleteAt": 0})
|
|
|
|
var field model.PropertyField
|
|
if err := s.GetReplica().GetBuilder(&field, builder); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_get_by_name_select")
|
|
}
|
|
|
|
return &field, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) GetMany(groupID string, ids []string) ([]*model.PropertyField, error) {
|
|
builder := s.tableSelectQuery.Where(sq.Eq{"id": ids})
|
|
|
|
if groupID != "" {
|
|
builder = builder.Where(sq.Eq{"GroupID": groupID})
|
|
}
|
|
|
|
fields := []*model.PropertyField{}
|
|
if err := s.GetReplica().SelectBuilder(&fields, builder); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_get_many_query")
|
|
}
|
|
|
|
if len(fields) < len(ids) {
|
|
return nil, fmt.Errorf("missmatch results: got %d results of the %d ids passed", len(fields), len(ids))
|
|
}
|
|
|
|
return fields, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) CountForGroup(groupID string, includeDeleted bool) (int64, error) {
|
|
var count int64
|
|
builder := s.getQueryBuilder().
|
|
Select("COUNT(id)").
|
|
From("PropertyFields").
|
|
Where(sq.Eq{"GroupID": groupID})
|
|
|
|
if !includeDeleted {
|
|
builder = builder.Where(sq.Eq{"DeleteAt": 0})
|
|
}
|
|
|
|
if err := s.GetReplica().GetBuilder(&count, builder); err != nil {
|
|
return int64(0), errors.Wrap(err, "failed to count Sessions")
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) CountForTarget(groupID, targetType, targetID string, includeDeleted bool) (int64, error) {
|
|
var count int64
|
|
builder := s.getQueryBuilder().
|
|
Select("COUNT(id)").
|
|
From("PropertyFields").
|
|
Where(sq.Eq{"GroupID": groupID}).
|
|
Where(sq.Eq{"TargetType": targetType}).
|
|
Where(sq.Eq{"TargetID": targetID})
|
|
|
|
if !includeDeleted {
|
|
builder = builder.Where(sq.Eq{"DeleteAt": 0})
|
|
}
|
|
|
|
if err := s.GetReplica().GetBuilder(&count, builder); err != nil {
|
|
return int64(0), errors.Wrap(err, "failed to count property fields for target")
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
|
|
if err := opts.Cursor.IsValid(); err != nil {
|
|
return nil, fmt.Errorf("cursor is invalid: %w", err)
|
|
}
|
|
|
|
if opts.PerPage < 1 {
|
|
return nil, errors.New("per page must be positive integer greater than zero")
|
|
}
|
|
|
|
builder := s.tableSelectQuery.
|
|
OrderBy("CreateAt ASC, Id ASC").
|
|
Limit(uint64(opts.PerPage))
|
|
|
|
if !opts.Cursor.IsEmpty() {
|
|
builder = builder.Where(sq.Or{
|
|
sq.Gt{"CreateAt": opts.Cursor.CreateAt},
|
|
sq.And{
|
|
sq.Eq{"CreateAt": opts.Cursor.CreateAt},
|
|
sq.Gt{"Id": opts.Cursor.PropertyFieldID},
|
|
},
|
|
})
|
|
}
|
|
|
|
if !opts.IncludeDeleted {
|
|
builder = builder.Where(sq.Eq{"DeleteAt": 0})
|
|
}
|
|
|
|
if opts.GroupID != "" {
|
|
builder = builder.Where(sq.Eq{"GroupID": opts.GroupID})
|
|
}
|
|
|
|
if opts.TargetType != "" {
|
|
builder = builder.Where(sq.Eq{"TargetType": opts.TargetType})
|
|
}
|
|
|
|
if len(opts.TargetIDs) > 0 {
|
|
builder = builder.Where(sq.Eq{"TargetID": opts.TargetIDs})
|
|
}
|
|
|
|
if opts.SinceUpdateAt > 0 {
|
|
builder = builder.Where(sq.Gt{"UpdateAt": opts.SinceUpdateAt})
|
|
}
|
|
|
|
fields := []*model.PropertyField{}
|
|
if err := s.GetReplica().SelectBuilder(&fields, builder); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_search_query")
|
|
}
|
|
|
|
return fields, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) Update(groupID string, fields []*model.PropertyField) (_ []*model.PropertyField, err error) {
|
|
if len(fields) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
transaction, err := s.GetMaster().Beginx()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "property_field_update_begin_transaction")
|
|
}
|
|
defer finalizeTransactionX(transaction, &err)
|
|
|
|
updateTime := model.GetMillis()
|
|
isPostgres := s.DriverName() == model.DatabaseDriverPostgres
|
|
nameCase := sq.Case("id")
|
|
typeCase := sq.Case("id")
|
|
attrsCase := sq.Case("id")
|
|
targetIDCase := sq.Case("id")
|
|
targetTypeCase := sq.Case("id")
|
|
deleteAtCase := sq.Case("id")
|
|
ids := make([]string, len(fields))
|
|
|
|
for i, field := range fields {
|
|
field.UpdateAt = updateTime
|
|
if vErr := field.IsValid(); vErr != nil {
|
|
return nil, errors.Wrap(vErr, "property_field_update_isvalid")
|
|
}
|
|
|
|
ids[i] = field.ID
|
|
whenID := sq.Expr("?", field.ID)
|
|
if isPostgres {
|
|
nameCase = nameCase.When(whenID, sq.Expr("?::text", field.Name))
|
|
typeCase = typeCase.When(whenID, sq.Expr("?::property_field_type", field.Type))
|
|
attrsCase = attrsCase.When(whenID, sq.Expr("?::jsonb", field.Attrs))
|
|
targetIDCase = targetIDCase.When(whenID, sq.Expr("?::text", field.TargetID))
|
|
targetTypeCase = targetTypeCase.When(whenID, sq.Expr("?::text", field.TargetType))
|
|
deleteAtCase = deleteAtCase.When(whenID, sq.Expr("?::bigint", field.DeleteAt))
|
|
} else {
|
|
nameCase = nameCase.When(whenID, sq.Expr("?", field.Name))
|
|
typeCase = typeCase.When(whenID, sq.Expr("?", field.Type))
|
|
attrsCase = attrsCase.When(whenID, sq.Expr("?", field.Attrs))
|
|
targetIDCase = targetIDCase.When(whenID, sq.Expr("?", field.TargetID))
|
|
targetTypeCase = targetTypeCase.When(whenID, sq.Expr("?", field.TargetType))
|
|
deleteAtCase = deleteAtCase.When(whenID, sq.Expr("?", field.DeleteAt))
|
|
}
|
|
}
|
|
|
|
builder := s.getQueryBuilder().
|
|
Update("PropertyFields").
|
|
Set("Name", nameCase).
|
|
Set("Type", typeCase).
|
|
Set("Attrs", attrsCase).
|
|
Set("TargetID", targetIDCase).
|
|
Set("TargetType", targetTypeCase).
|
|
Set("UpdateAt", updateTime).
|
|
Set("DeleteAt", deleteAtCase).
|
|
Where(sq.Eq{"id": ids})
|
|
|
|
if groupID != "" {
|
|
builder = builder.Where(sq.Eq{"GroupID": groupID})
|
|
}
|
|
|
|
result, err := transaction.ExecBuilder(builder)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "property_field_update_exec")
|
|
}
|
|
|
|
count, err := result.RowsAffected()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "property_field_update_rowsaffected")
|
|
}
|
|
if count != int64(len(fields)) {
|
|
return nil, errors.Errorf("failed to update, some property fields were not found, got %d of %d", count, len(fields))
|
|
}
|
|
|
|
if err := transaction.Commit(); err != nil {
|
|
return nil, errors.Wrap(err, "property_field_update_commit_transaction")
|
|
}
|
|
|
|
return fields, nil
|
|
}
|
|
|
|
func (s *SqlPropertyFieldStore) Delete(groupID string, id string) error {
|
|
builder := s.getQueryBuilder().
|
|
Update("PropertyFields").
|
|
Set("DeleteAt", model.GetMillis()).
|
|
Where(sq.Eq{"id": id})
|
|
|
|
if groupID != "" {
|
|
builder = builder.Where(sq.Eq{"GroupID": groupID})
|
|
}
|
|
|
|
result, err := s.GetMaster().ExecBuilder(builder)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to delete property field with id: %s", id)
|
|
}
|
|
|
|
count, err := result.RowsAffected()
|
|
if err != nil {
|
|
return errors.Wrap(err, "property_field_delete_rowsaffected")
|
|
}
|
|
if count == 0 {
|
|
return store.NewErrNotFound("PropertyField", id)
|
|
}
|
|
|
|
return nil
|
|
}
|