From 7ed065ddfeb346743d78eb5250de119a023249d3 Mon Sep 17 00:00:00 2001 From: Pascal Bleser Date: Wed, 24 Jun 2026 15:42:09 +0200 Subject: [PATCH] groupware: fix query objects in a container with multiple suppliers * by adding a predicate that can analyze a filter and inform the template methods whether the filter is applicable for that supplier or not * this fixed /groupware/accounts/.../addressbooks/.../contacts --- services/groupware/pkg/groupware/api_contacts.go | 11 +++++++++++ services/groupware/pkg/groupware/suppliers.go | 8 ++++++++ services/groupware/pkg/groupware/suppliers_test.go | 3 +++ 3 files changed, 22 insertions(+) diff --git a/services/groupware/pkg/groupware/api_contacts.go b/services/groupware/pkg/groupware/api_contacts.go index cfe96db940..dc460459c9 100644 --- a/services/groupware/pkg/groupware/api_contacts.go +++ b/services/groupware/pkg/groupware/api_contacts.go @@ -86,6 +86,17 @@ func (g *Groupware) contacts(accountIds []jmap.AccountId, qps QueryParamsSupplie filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[*jmap.ContactCardSearchResults], NextToken, error) { return squery(g.contactCardQuerySuppliers, accountIds, qps, limit, filter, sortBy, calculateTotal, ctx, + func(supplier QuerySupplier[jmap.ContactCard, *jmap.ContactCardSearchResults, jmap.ContactCardFilterElement, jmap.ContactCardComparator], filter jmap.ContactCardFilterElement) bool { + switch c := filter.(type) { + case jmap.ContactCardFilterCondition: + if c.InAddressBook != "" { + if !supplier.IsMine(c.InAddressBook) { + return false + } + } + } + return true + }, func(a, b jmap.ContactCard) int { return a.Created.Compare(b.Created) }, func(canCalculateChanges jmap.ChangeCalculation, position, limit, total *uint, results []jmap.ContactCard) *jmap.ContactCardSearchResults { return &jmap.ContactCardSearchResults{ diff --git a/services/groupware/pkg/groupware/suppliers.go b/services/groupware/pkg/groupware/suppliers.go index 47ec77fa0f..d7535f465b 100644 --- a/services/groupware/pkg/groupware/suppliers.go +++ b/services/groupware/pkg/groupware/suppliers.go @@ -13,6 +13,8 @@ import ( type SupplierId string +const EmptySupplierId = "" + type Supplier[T jmap.Foo] interface { GetId() SupplierId IsMine(id string) bool @@ -171,6 +173,7 @@ func fillMissingAccounts(qps QueryParamsSupplier, supplierId SupplierId, account func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], F jmap.FilterElement[T], C jmap.Comparator[T]]( //NOSONAR suppliers []S, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter F, sortBy []C, calculateTotal bool, ctx jmap.Context, + filterSupplierPredicate func(supplier S, filter F) bool, sorter func(T, T) int, searchResultCtor func(canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint, results []T) R) ( jmap.Result[R], NextToken, error, @@ -296,6 +299,11 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], lang := jmap.NoLanguage durations := make([][]time.Duration, len(suppliers)) for i, supplier := range suppliers { + if !filterSupplierPredicate(supplier, filter) { + // the filter is not applicable for this supplier => skip + continue + } + // we are not injecting id prefixes here for all the objects, as each supplier is responsible for doing that if necessary if result, err := supplier.Query(accountIds, qps, limit, filter, sortBy, calculateTotal, ctx); err != nil { return jmap.ZeroResult[R](result.Durations), NoNextToken, err diff --git a/services/groupware/pkg/groupware/suppliers_test.go b/services/groupware/pkg/groupware/suppliers_test.go index 9972314585..e08f198f9a 100644 --- a/services/groupware/pkg/groupware/suppliers_test.go +++ b/services/groupware/pkg/groupware/suppliers_test.go @@ -101,6 +101,9 @@ func pets( calculateTotal bool, ctx jmap.Context) (jmap.Result[*PetSearchResults], NextToken, error) { return squery(suppliers, accountIds, qps, limit, filter, sortBy, calculateTotal, ctx, + func(supplier QuerySupplier[Pet, *PetSearchResults, PetFilterElement, PetComparator], filter PetFilterElement) bool { + return true + }, func(a, b Pet) int { return strings.Compare(a.name, b.name) }, func(canCalculateChanges jmap.ChangeCalculation, position, limit, total *uint, results []Pet) *PetSearchResults { return &PetSearchResults{