diff --git a/server/channels/app/properties/access_control.go b/server/channels/app/properties/access_control.go index 50bcc9bbb9a..f546b8b7377 100644 --- a/server/channels/app/properties/access_control.go +++ b/server/channels/app/properties/access_control.go @@ -304,7 +304,7 @@ func (h *AccessControlHook) PostGetPropertyField(rctx request.CTX, field *model. } callerID := h.extractCallerID(rctx) - return h.applyFieldReadAccessControl(field, callerID), nil + return h.applyFieldReadAccessControl(rctx, field, callerID), nil } // PostGetPropertyFields applies read access control to a list of fields. @@ -319,7 +319,7 @@ func (h *AccessControlHook) PostGetPropertyFields(rctx request.CTX, fields []*mo } callerID := h.extractCallerID(rctx) - return h.applyFieldReadAccessControlToList(fields, callerID), nil + return h.applyFieldReadAccessControlToList(rctx, fields, callerID), nil } // Value Pre-Hooks @@ -516,7 +516,7 @@ func (h *AccessControlHook) PreDeletePropertyValuesForTarget(rctx request.CTX, g opts.Cursor = cursor } - values, err := h.propertyService.searchPropertyValues(groupID, opts) + values, err := h.propertyService.searchPropertyValues(rctx, groupID, opts) if err != nil { return err } @@ -911,7 +911,7 @@ func (h *AccessControlHook) checkValueWriteAccess(field *model.PropertyField, ca } // getCallerValuesForField retrieves all property values for the caller on a specific field. -func (h *AccessControlHook) getCallerValuesForField(groupID, fieldID, callerID string) ([]*model.PropertyValue, error) { +func (h *AccessControlHook) getCallerValuesForField(rctx request.CTX, groupID, fieldID, callerID string) ([]*model.PropertyValue, error) { if callerID == "" { return []*model.PropertyValue{}, nil } @@ -936,7 +936,7 @@ func (h *AccessControlHook) getCallerValuesForField(groupID, fieldID, callerID s opts.Cursor = cursor } - values, err := h.propertyService.searchPropertyValues(groupID, opts) + values, err := h.propertyService.searchPropertyValues(rctx, groupID, opts) if err != nil { return nil, fmt.Errorf("failed to get caller values for field: %w", err) } @@ -1010,8 +1010,8 @@ func (h *AccessControlHook) copyPropertyField(field *model.PropertyField) *model } // getCallerOptionIDsForField retrieves the caller's values for a field and extracts all option IDs. -func (h *AccessControlHook) getCallerOptionIDsForField(groupID, fieldID, callerID string, fieldType model.PropertyFieldType) (map[string]struct{}, error) { - callerValues, err := h.getCallerValuesForField(groupID, fieldID, callerID) +func (h *AccessControlHook) getCallerOptionIDsForField(rctx request.CTX, groupID, fieldID, callerID string, fieldType model.PropertyFieldType) (map[string]struct{}, error) { + callerValues, err := h.getCallerValuesForField(rctx, groupID, fieldID, callerID) if err != nil { return make(map[string]struct{}), err } @@ -1039,16 +1039,16 @@ func (h *AccessControlHook) getCallerOptionIDsForField(groupID, fieldID, callerI // field exposes every option at or below the caller's own rank ("everything at // your rank and lower"), so a higher-cleared caller sees the full ladder up to // their level. See filterSharedOnlyRankFieldOptions. -func (h *AccessControlHook) filterSharedOnlyFieldOptions(field *model.PropertyField, callerID string) *model.PropertyField { +func (h *AccessControlHook) filterSharedOnlyFieldOptions(rctx request.CTX, field *model.PropertyField, callerID string) *model.PropertyField { if !field.Type.SupportsOptions() { return field } if field.Type == model.PropertyFieldTypeRank { - return h.filterSharedOnlyRankFieldOptions(field, callerID) + return h.filterSharedOnlyRankFieldOptions(rctx, field, callerID) } - callerOptionIDs, err := h.getCallerOptionIDsForField(field.GroupID, field.ID, callerID, field.Type) + callerOptionIDs, err := h.getCallerOptionIDsForField(rctx, field.GroupID, field.ID, callerID, field.Type) if err != nil || len(callerOptionIDs) == 0 { filteredField := h.copyPropertyField(field) filteredField.Attrs[model.PropertyFieldAttributeOptions] = []any{} @@ -1092,7 +1092,7 @@ func (h *AccessControlHook) filterSharedOnlyFieldOptions(field *model.PropertyFi // or below the caller's own rank, rather than the exact-match intersection // used for select/multiselect. A caller who holds no value for the field (and // therefore has no rank) sees no options. -func (h *AccessControlHook) filterSharedOnlyRankFieldOptions(field *model.PropertyField, callerID string) *model.PropertyField { +func (h *AccessControlHook) filterSharedOnlyRankFieldOptions(rctx request.CTX, field *model.PropertyField, callerID string) *model.PropertyField { // Bail out before building the rank map or the caller-rank store lookup when // there are no options to filter: an absent or malformed options array has // nothing to hide, so the field is returned untouched. @@ -1109,7 +1109,7 @@ func (h *AccessControlHook) filterSharedOnlyRankFieldOptions(field *model.Proper } rankByID := buildOptionRankMap(field) - callerRank, ok := h.callerRankForField(field, callerID, rankByID) + callerRank, ok := h.callerRankForField(rctx, field, callerID, rankByID) if !ok { filteredField := h.copyPropertyField(field) filteredField.Attrs[model.PropertyFieldAttributeOptions] = []any{} @@ -1145,8 +1145,8 @@ func (h *AccessControlHook) filterSharedOnlyRankFieldOptions(field *model.Proper // single value per user), so the caller has at most one option; we take it. // ok is false when the caller has no value for the field or the option carries // no rank, in which case the caller has no clearance and sees nothing. -func (h *AccessControlHook) callerRankForField(field *model.PropertyField, callerID string, rankByID map[string]int) (int, bool) { - callerOptionIDs, err := h.getCallerOptionIDsForField(field.GroupID, field.ID, callerID, field.Type) +func (h *AccessControlHook) callerRankForField(rctx request.CTX, field *model.PropertyField, callerID string, rankByID map[string]int) (int, bool) { + callerOptionIDs, err := h.getCallerOptionIDsForField(rctx, field.GroupID, field.ID, callerID, field.Type) if err != nil || len(callerOptionIDs) == 0 { return 0, false } @@ -1197,16 +1197,16 @@ func buildOptionRankMap(field *model.PropertyField) map[string]int { // The binary path is what protects scenarios like LDAP/SAML-synced text codenames whose // existence is itself controlled information: a caller who doesn't hold the same value // must not see the target's value through any read endpoint. -func (h *AccessControlHook) filterSharedOnlyValue(field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { +func (h *AccessControlHook) filterSharedOnlyValue(rctx request.CTX, field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { if field.Type == model.PropertyFieldTypeRank { - return h.filterSharedOnlyRankValue(field, value, callerID) + return h.filterSharedOnlyRankValue(rctx, field, value, callerID) } if field.Type != model.PropertyFieldTypeSelect && field.Type != model.PropertyFieldTypeMultiselect { - return h.filterSharedOnlyScalarValue(field, value, callerID) + return h.filterSharedOnlyScalarValue(rctx, field, value, callerID) } - callerOptionIDs, err := h.getCallerOptionIDsForField(field.GroupID, field.ID, callerID, field.Type) + callerOptionIDs, err := h.getCallerOptionIDsForField(rctx, field.GroupID, field.ID, callerID, field.Type) if err != nil || len(callerOptionIDs) == 0 { return nil } @@ -1261,9 +1261,9 @@ func (h *AccessControlHook) filterSharedOnlyValue(field *model.PropertyField, va // option match. A caller who holds no value of their own (and therefore has no // rank) sees nothing. A rank field is select-shaped, so the target has at most // one option. -func (h *AccessControlHook) filterSharedOnlyRankValue(field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { +func (h *AccessControlHook) filterSharedOnlyRankValue(rctx request.CTX, field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { rankByID := buildOptionRankMap(field) - callerRank, ok := h.callerRankForField(field, callerID, rankByID) + callerRank, ok := h.callerRankForField(rctx, field, callerID, rankByID) if !ok { return nil } @@ -1319,12 +1319,12 @@ func (h *AccessControlHook) clampRankValueToRank(value *model.PropertyValue, ran // returns the value as-is if the caller's own stored value for the same field equals // the target's value, otherwise nil. Caller and target may legitimately store nothing, // in which case the value is hidden. -func (h *AccessControlHook) filterSharedOnlyScalarValue(field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { +func (h *AccessControlHook) filterSharedOnlyScalarValue(rctx request.CTX, field *model.PropertyField, value *model.PropertyValue, callerID string) *model.PropertyValue { if value == nil || len(value.Value) == 0 { return nil } - callerValues, err := h.getCallerValuesForField(field.GroupID, field.ID, callerID) + callerValues, err := h.getCallerValuesForField(rctx, field.GroupID, field.ID, callerID) if err != nil || len(callerValues) == 0 { return nil } @@ -1344,7 +1344,7 @@ func (h *AccessControlHook) filterSharedOnlyScalarValue(field *model.PropertyFie // - Any access mode when the caller is the field's source plugin: returned as-is // - Shared-only fields: returned with options filtered using filterSharedOnlyFieldOptions // - Source-only or unknown access modes: returned with empty options (secure default) -func (h *AccessControlHook) applyFieldReadAccessControl(field *model.PropertyField, callerID string) *model.PropertyField { +func (h *AccessControlHook) applyFieldReadAccessControl(rctx request.CTX, field *model.PropertyField, callerID string) *model.PropertyField { if h.hasUnrestrictedFieldReadAccess(field, callerID) { return field } @@ -1352,7 +1352,7 @@ func (h *AccessControlHook) applyFieldReadAccessControl(field *model.PropertyFie accessMode := h.getAccessMode(field) if accessMode == model.PropertyAccessModeSharedOnly { - return h.filterSharedOnlyFieldOptions(field, callerID) + return h.filterSharedOnlyFieldOptions(rctx, field, callerID) } // Source-only or unknown: return with empty options (secure default) @@ -1364,14 +1364,14 @@ func (h *AccessControlHook) applyFieldReadAccessControl(field *model.PropertyFie } // applyFieldReadAccessControlToList applies read access control to a list of fields. -func (h *AccessControlHook) applyFieldReadAccessControlToList(fields []*model.PropertyField, callerID string) []*model.PropertyField { +func (h *AccessControlHook) applyFieldReadAccessControlToList(rctx request.CTX, fields []*model.PropertyField, callerID string) []*model.PropertyField { if len(fields) == 0 { return fields } filtered := make([]*model.PropertyField, 0, len(fields)) for _, field := range fields { - filtered = append(filtered, h.applyFieldReadAccessControl(field, callerID)) + filtered = append(filtered, h.applyFieldReadAccessControl(rctx, field, callerID)) } return filtered @@ -1434,7 +1434,7 @@ func (h *AccessControlHook) applyValueReadAccessControl(rctx request.CTX, values if h.hasUnrestrictedFieldReadAccess(field, callerID) { filtered = append(filtered, value) } else if accessMode == model.PropertyAccessModeSharedOnly { - filteredValue := h.filterSharedOnlyValue(field, value, callerID) + filteredValue := h.filterSharedOnlyValue(rctx, field, value, callerID) if filteredValue != nil { filtered = append(filtered, filteredValue) } diff --git a/server/channels/app/properties/property_value.go b/server/channels/app/properties/property_value.go index 51f2cb2a010..d4e057870b1 100644 --- a/server/channels/app/properties/property_value.go +++ b/server/channels/app/properties/property_value.go @@ -79,11 +79,11 @@ func (ps *PropertyService) getPropertyValues(groupID string, ids []string) ([]*m return ps.valueStore.GetMany(groupID, ids) } -func (ps *PropertyService) searchPropertyValues(groupID string, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { +func (ps *PropertyService) searchPropertyValues(rctx request.CTX, groupID string, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { // groupID is part of the search method signature to // incentivize the use of the database indexes in searches opts.GroupID = groupID - return ps.valueStore.SearchPropertyValues(opts) + return ps.valueStore.SearchPropertyValues(rctx, opts) } func (ps *PropertyService) updatePropertyValue(rctx request.CTX, groupID string, value *model.PropertyValue) (*model.PropertyValue, error) { @@ -196,7 +196,7 @@ func (ps *PropertyService) GetPropertyValues(rctx request.CTX, groupID string, i } func (ps *PropertyService) SearchPropertyValues(rctx request.CTX, groupID string, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { - values, err := ps.searchPropertyValues(groupID, opts) + values, err := ps.searchPropertyValues(rctx, groupID, opts) if err != nil { return nil, fmt.Errorf("SearchPropertyValues: %w", err) } diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go index 718671354ce..25ffd9536ab 100644 --- a/server/channels/store/retrylayer/retrylayer.go +++ b/server/channels/store/retrylayer/retrylayer.go @@ -10980,11 +10980,11 @@ func (s *RetryLayerPropertyValueStore) GetMany(groupID string, ids []string) ([] } -func (s *RetryLayerPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { +func (s *RetryLayerPropertyValueStore) SearchPropertyValues(rctx request.CTX, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { tries := 0 for { - result, err := s.PropertyValueStore.SearchPropertyValues(opts) + result, err := s.PropertyValueStore.SearchPropertyValues(rctx, opts) if err == nil { return result, nil } diff --git a/server/channels/store/sqlstore/property_value_store.go b/server/channels/store/sqlstore/property_value_store.go index 98b75a0a81d..0a137dbf9c6 100644 --- a/server/channels/store/sqlstore/property_value_store.go +++ b/server/channels/store/sqlstore/property_value_store.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/store" ) @@ -134,7 +135,7 @@ func (s *SqlPropertyValueStore) GetMany(groupID string, ids []string) ([]*model. return values, nil } -func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { +func (s *SqlPropertyValueStore) SearchPropertyValues(rctx request.CTX, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { if err := opts.IsValid(); err != nil { return nil, fmt.Errorf("opts is invalid: %w", err) } @@ -204,7 +205,7 @@ func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSea } var values []*model.PropertyValue - if err := s.GetReplica().SelectBuilder(&values, builder); err != nil { + if err := s.DBXFromContext(rctx.Context()).SelectBuilder(&values, builder); err != nil { return nil, errors.Wrap(err, "property_value_search_query") } diff --git a/server/channels/store/store.go b/server/channels/store/store.go index d1110d11267..e352bd6b560 100644 --- a/server/channels/store/store.go +++ b/server/channels/store/store.go @@ -1209,7 +1209,7 @@ type PropertyValueStore interface { CreateMany(values []*model.PropertyValue) ([]*model.PropertyValue, error) Get(groupID, id string) (*model.PropertyValue, error) GetMany(groupID string, ids []string) ([]*model.PropertyValue, error) - SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) + SearchPropertyValues(rctx request.CTX, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) Update(groupID string, values []*model.PropertyValue) ([]*model.PropertyValue, error) Upsert(values []*model.PropertyValue) ([]*model.PropertyValue, error) Delete(groupID string, id string) error diff --git a/server/channels/store/storetest/mocks/PropertyValueStore.go b/server/channels/store/storetest/mocks/PropertyValueStore.go index b8a9cfbe736..adfd11875e9 100644 --- a/server/channels/store/storetest/mocks/PropertyValueStore.go +++ b/server/channels/store/storetest/mocks/PropertyValueStore.go @@ -6,6 +6,7 @@ package mocks import ( model "github.com/mattermost/mattermost/server/public/model" + request "github.com/mattermost/mattermost/server/public/shared/request" mock "github.com/stretchr/testify/mock" ) @@ -188,9 +189,9 @@ func (_m *PropertyValueStore) GetMany(groupID string, ids []string) ([]*model.Pr 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) +// SearchPropertyValues provides a mock function with given fields: rctx, opts +func (_m *PropertyValueStore) SearchPropertyValues(rctx request.CTX, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { + ret := _m.Called(rctx, opts) if len(ret) == 0 { panic("no return value specified for SearchPropertyValues") @@ -198,19 +199,19 @@ func (_m *PropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearc 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(request.CTX, model.PropertyValueSearchOpts) ([]*model.PropertyValue, error)); ok { + return rf(rctx, opts) } - if rf, ok := ret.Get(0).(func(model.PropertyValueSearchOpts) []*model.PropertyValue); ok { - r0 = rf(opts) + if rf, ok := ret.Get(0).(func(request.CTX, model.PropertyValueSearchOpts) []*model.PropertyValue); ok { + r0 = rf(rctx, 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) + if rf, ok := ret.Get(1).(func(request.CTX, model.PropertyValueSearchOpts) error); ok { + r1 = rf(rctx, opts) } else { r1 = ret.Error(1) } diff --git a/server/channels/store/storetest/property_value_store.go b/server/channels/store/storetest/property_value_store.go index c2d9b715b0d..821001af58d 100644 --- a/server/channels/store/storetest/property_value_store.go +++ b/server/channels/store/storetest/property_value_store.go @@ -97,7 +97,7 @@ func testCreatePropertyValue(t *testing.T, _ request.CTX, ss store.Store) { }) } -func testCreateManyPropertyValues(t *testing.T, _ request.CTX, ss store.Store) { +func testCreateManyPropertyValues(t *testing.T, rctx request.CTX, ss store.Store) { t.Run("should return nil when given empty slice", func(t *testing.T) { values, err := ss.PropertyValue().CreateMany([]*model.PropertyValue{}) require.NoError(t, err) @@ -126,7 +126,7 @@ func testCreateManyPropertyValues(t *testing.T, _ request.CTX, ss store.Store) { require.ErrorContains(t, err, "model.property_value.is_valid.app_error") // Verify no values were created - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ TargetIDs: []string{validValue.TargetID}, PerPage: 10, }) @@ -248,7 +248,7 @@ func testCreateManyPropertyValues(t *testing.T, _ request.CTX, ss store.Store) { require.Zero(t, values) // Verify the unique value was not created due to transaction rollback - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ TargetIDs: []string{value3.TargetID}, PerPage: 10, }) @@ -846,7 +846,7 @@ func testUpdatePropertyValue(t *testing.T, _ request.CTX, ss store.Store) { }) } -func testUpsertPropertyValue(t *testing.T, _ request.CTX, ss store.Store, s SqlStore) { +func testUpsertPropertyValue(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) { t.Run("should fail if the property value is not valid", func(t *testing.T) { value := &model.PropertyValue{ TargetID: "", @@ -1000,7 +1000,7 @@ func testUpsertPropertyValue(t *testing.T, _ request.CTX, ss store.Store, s SqlS require.Equal(t, originalValue.UpdateAt, retrieved.UpdateAt) // Verify the invalid value was not inserted - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ TargetIDs: []string{invalidValue.TargetID}, PerPage: 10, }) @@ -1215,7 +1215,7 @@ func testDeletePropertyValue(t *testing.T, _ request.CTX, ss store.Store) { }) } -func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s SqlStore) { +func testSearchPropertyValues(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) { groupID := model.NewId() targetID := model.NewId() fieldID := model.NewId() @@ -1408,7 +1408,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - results, err := ss.PropertyValue().SearchPropertyValues(tc.opts) + results, err := ss.PropertyValue().SearchPropertyValues(rctx, tc.opts) if tc.expectedError { require.Error(t, err) return @@ -1426,7 +1426,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("null columns, before createdBy and updatedBy migrations", func(t *testing.T) { nullGroupID, _, _, nullValueID := insertPropertyValueWithNullColumns(t, ss, s) - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: nullGroupID, PerPage: 10, }) @@ -1481,7 +1481,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("SinceUpdateAt filters correctly by UpdateAt", func(t *testing.T) { // `>=` semantics: value1 is included at the boundary, // plus value3 and the post-update value2. - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, PerPage: 10, @@ -1499,7 +1499,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("SinceUpdateAt with boundary condition", func(t *testing.T) { // `value3.UpdateAt - 1` keeps value3 in the window and value2's // post-Update timestamp is even later, so both are returned. - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value3.UpdateAt - 1, PerPage: 10, @@ -1517,7 +1517,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("SinceUpdateAt at the most recent update returns just that row", func(t *testing.T) { // `>=` semantics: querying at the highest UpdateAt in the // group returns the row at exactly that timestamp. - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: updatedValue2.UpdateAt, PerPage: 10, @@ -1528,7 +1528,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql }) t.Run("SinceUpdateAt with very recent timestamp", func(t *testing.T) { - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: model.GetMillis(), PerPage: 10, @@ -1566,7 +1566,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql require.Equal(t, tieUpdateAt, updated[1].UpdateAt) require.Equal(t, tieUpdateAt, updated[2].UpdateAt) - page1, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + page1, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: tieGroup, SinceUpdateAt: tieUpdateAt, PerPage: 2, @@ -1575,7 +1575,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql require.Len(t, page1, 2, "boundary row + one more must come back on the first page") last := page1[len(page1)-1] - page2, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + page2, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: tieGroup, SinceUpdateAt: tieUpdateAt, Cursor: model.PropertyValueSearchCursor{ @@ -1649,7 +1649,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql } t.Run("orders by UpdateAt ASC, Id ASC", func(t *testing.T) { - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, PerPage: 10, @@ -1664,7 +1664,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("auto-includes tombstones", func(t *testing.T) { require.NoError(t, ss.PropertyValue().Delete("", value3.ID)) - results, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + results, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, PerPage: 10, @@ -1685,7 +1685,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql t.Run("paginates with cursor UpdateAt", func(t *testing.T) { // `>=` semantics: first page is value1 (the boundary row), // cursored to value3, then value2. - first, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + first, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, PerPage: 1, @@ -1693,7 +1693,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql require.NoError(t, err) require.Equal(t, []string{value1.ID}, idsOf(first)) - second, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + second, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, Cursor: model.PropertyValueSearchCursor{ @@ -1705,7 +1705,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql require.NoError(t, err) require.Equal(t, []string{value3.ID}, idsOf(second)) - third, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + third, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, Cursor: model.PropertyValueSearchCursor{ @@ -1719,7 +1719,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql }) t.Run("directory mode rejects cursor_update_at", func(t *testing.T) { - _, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + _, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, Cursor: model.PropertyValueSearchCursor{ PropertyValueID: value1.ID, @@ -1731,7 +1731,7 @@ func testSearchPropertyValues(t *testing.T, _ request.CTX, ss store.Store, s Sql }) t.Run("delta mode rejects cursor_create_at", func(t *testing.T) { - _, err := ss.PropertyValue().SearchPropertyValues(model.PropertyValueSearchOpts{ + _, err := ss.PropertyValue().SearchPropertyValues(rctx, model.PropertyValueSearchOpts{ GroupID: groupID, SinceUpdateAt: value1.UpdateAt, Cursor: model.PropertyValueSearchCursor{ @@ -1795,7 +1795,7 @@ func testCreatePropertyValueWithArray(t *testing.T, _ request.CTX, ss store.Stor }) } -func testDeleteForField(t *testing.T, _ request.CTX, ss store.Store) { +func testDeleteForField(t *testing.T, rctx request.CTX, ss store.Store) { t.Run("should delete values with matching fieldID and groupID", func(t *testing.T) { fieldID := model.NewId() groupID := model.NewId() @@ -1927,7 +1927,7 @@ func testDeleteForField(t *testing.T, _ request.CTX, ss store.Store) { PerPage: 10, } - values, err := ss.PropertyValue().SearchPropertyValues(opts) + values, err := ss.PropertyValue().SearchPropertyValues(rctx, opts) require.NoError(t, err) // Find our two values within the results diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go index 91cd8059bd2..0c9ae7f8a19 100644 --- a/server/channels/store/timerlayer/timerlayer.go +++ b/server/channels/store/timerlayer/timerlayer.go @@ -8739,10 +8739,10 @@ func (s *TimerLayerPropertyValueStore) GetMany(groupID string, ids []string) ([] return result, err } -func (s *TimerLayerPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { +func (s *TimerLayerPropertyValueStore) SearchPropertyValues(rctx request.CTX, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { start := time.Now() - result, err := s.PropertyValueStore.SearchPropertyValues(opts) + result, err := s.PropertyValueStore.SearchPropertyValues(rctx, opts) elapsed := float64(time.Since(start)) / float64(time.Second) if s.Root.Metrics != nil {