groupware: rename 'offset' to 'position' to be consistent with the JMAP specification

This commit is contained in:
Pascal Bleser
2026-04-23 15:14:51 +02:00
parent eb4cd432e4
commit e4c5e6486d
8 changed files with 72 additions and 72 deletions

View File

@@ -56,7 +56,7 @@ type BlobClient interface {
const (
logOperation = "operation"
logFetchBodies = "fetch-bodies"
logOffset = "offset"
logPosition = "position"
logLimit = "limit"
logDownloadUrl = "download-url"
logBlobId = "blob-id"

View File

@@ -123,10 +123,10 @@ func (r EmailSearchResults) GetTotal() *uint { return r.Total }
// Retrieve all the Emails in a given Mailbox by its id.
func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOSONAR
offset int, limit uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool,
position 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 {
return z.Bool(logFetchBodies, fetchBodies).Int(logOffset, offset).Uint(logLimit, limit)
return z.Bool(logFetchBodies, fetchBodies).Int(logPosition, position).Uint(logLimit, limit)
})
ctx = ctx.WithLogger(logger)
@@ -137,8 +137,8 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOS
CollapseThreads: collapseThreads,
CalculateTotal: true,
}
if offset > 0 {
query.Position = offset
if position > 0 {
query.Position = position
}
if limit > 0 {
query.Limit = &limit
@@ -304,10 +304,10 @@ type SearchSnippetWithMeta struct {
type EmailSnippetSearchResults SearchResultsTemplate[SearchSnippetWithMeta]
func (j *Client) QueryEmailSnippets(accountIds []string, //NOSONAR
filter EmailFilterElement, offset int, limit uint,
filter EmailFilterElement, position int, limit uint,
ctx Context) (map[string]EmailSnippetSearchResults, SessionState, State, Language, Error) {
logger := j.loggerParams("QueryEmailSnippets", ctx, func(z zerolog.Context) zerolog.Context {
return z.Uint(logLimit, limit).Int(logOffset, offset)
return z.Uint(logLimit, limit).Int(logPosition, position)
})
ctx = ctx.WithLogger(logger)
@@ -321,8 +321,8 @@ func (j *Client) QueryEmailSnippets(accountIds []string, //NOSONAR
CollapseThreads: true,
CalculateTotal: true,
}
if offset > 0 {
query.Position = offset
if position > 0 {
query.Position = position
}
if limit > 0 {
query.Limit = &limit
@@ -426,7 +426,7 @@ type EmailQueryResult struct {
}
func (j *Client) QueryEmails(accountIds []string,
filter EmailFilterElement, offset int, limit uint, fetchBodies bool, maxBodyValueBytes uint,
filter EmailFilterElement, position int, limit uint, fetchBodies bool, maxBodyValueBytes uint,
ctx Context) (map[string]EmailQueryResult, SessionState, State, Language, Error) { //NOSONAR
logger := j.loggerParams("QueryEmails", ctx, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies)
@@ -443,8 +443,8 @@ func (j *Client) QueryEmails(accountIds []string,
CollapseThreads: true,
CalculateTotal: true,
}
if offset > 0 {
query.Position = offset
if position > 0 {
query.Position = position
}
if limit > 0 {
query.Limit = &limit
@@ -511,7 +511,7 @@ type EmailQueryWithSnippetsResult struct {
}
func (j *Client) QueryEmailsWithSnippets(accountIds []string, //NOSONAR
filter EmailFilterElement, offset int, limit uint, collapseThreads bool, calculateTotal bool, fetchBodies bool, maxBodyValueBytes uint,
filter EmailFilterElement, position int, limit uint, collapseThreads bool, calculateTotal bool, fetchBodies bool, maxBodyValueBytes uint,
ctx Context) (map[string]EmailQueryWithSnippetsResult, SessionState, State, Language, Error) {
logger := j.loggerParams("QueryEmailsWithSnippets", ctx, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies)
@@ -528,8 +528,8 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, //NOSONAR
CollapseThreads: collapseThreads,
CalculateTotal: calculateTotal,
}
if offset > 0 {
query.Position = offset
if position > 0 {
query.Position = position
}
if limit > 0 {
query.Limit = &limit
@@ -1000,11 +1000,11 @@ func (j *Client) EmailsInThread(accountId string, threadId string,
}
type EmailsSummary struct {
Emails []Email `json:"emails"`
Total uint `json:"total"`
Limit uint `json:"limit"`
Offset uint `json:"offset"`
State State `json:"state"`
Emails []Email `json:"emails"`
Total uint `json:"total"`
Limit uint `json:"limit"`
Position uint `json:"position"`
State State `json:"state"`
}
var EmailSummaryProperties = []string{
@@ -1103,11 +1103,11 @@ func (j *Client) QueryEmailSummaries(accountIds []string, //NOSONAR
}
resp[accountId] = EmailsSummary{
Emails: response.List,
Total: queryResponse.Total,
Limit: queryResponse.Limit,
Offset: queryResponse.Position,
State: response.State,
Emails: response.List,
Total: queryResponse.Total,
Limit: queryResponse.Limit,
Position: queryResponse.Position,
State: response.State,
}
}
return resp, squashStateFunc(resp, func(s EmailsSummary) State { return s.State }), nil

View File

@@ -4242,7 +4242,7 @@ type BlobGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
Properties []string `json:"properties,omitempty"`
Offset int `json:"offset,omitzero"`
Position int `json:"position,omitzero"`
Length int `json:"length,omitzero"`
}
@@ -4256,7 +4256,7 @@ type BlobGetRefCommand struct {
AccountId string `json:"accountId"`
IdRef *ResultReference `json:"#ids,omitempty"`
Properties []string `json:"properties,omitempty"`
Offset int `json:"offset,omitzero"`
Position int `json:"position,omitzero"`
Length int `json:"length,omitzero"`
}

View File

@@ -57,12 +57,12 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
}
l = l.Str(UriParamAddressBookId, log.SafeString(addressBookId))
offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
position, ok, err := req.parseIntParam(QueryParamPosition, 0)
if err != nil {
return req.errorN(accountIds, err)
}
if ok {
l = l.Int(QueryParamOffset, offset)
l = l.Int(QueryParamPosition, position)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaults.contactLimit)
@@ -85,7 +85,7 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
logger := log.From(l)
ctx := req.ctx.WithLogger(logger)
contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards(accountIds, filter, sortBy, offset, limit, true, ctx)
contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards(accountIds, filter, sortBy, position, limit, true, ctx)
if jerr != nil {
return req.jmapErrorN(accountIds, jerr, sessionState, lang)
}

View File

@@ -35,15 +35,15 @@ func (g *Groupware) GetEmailChanges(w http.ResponseWriter, r *http.Request) {
//
// The mailbox must be specified by its id, as part of the request URL path.
//
// A limit and an offset may be specified using the query parameters 'limit' and 'offset',
// A limit and a position may be specified using the query parameters 'limit' and 'position',
// respectively.
func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request) { //NOSONAR
collapseThreads := false
fetchBodies := false
withThreads := true
query(Email, w, r, g, g.defaults.emailLimit,
func(req Request, accountId, containerId string, offset 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, offset, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads, ctx)
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)
if jerr != nil {
return emails, sessionState, state, lang, req.apiErrorFromJmap(req.observeJmapError(jerr))
}
@@ -403,12 +403,12 @@ func (g *Groupware) buildEmailFilter(req Request) (bool, jmap.EmailFilterElement
l := req.logger.With()
offset, ok, err := req.parseIntParam(QueryParamOffset, 0) // pagination element offset
position, ok, err := req.parseIntParam(QueryParamPosition, 0) // pagination element position (offset)
if err != nil {
return false, nil, snippets, 0, 0, nil, err
}
if ok {
l = l.Int(QueryParamOffset, offset)
l = l.Int(QueryParamPosition, position)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaults.emailLimit) // maximum number of results (size of a page)
@@ -530,7 +530,7 @@ func (g *Groupware) buildEmailFilter(req Request) (bool, jmap.EmailFilterElement
}
}
return true, filter, snippets, offset, limit, logger, nil
return true, filter, snippets, position, limit, logger, nil
}
func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) { //NOSONAR
@@ -552,7 +552,7 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) { //NOSONA
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
ok, filter, makesSnippets, offset, limit, logger, err := g.buildEmailFilter(req)
ok, filter, makesSnippets, position, limit, logger, err := g.buildEmailFilter(req)
if !ok {
return req.error(accountId, err)
}
@@ -575,7 +575,7 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) { //NOSONA
logger = log.From(l)
ctx := req.ctx.WithLogger(logger)
resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets(single(accountId), filter, offset, limit, collapseThreads, calculateTotal, fetchBodies, g.config.maxBodyValueBytes, ctx)
resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets(single(accountId), filter, position, limit, collapseThreads, calculateTotal, fetchBodies, g.config.maxBodyValueBytes, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -628,7 +628,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
g.respond(w, r, func(req Request) Response {
allAccountIds := req.AllAccountIds()
ok, filter, makesSnippets, offset, limit, logger, err := g.buildEmailFilter(req)
ok, filter, makesSnippets, position, limit, logger, err := g.buildEmailFilter(req)
if !ok {
return req.errorN(allAccountIds, err)
}
@@ -640,7 +640,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
}
if makesSnippets {
resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSnippets(allAccountIds, filter, offset, limit, ctx)
resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSnippets(allAccountIds, filter, position, limit, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
@@ -669,7 +669,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
slices.SortFunc(flattened, func(a, b Snippet) int { return a.ReceivedAt.Compare(b.ReceivedAt) })
// TODO offset and limit over the aggregated results by account
// TODO position and limit over the aggregated results by account
body := EmailSearchSnippetsResults{
Results: flattened,
@@ -708,7 +708,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
slices.SortFunc(flattened, func(a, b jmap.Email) int { return a.ReceivedAt.Compare(b.ReceivedAt) })
// TODO offset and limit over the aggregated results by account
// TODO position and limit over the aggregated results by account
body := EmailSearchResults{
Results: flattened,
@@ -1439,11 +1439,11 @@ type emailWithAccountId struct {
}
type EmailSummaries struct {
Emails []EmailSummary `json:"emails,omitempty"`
Total uint `json:"total,omitzero"`
Limit uint `json:"limit,omitzero"`
Offset uint `json:"offset,omitzero"`
State jmap.State `json:"state,omitempty"`
Emails []EmailSummary `json:"emails,omitempty"`
Total uint `json:"total,omitzero"`
Limit uint `json:"limit,omitzero"`
Position uint `json:"position,omitzero"`
State jmap.State `json:"state,omitempty"`
}
// Get a summary of the latest emails across all the mailboxes, across all of a user's accounts.
@@ -1471,15 +1471,15 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
l = l.Uint(QueryParamLimit, limit)
}
offset, ok, err := req.parseUIntParam(QueryParamOffset, 0)
position, ok, err := req.parseUIntParam(QueryParamPosition, 0)
if err != nil {
return req.errorN(allAccountIds, err)
}
if offset > 0 {
if position > 0 {
return req.notImplementedN(allAccountIds, EmailResponseObjectType)
}
if ok {
l = l.Uint(QueryParamOffset, limit)
l = l.Uint(QueryParamPosition, limit)
}
seen, ok, err := req.parseBoolParam(QueryParamSeen, false)
@@ -1540,10 +1540,10 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
}
return req.respondN(allAccountIds, EmailSummaries{
Emails: summaries,
Total: total,
Limit: limit,
Offset: offset,
Emails: summaries,
Total: total,
Limit: limit,
Position: position,
}, sessionState, EmailResponseObjectType, state, lang)
})
}

View File

@@ -24,12 +24,12 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
}
l = l.Str(UriParamCalendarId, log.SafeString(calendarId))
offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
position, ok, err := req.parseIntParam(QueryParamPosition, 0)
if err != nil {
return req.error(accountId, err)
}
if ok {
l = l.Int(QueryParamOffset, offset)
l = l.Int(QueryParamPosition, position)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaults.contactLimit)
@@ -47,7 +47,7 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
logger := log.From(l)
ctx := req.ctx.WithLogger(logger)
eventsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryCalendarEvents(single(accountId), filter, sortBy, offset, limit, true, ctx)
eventsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryCalendarEvents(single(accountId), filter, sortBy, position, limit, true, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -62,9 +62,9 @@ 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, offset 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, offset 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, offset, limit, true, ctx)
) 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)
return m[accountId], sessionState, state, lang, err
}
}

View File

@@ -48,7 +48,7 @@ const (
QueryParamSearchMaxSize = "maxsize"
QueryParamSearchKeyword = "keyword"
QueryParamSearchMessageId = "messageId"
QueryParamOffset = "offset"
QueryParamPosition = "position"
QueryParamLimit = "limit"
QueryParamDays = "days"
QueryParamPartId = "partId"

View File

@@ -62,7 +62,7 @@ func getall[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jmap.G
}
l := req.logger.With().Str(accountId, log.SafeString(accountId))
if notok, resp := req.unsupportedParams(single(accountId), QueryParamOffset, QueryParamLimit); notok {
if notok, resp := req.unsupportedParams(single(accountId), QueryParamPosition, QueryParamLimit); notok {
return resp
}
@@ -76,7 +76,7 @@ func getall[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jmap.G
})
}
// Retrieve all the {{.Name}} with support for paging using the {{.QueryParam.QueryParamOffset.Name}} and {{.QueryParam.QueryParamLimit.Name}} query parameters.
// 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}}
func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER any, COMP any, SEARCHRESULTS jmap.SearchResults[T]]( //NOSONAR
o ObjectType[T, CHANGE, CHANGES],
@@ -84,7 +84,7 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
g *Groupware,
filterFunc func(containerId string) FILTER,
sortBy []COMP,
queryFunc func(req Request, accountId string, filter FILTER, sortBy []COMP, offset 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, 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)
@@ -93,12 +93,12 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
}
l := req.logger.With().Str(accountId, log.SafeString(accountId))
offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
position, ok, err := req.parseIntParam(QueryParamPosition, 0)
if err != nil {
return req.error(accountId, err)
}
if ok {
l = l.Int(QueryParamOffset, offset)
l = l.Int(QueryParamPosition, position)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, uint(0))
@@ -123,7 +123,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, offset, limit, ctx)
results, sessionState, state, lang, jerr := queryFunc(req, accountId, filter, sortBy, position, limit, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -131,14 +131,14 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER
})
}
// Query all the {{.Name}} with support for paging using the {{.QueryParam.QueryParamOffset.Name}} and {{.QueryParam.QueryParamLimit.Name}} query parameters.
// Query 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}} that match the filter, within the requested range, as well as the total amount of matches
func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULTS jmap.SearchResults[T]]( //NOSONAR
o ObjectType[T, CHANGE, CHANGES],
w http.ResponseWriter, r *http.Request,
g *Groupware,
defaultLimit uint,
queryFunc func(req Request, accountId string, containerId string, offset int, limit uint, ctx jmap.Context) (SEARCHRESULTS, jmap.SessionState, jmap.State, jmap.Language, *Error),
queryFunc func(req Request, accountId string, containerId string, position 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)
@@ -157,12 +157,12 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT
l = l.Str(o.containerUriParamName, log.SafeString(containerId))
}
offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
position, ok, err := req.parseIntParam(QueryParamPosition, 0)
if err != nil {
return req.error(accountId, err)
}
if ok {
l = l.Int(QueryParamOffset, offset)
l = l.Int(QueryParamPosition, position)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, defaultLimit)
@@ -176,7 +176,7 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT
logger := log.From(l)
ctx := req.ctx.WithLogger(logger)
results, sessionState, state, lang, err := queryFunc(req, accountId, containerId, offset, limit, ctx)
results, sessionState, state, lang, err := queryFunc(req, accountId, containerId, position, limit, ctx)
if err != nil {
return req.error(accountId, err)
}