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 удалений

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

@@ -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()