[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:
Guillermo Vayá
2025-09-26 13:14:29 +02:00
committed by GitHub
parent de686b80bf
commit 9d16035212
3 changed files with 145 additions and 0 deletions
@@ -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{
+1
View File
@@ -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