mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-24 16:05:00 +08:00
[MM-65769] Add UpdatedSince to properties search in pluginapi (#33959)
* add since to properties search * improve testing * fix tests * address naming concerns * adapt comments to new naming * style fixes * who you gonna call?
This commit is contained in:
@@ -133,6 +133,10 @@ func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSea
|
||||
builder = builder.Where(sq.Eq{"FieldID": opts.FieldID})
|
||||
}
|
||||
|
||||
if opts.SinceUpdateAt > 0 {
|
||||
builder = builder.Where(sq.Gt{"UpdateAt": opts.SinceUpdateAt})
|
||||
}
|
||||
|
||||
var values []*model.PropertyValue
|
||||
if err := s.GetReplica().SelectBuilder(&values, builder); err != nil {
|
||||
return nil, errors.Wrap(err, "property_value_search_query")
|
||||
|
||||
@@ -25,6 +25,7 @@ func TestPropertyValueStore(t *testing.T, rctx request.CTX, ss store.Store, s Sq
|
||||
t.Run("UpsertPropertyValue", func(t *testing.T) { testUpsertPropertyValue(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("SearchPropertyValuesSince", func(t *testing.T) { testSearchPropertyValuesSince(t, rctx, ss) })
|
||||
t.Run("DeleteForField", func(t *testing.T) { testDeleteForField(t, rctx, ss) })
|
||||
t.Run("DeleteForTarget", func(t *testing.T) { testDeleteForTarget(t, rctx, ss) })
|
||||
}
|
||||
@@ -879,6 +880,40 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store) {
|
||||
},
|
||||
expectedIDs: []string{value1.ID, value2.ID},
|
||||
},
|
||||
{
|
||||
name: "filter by SinceUpdateAt timestamp - no results before",
|
||||
opts: model.PropertyValueSearchOpts{
|
||||
SinceUpdateAt: value3.UpdateAt, // After all existing values
|
||||
PerPage: 10,
|
||||
},
|
||||
expectedIDs: []string{},
|
||||
},
|
||||
{
|
||||
name: "filter by SinceUpdateAt timestamp - get values after specific time",
|
||||
opts: model.PropertyValueSearchOpts{
|
||||
SinceUpdateAt: value1.UpdateAt, // After value1, should get value2 and value3
|
||||
PerPage: 10,
|
||||
},
|
||||
expectedIDs: []string{value2.ID, value3.ID},
|
||||
},
|
||||
{
|
||||
name: "filter by SinceUpdateAt timestamp with group filter",
|
||||
opts: model.PropertyValueSearchOpts{
|
||||
GroupID: groupID,
|
||||
SinceUpdateAt: value1.UpdateAt, // After value1, should only get value2 from same group
|
||||
PerPage: 10,
|
||||
},
|
||||
expectedIDs: []string{value2.ID},
|
||||
},
|
||||
{
|
||||
name: "filter by SinceUpdateAt timestamp including deleted",
|
||||
opts: model.PropertyValueSearchOpts{
|
||||
SinceUpdateAt: value3.UpdateAt, // After value3, should get value4 (deleted)
|
||||
IncludeDeleted: true,
|
||||
PerPage: 10,
|
||||
},
|
||||
expectedIDs: []string{value4.ID},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -899,6 +934,111 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store) {
|
||||
}
|
||||
}
|
||||
|
||||
func testSearchPropertyValuesSince(t *testing.T, _ request.CTX, ss store.Store) {
|
||||
// Create values with controlled timestamps for precise testing
|
||||
groupID := model.NewId()
|
||||
|
||||
// Create value 1 (will remain unchanged)
|
||||
value1, err := ss.PropertyValue().Create(&model.PropertyValue{
|
||||
GroupID: groupID,
|
||||
TargetID: model.NewId(),
|
||||
TargetType: "test_type",
|
||||
FieldID: model.NewId(),
|
||||
Value: json.RawMessage(`"value1"`),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(10 * time.Millisecond) // Ensure different timestamps
|
||||
|
||||
// Create value 2 (will be updated later)
|
||||
value2, err := ss.PropertyValue().Create(&model.PropertyValue{
|
||||
GroupID: groupID,
|
||||
TargetID: model.NewId(),
|
||||
TargetType: "test_type",
|
||||
FieldID: model.NewId(),
|
||||
Value: json.RawMessage(`"value2"`),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Create value 3 (will remain unchanged)
|
||||
value3, err := ss.PropertyValue().Create(&model.PropertyValue{
|
||||
GroupID: groupID,
|
||||
TargetID: model.NewId(),
|
||||
TargetType: "test_type",
|
||||
FieldID: model.NewId(),
|
||||
Value: json.RawMessage(`"value3"`),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update value2 to change its UpdateAt timestamp
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
value2.Value = json.RawMessage(`"value2_updated"`)
|
||||
updatedValues, err := ss.PropertyValue().Update("", []*model.PropertyValue{value2})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, updatedValues, 1)
|
||||
updatedValue2 := updatedValues[0]
|
||||
|
||||
t.Run("SinceUpdateAt filters correctly by UpdateAt", func(t *testing.T) {
|
||||
// Get values updated after value1 (should get value2 and value3)
|
||||
results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{
|
||||
GroupID: groupID,
|
||||
SinceUpdateAt: value1.UpdateAt,
|
||||
PerPage: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 2)
|
||||
|
||||
resultIDs := make([]string, len(results))
|
||||
for i, result := range results {
|
||||
resultIDs[i] = result.ID
|
||||
}
|
||||
require.ElementsMatch(t, []string{value2.ID, value3.ID}, resultIDs)
|
||||
})
|
||||
|
||||
t.Run("SinceUpdateAt with boundary condition", func(t *testing.T) {
|
||||
// Get values updated after value3's timestamp
|
||||
// Should get both value2 (updated) and value3, so expect 2 results
|
||||
results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{
|
||||
GroupID: groupID,
|
||||
SinceUpdateAt: value3.UpdateAt - 1, // Slightly before value3's timestamp
|
||||
PerPage: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 2)
|
||||
|
||||
resultIDs := make([]string, len(results))
|
||||
for i, result := range results {
|
||||
resultIDs[i] = result.ID
|
||||
}
|
||||
// Should get both value2 (updated with new timestamp) and value3
|
||||
require.ElementsMatch(t, []string{value2.ID, value3.ID}, resultIDs)
|
||||
})
|
||||
|
||||
t.Run("SinceUpdateAt after all updates", func(t *testing.T) {
|
||||
// Get values updated after the most recent update
|
||||
results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{
|
||||
GroupID: groupID,
|
||||
SinceUpdateAt: updatedValue2.UpdateAt, // After the update
|
||||
PerPage: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 0) // Should be empty
|
||||
})
|
||||
|
||||
t.Run("SinceUpdateAt with very recent timestamp", func(t *testing.T) {
|
||||
// Get values updated since current time
|
||||
results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{
|
||||
GroupID: groupID,
|
||||
SinceUpdateAt: model.GetMillis(),
|
||||
PerPage: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func testCreatePropertyValueWithArray(t *testing.T, _ request.CTX, ss store.Store) {
|
||||
t.Run("should create a property value with array", func(t *testing.T) {
|
||||
newValue := &model.PropertyValue{
|
||||
|
||||
@@ -108,6 +108,7 @@ type PropertyValueSearchOpts struct {
|
||||
TargetType string
|
||||
TargetIDs []string
|
||||
FieldID string
|
||||
SinceUpdateAt int64 // UpdateAt after which to send the items
|
||||
IncludeDeleted bool
|
||||
Cursor PropertyValueSearchCursor
|
||||
PerPage int
|
||||
|
||||
Reference in New Issue
Block a user