Adds the main Property System Architecture components (#29644)

* Adds the main Property System Architecture components

This change adds the necessary migrations for the Property Groups,
Fields and Values tables to be created, the store layer and a Property
Service that can be used from the app layer.

* Update property field type to use user instead of person

* Update PropertyFields to allow for unique nondeleted fields and remove redundant indexes

* Update PropertyValues to allow for unique nondeleted fields and remove redundant indexes

* Use StringMap instead of the map[string]any on property fields

* Add i18n strings

* Revert "Use StringMap instead of the map[string]any on property fields"

This reverts commit e2735ab0f8589d2524d636419ca0cb144575c4d6.

* Cast JSON binary data to string and add todo note for StringMap use

* Add mocks to the retrylayer tests

* Cast JSON binary data to string in property value store

* Check for binary parameter instead of casting to string for JSON data

* Check property field type is one of the allowed ones

* Avoid reusing err variable to be explicit about the returned value

* Merge Property System Migrations into one file

* Adds NOT NULL to timestamps at the DB level

* Update stores to use tableSelectQuery instead of a slice var

* Update PropertyField model translations to be more explicit and avoid repetition

* Update PropertyValue model translations to be more explicit and avoid repetition

* Use ExecBuilder instead of ToSql&Exec

* Update property field errors to add context

* Ensure PerPage is greater than zero

* Update store errors to give more context

* Use ExecBuilder in the property stores where possible

* Add an on conflict suffix to the group register to avoid race conditions

* Remove badly used translation string

* Remove unused get in register group method

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Miguel de la Cruz
2025-01-13 12:41:44 +01:00
коммит произвёл GitHub
родитель d2b334e605
Коммит ecdce71fc4
35 изменённых файлов: 3793 добавлений и 0 удалений

Просмотреть файл

@@ -46,6 +46,9 @@ type OpenTracingLayer struct {
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
PropertyFieldStore store.PropertyFieldStore
PropertyGroupStore store.PropertyGroupStore
PropertyValueStore store.PropertyValueStore
ReactionStore store.ReactionStore
RemoteClusterStore store.RemoteClusterStore
RetentionPolicyStore store.RetentionPolicyStore
@@ -175,6 +178,18 @@ func (s *OpenTracingLayer) ProductNotices() store.ProductNoticesStore {
return s.ProductNoticesStore
}
func (s *OpenTracingLayer) PropertyField() store.PropertyFieldStore {
return s.PropertyFieldStore
}
func (s *OpenTracingLayer) PropertyGroup() store.PropertyGroupStore {
return s.PropertyGroupStore
}
func (s *OpenTracingLayer) PropertyValue() store.PropertyValueStore {
return s.PropertyValueStore
}
func (s *OpenTracingLayer) Reaction() store.ReactionStore {
return s.ReactionStore
}
@@ -386,6 +401,21 @@ type OpenTracingLayerProductNoticesStore struct {
Root *OpenTracingLayer
}
type OpenTracingLayerPropertyFieldStore struct {
store.PropertyFieldStore
Root *OpenTracingLayer
}
type OpenTracingLayerPropertyGroupStore struct {
store.PropertyGroupStore
Root *OpenTracingLayer
}
type OpenTracingLayerPropertyValueStore struct {
store.PropertyValueStore
Root *OpenTracingLayer
}
type OpenTracingLayerReactionStore struct {
store.ReactionStore
Root *OpenTracingLayer
@@ -7730,6 +7760,276 @@ func (s *OpenTracingLayerProductNoticesStore) View(userID string, notices []stri
return err
}
func (s *OpenTracingLayerPropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.Create")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyFieldStore.Create(field)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyFieldStore) Delete(id string) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.Delete")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
err := s.PropertyFieldStore.Delete(id)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return err
}
func (s *OpenTracingLayerPropertyFieldStore) Get(id string) (*model.PropertyField, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.Get")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyFieldStore.Get(id)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.GetMany")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyFieldStore.GetMany(ids)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.SearchPropertyFields")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyFieldStore.SearchPropertyFields(opts)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyFieldStore) Update(field []*model.PropertyField) ([]*model.PropertyField, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyFieldStore.Update")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyFieldStore.Update(field)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyGroupStore.Get")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyGroupStore.Get(name)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyGroupStore.Register")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyGroupStore.Register(name)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.Create")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyValueStore.Create(value)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyValueStore) Delete(id string) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.Delete")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
err := s.PropertyValueStore.Delete(id)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return err
}
func (s *OpenTracingLayerPropertyValueStore) DeleteForField(id string) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.DeleteForField")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
err := s.PropertyValueStore.DeleteForField(id)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return err
}
func (s *OpenTracingLayerPropertyValueStore) Get(id string) (*model.PropertyValue, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.Get")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyValueStore.Get(id)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.GetMany")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyValueStore.GetMany(ids)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.SearchPropertyValues")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyValueStore.SearchPropertyValues(opts)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPropertyValueStore) Update(field []*model.PropertyValue) ([]*model.PropertyValue, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PropertyValueStore.Update")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PropertyValueStore.Update(field)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ReactionStore.BulkGetForPosts")
@@ -13853,6 +14153,9 @@ func New(childStore store.Store, ctx context.Context) *OpenTracingLayer {
newStore.PostPriorityStore = &OpenTracingLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
newStore.PreferenceStore = &OpenTracingLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
newStore.ProductNoticesStore = &OpenTracingLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
newStore.PropertyFieldStore = &OpenTracingLayerPropertyFieldStore{PropertyFieldStore: childStore.PropertyField(), Root: &newStore}
newStore.PropertyGroupStore = &OpenTracingLayerPropertyGroupStore{PropertyGroupStore: childStore.PropertyGroup(), Root: &newStore}
newStore.PropertyValueStore = &OpenTracingLayerPropertyValueStore{PropertyValueStore: childStore.PropertyValue(), Root: &newStore}
newStore.ReactionStore = &OpenTracingLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}
newStore.RemoteClusterStore = &OpenTracingLayerRemoteClusterStore{RemoteClusterStore: childStore.RemoteCluster(), Root: &newStore}
newStore.RetentionPolicyStore = &OpenTracingLayerRetentionPolicyStore{RetentionPolicyStore: childStore.RetentionPolicy(), Root: &newStore}

Просмотреть файл

@@ -50,6 +50,9 @@ type RetryLayer struct {
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
PropertyFieldStore store.PropertyFieldStore
PropertyGroupStore store.PropertyGroupStore
PropertyValueStore store.PropertyValueStore
ReactionStore store.ReactionStore
RemoteClusterStore store.RemoteClusterStore
RetentionPolicyStore store.RetentionPolicyStore
@@ -179,6 +182,18 @@ func (s *RetryLayer) ProductNotices() store.ProductNoticesStore {
return s.ProductNoticesStore
}
func (s *RetryLayer) PropertyField() store.PropertyFieldStore {
return s.PropertyFieldStore
}
func (s *RetryLayer) PropertyGroup() store.PropertyGroupStore {
return s.PropertyGroupStore
}
func (s *RetryLayer) PropertyValue() store.PropertyValueStore {
return s.PropertyValueStore
}
func (s *RetryLayer) Reaction() store.ReactionStore {
return s.ReactionStore
}
@@ -390,6 +405,21 @@ type RetryLayerProductNoticesStore struct {
Root *RetryLayer
}
type RetryLayerPropertyFieldStore struct {
store.PropertyFieldStore
Root *RetryLayer
}
type RetryLayerPropertyGroupStore struct {
store.PropertyGroupStore
Root *RetryLayer
}
type RetryLayerPropertyValueStore struct {
store.PropertyValueStore
Root *RetryLayer
}
type RetryLayerReactionStore struct {
store.ReactionStore
Root *RetryLayer
@@ -8793,6 +8823,321 @@ func (s *RetryLayerProductNoticesStore) View(userID string, notices []string) er
}
func (s *RetryLayerPropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
tries := 0
for {
result, err := s.PropertyFieldStore.Create(field)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyFieldStore) Delete(id string) error {
tries := 0
for {
err := s.PropertyFieldStore.Delete(id)
if err == nil {
return nil
}
if !isRepeatableError(err) {
return err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyFieldStore) Get(id string) (*model.PropertyField, error) {
tries := 0
for {
result, err := s.PropertyFieldStore.Get(id)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
tries := 0
for {
result, err := s.PropertyFieldStore.GetMany(ids)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
tries := 0
for {
result, err := s.PropertyFieldStore.SearchPropertyFields(opts)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyFieldStore) Update(field []*model.PropertyField) ([]*model.PropertyField, error) {
tries := 0
for {
result, err := s.PropertyFieldStore.Update(field)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
tries := 0
for {
result, err := s.PropertyGroupStore.Get(name)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
tries := 0
for {
result, err := s.PropertyGroupStore.Register(name)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
tries := 0
for {
result, err := s.PropertyValueStore.Create(value)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) Delete(id string) error {
tries := 0
for {
err := s.PropertyValueStore.Delete(id)
if err == nil {
return nil
}
if !isRepeatableError(err) {
return err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) DeleteForField(id string) error {
tries := 0
for {
err := s.PropertyValueStore.DeleteForField(id)
if err == nil {
return nil
}
if !isRepeatableError(err) {
return err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) Get(id string) (*model.PropertyValue, error) {
tries := 0
for {
result, err := s.PropertyValueStore.Get(id)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
tries := 0
for {
result, err := s.PropertyValueStore.GetMany(ids)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
tries := 0
for {
result, err := s.PropertyValueStore.SearchPropertyValues(opts)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPropertyValueStore) Update(field []*model.PropertyValue) ([]*model.PropertyValue, error) {
tries := 0
for {
result, err := s.PropertyValueStore.Update(field)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, error) {
tries := 0
@@ -15806,6 +16151,9 @@ func New(childStore store.Store) *RetryLayer {
newStore.PostPriorityStore = &RetryLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
newStore.PreferenceStore = &RetryLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
newStore.ProductNoticesStore = &RetryLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
newStore.PropertyFieldStore = &RetryLayerPropertyFieldStore{PropertyFieldStore: childStore.PropertyField(), Root: &newStore}
newStore.PropertyGroupStore = &RetryLayerPropertyGroupStore{PropertyGroupStore: childStore.PropertyGroup(), Root: &newStore}
newStore.PropertyValueStore = &RetryLayerPropertyValueStore{PropertyValueStore: childStore.PropertyValue(), Root: &newStore}
newStore.ReactionStore = &RetryLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}
newStore.RemoteClusterStore = &RetryLayerRemoteClusterStore{RemoteClusterStore: childStore.RemoteCluster(), Root: &newStore}
newStore.RetentionPolicyStore = &RetryLayerRetentionPolicyStore{RetentionPolicyStore: childStore.RetentionPolicy(), Root: &newStore}

Просмотреть файл

@@ -63,6 +63,9 @@ func genStore() *mocks.Store {
mock.On("DesktopTokens").Return(&mocks.DesktopTokensStore{})
mock.On("ChannelBookmark").Return(&mocks.ChannelBookmarkStore{})
mock.On("ScheduledPost").Return(&mocks.ScheduledPostStore{})
mock.On("PropertyField").Return(&mocks.PropertyFieldStore{})
mock.On("PropertyGroup").Return(&mocks.PropertyGroupStore{})
mock.On("PropertyValue").Return(&mocks.PropertyValueStore{})
return mock
}

Просмотреть файл

@@ -0,0 +1,321 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"database/sql"
"encoding/json"
"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"
)
func (s *SqlPropertyFieldStore) propertyFieldToInsertMap(field *model.PropertyField) (map[string]any, error) {
attrsJSON, err := json.Marshal(field.Attrs)
if err != nil {
return nil, errors.Wrap(err, "property_field_to_insert_map_marshal_attrs")
}
if s.IsBinaryParamEnabled() {
attrsJSON = AppendBinaryFlag(attrsJSON)
}
return map[string]any{
"ID": field.ID,
"GroupID": field.GroupID,
"Name": field.Name,
"Type": field.Type,
"Attrs": attrsJSON,
"TargetID": field.TargetID,
"TargetType": field.TargetType,
"CreateAt": field.CreateAt,
"UpdateAt": field.UpdateAt,
"DeleteAt": field.DeleteAt,
}, nil
}
func (s *SqlPropertyFieldStore) propertyFieldToUpdateMap(field *model.PropertyField) (map[string]any, error) {
attrsJSON, err := json.Marshal(field.Attrs)
if err != nil {
return nil, errors.Wrap(err, "property_field_to_update_map_marshal_attrs")
}
if s.IsBinaryParamEnabled() {
attrsJSON = AppendBinaryFlag(attrsJSON)
}
return map[string]any{
"Name": field.Name,
"Type": field.Type,
"Attrs": attrsJSON,
"TargetID": field.TargetID,
"TargetType": field.TargetType,
"UpdateAt": field.UpdateAt,
"DeleteAt": field.DeleteAt,
}, nil
}
func propertyFieldsFromRows(rows *sql.Rows) ([]*model.PropertyField, error) {
results := []*model.PropertyField{}
for rows.Next() {
var field model.PropertyField
var attrsJSON string
err := rows.Scan(
&field.ID,
&field.GroupID,
&field.Name,
&field.Type,
&attrsJSON,
&field.TargetID,
&field.TargetType,
&field.CreateAt,
&field.UpdateAt,
&field.DeleteAt,
)
if err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(attrsJSON), &field.Attrs); err != nil {
return nil, errors.Wrap(err, "property_fields_from_rows_unmarshal_attrs")
}
results = append(results, &field)
}
return results, nil
}
func propertyFieldFromRows(rows *sql.Rows) (*model.PropertyField, error) {
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, err
}
if len(fields) > 0 {
return fields[0], nil
}
return nil, sql.ErrNoRows
}
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")
}
insertMap, err := s.propertyFieldToInsertMap(field)
if err != nil {
return nil, err
}
builder := s.getQueryBuilder().
Insert("PropertyFields").
SetMap(insertMap)
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_field_create_insert")
}
return field, nil
}
func (s *SqlPropertyFieldStore) Get(id string) (*model.PropertyField, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": id}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_get_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_select")
}
defer rows.Close()
field, err := propertyFieldFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_propertyfieldfromrows")
}
return field, nil
}
func (s *SqlPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": ids}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_query")
}
defer rows.Close()
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_propertyfieldfromrows")
}
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) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
if opts.Page < 0 {
return nil, errors.New("page must be positive integer")
}
if opts.PerPage < 1 {
return nil, errors.New("per page must be positive integer greater than zero")
}
query := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
Limit(uint64(opts.PerPage))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"DeleteAt": 0})
}
if opts.GroupID != "" {
query = query.Where(sq.Eq{"GroupID": opts.GroupID})
}
if opts.TargetType != "" {
query = query.Where(sq.Eq{"TargetType": opts.TargetType})
}
if opts.TargetID != "" {
query = query.Where(sq.Eq{"TargetID": opts.TargetID})
}
queryString, args, err := query.ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_search_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_search_query")
}
defer rows.Close()
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_search_propertyfieldfromrows")
}
return fields, nil
}
func (s *SqlPropertyFieldStore) Update(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()
for _, field := range fields {
field.UpdateAt = updateTime
if vErr := field.IsValid(); vErr != nil {
return nil, errors.Wrap(vErr, "property_field_update_isvalid")
}
updateMap, err := s.propertyFieldToUpdateMap(field)
if err != nil {
return nil, err
}
queryString, args, err := s.getQueryBuilder().
Update("PropertyFields").
SetMap(updateMap).
Where(sq.Eq{"id": field.ID}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_update_tosql")
}
result, err := transaction.Exec(queryString, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to update property field with id: %s", field.ID)
}
count, err := result.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_field_update_rowsaffected")
}
if count == 0 {
return nil, store.NewErrNotFound("PropertyField", field.ID)
}
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_field_update_commit")
}
return fields, nil
}
func (s *SqlPropertyFieldStore) Delete(id string) error {
builder := s.getQueryBuilder().
Update("PropertyFields").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"id": id})
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
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyFieldStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyFieldStore)
}

Просмотреть файл

@@ -0,0 +1,78 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
var propertyGroupColumns = []string{"ID", "Name"}
type SqlPropertyGroupStore struct {
*SqlStore
}
func newPropertyGroupStore(sqlStore *SqlStore) store.PropertyGroupStore {
return &SqlPropertyGroupStore{sqlStore}
}
func (s *SqlPropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
if name == "" {
return nil, store.NewErrInvalidInput("PropertyGroup", "name", name)
}
group := &model.PropertyGroup{Name: name}
group.PreSave()
builder := s.getQueryBuilder().
Insert("PropertyGroups").
Columns("ID", "Name").
Values(group.ID, group.Name)
if s.DriverName() == model.DatabaseDriverMysql {
builder = builder.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE Name=Name"))
} else {
builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (Name) DO NOTHING"))
}
r, err := s.GetMaster().ExecBuilder(builder)
if err != nil {
return nil, errors.Wrap(err, "property_group_register_insert")
}
rowsAffected, err := r.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_group_register_rows_affected")
}
// there was a conflict during the insert, so we need to fetch the
// group to get its data
if rowsAffected == 0 {
return s.Get(name)
}
return group, nil
}
func (s *SqlPropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
queryString, args, err := s.getQueryBuilder().
Select(propertyGroupColumns...).
From("PropertyGroups").
Where(sq.Eq{"Name": name}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_group_get_tosql")
}
var propertyGroup model.PropertyGroup
if err := s.GetReplica().Get(&propertyGroup, queryString, args...); err != nil {
return nil, store.NewErrNotFound("PropertyGroup", name)
}
return &propertyGroup, nil
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyGroupStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyGroupStore)
}

Просмотреть файл

@@ -0,0 +1,332 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"database/sql"
"encoding/json"
"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"
)
func (s *SqlPropertyValueStore) propertyValueToInsertMap(value *model.PropertyValue) (map[string]any, error) {
valueJSON, err := json.Marshal(value.Value)
if err != nil {
return nil, errors.Wrap(err, "property_value_to_insert_map_marshal_value")
}
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
return map[string]any{
"ID": value.ID,
"TargetID": value.TargetID,
"TargetType": value.TargetType,
"GroupID": value.GroupID,
"FieldID": value.FieldID,
"Value": valueJSON,
"CreateAt": value.CreateAt,
"UpdateAt": value.UpdateAt,
"DeleteAt": value.DeleteAt,
}, nil
}
func (s *SqlPropertyValueStore) propertyValueToUpdateMap(value *model.PropertyValue) (map[string]any, error) {
valueJSON, err := json.Marshal(value.Value)
if err != nil {
return nil, errors.Wrap(err, "property_value_to_udpate_map_marshal_value")
}
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
return map[string]any{
"Value": valueJSON,
"UpdateAt": value.UpdateAt,
"DeleteAt": value.DeleteAt,
}, nil
}
func propertyValuesFromRows(rows *sql.Rows) ([]*model.PropertyValue, error) {
results := []*model.PropertyValue{}
for rows.Next() {
var value model.PropertyValue
var valueJSON string
err := rows.Scan(
&value.ID,
&value.TargetID,
&value.TargetType,
&value.GroupID,
&value.FieldID,
&valueJSON,
&value.CreateAt,
&value.UpdateAt,
&value.DeleteAt,
)
if err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(valueJSON), &value.Value); err != nil {
return nil, errors.Wrap(err, "property_values_from_rows_unmarshal_value")
}
results = append(results, &value)
}
return results, nil
}
func propertyValueFromRows(rows *sql.Rows) (*model.PropertyValue, error) {
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, err
}
if len(values) > 0 {
return values[0], nil
}
return nil, sql.ErrNoRows
}
type SqlPropertyValueStore struct {
*SqlStore
tableSelectQuery sq.SelectBuilder
}
func newPropertyValueStore(sqlStore *SqlStore) store.PropertyValueStore {
s := SqlPropertyValueStore{SqlStore: sqlStore}
s.tableSelectQuery = s.getQueryBuilder().
Select("ID", "TargetID", "TargetType", "GroupID", "FieldID", "Value", "CreateAt", "UpdateAt", "DeleteAt").
From("PropertyValues")
return &s
}
func (s *SqlPropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
if value.ID != "" {
return nil, store.NewErrInvalidInput("PropertyValue", "id", value.ID)
}
value.PreSave()
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_create_isvalid")
}
insertMap, err := s.propertyValueToInsertMap(value)
if err != nil {
return nil, err
}
builder := s.getQueryBuilder().
Insert("PropertyValues").
SetMap(insertMap)
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_value_create_insert")
}
return value, nil
}
func (s *SqlPropertyValueStore) Get(id string) (*model.PropertyValue, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": id}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_get_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_select")
}
defer rows.Close()
value, err := propertyValueFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_propertyvaluefromrows")
}
return value, nil
}
func (s *SqlPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": ids}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_query")
}
defer rows.Close()
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_propertyvaluesfromrows")
}
if len(values) < len(ids) {
return nil, fmt.Errorf("missmatch results: got %d results of the %d ids passed", len(values), len(ids))
}
return values, nil
}
func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
if opts.Page < 0 {
return nil, errors.New("page must be positive integer")
}
if opts.PerPage < 1 {
return nil, errors.New("per page must be positive integer greater than zero")
}
query := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
Limit(uint64(opts.PerPage))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"DeleteAt": 0})
}
if opts.GroupID != "" {
query = query.Where(sq.Eq{"GroupID": opts.GroupID})
}
if opts.TargetType != "" {
query = query.Where(sq.Eq{"TargetType": opts.TargetType})
}
if opts.TargetID != "" {
query = query.Where(sq.Eq{"TargetID": opts.TargetID})
}
if opts.FieldID != "" {
query = query.Where(sq.Eq{"FieldID": opts.FieldID})
}
queryString, args, err := query.ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_search_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_search_query")
}
defer rows.Close()
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_search_propertyvaluesfromrows")
}
return values, nil
}
func (s *SqlPropertyValueStore) Update(values []*model.PropertyValue) (_ []*model.PropertyValue, err error) {
if len(values) == 0 {
return nil, nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
updateTime := model.GetMillis()
for _, value := range values {
value.UpdateAt = updateTime
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_update_isvalid")
}
updateMap, err := s.propertyValueToUpdateMap(value)
if err != nil {
return nil, err
}
queryString, args, err := s.getQueryBuilder().
Update("PropertyValues").
SetMap(updateMap).
Where(sq.Eq{"id": value.ID}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_tosql")
}
result, err := transaction.Exec(queryString, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to update property value with id: %s", value.ID)
}
count, err := result.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_rowsaffected")
}
if count == 0 {
return nil, store.NewErrNotFound("PropertyValue", value.ID)
}
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_value_update_commit")
}
return values, nil
}
func (s *SqlPropertyValueStore) Delete(id string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"id": id})
result, err := s.GetMaster().ExecBuilder(builder)
if err != nil {
return errors.Wrapf(err, "failed to delete property value with id: %s", id)
}
count, err := result.RowsAffected()
if err != nil {
return errors.Wrap(err, "property_value_delete_rowsaffected")
}
if count == 0 {
return store.NewErrNotFound("PropertyValue", id)
}
return nil
}
func (s *SqlPropertyValueStore) DeleteForField(fieldID string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"FieldID": fieldID})
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return errors.Wrap(err, "property_value_delete_for_field_exec")
}
return nil
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyValueStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyValueStore)
}

Просмотреть файл

@@ -115,6 +115,9 @@ type SqlStoreStores struct {
desktopTokens store.DesktopTokensStore
channelBookmarks store.ChannelBookmarkStore
scheduledPost store.ScheduledPostStore
propertyGroup store.PropertyGroupStore
propertyField store.PropertyFieldStore
propertyValue store.PropertyValueStore
}
type SqlStore struct {
@@ -257,6 +260,9 @@ func New(settings model.SqlSettings, logger mlog.LoggerIFace, metrics einterface
store.stores.desktopTokens = newSqlDesktopTokensStore(store, metrics)
store.stores.channelBookmarks = newSqlChannelBookmarkStore(store)
store.stores.scheduledPost = newScheduledPostStore(store)
store.stores.propertyGroup = newPropertyGroupStore(store)
store.stores.propertyField = newPropertyFieldStore(store)
store.stores.propertyValue = newPropertyValueStore(store)
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
@@ -1060,6 +1066,18 @@ func (ss *SqlStore) ChannelBookmark() store.ChannelBookmarkStore {
return ss.stores.channelBookmarks
}
func (ss *SqlStore) PropertyGroup() store.PropertyGroupStore {
return ss.stores.propertyGroup
}
func (ss *SqlStore) PropertyField() store.PropertyFieldStore {
return ss.stores.propertyField
}
func (ss *SqlStore) PropertyValue() store.PropertyValueStore {
return ss.stores.propertyValue
}
func (ss *SqlStore) DropAllTables() {
if ss.DriverName() == model.DatabaseDriverPostgres {
ss.masterX.Exec(`DO

Просмотреть файл

@@ -92,6 +92,9 @@ type Store interface {
DesktopTokens() DesktopTokensStore
ChannelBookmark() ChannelBookmarkStore
ScheduledPost() ScheduledPostStore
PropertyGroup() PropertyGroupStore
PropertyField() PropertyFieldStore
PropertyValue() PropertyValueStore
}
type RetentionPolicyStore interface {
@@ -1069,6 +1072,30 @@ type ScheduledPostStore interface {
PermanentDeleteByUser(userId string) error
}
type PropertyGroupStore interface {
Register(name string) (*model.PropertyGroup, error)
Get(name string) (*model.PropertyGroup, error)
}
type PropertyFieldStore interface {
Create(field *model.PropertyField) (*model.PropertyField, error)
Get(id string) (*model.PropertyField, error)
GetMany(ids []string) ([]*model.PropertyField, error)
SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error)
Update(field []*model.PropertyField) ([]*model.PropertyField, error)
Delete(id string) error
}
type PropertyValueStore interface {
Create(value *model.PropertyValue) (*model.PropertyValue, error)
Get(id string) (*model.PropertyValue, error)
GetMany(ids []string) ([]*model.PropertyValue, error)
SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error)
Update(field []*model.PropertyValue) ([]*model.PropertyValue, error)
Delete(id string) error
DeleteForField(id string) error
}
// ChannelSearchOpts contains options for searching channels.
//
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.

Просмотреть файл

@@ -0,0 +1,197 @@
// Code generated by mockery v2.42.2. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import (
model "github.com/mattermost/mattermost/server/public/model"
mock "github.com/stretchr/testify/mock"
)
// PropertyFieldStore is an autogenerated mock type for the PropertyFieldStore type
type PropertyFieldStore struct {
mock.Mock
}
// Create provides a mock function with given fields: field
func (_m *PropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
ret := _m.Called(field)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 *model.PropertyField
var r1 error
if rf, ok := ret.Get(0).(func(*model.PropertyField) (*model.PropertyField, error)); ok {
return rf(field)
}
if rf, ok := ret.Get(0).(func(*model.PropertyField) *model.PropertyField); ok {
r0 = rf(field)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyField)
}
}
if rf, ok := ret.Get(1).(func(*model.PropertyField) error); ok {
r1 = rf(field)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: id
func (_m *PropertyFieldStore) Delete(id string) error {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: id
func (_m *PropertyFieldStore) Get(id string) (*model.PropertyField, error) {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 *model.PropertyField
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.PropertyField, error)); ok {
return rf(id)
}
if rf, ok := ret.Get(0).(func(string) *model.PropertyField); ok {
r0 = rf(id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyField)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetMany provides a mock function with given fields: ids
func (_m *PropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
ret := _m.Called(ids)
if len(ret) == 0 {
panic("no return value specified for GetMany")
}
var r0 []*model.PropertyField
var r1 error
if rf, ok := ret.Get(0).(func([]string) ([]*model.PropertyField, error)); ok {
return rf(ids)
}
if rf, ok := ret.Get(0).(func([]string) []*model.PropertyField); ok {
r0 = rf(ids)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyField)
}
}
if rf, ok := ret.Get(1).(func([]string) error); ok {
r1 = rf(ids)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SearchPropertyFields provides a mock function with given fields: opts
func (_m *PropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
ret := _m.Called(opts)
if len(ret) == 0 {
panic("no return value specified for SearchPropertyFields")
}
var r0 []*model.PropertyField
var r1 error
if rf, ok := ret.Get(0).(func(model.PropertyFieldSearchOpts) ([]*model.PropertyField, error)); ok {
return rf(opts)
}
if rf, ok := ret.Get(0).(func(model.PropertyFieldSearchOpts) []*model.PropertyField); ok {
r0 = rf(opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyField)
}
}
if rf, ok := ret.Get(1).(func(model.PropertyFieldSearchOpts) error); ok {
r1 = rf(opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: field
func (_m *PropertyFieldStore) Update(field []*model.PropertyField) ([]*model.PropertyField, error) {
ret := _m.Called(field)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 []*model.PropertyField
var r1 error
if rf, ok := ret.Get(0).(func([]*model.PropertyField) ([]*model.PropertyField, error)); ok {
return rf(field)
}
if rf, ok := ret.Get(0).(func([]*model.PropertyField) []*model.PropertyField); ok {
r0 = rf(field)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyField)
}
}
if rf, ok := ret.Get(1).(func([]*model.PropertyField) error); ok {
r1 = rf(field)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewPropertyFieldStore creates a new instance of PropertyFieldStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewPropertyFieldStore(t interface {
mock.TestingT
Cleanup(func())
}) *PropertyFieldStore {
mock := &PropertyFieldStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

Просмотреть файл

@@ -0,0 +1,89 @@
// Code generated by mockery v2.42.2. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import (
model "github.com/mattermost/mattermost/server/public/model"
mock "github.com/stretchr/testify/mock"
)
// PropertyGroupStore is an autogenerated mock type for the PropertyGroupStore type
type PropertyGroupStore struct {
mock.Mock
}
// Get provides a mock function with given fields: name
func (_m *PropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
ret := _m.Called(name)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 *model.PropertyGroup
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.PropertyGroup, error)); ok {
return rf(name)
}
if rf, ok := ret.Get(0).(func(string) *model.PropertyGroup); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyGroup)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(name)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Register provides a mock function with given fields: name
func (_m *PropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
ret := _m.Called(name)
if len(ret) == 0 {
panic("no return value specified for Register")
}
var r0 *model.PropertyGroup
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.PropertyGroup, error)); ok {
return rf(name)
}
if rf, ok := ret.Get(0).(func(string) *model.PropertyGroup); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyGroup)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(name)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewPropertyGroupStore creates a new instance of PropertyGroupStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewPropertyGroupStore(t interface {
mock.TestingT
Cleanup(func())
}) *PropertyGroupStore {
mock := &PropertyGroupStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

Просмотреть файл

@@ -0,0 +1,215 @@
// Code generated by mockery v2.42.2. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import (
model "github.com/mattermost/mattermost/server/public/model"
mock "github.com/stretchr/testify/mock"
)
// PropertyValueStore is an autogenerated mock type for the PropertyValueStore type
type PropertyValueStore struct {
mock.Mock
}
// Create provides a mock function with given fields: value
func (_m *PropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
ret := _m.Called(value)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 *model.PropertyValue
var r1 error
if rf, ok := ret.Get(0).(func(*model.PropertyValue) (*model.PropertyValue, error)); ok {
return rf(value)
}
if rf, ok := ret.Get(0).(func(*model.PropertyValue) *model.PropertyValue); ok {
r0 = rf(value)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyValue)
}
}
if rf, ok := ret.Get(1).(func(*model.PropertyValue) error); ok {
r1 = rf(value)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: id
func (_m *PropertyValueStore) Delete(id string) error {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(id)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeleteForField provides a mock function with given fields: id
func (_m *PropertyValueStore) DeleteForField(id string) error {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for DeleteForField")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: id
func (_m *PropertyValueStore) Get(id string) (*model.PropertyValue, error) {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 *model.PropertyValue
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.PropertyValue, error)); ok {
return rf(id)
}
if rf, ok := ret.Get(0).(func(string) *model.PropertyValue); ok {
r0 = rf(id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PropertyValue)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetMany provides a mock function with given fields: ids
func (_m *PropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
ret := _m.Called(ids)
if len(ret) == 0 {
panic("no return value specified for GetMany")
}
var r0 []*model.PropertyValue
var r1 error
if rf, ok := ret.Get(0).(func([]string) ([]*model.PropertyValue, error)); ok {
return rf(ids)
}
if rf, ok := ret.Get(0).(func([]string) []*model.PropertyValue); ok {
r0 = rf(ids)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyValue)
}
}
if rf, ok := ret.Get(1).(func([]string) error); ok {
r1 = rf(ids)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SearchPropertyValues provides a mock function with given fields: opts
func (_m *PropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
ret := _m.Called(opts)
if len(ret) == 0 {
panic("no return value specified for SearchPropertyValues")
}
var r0 []*model.PropertyValue
var r1 error
if rf, ok := ret.Get(0).(func(model.PropertyValueSearchOpts) ([]*model.PropertyValue, error)); ok {
return rf(opts)
}
if rf, ok := ret.Get(0).(func(model.PropertyValueSearchOpts) []*model.PropertyValue); ok {
r0 = rf(opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyValue)
}
}
if rf, ok := ret.Get(1).(func(model.PropertyValueSearchOpts) error); ok {
r1 = rf(opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: field
func (_m *PropertyValueStore) Update(field []*model.PropertyValue) ([]*model.PropertyValue, error) {
ret := _m.Called(field)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 []*model.PropertyValue
var r1 error
if rf, ok := ret.Get(0).(func([]*model.PropertyValue) ([]*model.PropertyValue, error)); ok {
return rf(field)
}
if rf, ok := ret.Get(0).(func([]*model.PropertyValue) []*model.PropertyValue); ok {
r0 = rf(field)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PropertyValue)
}
}
if rf, ok := ret.Get(1).(func([]*model.PropertyValue) error); ok {
r1 = rf(field)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewPropertyValueStore creates a new instance of PropertyValueStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewPropertyValueStore(t interface {
mock.TestingT
Cleanup(func())
}) *PropertyValueStore {
mock := &PropertyValueStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

Просмотреть файл

@@ -798,6 +798,66 @@ func (_m *Store) ProductNotices() store.ProductNoticesStore {
return r0
}
// PropertyField provides a mock function with given fields:
func (_m *Store) PropertyField() store.PropertyFieldStore {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for PropertyField")
}
var r0 store.PropertyFieldStore
if rf, ok := ret.Get(0).(func() store.PropertyFieldStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.PropertyFieldStore)
}
}
return r0
}
// PropertyGroup provides a mock function with given fields:
func (_m *Store) PropertyGroup() store.PropertyGroupStore {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for PropertyGroup")
}
var r0 store.PropertyGroupStore
if rf, ok := ret.Get(0).(func() store.PropertyGroupStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.PropertyGroupStore)
}
}
return r0
}
// PropertyValue provides a mock function with given fields:
func (_m *Store) PropertyValue() store.PropertyValueStore {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for PropertyValue")
}
var r0 store.PropertyValueStore
if rf, ok := ret.Get(0).(func() store.PropertyValueStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.PropertyValueStore)
}
}
return r0
}
// Reaction provides a mock function with given fields:
func (_m *Store) Reaction() store.ReactionStore {
ret := _m.Called()

Просмотреть файл

@@ -0,0 +1,461 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"database/sql"
"testing"
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/stretchr/testify/require"
)
func TestPropertyFieldStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
t.Run("CreatePropertyField", func(t *testing.T) { testCreatePropertyField(t, rctx, ss) })
t.Run("GetPropertyField", func(t *testing.T) { testGetPropertyField(t, rctx, ss) })
t.Run("GetManyPropertyFields", func(t *testing.T) { testGetManyPropertyFields(t, rctx, ss) })
t.Run("UpdatePropertyField", func(t *testing.T) { testUpdatePropertyField(t, rctx, ss) })
t.Run("DeletePropertyField", func(t *testing.T) { testDeletePropertyField(t, rctx, ss) })
t.Run("SearchPropertyFields", func(t *testing.T) { testSearchPropertyFields(t, rctx, ss) })
}
func testCreatePropertyField(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail if the property field already has an ID set", func(t *testing.T) {
newField := &model.PropertyField{ID: "sampleid"}
field, err := ss.PropertyField().Create(newField)
require.Zero(t, field)
var eii *store.ErrInvalidInput
require.ErrorAs(t, err, &eii)
})
t.Run("should fail if the property field is not valid", func(t *testing.T) {
newField := &model.PropertyField{GroupID: ""}
field, err := ss.PropertyField().Create(newField)
require.Zero(t, field)
require.ErrorContains(t, err, "model.property_field.is_valid.app_error")
newField = &model.PropertyField{GroupID: model.NewId(), Name: ""}
field, err = ss.PropertyField().Create(newField)
require.Zero(t, field)
require.ErrorContains(t, err, "model.property_field.is_valid.app_error")
})
newField := &model.PropertyField{
GroupID: model.NewId(),
Name: "My new property field",
Type: model.PropertyFieldTypeText,
Attrs: map[string]any{
"locked": true,
"special": "value",
},
}
t.Run("should be able to create a property field", func(t *testing.T) {
field, err := ss.PropertyField().Create(newField)
require.NoError(t, err)
require.NotZero(t, field.ID)
require.NotZero(t, field.CreateAt)
require.NotZero(t, field.UpdateAt)
require.Zero(t, field.DeleteAt)
})
t.Run("should enforce the field's uniqueness", func(t *testing.T) {
newField.ID = ""
field, err := ss.PropertyField().Create(newField)
require.Error(t, err)
require.Empty(t, field)
})
}
func testGetPropertyField(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting field", func(t *testing.T) {
field, err := ss.PropertyField().Get(model.NewId())
require.Zero(t, field)
require.ErrorIs(t, err, sql.ErrNoRows)
})
t.Run("should be able to retrieve an existing property field", func(t *testing.T) {
newField := &model.PropertyField{
GroupID: model.NewId(),
Name: "My new property field",
Type: model.PropertyFieldTypeText,
Attrs: map[string]any{
"locked": true,
"special": "value",
},
}
_, err := ss.PropertyField().Create(newField)
require.NoError(t, err)
require.NotZero(t, newField.ID)
field, err := ss.PropertyField().Get(newField.ID)
require.NoError(t, err)
require.Equal(t, newField.ID, field.ID)
require.True(t, field.Attrs["locked"].(bool))
require.Equal(t, "value", field.Attrs["special"])
})
}
func testGetManyPropertyFields(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting fields", func(t *testing.T) {
fields, err := ss.PropertyField().GetMany([]string{model.NewId(), model.NewId()})
require.Empty(t, fields)
require.ErrorContains(t, err, "missmatch results")
})
newFields := []*model.PropertyField{}
for _, fieldName := range []string{"field1", "field2", "field3"} {
newField := &model.PropertyField{
GroupID: model.NewId(),
Name: fieldName,
Type: model.PropertyFieldTypeText,
}
_, err := ss.PropertyField().Create(newField)
require.NoError(t, err)
require.NotZero(t, newField.ID)
newFields = append(newFields, newField)
}
t.Run("should fail if at least one of the ids is nonexistent", func(t *testing.T) {
fields, err := ss.PropertyField().GetMany([]string{newFields[0].ID, newFields[1].ID, model.NewId()})
require.Empty(t, fields)
require.ErrorContains(t, err, "missmatch results")
})
t.Run("should be able to retrieve existing property fields", func(t *testing.T) {
fields, err := ss.PropertyField().GetMany([]string{newFields[0].ID, newFields[1].ID, newFields[2].ID})
require.NoError(t, err)
require.Len(t, fields, 3)
require.ElementsMatch(t, newFields, fields)
})
}
func testUpdatePropertyField(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting field", func(t *testing.T) {
field := &model.PropertyField{
ID: model.NewId(),
GroupID: model.NewId(),
Name: "My property field",
Type: model.PropertyFieldTypeText,
CreateAt: model.GetMillis(),
}
updatedField, err := ss.PropertyField().Update([]*model.PropertyField{field})
require.Zero(t, updatedField)
var enf *store.ErrNotFound
require.ErrorAs(t, err, &enf)
})
t.Run("should fail if the property field is not valid", func(t *testing.T) {
field := &model.PropertyField{
GroupID: model.NewId(),
Name: "My property field",
Type: model.PropertyFieldTypeText,
}
_, err := ss.PropertyField().Create(field)
require.NoError(t, err)
require.NotZero(t, field.ID)
field.GroupID = ""
updatedField, err := ss.PropertyField().Update([]*model.PropertyField{field})
require.Zero(t, updatedField)
require.ErrorContains(t, err, "model.property_field.is_valid.app_error")
field.GroupID = model.NewId()
field.Name = ""
updatedField, err = ss.PropertyField().Update([]*model.PropertyField{field})
require.Zero(t, updatedField)
require.ErrorContains(t, err, "model.property_field.is_valid.app_error")
})
t.Run("should be able to update multiple property fields", func(t *testing.T) {
field1 := &model.PropertyField{
GroupID: model.NewId(),
Name: "First field",
Type: model.PropertyFieldTypeText,
Attrs: map[string]any{
"locked": true,
"special": "value",
},
}
field2 := &model.PropertyField{
GroupID: model.NewId(),
Name: "Second field",
Type: model.PropertyFieldTypeSelect,
Attrs: map[string]any{
"options": []string{"a", "b"},
},
}
for _, field := range []*model.PropertyField{field1, field2} {
_, err := ss.PropertyField().Create(field)
require.NoError(t, err)
require.NotZero(t, field.ID)
}
time.Sleep(10 * time.Millisecond)
field1.Name = "Updated first"
field1.Type = model.PropertyFieldTypeSelect
field1.Attrs = map[string]any{
"locked": false,
"new_field": "new_value",
}
field2.Name = "Updated second"
field2.Attrs = map[string]any{
"options": []string{"x", "y", "z"},
}
_, err := ss.PropertyField().Update([]*model.PropertyField{field1, field2})
require.NoError(t, err)
// Verify first field
updated1, err := ss.PropertyField().Get(field1.ID)
require.NoError(t, err)
require.Equal(t, "Updated first", updated1.Name)
require.Equal(t, model.PropertyFieldTypeSelect, updated1.Type)
require.False(t, updated1.Attrs["locked"].(bool))
require.NotContains(t, updated1.Attrs, "special")
require.Equal(t, "new_value", updated1.Attrs["new_field"])
require.Greater(t, updated1.UpdateAt, updated1.CreateAt)
// Verify second field
updated2, err := ss.PropertyField().Get(field2.ID)
require.NoError(t, err)
require.Equal(t, "Updated second", updated2.Name)
require.Equal(t, model.PropertyFieldTypeSelect, updated2.Type)
require.ElementsMatch(t, []string{"x", "y", "z"}, updated2.Attrs["options"])
require.Greater(t, updated2.UpdateAt, updated2.CreateAt)
})
t.Run("should not update any fields if one update is invalid", func(t *testing.T) {
// Create two valid fields
groupID := model.NewId()
field1 := &model.PropertyField{
GroupID: groupID,
Name: "Field 1",
Type: model.PropertyFieldTypeText,
Attrs: map[string]any{
"key": "value",
},
}
field2 := &model.PropertyField{
GroupID: groupID,
Name: "Field 2",
Type: model.PropertyFieldTypeText,
Attrs: map[string]any{
"key": "value",
},
}
for _, field := range []*model.PropertyField{field1, field2} {
_, err := ss.PropertyField().Create(field)
require.NoError(t, err)
}
originalUpdateAt1 := field1.UpdateAt
originalUpdateAt2 := field2.UpdateAt
// Try to update both fields, but make one invalid
field1.Name = "Valid update"
field2.GroupID = "Invalid ID"
_, err := ss.PropertyField().Update([]*model.PropertyField{field1, field2})
require.ErrorContains(t, err, "model.property_field.is_valid.app_error")
// Check that fields were not updated
updated1, err := ss.PropertyField().Get(field1.ID)
require.NoError(t, err)
require.Equal(t, "Field 1", updated1.Name)
require.Equal(t, originalUpdateAt1, updated1.UpdateAt)
updated2, err := ss.PropertyField().Get(field2.ID)
require.NoError(t, err)
require.Equal(t, groupID, updated2.GroupID)
require.Equal(t, originalUpdateAt2, updated2.UpdateAt)
})
}
func testDeletePropertyField(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting field", func(t *testing.T) {
err := ss.PropertyField().Delete(model.NewId())
var enf *store.ErrNotFound
require.ErrorAs(t, err, &enf)
})
newField := &model.PropertyField{
GroupID: model.NewId(),
Name: "My property field",
Type: model.PropertyFieldTypeText,
}
t.Run("should be able to delete an existing property field", func(t *testing.T) {
field, err := ss.PropertyField().Create(newField)
require.NoError(t, err)
require.NotEmpty(t, field.ID)
err = ss.PropertyField().Delete(field.ID)
require.NoError(t, err)
// Verify the field was soft-deleted
deletedField, err := ss.PropertyField().Get(field.ID)
require.NoError(t, err)
require.NotZero(t, deletedField.DeleteAt)
})
t.Run("should be able to create a new field with the same details as the deleted one", func(t *testing.T) {
newField.ID = ""
field, err := ss.PropertyField().Create(newField)
require.NoError(t, err)
require.NotEmpty(t, field.ID)
})
}
func testSearchPropertyFields(t *testing.T, _ request.CTX, ss store.Store) {
groupID := model.NewId()
targetID := model.NewId()
// Define test property fields
field1 := &model.PropertyField{
GroupID: groupID,
Name: "Field 1",
Type: model.PropertyFieldTypeText,
TargetID: targetID,
TargetType: "test_type",
}
field2 := &model.PropertyField{
GroupID: groupID,
Name: "Field 2",
Type: model.PropertyFieldTypeSelect,
TargetID: targetID,
TargetType: "other_type",
}
field3 := &model.PropertyField{
GroupID: model.NewId(),
Name: "Field 3",
Type: model.PropertyFieldTypeText,
TargetType: "test_type",
}
field4 := &model.PropertyField{
GroupID: groupID,
Name: "Field 4",
Type: model.PropertyFieldTypeText,
TargetType: "test_type",
}
for _, field := range []*model.PropertyField{field1, field2, field3, field4} {
_, err := ss.PropertyField().Create(field)
require.NoError(t, err)
time.Sleep(10 * time.Millisecond)
}
// Delete one field for deletion tests
require.NoError(t, ss.PropertyField().Delete(field4.ID))
tests := []struct {
name string
opts model.PropertyFieldSearchOpts
expectedError bool
expectedIDs []string
}{
{
name: "negative page",
opts: model.PropertyFieldSearchOpts{
Page: -1,
PerPage: 10,
},
expectedError: true,
},
{
name: "negative per_page",
opts: model.PropertyFieldSearchOpts{
Page: 0,
PerPage: -1,
},
expectedError: true,
},
{
name: "filter by group_id",
opts: model.PropertyFieldSearchOpts{
GroupID: groupID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{field1.ID, field2.ID},
},
{
name: "filter by group_id including deleted",
opts: model.PropertyFieldSearchOpts{
GroupID: groupID,
Page: 0,
PerPage: 10,
IncludeDeleted: true,
},
expectedIDs: []string{field1.ID, field2.ID, field4.ID},
},
{
name: "filter by target_type",
opts: model.PropertyFieldSearchOpts{
TargetType: "test_type",
Page: 0,
PerPage: 10,
},
expectedIDs: []string{field1.ID, field3.ID},
},
{
name: "filter by target_id",
opts: model.PropertyFieldSearchOpts{
TargetID: targetID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{field1.ID, field2.ID},
},
{
name: "pagination page 0",
opts: model.PropertyFieldSearchOpts{
GroupID: groupID,
Page: 0,
PerPage: 2,
IncludeDeleted: true,
},
expectedIDs: []string{field1.ID, field2.ID},
},
{
name: "pagination page 1",
opts: model.PropertyFieldSearchOpts{
GroupID: groupID,
Page: 1,
PerPage: 2,
IncludeDeleted: true,
},
expectedIDs: []string{field4.ID},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
results, err := ss.PropertyField().SearchPropertyFields(tc.opts)
if tc.expectedError {
require.Error(t, err)
return
}
require.NoError(t, err)
var ids = make([]string, len(results))
for i, field := range results {
ids[i] = field.ID
}
require.ElementsMatch(t, tc.expectedIDs, ids)
})
}
}

Просмотреть файл

@@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"testing"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/stretchr/testify/require"
)
func TestPropertyGroupStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
t.Run("RegisterAndGetPropertyGroup", func(t *testing.T) { testRegisterAndGetPropertyGroup(t, rctx, ss) })
}
func testRegisterAndGetPropertyGroup(t *testing.T, _ request.CTX, ss store.Store) {
groupName := "samplename"
var id string
t.Run("should be able to register a new group", func(t *testing.T) {
group, err := ss.PropertyGroup().Register(groupName)
require.NoError(t, err)
require.NotZero(t, group.ID)
require.Equal(t, groupName, group.Name)
id = group.ID
})
t.Run("should be able to retrieve an existing group", func(t *testing.T) {
group, err := ss.PropertyGroup().Register(groupName)
require.NoError(t, err)
require.Equal(t, groupName, group.Name)
require.Equal(t, id, group.ID)
})
}

Просмотреть файл

@@ -0,0 +1,535 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"database/sql"
"fmt"
"testing"
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/stretchr/testify/require"
)
func TestPropertyValueStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
t.Run("CreatePropertyValue", func(t *testing.T) { testCreatePropertyValue(t, rctx, ss) })
t.Run("GetPropertyValue", func(t *testing.T) { testGetPropertyValue(t, rctx, ss) })
t.Run("GetManyPropertyValues", func(t *testing.T) { testGetManyPropertyValues(t, rctx, ss) })
t.Run("UpdatePropertyValue", func(t *testing.T) { testUpdatePropertyValue(t, rctx, ss) })
t.Run("DeletePropertyValue", func(t *testing.T) { testDeletePropertyValue(t, rctx, ss) })
t.Run("SearchPropertyValues", func(t *testing.T) { testSearchPropertyValues(t, rctx, ss) })
t.Run("DeleteForField", func(t *testing.T) { testDeleteForField(t, rctx, ss) })
}
func testCreatePropertyValue(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail if the property value already has an ID set", func(t *testing.T) {
newValue := &model.PropertyValue{ID: "sampleid"}
value, err := ss.PropertyValue().Create(newValue)
require.Zero(t, value)
var eii *store.ErrInvalidInput
require.ErrorAs(t, err, &eii)
})
t.Run("should fail if the property value is not valid", func(t *testing.T) {
newValue := &model.PropertyValue{TargetID: ""}
value, err := ss.PropertyValue().Create(newValue)
require.Zero(t, value)
require.ErrorContains(t, err, "model.property_value.is_valid.app_error")
newValue = &model.PropertyValue{TargetID: model.NewId(), TargetType: ""}
value, err = ss.PropertyValue().Create(newValue)
require.Zero(t, value)
require.ErrorContains(t, err, "model.property_value.is_valid.app_error")
})
newValue := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
}
t.Run("should be able to create a property value", func(t *testing.T) {
value, err := ss.PropertyValue().Create(newValue)
require.NoError(t, err)
require.NotZero(t, value.ID)
require.NotZero(t, value.CreateAt)
require.NotZero(t, value.UpdateAt)
require.Zero(t, value.DeleteAt)
})
t.Run("should enforce the value's uniqueness", func(t *testing.T) {
newValue.ID = ""
value, err := ss.PropertyValue().Create(newValue)
require.Error(t, err)
require.Zero(t, value)
})
}
func testGetPropertyValue(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting value", func(t *testing.T) {
value, err := ss.PropertyValue().Get(model.NewId())
require.Zero(t, value)
require.ErrorIs(t, err, sql.ErrNoRows)
})
t.Run("should be able to retrieve an existing property value", func(t *testing.T) {
newValue := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
}
_, err := ss.PropertyValue().Create(newValue)
require.NoError(t, err)
require.NotZero(t, newValue.ID)
value, err := ss.PropertyValue().Get(newValue.ID)
require.NoError(t, err)
require.Equal(t, newValue.ID, value.ID)
require.Equal(t, newValue.Value, value.Value)
})
}
func testGetManyPropertyValues(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting values", func(t *testing.T) {
values, err := ss.PropertyValue().GetMany([]string{model.NewId(), model.NewId()})
require.Empty(t, values)
require.ErrorContains(t, err, "missmatch results")
})
newValues := []*model.PropertyValue{}
for i := 0; i < 3; i++ {
newValue := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: fmt.Sprintf("test value %d", i),
}
_, err := ss.PropertyValue().Create(newValue)
require.NoError(t, err)
require.NotZero(t, newValue.ID)
newValues = append(newValues, newValue)
}
t.Run("should fail if at least one of the ids is nonexistent", func(t *testing.T) {
values, err := ss.PropertyValue().GetMany([]string{newValues[0].ID, newValues[1].ID, model.NewId()})
require.Empty(t, values)
require.ErrorContains(t, err, "missmatch results")
})
t.Run("should be able to retrieve existing property values", func(t *testing.T) {
values, err := ss.PropertyValue().GetMany([]string{newValues[0].ID, newValues[1].ID, newValues[2].ID})
require.NoError(t, err)
require.Len(t, values, 3)
require.ElementsMatch(t, newValues, values)
})
}
func testUpdatePropertyValue(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting value", func(t *testing.T) {
value := &model.PropertyValue{
ID: model.NewId(),
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
CreateAt: model.GetMillis(),
}
updatedValue, err := ss.PropertyValue().Update([]*model.PropertyValue{value})
require.Zero(t, updatedValue)
var enf *store.ErrNotFound
require.ErrorAs(t, err, &enf)
})
t.Run("should fail if the property value is not valid", func(t *testing.T) {
value := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
}
_, err := ss.PropertyValue().Create(value)
require.NoError(t, err)
require.NotZero(t, value.ID)
value.TargetID = ""
updatedValue, err := ss.PropertyValue().Update([]*model.PropertyValue{value})
require.Zero(t, updatedValue)
require.ErrorContains(t, err, "model.property_value.is_valid.app_error")
value.TargetID = model.NewId()
value.GroupID = ""
updatedValue, err = ss.PropertyValue().Update([]*model.PropertyValue{value})
require.Zero(t, updatedValue)
require.ErrorContains(t, err, "model.property_value.is_valid.app_error")
})
t.Run("should be able to update multiple property values", func(t *testing.T) {
value1 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "value 1",
}
value2 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "value 2",
}
for _, value := range []*model.PropertyValue{value1, value2} {
_, err := ss.PropertyValue().Create(value)
require.NoError(t, err)
require.NotZero(t, value.ID)
}
time.Sleep(10 * time.Millisecond)
value1.Value = "updated value 1"
value2.Value = "updated value 2"
_, err := ss.PropertyValue().Update([]*model.PropertyValue{value1, value2})
require.NoError(t, err)
// Verify first value
updated1, err := ss.PropertyValue().Get(value1.ID)
require.NoError(t, err)
require.Equal(t, "updated value 1", updated1.Value)
require.Greater(t, updated1.UpdateAt, updated1.CreateAt)
// Verify second value
updated2, err := ss.PropertyValue().Get(value2.ID)
require.NoError(t, err)
require.Equal(t, "updated value 2", updated2.Value)
require.Greater(t, updated2.UpdateAt, updated2.CreateAt)
})
t.Run("should not update any fields if one update is invalid", func(t *testing.T) {
// Create two valid values
groupID := model.NewId()
value1 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: groupID,
FieldID: model.NewId(),
Value: "Value 1",
}
value2 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: groupID,
FieldID: model.NewId(),
Value: "Value 2",
}
for _, value := range []*model.PropertyValue{value1, value2} {
_, err := ss.PropertyValue().Create(value)
require.NoError(t, err)
}
originalUpdateAt1 := value1.UpdateAt
originalUpdateAt2 := value2.UpdateAt
// Try to update both value, but make one invalid
value1.Value = "Valid update"
value2.GroupID = "Invalid ID"
_, err := ss.PropertyValue().Update([]*model.PropertyValue{value1, value2})
require.Error(t, err)
require.Contains(t, err.Error(), "model.property_value.is_valid.app_error")
// Check that values were not updated
updated1, err := ss.PropertyValue().Get(value1.ID)
require.NoError(t, err)
require.Equal(t, "Value 1", updated1.Value)
require.Equal(t, originalUpdateAt1, updated1.UpdateAt)
updated2, err := ss.PropertyValue().Get(value2.ID)
require.NoError(t, err)
require.Equal(t, groupID, updated2.GroupID)
require.Equal(t, originalUpdateAt2, updated2.UpdateAt)
})
}
func testDeletePropertyValue(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should fail on nonexisting value", func(t *testing.T) {
err := ss.PropertyValue().Delete(model.NewId())
var enf *store.ErrNotFound
require.ErrorAs(t, err, &enf)
})
t.Run("should be able to delete an existing property value", func(t *testing.T) {
newValue := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
}
value, err := ss.PropertyValue().Create(newValue)
require.NoError(t, err)
require.NotEmpty(t, value.ID)
err = ss.PropertyValue().Delete(value.ID)
require.NoError(t, err)
// Verify the value was soft-deleted
deletedValue, err := ss.PropertyValue().Get(value.ID)
require.NoError(t, err)
require.NotZero(t, deletedValue.DeleteAt)
})
t.Run("should be able to create a new value with the same details as the deleted one", func(t *testing.T) {
sameDetailsValue := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(),
Value: "test value",
}
value, err := ss.PropertyValue().Create(sameDetailsValue)
require.NoError(t, err)
require.NotEmpty(t, value.ID)
require.Equal(t, sameDetailsValue.Value, value.Value)
})
}
func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store) {
groupID := model.NewId()
targetID := model.NewId()
fieldID := model.NewId()
// Define test property values
value1 := &model.PropertyValue{
GroupID: groupID,
TargetID: targetID,
TargetType: "test_type",
FieldID: fieldID,
Value: "value 1",
}
value2 := &model.PropertyValue{
GroupID: groupID,
TargetID: targetID,
TargetType: "other_type",
FieldID: model.NewId(),
Value: "value 2",
}
value3 := &model.PropertyValue{
GroupID: model.NewId(),
TargetID: model.NewId(),
TargetType: "test_type",
FieldID: model.NewId(),
Value: "value 3",
}
value4 := &model.PropertyValue{
GroupID: groupID,
TargetID: model.NewId(),
TargetType: "test_type",
FieldID: fieldID,
Value: "value 4",
}
for _, value := range []*model.PropertyValue{value1, value2, value3, value4} {
_, err := ss.PropertyValue().Create(value)
require.NoError(t, err)
time.Sleep(10 * time.Millisecond)
}
// Delete one value for deletion tests
require.NoError(t, ss.PropertyValue().Delete(value4.ID))
tests := []struct {
name string
opts model.PropertyValueSearchOpts
expectedError bool
expectedIDs []string
}{
{
name: "negative page",
opts: model.PropertyValueSearchOpts{
Page: -1,
PerPage: 10,
},
expectedError: true,
},
{
name: "negative per_page",
opts: model.PropertyValueSearchOpts{
Page: 0,
PerPage: -1,
},
expectedError: true,
},
{
name: "filter by group_id",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID, value2.ID},
},
{
name: "filter by group_id and target_type",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
TargetType: "test_type",
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID},
},
{
name: "filter by group_id and target_type including deleted",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
TargetType: "test_type",
IncludeDeleted: true,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID, value4.ID},
},
{
name: "filter by target_id",
opts: model.PropertyValueSearchOpts{
TargetID: targetID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID, value2.ID},
},
{
name: "filter by group_id and target_id",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
TargetID: targetID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID, value2.ID},
},
{
name: "filter by field_id",
opts: model.PropertyValueSearchOpts{
FieldID: fieldID,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID},
},
{
name: "filter by field_id including deleted",
opts: model.PropertyValueSearchOpts{
FieldID: fieldID,
IncludeDeleted: true,
Page: 0,
PerPage: 10,
},
expectedIDs: []string{value1.ID, value4.ID},
},
{
name: "pagination page 0",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
Page: 0,
PerPage: 1,
},
expectedIDs: []string{value1.ID},
},
{
name: "pagination page 1",
opts: model.PropertyValueSearchOpts{
GroupID: groupID,
Page: 1,
PerPage: 1,
},
expectedIDs: []string{value2.ID},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
results, err := ss.PropertyValue().SearchPropertyValues(tc.opts)
if tc.expectedError {
require.Error(t, err)
return
}
require.NoError(t, err)
var ids = make([]string, len(results))
for i, value := range results {
ids[i] = value.ID
}
require.ElementsMatch(t, tc.expectedIDs, ids)
})
}
}
func testDeleteForField(t *testing.T, _ request.CTX, ss store.Store) {
fieldID := model.NewId()
// Create test values
value1 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: fieldID,
Value: "value 1",
}
value2 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: fieldID,
Value: "value 2",
}
value3 := &model.PropertyValue{
TargetID: model.NewId(),
TargetType: "test_type",
GroupID: model.NewId(),
FieldID: model.NewId(), // Different field ID
Value: "value 3",
}
for _, value := range []*model.PropertyValue{value1, value2, value3} {
_, err := ss.PropertyValue().Create(value)
require.NoError(t, err)
}
// Delete values for the field
err := ss.PropertyValue().DeleteForField(fieldID)
require.NoError(t, err)
// Verify values were soft-deleted
deletedValues, err := ss.PropertyValue().GetMany([]string{value1.ID, value2.ID})
require.NoError(t, err)
require.Len(t, deletedValues, 2)
require.NotZero(t, deletedValues[0].DeleteAt)
require.NotZero(t, deletedValues[1].DeleteAt)
// Verify value with different field ID was not deleted
nonDeletedValue, err := ss.PropertyValue().Get(value3.ID)
require.NoError(t, err)
require.Zero(t, nonDeletedValue.DeleteAt)
}

Просмотреть файл

@@ -66,6 +66,9 @@ type Store struct {
DesktopTokensStore mocks.DesktopTokensStore
ChannelBookmarkStore mocks.ChannelBookmarkStore
ScheduledPostStore mocks.ScheduledPostStore
PropertyGroupStore mocks.PropertyGroupStore
PropertyFieldStore mocks.PropertyFieldStore
PropertyValueStore mocks.PropertyValueStore
}
func (s *Store) SetContext(context context.Context) { s.context = context }
@@ -119,6 +122,9 @@ func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMet
func (s *Store) SharedChannel() store.SharedChannelStore { return &s.SharedChannelStore }
func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorityStore }
func (s *Store) ScheduledPost() store.ScheduledPostStore { return &s.ScheduledPostStore }
func (s *Store) PropertyGroup() store.PropertyGroupStore { return &s.PropertyGroupStore }
func (s *Store) PropertyField() store.PropertyFieldStore { return &s.PropertyFieldStore }
func (s *Store) PropertyValue() store.PropertyValueStore { return &s.PropertyValueStore }
func (s *Store) PostAcknowledgement() store.PostAcknowledgementStore {
return &s.PostAcknowledgementStore
}

Просмотреть файл

@@ -46,6 +46,9 @@ type TimerLayer struct {
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
PropertyFieldStore store.PropertyFieldStore
PropertyGroupStore store.PropertyGroupStore
PropertyValueStore store.PropertyValueStore
ReactionStore store.ReactionStore
RemoteClusterStore store.RemoteClusterStore
RetentionPolicyStore store.RetentionPolicyStore
@@ -175,6 +178,18 @@ func (s *TimerLayer) ProductNotices() store.ProductNoticesStore {
return s.ProductNoticesStore
}
func (s *TimerLayer) PropertyField() store.PropertyFieldStore {
return s.PropertyFieldStore
}
func (s *TimerLayer) PropertyGroup() store.PropertyGroupStore {
return s.PropertyGroupStore
}
func (s *TimerLayer) PropertyValue() store.PropertyValueStore {
return s.PropertyValueStore
}
func (s *TimerLayer) Reaction() store.ReactionStore {
return s.ReactionStore
}
@@ -386,6 +401,21 @@ type TimerLayerProductNoticesStore struct {
Root *TimerLayer
}
type TimerLayerPropertyFieldStore struct {
store.PropertyFieldStore
Root *TimerLayer
}
type TimerLayerPropertyGroupStore struct {
store.PropertyGroupStore
Root *TimerLayer
}
type TimerLayerPropertyValueStore struct {
store.PropertyValueStore
Root *TimerLayer
}
type TimerLayerReactionStore struct {
store.ReactionStore
Root *TimerLayer
@@ -6979,6 +7009,246 @@ func (s *TimerLayerProductNoticesStore) View(userID string, notices []string) er
return err
}
func (s *TimerLayerPropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
start := time.Now()
result, err := s.PropertyFieldStore.Create(field)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.Create", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyFieldStore) Delete(id string) error {
start := time.Now()
err := s.PropertyFieldStore.Delete(id)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.Delete", success, elapsed)
}
return err
}
func (s *TimerLayerPropertyFieldStore) Get(id string) (*model.PropertyField, error) {
start := time.Now()
result, err := s.PropertyFieldStore.Get(id)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.Get", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
start := time.Now()
result, err := s.PropertyFieldStore.GetMany(ids)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.GetMany", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
start := time.Now()
result, err := s.PropertyFieldStore.SearchPropertyFields(opts)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.SearchPropertyFields", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyFieldStore) Update(field []*model.PropertyField) ([]*model.PropertyField, error) {
start := time.Now()
result, err := s.PropertyFieldStore.Update(field)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyFieldStore.Update", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
start := time.Now()
result, err := s.PropertyGroupStore.Get(name)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyGroupStore.Get", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
start := time.Now()
result, err := s.PropertyGroupStore.Register(name)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyGroupStore.Register", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
start := time.Now()
result, err := s.PropertyValueStore.Create(value)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.Create", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyValueStore) Delete(id string) error {
start := time.Now()
err := s.PropertyValueStore.Delete(id)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.Delete", success, elapsed)
}
return err
}
func (s *TimerLayerPropertyValueStore) DeleteForField(id string) error {
start := time.Now()
err := s.PropertyValueStore.DeleteForField(id)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.DeleteForField", success, elapsed)
}
return err
}
func (s *TimerLayerPropertyValueStore) Get(id string) (*model.PropertyValue, error) {
start := time.Now()
result, err := s.PropertyValueStore.Get(id)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.Get", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
start := time.Now()
result, err := s.PropertyValueStore.GetMany(ids)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.GetMany", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
start := time.Now()
result, err := s.PropertyValueStore.SearchPropertyValues(opts)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.SearchPropertyValues", success, elapsed)
}
return result, err
}
func (s *TimerLayerPropertyValueStore) Update(field []*model.PropertyValue) ([]*model.PropertyValue, error) {
start := time.Now()
result, err := s.PropertyValueStore.Update(field)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PropertyValueStore.Update", success, elapsed)
}
return result, err
}
func (s *TimerLayerReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, error) {
start := time.Now()
@@ -12479,6 +12749,9 @@ func New(childStore store.Store, metrics einterfaces.MetricsInterface) *TimerLay
newStore.PostPriorityStore = &TimerLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
newStore.PreferenceStore = &TimerLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
newStore.ProductNoticesStore = &TimerLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
newStore.PropertyFieldStore = &TimerLayerPropertyFieldStore{PropertyFieldStore: childStore.PropertyField(), Root: &newStore}
newStore.PropertyGroupStore = &TimerLayerPropertyGroupStore{PropertyGroupStore: childStore.PropertyGroup(), Root: &newStore}
newStore.PropertyValueStore = &TimerLayerPropertyValueStore{PropertyValueStore: childStore.PropertyValue(), Root: &newStore}
newStore.ReactionStore = &TimerLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}
newStore.RemoteClusterStore = &TimerLayerRemoteClusterStore{RemoteClusterStore: childStore.RemoteCluster(), Root: &newStore}
newStore.RetentionPolicyStore = &TimerLayerRetentionPolicyStore{RetentionPolicyStore: childStore.RetentionPolicy(), Root: &newStore}