mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-06-17 04:18:53 -04:00
groupware: add support for anchor and anchor offset pagination
* add query parameters 'anchor' and 'offset': - anchor is an object identifier - offset is a numeric offset relative to the anchor
This commit is contained in:
@@ -74,14 +74,14 @@ func (r *ContactCardSearchResults) GetTotal() *uint { return r.Tota
|
||||
func (r *ContactCardSearchResults) RemoveResults() { r.Results = nil }
|
||||
func (r *ContactCardSearchResults) SetLimit(limit *uint) { r.Limit = limit }
|
||||
|
||||
func (j *Client) QueryContactCards(accountIds []string,
|
||||
func (j *Client) QueryContactCards(accountIds []string, //NOSONAR
|
||||
filter ContactCardFilterElement, sortBy []ContactCardComparator,
|
||||
position int, limit *uint, calculateTotal bool,
|
||||
position int, anchor string, anchorOffset *int, limit *uint, calculateTotal bool,
|
||||
ctx Context) (map[string]*ContactCardSearchResults, SessionState, State, Language, Error) {
|
||||
return queryN(j, "QueryContactCards", ContactCardType,
|
||||
[]ContactCardComparator{{Property: ContactCardPropertyUpdated, IsAscending: false}},
|
||||
func(accountId string, filter ContactCardFilterElement, sortBy []ContactCardComparator, position int, limit *uint) ContactCardQueryCommand {
|
||||
return ContactCardQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Limit: limit, CalculateTotal: calculateTotal}
|
||||
func(accountId string, filter ContactCardFilterElement, sortBy []ContactCardComparator, position int, anchor string, anchorOffset *int, limit *uint) ContactCardQueryCommand {
|
||||
return ContactCardQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Anchor: anchor, AnchorOffset: anchorOffset, Limit: limit, CalculateTotal: calculateTotal}
|
||||
},
|
||||
func(accountId string, cmd Command, path string, rof string) ContactCardGetRefCommand {
|
||||
return ContactCardGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}}
|
||||
@@ -96,7 +96,7 @@ func (j *Client) QueryContactCards(accountIds []string,
|
||||
}
|
||||
},
|
||||
accountIds,
|
||||
filter, sortBy, limit, position, ctx,
|
||||
filter, sortBy, position, anchor, anchorOffset, limit, ctx,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ func (r *EmailSearchResults) SetLimit(limit *uint) { r.Limit = limit }
|
||||
|
||||
// Retrieve all the Emails in a given Mailbox by its id.
|
||||
func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOSONAR
|
||||
position int, limit *uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool,
|
||||
position int, anchor string, anchorOffset *int, limit *uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool,
|
||||
ctx Context) (*EmailSearchResults, SessionState, State, Language, Error) {
|
||||
logger := j.loggerParams("GetAllEmailsInMailbox", ctx, func(z zerolog.Context) zerolog.Context {
|
||||
l := z.Bool(logFetchBodies, fetchBodies).Int(logPosition, position)
|
||||
@@ -143,6 +143,8 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOS
|
||||
CollapseThreads: collapseThreads,
|
||||
CalculateTotal: true,
|
||||
Position: position,
|
||||
Anchor: anchor,
|
||||
AnchorOffset: anchorOffset,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ func (j *Client) GetCalendarEvents(accountId string, eventIds []string, ctx Cont
|
||||
|
||||
func (j *Client) QueryCalendarEvents(accountIds []string, //NOSONAR
|
||||
filter CalendarEventFilterElement, sortBy []CalendarEventComparator,
|
||||
position int, limit *uint, calculateTotal bool,
|
||||
position int, anchor string, anchorOffset *int, limit *uint, calculateTotal bool,
|
||||
ctx Context) (map[string]*CalendarEventSearchResults, SessionState, State, Language, Error) {
|
||||
return queryN(j, "QueryCalendarEvents", CalendarEventType,
|
||||
[]CalendarEventComparator{{Property: CalendarEventPropertyStart, IsAscending: false}},
|
||||
func(accountId string, filter CalendarEventFilterElement, sortBy []CalendarEventComparator, position int, limit *uint) CalendarEventQueryCommand {
|
||||
return CalendarEventQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Limit: limit, CalculateTotal: calculateTotal}
|
||||
func(accountId string, filter CalendarEventFilterElement, sortBy []CalendarEventComparator, position int, anchor string, anchorOffset *int, limit *uint) CalendarEventQueryCommand {
|
||||
return CalendarEventQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Anchor: anchor, AnchorOffset: anchorOffset, Limit: limit, CalculateTotal: calculateTotal}
|
||||
},
|
||||
func(accountId string, cmd Command, path string, rof string) CalendarEventGetRefCommand {
|
||||
return CalendarEventGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}}
|
||||
@@ -48,7 +48,7 @@ func (j *Client) QueryCalendarEvents(accountIds []string, //NOSONAR
|
||||
}
|
||||
},
|
||||
accountIds,
|
||||
filter, sortBy, limit, position, ctx,
|
||||
filter, sortBy, position, anchor, anchorOffset, limit, ctx,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -185,8 +185,8 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, //NOS
|
||||
func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, ctx Context) (map[string]*[]string, SessionState, State, Language, Error) {
|
||||
return queryN(j, "GetMailboxRolesForMultipleAccounts", MailboxType,
|
||||
[]MailboxComparator{{Property: MailboxPropertySortOrder, IsAscending: true}},
|
||||
func(accountId string, filter MailboxFilterCondition, sortBy []MailboxComparator, _ int, _ *uint) MailboxQueryCommand {
|
||||
return MailboxQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, SortAsTree: false, FilterAsTree: false, Position: 0, Limit: nil, CalculateTotal: false}
|
||||
func(accountId string, filter MailboxFilterCondition, sortBy []MailboxComparator, _ int, _ string, _ *int, _ *uint) MailboxQueryCommand {
|
||||
return MailboxQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, SortAsTree: false, FilterAsTree: false, Position: 0, Anchor: "", AnchorOffset: nil, Limit: nil, CalculateTotal: false}
|
||||
},
|
||||
func(accountId string, cmd Command, path, rof string) MailboxGetRefCommand {
|
||||
return MailboxGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}}
|
||||
@@ -196,7 +196,7 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, ctx Con
|
||||
slices.Sort(roles)
|
||||
return &roles
|
||||
},
|
||||
accountIds, MailboxFilterCondition{HasAnyRole: truep}, nil, nil, 0,
|
||||
accountIds, MailboxFilterCondition{HasAnyRole: truep}, nil, 0, "", nil, nil,
|
||||
ctx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ func (r *PrincipalSearchResults) GetTotal() *uint { return r.Total
|
||||
func (r *PrincipalSearchResults) RemoveResults() { r.Results = nil }
|
||||
func (r *PrincipalSearchResults) SetLimit(limit *uint) { r.Limit = limit }
|
||||
|
||||
func (j *Client) QueryPrincipals(accountId string,
|
||||
func (j *Client) QueryPrincipals(accountId string, //NOSONAR
|
||||
filter PrincipalFilterElement, sortBy []PrincipalComparator,
|
||||
position uint, limit *uint, calculateTotal bool,
|
||||
position int, anchor string, anchorOffset *int, limit *uint, calculateTotal bool,
|
||||
ctx Context) (*PrincipalSearchResults, SessionState, State, Language, Error) {
|
||||
return query(j, "QueryPrincipals", PrincipalType,
|
||||
[]PrincipalComparator{{Property: PrincipalPropertyName, IsAscending: true}},
|
||||
func(filter PrincipalFilterElement, sortBy []PrincipalComparator, position uint, limit *uint) PrincipalQueryCommand {
|
||||
return PrincipalQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Limit: limit, CalculateTotal: calculateTotal}
|
||||
func(filter PrincipalFilterElement, sortBy []PrincipalComparator, position int, anchor string, anchorOffset *int, limit *uint) PrincipalQueryCommand {
|
||||
return PrincipalQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: position, Anchor: anchor, AnchorOffset: anchorOffset, Limit: limit, CalculateTotal: calculateTotal}
|
||||
},
|
||||
func(cmd Command, path string, rof string) PrincipalGetRefCommand {
|
||||
return PrincipalGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}}
|
||||
@@ -47,6 +47,6 @@ func (j *Client) QueryPrincipals(accountId string,
|
||||
Limit: ptrIf(query.Limit, limit != nil),
|
||||
}
|
||||
},
|
||||
filter, sortBy, limit, position, ctx,
|
||||
filter, sortBy, position, anchor, anchorOffset, limit, ctx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,14 +100,14 @@ func TestContacts(t *testing.T) {
|
||||
{Property: ContactCardPropertyCreated, IsAscending: true},
|
||||
}
|
||||
|
||||
contactsByAccount, ss, os, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, nil, true, ctx)
|
||||
contactsByAccount, ss, os, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, "", nil, nil, true, ctx)
|
||||
require.NoError(err)
|
||||
|
||||
require.Len(contactsByAccount, 1)
|
||||
require.Contains(contactsByAccount, accountId)
|
||||
results := contactsByAccount[accountId]
|
||||
require.Len(results.Results, int(count))
|
||||
require.Equal(uint(0), results.Limit)
|
||||
require.Nil(results.Limit)
|
||||
require.Equal(uint(0), results.Position)
|
||||
require.NotNil(results.Total)
|
||||
require.Equal(count, *results.Total)
|
||||
@@ -142,6 +142,70 @@ func TestContacts(t *testing.T) {
|
||||
matchContact(t, fetched.List[0], actual)
|
||||
}
|
||||
|
||||
{
|
||||
limit := uint(10)
|
||||
slices := count / limit
|
||||
remainder := count
|
||||
require.Greater(slices, uint(1), "we need to have more than 10 objects in order to test the pagination of search results")
|
||||
for i := range slices {
|
||||
position := int(i * limit)
|
||||
page := min(remainder, limit)
|
||||
m, sessionState, _, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, position, "", nil, &limit, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Len(m, 1)
|
||||
require.Contains(m, accountId)
|
||||
results := m[accountId]
|
||||
require.Equal(len(results.Results), int(page))
|
||||
require.NotNil(results.Limit)
|
||||
require.Equal(limit, *results.Limit)
|
||||
require.Equal(uint(position), results.Position)
|
||||
require.Equal(true, results.CanCalculateChanges)
|
||||
require.NotNil(results.Total)
|
||||
require.Equal(count, *results.Total)
|
||||
remainder -= uint(len(results.Results))
|
||||
|
||||
require.Equal(ss, sessionState)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
chunkSize := 3
|
||||
anchor := results.Results[0].Id
|
||||
offset := 0
|
||||
i := 0
|
||||
for chunk := range slices.Chunk(results.Results, chunkSize) {
|
||||
m, sessionState, _, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, anchor, &offset, uintPtr(chunkSize), true, ctx)
|
||||
require.Equal(ss, sessionState)
|
||||
require.NoError(err)
|
||||
require.Len(m, 1)
|
||||
require.Contains(m, accountId)
|
||||
results := m[accountId]
|
||||
l := len(results.Results)
|
||||
require.LessOrEqual(l, chunkSize)
|
||||
require.NotZero(l)
|
||||
require.NotNil(results.Limit)
|
||||
require.Equal(uint(chunkSize), *results.Limit)
|
||||
//require.Equal(uint(i*chunkSize), results.Position)
|
||||
require.Equal(true, results.CanCalculateChanges)
|
||||
require.NotNil(results.Total)
|
||||
require.Equal(count, *results.Total)
|
||||
|
||||
fmt.Printf("\x1b[34;1m===[%d]========================================\x1b[0m\n", i)
|
||||
fmt.Printf("pos: %d\n", results.Position)
|
||||
fmt.Printf("chunk : %s\n", strings.Join(structs.Map(chunk, func(c ContactCard) string { return c.Id }), " | "))
|
||||
fmt.Printf("results: %s\n", strings.Join(structs.Map(results.Results, func(c ContactCard) string { return c.Id }), " | "))
|
||||
fmt.Printf("============================================\n")
|
||||
|
||||
for i := range l {
|
||||
require.Equal(chunk[i].Id, results.Results[i].Id)
|
||||
}
|
||||
anchor = chunk[len(chunk)-1].Id
|
||||
offset = 1
|
||||
i++
|
||||
}
|
||||
require.True(false)
|
||||
}
|
||||
|
||||
{
|
||||
now := time.Now().Truncate(time.Duration(1) * time.Second).UTC()
|
||||
for _, event := range expectedContactCardsById {
|
||||
@@ -169,7 +233,7 @@ func TestContacts(t *testing.T) {
|
||||
os = state
|
||||
}
|
||||
{
|
||||
shouldBeEmpty, sessionState, state, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, nil, true, ctx)
|
||||
shouldBeEmpty, sessionState, state, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, "", nil, nil, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Contains(shouldBeEmpty, accountId)
|
||||
resp := shouldBeEmpty[accountId]
|
||||
@@ -93,7 +93,7 @@ func TestEvents(t *testing.T) {
|
||||
ss := EmptySessionState
|
||||
os := EmptyState
|
||||
{
|
||||
resultsByAccount, sessionState, state, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, 0, nil, true, ctx)
|
||||
resultsByAccount, sessionState, state, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, 0, "", nil, nil, true, ctx)
|
||||
require.NoError(err)
|
||||
|
||||
require.Len(resultsByAccount, 1)
|
||||
@@ -124,7 +124,7 @@ func TestEvents(t *testing.T) {
|
||||
for i := range slices {
|
||||
position := int(i * limit)
|
||||
page := min(remainder, limit)
|
||||
m, sessionState, _, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, position, &limit, true, ctx)
|
||||
m, sessionState, _, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, position, "", nil, &limit, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Len(m, 1)
|
||||
require.Contains(m, accountId)
|
||||
@@ -173,7 +173,7 @@ func TestEvents(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
shouldBeEmpty, sessionState, state, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, 0, nil, true, ctx)
|
||||
shouldBeEmpty, sessionState, state, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, 0, "", nil, nil, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Contains(shouldBeEmpty, accountId)
|
||||
resp := shouldBeEmpty[accountId]
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestEmails(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, nil, true, false, 0, true, ctx)
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, "", nil, nil, true, false, 0, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Equal(session.State, sessionState)
|
||||
|
||||
@@ -95,7 +95,7 @@ func TestEmails(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, nil, false, false, 0, true, ctx)
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, "", nil, nil, false, false, 0, true, ctx)
|
||||
require.NoError(err)
|
||||
require.Equal(session.State, sessionState)
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ func withDirectoryQueries(allowDirectoryQueries bool) func(map[string]any) {
|
||||
}
|
||||
|
||||
func newStalwartTest(t *testing.T, options ...func(map[string]any)) (*StalwartTest, error) { //NOSONAR
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
var _ context.CancelFunc = cancel // ignore context leak warning: it is passed in the struct and called in Close()
|
||||
|
||||
// A master user name different from "master" does not seem to work as of the current Stalwart version
|
||||
|
||||
@@ -1833,7 +1833,7 @@ type MailboxQueryCommand struct {
|
||||
//
|
||||
// For example, -1 means the object immediately preceding the anchor is the first result in
|
||||
// the list returned.
|
||||
AnchorOffset int `json:"anchorOffset,omitzero" doc:"opt" default:"0"`
|
||||
AnchorOffset *int `json:"anchorOffset,omitempty" doc:"opt" default:"0"`
|
||||
|
||||
// The maximum number of results to return.
|
||||
//
|
||||
@@ -2129,7 +2129,7 @@ type EmailQueryCommand struct {
|
||||
//
|
||||
// For example, -1 means the Email immediately preceding the anchor is the first result in
|
||||
// the list returned.
|
||||
AnchorOffset int `json:"anchorOffset,omitzero" doc:"opt" default:"0"`
|
||||
AnchorOffset *int `json:"anchorOffset,omitempty" doc:"opt" default:"0"`
|
||||
|
||||
// The maximum number of results to return.
|
||||
//
|
||||
@@ -7074,7 +7074,7 @@ type ContactCardQueryCommand struct {
|
||||
//
|
||||
// For example, -1 means the Email immediately preceding the anchor is the first result in
|
||||
// the list returned.
|
||||
AnchorOffset int `json:"anchorOffset,omitzero" default:"0" doc:"opt"`
|
||||
AnchorOffset *int `json:"anchorOffset,omitempty" default:"0" doc:"opt"`
|
||||
|
||||
// The maximum number of results to return.
|
||||
//
|
||||
@@ -7804,7 +7804,7 @@ type CalendarEventQueryCommand struct {
|
||||
//
|
||||
// For example, -1 means the Email immediately preceding the anchor is the first result in
|
||||
// the list returned.
|
||||
AnchorOffset int `json:"anchorOffset,omitzero" doc:"opt" default:"0"`
|
||||
AnchorOffset *int `json:"anchorOffset,omitempty" doc:"opt" default:"0"`
|
||||
|
||||
// The maximum number of results to return.
|
||||
//
|
||||
@@ -8265,7 +8265,7 @@ type PrincipalQueryCommand struct {
|
||||
//
|
||||
// If the index is greater than or equal to the total number of objects in the results
|
||||
// list, then the ids array in the response will be empty, but this is not an error.
|
||||
Position uint `json:"position,omitzero" default:"0" doc:"opt"`
|
||||
Position int `json:"position,omitzero" default:"0" doc:"opt"`
|
||||
|
||||
// An Email id.
|
||||
//
|
||||
@@ -8281,7 +8281,7 @@ type PrincipalQueryCommand struct {
|
||||
//
|
||||
// For example, -1 means the Principal immediately preceding the anchor is the first result in
|
||||
// the list returned.
|
||||
AnchorOffset int `json:"anchorOffset,omitzero" default:"0" doc:"opt"`
|
||||
AnchorOffset *int `json:"anchorOffset,omitempty" default:"0" doc:"opt"`
|
||||
|
||||
// The maximum number of results to return.
|
||||
//
|
||||
|
||||
@@ -430,10 +430,10 @@ func update[T Foo, CHANGES Change, SET SetCommand[T], GET GetCommand[T], RESP an
|
||||
func query[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T], QUERYRESP QueryResponse[T], GETRESP GetResponse[T], RESP any]( //NOSONAR
|
||||
client *Client, name string, objType ObjectType,
|
||||
defaultSortBy []SORT,
|
||||
queryCommandFactory func(filter FILTER, sortBy []SORT, position uint, limit *uint) QUERY,
|
||||
queryCommandFactory func(filter FILTER, sortBy []SORT, position int, anchor string, anchorOffset *int, limit *uint) QUERY,
|
||||
getCommandFactory func(cmd Command, path string, rof string) GET,
|
||||
respMapper func(query QUERYRESP, get GETRESP) *RESP,
|
||||
filter FILTER, sortBy []SORT, limit *uint, position uint,
|
||||
filter FILTER, sortBy []SORT, position int, anchor string, anchorOffset *int, limit *uint,
|
||||
ctx Context) (*RESP, SessionState, State, Language, Error) {
|
||||
|
||||
logger := client.logger(name, ctx)
|
||||
@@ -443,7 +443,7 @@ func query[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T]
|
||||
sortBy = defaultSortBy
|
||||
}
|
||||
|
||||
query := queryCommandFactory(filter, sortBy, position, limit)
|
||||
query := queryCommandFactory(filter, sortBy, position, anchor, anchorOffset, limit)
|
||||
get := getCommandFactory(query.GetCommand(), "/ids/*", "0")
|
||||
|
||||
cmd, err := client.request(ctx, objType.Namespaces, invocation(query, "0"), invocation(get, "1"))
|
||||
@@ -469,11 +469,11 @@ func query[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T]
|
||||
func queryN[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T], QUERYRESP QueryResponse[T], GETRESP GetResponse[T], RESP any]( //NOSONAR
|
||||
client *Client, name string, objType ObjectType,
|
||||
defaultSortBy []SORT,
|
||||
queryCommandFactory func(accountId string, filter FILTER, sortBy []SORT, position int, limit *uint) QUERY,
|
||||
queryCommandFactory func(accountId string, filter FILTER, sortBy []SORT, position int, anchor string, anchorOffset *int, imit *uint) QUERY,
|
||||
getCommandFactory func(accountId string, cmd Command, path string, rof string) GET,
|
||||
respMapper func(query QUERYRESP, get GETRESP) *RESP,
|
||||
accountIds []string,
|
||||
filter FILTER, sortBy []SORT, limit *uint, position int,
|
||||
filter FILTER, sortBy []SORT, position int, anchor string, anchorOffset *int, limit *uint,
|
||||
ctx Context) (map[string]*RESP, SessionState, State, Language, Error) {
|
||||
logger := client.logger(name, ctx)
|
||||
ctx = ctx.WithLogger(logger)
|
||||
@@ -488,7 +488,7 @@ func queryN[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T
|
||||
var g GET
|
||||
var q QUERY
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
query := queryCommandFactory(accountId, filter, sortBy, position, limit)
|
||||
query := queryCommandFactory(accountId, filter, sortBy, position, anchor, anchorOffset, limit)
|
||||
get := getCommandFactory(accountId, query.GetCommand(), "/ids/*", mcid(accountId, "0"))
|
||||
invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
|
||||
invocations[i*2+1] = invocation(get, mcid(accountId, "1"))
|
||||
|
||||
@@ -409,8 +409,8 @@ func identity1[T any](t T) T {
|
||||
func list[T Foo, GETRESP GetResponse[T]](r GETRESP) []T { return r.GetList() }
|
||||
func getid[T Idable](r T) string { return r.GetId() }
|
||||
|
||||
func uintPtr(i uint) *uint {
|
||||
return ptr(i)
|
||||
func uintPtr[T int | uint](i T) *uint {
|
||||
return ptr(uint(i))
|
||||
}
|
||||
|
||||
func valueIf[T any | uint | int | bool](value *T, condition bool) *T {
|
||||
|
||||
@@ -42,8 +42,8 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
|
||||
fetchBodies := false
|
||||
withThreads := true
|
||||
query(Email, w, r, g, g.defaults.emailLimit,
|
||||
func(req Request, accountId, containerId string, position int, limit *uint, ctx jmap.Context) (*jmap.EmailSearchResults, jmap.SessionState, jmap.State, jmap.Language, *Error) {
|
||||
emails, sessionState, state, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, containerId, position, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads, ctx)
|
||||
func(req Request, accountId, containerId string, position int, anchor string, anchorOffset *int, limit *uint, ctx jmap.Context) (*jmap.EmailSearchResults, jmap.SessionState, jmap.State, jmap.Language, *Error) { //NOSONAR
|
||||
emails, sessionState, state, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, containerId, position, anchor, anchorOffset, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads, ctx)
|
||||
if jerr != nil {
|
||||
return emails, sessionState, state, lang, req.apiErrorFromJmap(req.observeJmapError(jerr))
|
||||
}
|
||||
|
||||
@@ -72,10 +72,10 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
func curryMapQuery[SRES jmap.SearchResults[T], T jmap.Foo, FILTER any, COMP any](
|
||||
f func(accountIds []string, filter FILTER, sortBy []COMP, position int, limit *uint, calculateTotal bool, ctx jmap.Context) (map[string]SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error),
|
||||
) func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, limit *uint, ctx jmap.Context) (SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error) {
|
||||
return func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, limit *uint, ctx jmap.Context) (SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error) {
|
||||
m, sessionState, state, lang, err := f(single(accountId), filter, sortBy, position, limit, true, ctx)
|
||||
f func(accountIds []string, filter FILTER, sortBy []COMP, position int, anchor string, anchorOffset *int, limit *uint, calculateTotal bool, ctx jmap.Context) (map[string]SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error),
|
||||
) func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, anchor string, anchorOffset *int, limit *uint, ctx jmap.Context) (SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error) {
|
||||
return func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, anchor string, anchorOffset *int, limit *uint, ctx jmap.Context) (SRES, jmap.SessionState, jmap.State, jmap.Language, jmap.Error) { //NOSONAR
|
||||
m, sessionState, state, lang, err := f(single(accountId), filter, sortBy, position, anchor, anchorOffset, limit, true, ctx)
|
||||
return m[accountId], sessionState, state, lang, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ const (
|
||||
QueryParamSearchKeyword = "keyword"
|
||||
QueryParamSearchMessageId = "messageId"
|
||||
QueryParamPosition = "position"
|
||||
QueryParamAnchor = "anchor"
|
||||
QueryParamAnchorOffset = "offset"
|
||||
QueryParamLimit = "limit"
|
||||
QueryParamDays = "days"
|
||||
QueryParamPartId = "partId"
|
||||
|
||||
@@ -80,7 +80,7 @@ func getall[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jmap.G
|
||||
})
|
||||
}
|
||||
|
||||
var paginationQueryParams = toSupportedQueryParams(QueryParamPosition, QueryParamLimit)
|
||||
var paginationQueryParams = toSupportedQueryParams(QueryParamPosition, QueryParamAnchor, QueryParamAnchorOffset, QueryParamLimit)
|
||||
|
||||
// Retrieve all the {{.Name}} with support for paging using the {{.QueryParam.QueryParamPosition.Name}} and {{.QueryParam.QueryParamLimit.Name}} query parameters.
|
||||
// @api:response 200:SEARCHRESULTS returns the {{.Names}} within the requested range, as well as the total amount of {{.Names}}
|
||||
@@ -91,7 +91,7 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
|
||||
withContainerId bool,
|
||||
filterFunc func(containerId string) FILTER,
|
||||
sortBy []COMP,
|
||||
queryFunc func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, limit *uint, ctx jmap.Context) (SEARCHRESULTS, jmap.SessionState, jmap.State, jmap.Language, jmap.Error),
|
||||
queryFunc func(req Request, accountId string, filter FILTER, sortBy []COMP, position int, anchor string, anchorOffset *int, limit *uint, ctx jmap.Context) (SEARCHRESULTS, jmap.SessionState, jmap.State, jmap.Language, jmap.Error),
|
||||
) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := o.accountFunc(&req)
|
||||
@@ -108,6 +108,23 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
|
||||
l = l.Int(QueryParamPosition, position)
|
||||
}
|
||||
|
||||
anchor, ok := req.getStringParam(QueryParamAnchor, "")
|
||||
if ok {
|
||||
l = l.Str(QueryParamAnchor, log.SafeString(anchor))
|
||||
}
|
||||
|
||||
var anchorOffset *int = nil
|
||||
{
|
||||
v, ok, err := req.parseIntParam(QueryParamAnchorOffset, 0)
|
||||
if err != nil {
|
||||
return req.error(accountId, err)
|
||||
}
|
||||
if ok {
|
||||
l = l.Int(QueryParamAnchorOffset, v)
|
||||
anchorOffset = &v
|
||||
}
|
||||
}
|
||||
|
||||
var limit *uint = nil
|
||||
{
|
||||
v, ok, err := req.parseUIntParam(QueryParamLimit, uint(0))
|
||||
@@ -143,7 +160,7 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
|
||||
|
||||
logger := log.From(l)
|
||||
ctx := req.ctx.WithLogger(logger)
|
||||
results, sessionState, state, lang, jerr := queryFunc(req, accountId, filter, sortBy, position, jmaplimit, ctx)
|
||||
results, sessionState, state, lang, jerr := queryFunc(req, accountId, filter, sortBy, position, anchor, anchorOffset, jmaplimit, ctx)
|
||||
if jerr != nil {
|
||||
return req.jmapError(accountId, jerr, sessionState, lang)
|
||||
}
|
||||
@@ -164,7 +181,7 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT
|
||||
w http.ResponseWriter, r *http.Request,
|
||||
g *Groupware,
|
||||
defaultLimit uint,
|
||||
queryFunc func(req Request, accountId string, containerId string, position int, limit *uint, ctx jmap.Context) (SEARCHRESULTS, jmap.SessionState, jmap.State, jmap.Language, *Error),
|
||||
queryFunc func(req Request, accountId string, containerId string, position int, anchor string, anchorOffset *int, limit *uint, ctx jmap.Context) (SEARCHRESULTS, jmap.SessionState, jmap.State, jmap.Language, *Error),
|
||||
) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := o.accountFunc(&req)
|
||||
@@ -191,6 +208,23 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT
|
||||
l = l.Int(QueryParamPosition, position)
|
||||
}
|
||||
|
||||
anchor, ok := req.getStringParam(QueryParamAnchor, "")
|
||||
if ok {
|
||||
l = l.Str(QueryParamAnchor, log.SafeString(anchor))
|
||||
}
|
||||
|
||||
var anchorOffset *int = nil
|
||||
{
|
||||
v, ok, err := req.parseIntParam(QueryParamAnchorOffset, 0)
|
||||
if err != nil {
|
||||
return req.error(accountId, err)
|
||||
}
|
||||
if ok {
|
||||
l = l.Int(QueryParamAnchorOffset, v)
|
||||
anchorOffset = &v
|
||||
}
|
||||
}
|
||||
|
||||
var limit *uint = nil
|
||||
{
|
||||
v, ok, err := req.parseUIntParam(QueryParamLimit, defaultLimit)
|
||||
@@ -213,7 +247,7 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT
|
||||
jmaplimit = UintPtrOne
|
||||
}
|
||||
|
||||
results, sessionState, state, lang, err := queryFunc(req, accountId, containerId, position, jmaplimit, ctx)
|
||||
results, sessionState, state, lang, err := queryFunc(req, accountId, containerId, position, anchor, anchorOffset, jmaplimit, ctx)
|
||||
if err != nil {
|
||||
return req.error(accountId, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user