[MM-68999] Add SchemaVersion to PropertyGroup for group-specific field schema versioning (#36747)

This commit is contained in:
David Krauser
2026-05-27 14:27:16 -04:00
committed by GitHub
parent 159ed5ad96
commit a6e019863e
7 changed files with 90 additions and 7 deletions
+1 -1
View File
@@ -272,7 +272,7 @@ func NewServer(options ...Option) (*Server, error) {
// Register builtin property groups before creating hooks that reference them
if err = s.propertyService.RegisterBuiltinGroups([]*model.PropertyGroup{
{Name: model.AccessControlPropertyGroupName, Version: model.PropertyGroupVersionV2},
{Name: model.AccessControlPropertyGroupName, Version: model.PropertyGroupVersionV2, SchemaVersion: model.AccessControlPropertyGroupSchemaVersion},
{Name: model.ContentFlaggingGroupName, Version: model.PropertyGroupVersionV1},
}); err != nil {
return nil, errors.Wrap(err, "failed to register builtin property groups")
@@ -0,0 +1 @@
ALTER TABLE PropertyGroups DROP COLUMN IF EXISTS SchemaVersion;
@@ -0,0 +1 @@
ALTER TABLE PropertyGroups ADD COLUMN IF NOT EXISTS SchemaVersion integer DEFAULT 1 NOT NULL;
@@ -11,7 +11,7 @@ import (
"github.com/mattermost/mattermost/server/v8/channels/store"
)
var propertyGroupColumns = []string{"ID", "Name", "Version"}
var propertyGroupColumns = []string{"ID", "Name", "Version", "SchemaVersion"}
type SqlPropertyGroupStore struct {
*SqlStore
@@ -34,8 +34,8 @@ func (s *SqlPropertyGroupStore) Register(group *model.PropertyGroup) (*model.Pro
builder := s.getQueryBuilder().
Insert("PropertyGroups").
Columns("ID", "Name", "Version").
Values(group.ID, group.Name, group.Version)
Columns("ID", "Name", "Version", "SchemaVersion").
Values(group.ID, group.Name, group.Version, group.SchemaVersion)
builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (Name) DO NOTHING"))
@@ -15,6 +15,7 @@ import (
func TestPropertyGroupStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
t.Run("RegisterAndGetPropertyGroup", func(t *testing.T) { testRegisterAndGetPropertyGroup(t, rctx, ss) })
t.Run("IncrementVersion", func(t *testing.T) { testIncrementVersion(t, rctx, ss) })
t.Run("SchemaVersionPersistence", func(t *testing.T) { testSchemaVersionPersistence(t, rctx, ss) })
}
func testRegisterAndGetPropertyGroup(t *testing.T, _ request.CTX, ss store.Store) {
@@ -121,6 +122,57 @@ func testRegisterAndGetPropertyGroup(t *testing.T, _ request.CTX, ss store.Store
})
}
func testSchemaVersionPersistence(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("explicit SchemaVersion is persisted and returned", func(t *testing.T) {
group, err := ss.PropertyGroup().Register(&model.PropertyGroup{
Name: "schema_version_explicit_test",
Version: model.PropertyGroupVersionV1,
SchemaVersion: 5,
})
require.NoError(t, err)
require.Equal(t, 5, group.SchemaVersion)
fetched, err := ss.PropertyGroup().Get("schema_version_explicit_test")
require.NoError(t, err)
require.Equal(t, 5, fetched.SchemaVersion)
})
t.Run("SchemaVersion defaults to 1 when not set", func(t *testing.T) {
group, err := ss.PropertyGroup().Register(&model.PropertyGroup{
Name: "schema_version_default_test",
Version: model.PropertyGroupVersionV1,
})
require.NoError(t, err)
require.Equal(t, 1, group.SchemaVersion)
fetched, err := ss.PropertyGroup().Get("schema_version_default_test")
require.NoError(t, err)
require.Equal(t, 1, fetched.SchemaVersion)
})
t.Run("SchemaVersion is not updated on re-registration", func(t *testing.T) {
original, err := ss.PropertyGroup().Register(&model.PropertyGroup{
Name: "schema_version_immutable_test",
Version: model.PropertyGroupVersionV1,
SchemaVersion: 2,
})
require.NoError(t, err)
require.Equal(t, 2, original.SchemaVersion)
reregistered, err := ss.PropertyGroup().Register(&model.PropertyGroup{
Name: "schema_version_immutable_test",
Version: model.PropertyGroupVersionV1,
SchemaVersion: 99,
})
require.NoError(t, err)
require.Equal(t, 2, reregistered.SchemaVersion)
fetched, err := ss.PropertyGroup().Get("schema_version_immutable_test")
require.NoError(t, err)
require.Equal(t, 2, fetched.SchemaVersion)
})
}
func testIncrementVersion(t *testing.T, _ request.CTX, ss store.Store) {
t.Run("should increment version of an existing group", func(t *testing.T) {
registered, err := ss.PropertyGroup().Register(&model.PropertyGroup{
+14 -3
View File
@@ -32,10 +32,17 @@ const (
PropertyGroupVersionV2 = 2
)
// AccessControlPropertyGroupSchemaVersion is the current schema version for
// the access_control group's field definitions. Increment this constant
// whenever the shape of access_control fields (attrs, types, options) changes
// in a way that consumers need to detect.
const AccessControlPropertyGroupSchemaVersion = 1
type PropertyGroup struct {
ID string `json:"id"`
Name string `json:"name"`
Version int `json:"version"`
ID string `json:"id"`
Name string `json:"name"`
Version int `json:"version"`
SchemaVersion int `json:"schema_version"`
}
func (pg *PropertyGroup) IsPSAv1() bool {
@@ -54,6 +61,10 @@ func (pg *PropertyGroup) PreSave() {
if pg.Version == 0 {
pg.Version = PropertyGroupVersionV1
}
if pg.SchemaVersion <= 0 {
pg.SchemaVersion = 1
}
}
func (pg *PropertyGroup) IsValid() *AppError {
@@ -129,4 +129,22 @@ func TestPropertyGroupPreSave(t *testing.T) {
pg.PreSave()
assert.Equal(t, PropertyGroupVersionV2, pg.Version)
})
t.Run("defaults schema_version to 1 when zero", func(t *testing.T) {
pg := &PropertyGroup{Name: "test_group"}
pg.PreSave()
assert.Equal(t, 1, pg.SchemaVersion)
})
t.Run("defaults schema_version to 1 when negative", func(t *testing.T) {
pg := &PropertyGroup{Name: "test_group", SchemaVersion: -5}
pg.PreSave()
assert.Equal(t, 1, pg.SchemaVersion)
})
t.Run("does not overwrite existing schema_version", func(t *testing.T) {
pg := &PropertyGroup{Name: "test_group", SchemaVersion: 3}
pg.PreSave()
assert.Equal(t, 3, pg.SchemaVersion)
})
}