From b6cedcbe902980ade443e10e48886bcdecb3f9fc Mon Sep 17 00:00:00 2001
From: Pascal Bleser
Date: Thu, 9 Apr 2026 18:35:29 +0200
Subject: [PATCH] groupware: refactor for conciseness
* introduce jmap.Context to hold multiple parameters and shorten
function calls
* introduce SearchResultsTemplate
---
pkg/jmap/api.go | 21 +-
pkg/jmap/api_addressbook.go | 32 +-
pkg/jmap/api_blob.go | 60 ++--
pkg/jmap/api_bootstrap.go | 16 +-
pkg/jmap/api_calendar.go | 153 ++++-----
pkg/jmap/api_changes.go | 43 ++-
pkg/jmap/api_contact.go | 183 ++++------
pkg/jmap/api_email.go | 283 ++++++++-------
pkg/jmap/api_identity.go | 143 ++++----
pkg/jmap/api_mailbox.go | 191 +++++------
pkg/jmap/api_objects.go | 32 +-
pkg/jmap/api_principal.go | 75 ++--
pkg/jmap/api_quota.go | 27 +-
pkg/jmap/api_vacation.go | 54 +--
pkg/jmap/client.go | 38 ++-
pkg/jmap/http.go | 35 +-
pkg/jmap/integration_contact_test.go | 47 +--
pkg/jmap/integration_email_test.go | 42 +--
pkg/jmap/integration_event_test.go | 76 +++--
pkg/jmap/integration_test.go | 36 +-
pkg/jmap/integration_ws_test.go | 18 +-
pkg/jmap/model.go | 289 ++++++++++++----
pkg/jmap/templates.go | 231 +++++++++----
pkg/jmap/tools.go | 69 ++--
pkg/jscontact/model.go | 321 ------------------
.../groupware/pkg/groupware/api_account.go | 2 +-
.../pkg/groupware/api_addressbooks.go | 15 +-
services/groupware/pkg/groupware/api_blob.go | 17 +-
.../groupware/pkg/groupware/api_calendars.go | 15 +-
.../groupware/pkg/groupware/api_changes.go | 3 +-
.../groupware/pkg/groupware/api_contacts.go | 23 +-
.../groupware/pkg/groupware/api_emails.go | 99 +++---
.../groupware/pkg/groupware/api_events.go | 21 +-
.../groupware/pkg/groupware/api_identity.go | 41 ++-
services/groupware/pkg/groupware/api_index.go | 2 +-
.../groupware/pkg/groupware/api_mailbox.go | 33 +-
.../groupware/pkg/groupware/api_objects.go | 7 +-
services/groupware/pkg/groupware/api_quota.go | 6 +-
.../groupware/pkg/groupware/api_vacation.go | 8 +-
services/groupware/pkg/groupware/error.go | 2 +-
services/groupware/pkg/groupware/framework.go | 53 ++-
services/groupware/pkg/groupware/request.go | 7 +-
.../groupware/pkg/groupware/request_test.go | 7 +-
43 files changed, 1445 insertions(+), 1431 deletions(-)
diff --git a/pkg/jmap/api.go b/pkg/jmap/api.go
index 7b049e1f68..e53db6c758 100644
--- a/pkg/jmap/api.go
+++ b/pkg/jmap/api.go
@@ -8,8 +8,23 @@ import (
"github.com/opencloud-eu/opencloud/pkg/log"
)
+type Context struct {
+ Session *Session
+ Context context.Context
+ Logger *log.Logger
+ AcceptLanguage string
+}
+
+func (c Context) WithLogger(newLogger *log.Logger) Context {
+ return Context{Session: c.Session, Context: c.Context, AcceptLanguage: c.AcceptLanguage, Logger: newLogger}
+}
+
+func (c Context) WithContext(newContext context.Context) Context {
+ return Context{Session: c.Session, Context: newContext, AcceptLanguage: c.AcceptLanguage, Logger: c.Logger}
+}
+
type ApiClient interface {
- Command(ctx context.Context, logger *log.Logger, session *Session, request Request, acceptLanguage string) ([]byte, Language, Error)
+ Command(request Request, ctx Context) ([]byte, Language, Error)
io.Closer
}
@@ -33,8 +48,8 @@ type SessionClient interface {
}
type BlobClient interface {
- UploadBinary(ctx context.Context, logger *log.Logger, session *Session, uploadUrl string, endpoint string, contentType string, acceptLanguage string, content io.Reader) (UploadedBlob, Language, Error)
- DownloadBinary(ctx context.Context, logger *log.Logger, session *Session, downloadUrl string, endpoint string, acceptLanguage string) (*BlobDownload, Language, Error)
+ UploadBinary(uploadUrl string, endpoint string, contentType string, content io.Reader, ctx Context) (UploadedBlob, Language, Error)
+ DownloadBinary(downloadUrl string, endpoint string, ctx Context) (*BlobDownload, Language, Error)
io.Closer
}
diff --git a/pkg/jmap/api_addressbook.go b/pkg/jmap/api_addressbook.go
index bcd55ecaa4..299b5ab944 100644
--- a/pkg/jmap/api_addressbook.go
+++ b/pkg/jmap/api_addressbook.go
@@ -1,21 +1,16 @@
package jmap
-import (
- "context"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
-)
-
var NS_ADDRESSBOOKS = ns(JmapContacts)
-func (j *Client) GetAddressbooks(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (AddressBookGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetAddressbooks(accountId string, ids []string, ctx Context) (AddressBookGetResponse, SessionState, State, Language, Error) {
return get(j, "GetAddressbooks", NS_ADDRESSBOOKS,
func(accountId string, ids []string) AddressBookGetCommand {
return AddressBookGetCommand{AccountId: accountId, Ids: ids}
},
AddressBookGetResponse{},
identity1,
- accountId, session, ctx, logger, acceptLanguage, ids,
+ accountId, ids,
+ ctx,
)
}
@@ -23,10 +18,10 @@ type AddressBookChanges = ChangesTemplate[AddressBook]
// Retrieve Address Book changes since a given state.
// @apidoc addressbook,changes
-func (j *Client) GetAddressbookChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (AddressBookChanges, SessionState, State, Language, Error) {
+func (j *Client) GetAddressbookChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (AddressBookChanges, SessionState, State, Language, Error) {
return changesA(j, "GetAddressbookChanges", NS_ADDRESSBOOKS,
func() AddressBookChangesCommand {
- return AddressBookChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return AddressBookChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
AddressBookChangesResponse{},
AddressBookGetResponse{},
@@ -50,11 +45,11 @@ func (j *Client) GetAddressbookChanges(accountId string, session *Session, ctx c
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) CreateAddressBook(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, addressbook AddressBookChange) (*AddressBook, SessionState, State, Language, Error) {
+func (j *Client) CreateAddressBook(accountId string, addressbook AddressBookChange, ctx Context) (*AddressBook, SessionState, State, Language, Error) {
return create(j, "CreateAddressBook", NS_ADDRESSBOOKS,
func(accountId string, create map[string]AddressBookChange) AddressBookSetCommand {
return AddressBookSetCommand{AccountId: accountId, Create: create}
@@ -68,21 +63,23 @@ func (j *Client) CreateAddressBook(accountId string, session *Session, ctx conte
func(resp AddressBookGetResponse) []AddressBook {
return resp.List
},
- accountId, session, ctx, logger, acceptLanguage, addressbook,
+ accountId, addressbook,
+ ctx,
)
}
-func (j *Client) DeleteAddressBook(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+func (j *Client) DeleteAddressBook(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
return destroy(j, "DeleteAddressBook", NS_ADDRESSBOOKS,
func(accountId string, destroy []string) AddressBookSetCommand {
return AddressBookSetCommand{AccountId: accountId, Destroy: destroy}
},
AddressBookSetResponse{},
- accountId, destroyIds, session, ctx, logger, acceptLanguage,
+ accountId, destroyIds,
+ ctx,
)
}
-func (j *Client) UpdateAddressBook(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string, changes AddressBookChange) (AddressBook, SessionState, State, Language, Error) {
+func (j *Client) UpdateAddressBook(accountId string, id string, changes AddressBookChange, ctx Context) (AddressBook, SessionState, State, Language, Error) {
return update(j, "UpdateAddressBook", NS_ADDRESSBOOKS,
func(update map[string]PatchObject) AddressBookSetCommand {
return AddressBookSetCommand{AccountId: accountId, Update: update}
@@ -92,6 +89,7 @@ func (j *Client) UpdateAddressBook(accountId string, session *Session, ctx conte
},
func(resp AddressBookSetResponse) map[string]SetError { return resp.NotUpdated },
func(resp AddressBookGetResponse) AddressBook { return resp.List[0] },
- id, changes, session, ctx, logger, acceptLanguage,
+ id, changes,
+ ctx,
)
}
diff --git a/pkg/jmap/api_blob.go b/pkg/jmap/api_blob.go
index 27b16cdb49..4024686422 100644
--- a/pkg/jmap/api_blob.go
+++ b/pkg/jmap/api_blob.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"encoding/base64"
"io"
"strings"
@@ -9,28 +8,31 @@ import (
"github.com/opencloud-eu/opencloud/pkg/log"
)
-func (j *Client) GetBlobMetadata(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (*Blob, SessionState, State, Language, Error) {
- cmd, jerr := j.request(session, logger, ns(JmapBlob),
- invocation(BlobGetCommand{
- AccountId: accountId,
- Ids: []string{id},
- // add BlobPropertyData to retrieve the data
- Properties: []string{BlobPropertyDigestSha256, BlobPropertyDigestSha512, BlobPropertySize},
- }, "0"),
+var NS_BLOB = ns(JmapBlob)
+
+func (j *Client) GetBlobMetadata(accountId string, id string, ctx Context) (*Blob, SessionState, State, Language, Error) {
+ get := BlobGetCommand{
+ AccountId: accountId,
+ Ids: []string{id},
+ // add BlobPropertyData to retrieve the data
+ Properties: []string{BlobPropertyDigestSha256, BlobPropertyDigestSha512, BlobPropertySize},
+ }
+ cmd, jerr := j.request(ctx, NS_BLOB,
+ invocation(get, "0"),
)
if jerr != nil {
return nil, "", "", "", jerr
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*Blob, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (*Blob, State, Error) {
var response BlobGetResponse
- err := retrieveResponseMatchParameters(logger, body, CommandBlobGet, "0", &response)
+ err := retrieveGet(ctx, body, get, "0", &response)
if err != nil {
return nil, "", err
}
if len(response.List) != 1 {
- logger.Error().Msgf("%T.List has %v entries instead of 1", response, len(response.List))
+ ctx.Logger.Error().Msgf("%T.List has %v entries instead of 1", response, len(response.List))
return nil, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
get := response.List[0]
@@ -45,26 +47,28 @@ type UploadedBlobWithHash struct {
Sha512 string `json:"sha:512,omitempty"`
}
-func (j *Client) UploadBlobStream(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, contentType string, body io.Reader) (UploadedBlob, Language, Error) {
- logger = log.From(logger.With().Str(logEndpoint, session.UploadEndpoint))
+func (j *Client) UploadBlobStream(accountId string, contentType string, body io.Reader, ctx Context) (UploadedBlob, Language, Error) {
+ logger := log.From(ctx.Logger.With().Str(logEndpoint, ctx.Session.UploadEndpoint))
+ ctx = ctx.WithLogger(logger)
// TODO(pbleser-oc) use a library for proper URL template parsing
- uploadUrl := strings.ReplaceAll(session.UploadUrlTemplate, "{accountId}", accountId)
- return j.blob.UploadBinary(ctx, logger, session, uploadUrl, session.UploadEndpoint, contentType, acceptLanguage, body)
+ uploadUrl := strings.ReplaceAll(ctx.Session.UploadUrlTemplate, "{accountId}", accountId)
+ return j.blob.UploadBinary(uploadUrl, ctx.Session.UploadEndpoint, contentType, body, ctx)
}
-func (j *Client) DownloadBlobStream(accountId string, blobId string, name string, typ string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (*BlobDownload, Language, Error) { //NOSONAR
- logger = log.From(logger.With().Str(logEndpoint, session.DownloadEndpoint))
+func (j *Client) DownloadBlobStream(accountId string, blobId string, name string, typ string, ctx Context) (*BlobDownload, Language, Error) { //NOSONAR
+ logger := log.From(ctx.Logger.With().Str(logEndpoint, ctx.Session.DownloadEndpoint))
+ ctx = ctx.WithLogger(logger)
// TODO(pbleser-oc) use a library for proper URL template parsing
- downloadUrl := session.DownloadUrlTemplate
+ downloadUrl := ctx.Session.DownloadUrlTemplate
downloadUrl = strings.ReplaceAll(downloadUrl, "{accountId}", accountId)
downloadUrl = strings.ReplaceAll(downloadUrl, "{blobId}", blobId)
downloadUrl = strings.ReplaceAll(downloadUrl, "{name}", name)
downloadUrl = strings.ReplaceAll(downloadUrl, "{type}", typ)
logger = log.From(logger.With().Str(logDownloadUrl, downloadUrl).Str(logBlobId, blobId))
- return j.blob.DownloadBinary(ctx, logger, session, downloadUrl, session.DownloadEndpoint, acceptLanguage)
+ return j.blob.DownloadBinary(downloadUrl, ctx.Session.DownloadEndpoint, ctx)
}
-func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte, contentType string) (UploadedBlobWithHash, SessionState, State, Language, Error) {
+func (j *Client) UploadBlob(accountId string, data []byte, contentType string, ctx Context) (UploadedBlobWithHash, SessionState, State, Language, Error) {
encoded := base64.StdEncoding.EncodeToString(data)
upload := BlobUploadCommand{
@@ -89,7 +93,7 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
Properties: []string{BlobPropertyDigestSha512},
}
- cmd, jerr := j.request(session, logger, ns(JmapBlob),
+ cmd, jerr := j.request(ctx, ns(JmapBlob),
invocation(upload, "0"),
invocation(getHash, "1"),
)
@@ -97,31 +101,31 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
return UploadedBlobWithHash{}, "", "", "", jerr
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedBlobWithHash, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (UploadedBlobWithHash, State, Error) {
var uploadResponse BlobUploadResponse
- err := retrieveResponseMatchParameters(logger, body, upload.GetCommand(), "0", &uploadResponse)
+ err := retrieveUpload(ctx, body, upload, "0", &uploadResponse)
if err != nil {
return UploadedBlobWithHash{}, "", err
}
var getResponse BlobGetResponse
- err = retrieveResponseMatchParameters(logger, body, getHash.GetCommand(), "1", &getResponse)
+ err = retrieveGet(ctx, body, getHash, "1", &getResponse)
if err != nil {
return UploadedBlobWithHash{}, "", err
}
if len(uploadResponse.Created) != 1 {
- logger.Error().Msgf("%T.Created has %v entries instead of 1", uploadResponse, len(uploadResponse.Created))
+ ctx.Logger.Error().Msgf("%T.Created has %v entries instead of 1", uploadResponse, len(uploadResponse.Created))
return UploadedBlobWithHash{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
upload, ok := uploadResponse.Created["0"]
if !ok {
- logger.Error().Msgf("%T.Created has no item '0'", uploadResponse)
+ ctx.Logger.Error().Msgf("%T.Created has no item '0'", uploadResponse)
return UploadedBlobWithHash{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
if len(getResponse.List) != 1 {
- logger.Error().Msgf("%T.List has %v entries instead of 1", getResponse, len(getResponse.List))
+ ctx.Logger.Error().Msgf("%T.List has %v entries instead of 1", getResponse, len(getResponse.List))
return UploadedBlobWithHash{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
get := getResponse.List[0]
diff --git a/pkg/jmap/api_bootstrap.go b/pkg/jmap/api_bootstrap.go
index 2101863af6..27767ca309 100644
--- a/pkg/jmap/api_bootstrap.go
+++ b/pkg/jmap/api_bootstrap.go
@@ -1,9 +1,6 @@
package jmap
import (
- "context"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/structs"
)
@@ -14,10 +11,11 @@ type AccountBootstrapResult struct {
var NS_MAIL_QUOTA = ns(JmapMail, JmapQuota)
-func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]AccountBootstrapResult, SessionState, State, Language, Error) { //NOSONAR
+func (j *Client) GetBootstrap(accountIds []string, ctx Context) (map[string]AccountBootstrapResult, SessionState, State, Language, Error) { //NOSONAR
uniqueAccountIds := structs.Uniq(accountIds)
- logger = j.logger("GetBootstrap", session, logger)
+ logger := j.logger("GetBootstrap", ctx)
+ ctx = ctx.WithLogger(logger)
calls := make([]Invocation, len(uniqueAccountIds)*2)
for i, accountId := range uniqueAccountIds {
@@ -25,18 +23,18 @@ func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context
calls[i*2+1] = invocation(QuotaGetCommand{AccountId: accountId}, mcid(accountId, "Q"))
}
- cmd, err := j.request(session, logger, NS_MAIL_QUOTA, calls...)
+ cmd, err := j.request(ctx, NS_MAIL_QUOTA, calls...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]AccountBootstrapResult, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]AccountBootstrapResult, State, Error) {
identityPerAccount := map[string][]Identity{}
quotaPerAccount := map[string][]Quota{}
identityStatesPerAccount := map[string]State{}
quotaStatesPerAccount := map[string]State{}
for _, accountId := range uniqueAccountIds {
var identityResponse IdentityGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, mcid(accountId, "I"), &identityResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandIdentityGet, mcid(accountId, "I"), &identityResponse)
if err != nil {
return nil, "", err
} else {
@@ -45,7 +43,7 @@ func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context
}
var quotaResponse QuotaGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandQuotaGet, mcid(accountId, "Q"), "aResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandQuotaGet, mcid(accountId, "Q"), "aResponse)
if err != nil {
return nil, "", err
} else {
diff --git a/pkg/jmap/api_calendar.go b/pkg/jmap/api_calendar.go
index bb5e49c539..6f13cf7683 100644
--- a/pkg/jmap/api_calendar.go
+++ b/pkg/jmap/api_calendar.go
@@ -1,27 +1,21 @@
package jmap
-import (
- "context"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
- "github.com/opencloud-eu/opencloud/pkg/structs"
-)
-
var NS_CALENDARS = ns(JmapCalendars)
-func (j *Client) ParseICalendarBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, blobIds []string) (CalendarEventParseResponse, SessionState, State, Language, Error) {
- logger = j.logger("ParseICalendarBlob", session, logger)
+func (j *Client) ParseICalendarBlob(accountId string, blobIds []string, ctx Context) (CalendarEventParseResponse, SessionState, State, Language, Error) {
+ logger := j.logger("ParseICalendarBlob", ctx)
- cmd, err := j.request(session, logger, NS_CALENDARS,
- invocation(CalendarEventParseCommand{AccountId: accountId, BlobIds: blobIds}, "0"),
+ parse := CalendarEventParseCommand{AccountId: accountId, BlobIds: blobIds}
+ cmd, err := j.request(ctx.WithLogger(logger), NS_CALENDARS,
+ invocation(parse, "0"),
)
if err != nil {
return CalendarEventParseResponse{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (CalendarEventParseResponse, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (CalendarEventParseResponse, State, Error) {
var response CalendarEventParseResponse
- err = retrieveResponseMatchParameters(logger, body, CommandCalendarEventParse, "0", &response)
+ err = retrieveParse(ctx, body, parse, "0", &response)
if err != nil {
return CalendarEventParseResponse{}, "", err
}
@@ -29,14 +23,15 @@ func (j *Client) ParseICalendarBlob(accountId string, session *Session, ctx cont
})
}
-func (j *Client) GetCalendars(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (CalendarGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetCalendars(accountId string, ids []string, ctx Context) (CalendarGetResponse, SessionState, State, Language, Error) {
return get(j, "GetCalendars", NS_CALENDARS,
func(accountId string, ids []string) CalendarGetCommand {
return CalendarGetCommand{AccountId: accountId, Ids: ids}
},
CalendarGetResponse{},
identity1,
- accountId, session, ctx, logger, acceptLanguage, ids,
+ accountId, ids,
+ ctx,
)
}
@@ -44,10 +39,10 @@ type CalendarChanges = ChangesTemplate[Calendar]
// Retrieve Calendar changes since a given state.
// @apidoc calendar,changes
-func (j *Client) GetCalendarChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (CalendarChanges, SessionState, State, Language, Error) {
+func (j *Client) GetCalendarChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (CalendarChanges, SessionState, State, Language, Error) {
return changes(j, "GetCalendarChanges", NS_CALENDARS,
func() CalendarChangesCommand {
- return CalendarChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return CalendarChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
CalendarChangesResponse{},
func(path string, rof string) CalendarGetRefCommand {
@@ -71,78 +66,55 @@ func (j *Client) GetCalendarChanges(accountId string, session *Session, ctx cont
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) QueryCalendarEvents(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, //NOSONAR
+type CalendarEventSearchResults SearchResultsTemplate[CalendarEvent]
+
+var _ SearchResults[CalendarEvent] = CalendarEventSearchResults{}
+
+func (r CalendarEventSearchResults) GetResults() []CalendarEvent { return r.Results }
+func (r CalendarEventSearchResults) GetCanCalculateChanges() bool { return r.CanCalculateChanges }
+func (r CalendarEventSearchResults) GetPosition() uint { return r.Position }
+func (r CalendarEventSearchResults) GetLimit() uint { return r.Limit }
+func (r CalendarEventSearchResults) GetTotal() *uint { return r.Total }
+
+func (j *Client) QueryCalendarEvents(accountIds []string, //NOSONAR
filter CalendarEventFilterElement, sortBy []CalendarEventComparator,
- position uint, limit uint) (map[string][]CalendarEvent, SessionState, State, Language, Error) {
- logger = j.logger("QueryCalendarEvents", session, logger)
-
- uniqueAccountIds := structs.Uniq(accountIds)
-
- if sortBy == nil {
- sortBy = []CalendarEventComparator{{Property: CalendarEventPropertyStart, IsAscending: false}}
- }
-
- invocations := make([]Invocation, len(uniqueAccountIds)*2)
- for i, accountId := range uniqueAccountIds {
- query := CalendarEventQueryCommand{
- AccountId: accountId,
- Filter: filter,
- Sort: sortBy,
- }
- if limit > 0 {
- query.Limit = limit
- }
- if position > 0 {
- query.Position = position
- }
- invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CalendarEventGetRefCommand{
- AccountId: accountId,
- IdsRef: &ResultReference{
- Name: CommandCalendarEventQuery,
- Path: "/ids/*",
- ResultOf: mcid(accountId, "0"),
- },
- // Properties: CalendarEventProperties, // to also retrieve UTCStart and UTCEnd
- }, mcid(accountId, "1"))
- }
- cmd, err := j.request(session, logger, NS_CALENDARS, invocations...)
- if err != nil {
- return nil, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]CalendarEvent, State, Error) {
- resp := map[string][]CalendarEvent{}
- stateByAccountId := map[string]State{}
- for _, accountId := range uniqueAccountIds {
- var response CalendarEventGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandCalendarEventGet, mcid(accountId, "1"), &response)
- if err != nil {
- return nil, "", err
+ position int, limit uint, calculateTotal bool,
+ ctx Context) (map[string]CalendarEventSearchResults, SessionState, State, Language, Error) {
+ return queryN(j, "QueryCalendarEvents", NS_CALENDARS,
+ []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: uintPtr(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}}
+ },
+ func(query CalendarEventQueryResponse, get CalendarEventGetResponse) CalendarEventSearchResults {
+ return CalendarEventSearchResults{
+ Results: get.List,
+ CanCalculateChanges: query.CanCalculateChanges,
+ Position: query.Position,
+ Total: uintPtrIf(query.Total, calculateTotal),
+ Limit: query.Limit,
}
- if len(response.NotFound) > 0 {
- // TODO what to do when there are not-found calendarevents here? potentially nothing, they could have been deleted between query and get?
- }
- resp[accountId] = response.List
- stateByAccountId[accountId] = response.State
- }
- return resp, squashState(stateByAccountId), nil
- })
+ },
+ accountIds,
+ filter, sortBy, limit, position, ctx,
+ )
}
type CalendarEventChanges = ChangesTemplate[CalendarEvent]
// Retrieve the changes in Calendar Events since a given State.
// @api:tags event,changes
-func (j *Client) GetCalendarEventChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (CalendarEventChanges, SessionState, State, Language, Error) {
+func (j *Client) GetCalendarEventChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (CalendarEventChanges, SessionState, State, Language, Error) {
return changes(j, "GetCalendarEventChanges", NS_CALENDARS,
func() CalendarEventChangesCommand {
- return CalendarEventChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return CalendarEventChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
CalendarEventChangesResponse{},
func(path string, rof string) CalendarEventGetRefCommand {
@@ -166,11 +138,11 @@ func (j *Client) GetCalendarEventChanges(accountId string, session *Session, ctx
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) CreateCalendarEvent(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, event CalendarEvent) (*CalendarEvent, SessionState, State, Language, Error) {
+func (j *Client) CreateCalendarEvent(accountId string, event CalendarEvent, ctx Context) (*CalendarEvent, SessionState, State, Language, Error) {
return create(j, "CreateCalendarEvent", NS_CALENDARS,
func(accountId string, create map[string]CalendarEvent) CalendarEventSetCommand {
return CalendarEventSetCommand{AccountId: accountId, Create: create}
@@ -184,19 +156,23 @@ func (j *Client) CreateCalendarEvent(accountId string, session *Session, ctx con
func(resp CalendarEventGetResponse) []CalendarEvent {
return resp.List
},
- accountId, session, ctx, logger, acceptLanguage, event)
+ accountId, event,
+ ctx,
+ )
}
-func (j *Client) DeleteCalendarEvent(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+func (j *Client) DeleteCalendarEvent(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
return destroy(j, "DeleteCalendarEvent", NS_CALENDARS,
func(accountId string, destroy []string) CalendarEventSetCommand {
return CalendarEventSetCommand{AccountId: accountId, Destroy: destroy}
},
CalendarEventSetResponse{},
- accountId, destroyIds, session, ctx, logger, acceptLanguage)
+ accountId, destroyIds,
+ ctx,
+ )
}
-func (j *Client) CreateCalendar(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, calendar CalendarChange) (*Calendar, SessionState, State, Language, Error) {
+func (j *Client) CreateCalendar(accountId string, calendar CalendarChange, ctx Context) (*Calendar, SessionState, State, Language, Error) {
return create(j, "CreateCalendar", NS_CALENDARS,
func(accountId string, create map[string]CalendarChange) CalendarSetCommand {
return CalendarSetCommand{AccountId: accountId, Create: create}
@@ -210,21 +186,23 @@ func (j *Client) CreateCalendar(accountId string, session *Session, ctx context.
func(resp CalendarGetResponse) []Calendar {
return resp.List
},
- accountId, session, ctx, logger, acceptLanguage, calendar,
+ accountId, calendar,
+ ctx,
)
}
-func (j *Client) DeleteCalendar(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+func (j *Client) DeleteCalendar(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
return destroy(j, "DeleteCalendar", NS_CALENDARS,
func(accountId string, destroy []string) CalendarSetCommand {
return CalendarSetCommand{AccountId: accountId, Destroy: destroy}
},
CalendarSetResponse{},
- accountId, destroyIds, session, ctx, logger, acceptLanguage,
+ accountId, destroyIds,
+ ctx,
)
}
-func (j *Client) UpdateCalendar(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string, changes CalendarChange) (Calendar, SessionState, State, Language, Error) {
+func (j *Client) UpdateCalendar(accountId string, id string, changes CalendarChange, ctx Context) (Calendar, SessionState, State, Language, Error) {
return update(j, "UpdateCalendar", NS_CALENDARS,
func(update map[string]PatchObject) CalendarSetCommand {
return CalendarSetCommand{AccountId: accountId, Update: update}
@@ -234,6 +212,7 @@ func (j *Client) UpdateCalendar(accountId string, session *Session, ctx context.
},
func(resp CalendarSetResponse) map[string]SetError { return resp.NotUpdated },
func(resp CalendarGetResponse) Calendar { return resp.List[0] },
- id, changes, session, ctx, logger, acceptLanguage,
+ id, changes,
+ ctx,
)
}
diff --git a/pkg/jmap/api_changes.go b/pkg/jmap/api_changes.go
index 234b692141..db2d715038 100644
--- a/pkg/jmap/api_changes.go
+++ b/pkg/jmap/api_changes.go
@@ -1,8 +1,6 @@
package jmap
import (
- "context"
-
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/rs/zerolog"
)
@@ -76,49 +74,50 @@ func (s StateMap) MarshalZerologObject(e *zerolog.Event) {
// Retrieve the changes in any type of objects at once since a given State.
// @api:tags changes
-func (j *Client) GetChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, stateMap StateMap, maxChanges uint) (Changes, SessionState, State, Language, Error) { //NOSONAR
- logger = log.From(j.logger("GetChanges", session, logger).With().Object("state", stateMap).Uint("maxChanges", maxChanges))
+func (j *Client) GetChanges(accountId string, stateMap StateMap, maxChanges uint, ctx Context) (Changes, SessionState, State, Language, Error) { //NOSONAR
+ logger := log.From(j.logger("GetChanges", ctx).With().Object("state", stateMap).Uint("maxChanges", maxChanges))
+ ctx = ctx.WithLogger(logger)
methodCalls := []Invocation{}
if stateMap.Mailboxes != nil {
- methodCalls = append(methodCalls, invocation(MailboxChangesCommand{AccountId: accountId, SinceState: *stateMap.Mailboxes, MaxChanges: posUIntPtr(maxChanges)}, "mailboxes"))
+ methodCalls = append(methodCalls, invocation(MailboxChangesCommand{AccountId: accountId, SinceState: *stateMap.Mailboxes, MaxChanges: uintPtr(maxChanges)}, "mailboxes"))
}
if stateMap.Emails != nil {
- methodCalls = append(methodCalls, invocation(EmailChangesCommand{AccountId: accountId, SinceState: *stateMap.Emails, MaxChanges: posUIntPtr(maxChanges)}, "emails"))
+ methodCalls = append(methodCalls, invocation(EmailChangesCommand{AccountId: accountId, SinceState: *stateMap.Emails, MaxChanges: uintPtr(maxChanges)}, "emails"))
}
if stateMap.Calendars != nil {
- methodCalls = append(methodCalls, invocation(CalendarChangesCommand{AccountId: accountId, SinceState: *stateMap.Calendars, MaxChanges: posUIntPtr(maxChanges)}, "calendars"))
+ methodCalls = append(methodCalls, invocation(CalendarChangesCommand{AccountId: accountId, SinceState: *stateMap.Calendars, MaxChanges: uintPtr(maxChanges)}, "calendars"))
}
if stateMap.Events != nil {
- methodCalls = append(methodCalls, invocation(CalendarEventChangesCommand{AccountId: accountId, SinceState: *stateMap.Events, MaxChanges: posUIntPtr(maxChanges)}, "events"))
+ methodCalls = append(methodCalls, invocation(CalendarEventChangesCommand{AccountId: accountId, SinceState: *stateMap.Events, MaxChanges: uintPtr(maxChanges)}, "events"))
}
if stateMap.Addressbooks != nil {
- methodCalls = append(methodCalls, invocation(AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: posUIntPtr(maxChanges)}, "addressbooks"))
+ methodCalls = append(methodCalls, invocation(AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: uintPtr(maxChanges)}, "addressbooks"))
}
if stateMap.Contacts != nil {
- methodCalls = append(methodCalls, invocation(ContactCardChangesCommand{AccountId: accountId, SinceState: *stateMap.Contacts, MaxChanges: posUIntPtr(maxChanges)}, "contacts"))
+ methodCalls = append(methodCalls, invocation(ContactCardChangesCommand{AccountId: accountId, SinceState: *stateMap.Contacts, MaxChanges: uintPtr(maxChanges)}, "contacts"))
}
if stateMap.Identities != nil {
- methodCalls = append(methodCalls, invocation(IdentityChangesCommand{AccountId: accountId, SinceState: *stateMap.Identities, MaxChanges: posUIntPtr(maxChanges)}, "identities"))
+ methodCalls = append(methodCalls, invocation(IdentityChangesCommand{AccountId: accountId, SinceState: *stateMap.Identities, MaxChanges: uintPtr(maxChanges)}, "identities"))
}
if stateMap.EmailSubmissions != nil {
- methodCalls = append(methodCalls, invocation(EmailSubmissionChangesCommand{AccountId: accountId, SinceState: *stateMap.EmailSubmissions, MaxChanges: posUIntPtr(maxChanges)}, "submissions"))
+ methodCalls = append(methodCalls, invocation(EmailSubmissionChangesCommand{AccountId: accountId, SinceState: *stateMap.EmailSubmissions, MaxChanges: uintPtr(maxChanges)}, "submissions"))
}
// if stateMap.Quotas != nil { methodCalls = append(methodCalls, invocation(CommandQuotaChanges, QuotaChangesCommand{AccountId: accountId, SinceState: *stateMap.Quotas, MaxChanges: posUIntPtr(maxChanges)}, "quotas")) }
- cmd, err := j.request(session, logger, NS_CHANGES, methodCalls...)
+ cmd, err := j.request(ctx, NS_CHANGES, methodCalls...)
if err != nil {
return Changes{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Changes, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (Changes, State, Error) {
changes := Changes{
MaxChanges: maxChanges,
}
states := map[string]State{}
var mailboxes MailboxChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandMailboxChanges, "mailboxes", &mailboxes); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandMailboxChanges, "mailboxes", &mailboxes); err != nil {
return Changes{}, "", err
} else if ok {
changes.Mailboxes = &mailboxes
@@ -126,7 +125,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var emails EmailChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandEmailChanges, "emails", &emails); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandEmailChanges, "emails", &emails); err != nil {
return Changes{}, "", err
} else if ok {
changes.Emails = &emails
@@ -134,7 +133,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var calendars CalendarChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandCalendarChanges, "calendars", &calendars); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandCalendarChanges, "calendars", &calendars); err != nil {
return Changes{}, "", err
} else if ok {
changes.Calendars = &calendars
@@ -142,7 +141,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var events CalendarEventChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandCalendarEventChanges, "events", &events); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandCalendarEventChanges, "events", &events); err != nil {
return Changes{}, "", err
} else if ok {
changes.Events = &events
@@ -150,7 +149,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var addressbooks AddressBookChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandAddressBookChanges, "addressbooks", &addressbooks); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandAddressBookChanges, "addressbooks", &addressbooks); err != nil {
return Changes{}, "", err
} else if ok {
changes.Addressbooks = &addressbooks
@@ -158,7 +157,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var contacts ContactCardChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandContactCardChanges, "contacts", &contacts); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandContactCardChanges, "contacts", &contacts); err != nil {
return Changes{}, "", err
} else if ok {
changes.Contacts = &contacts
@@ -166,7 +165,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var identities IdentityChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandIdentityChanges, "identities", &identities); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandIdentityChanges, "identities", &identities); err != nil {
return Changes{}, "", err
} else if ok {
changes.Identities = &identities
@@ -174,7 +173,7 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
}
var submissions EmailSubmissionChangesResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandEmailSubmissionChanges, "submissions", &submissions); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandEmailSubmissionChanges, "submissions", &submissions); err != nil {
return Changes{}, "", err
} else if ok {
changes.EmailSubmissions = &submissions
diff --git a/pkg/jmap/api_contact.go b/pkg/jmap/api_contact.go
index bc658f3f33..1ae7e2f9e8 100644
--- a/pkg/jmap/api_contact.go
+++ b/pkg/jmap/api_contact.go
@@ -1,24 +1,16 @@
package jmap
-import (
- "context"
- "fmt"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
- "github.com/opencloud-eu/opencloud/pkg/structs"
-)
-
var NS_CONTACTS = ns(JmapContacts)
-func (j *Client) GetContactCards(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, contactIds []string) (ContactCardGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetContactCards(accountId string, contactIds []string, ctx Context) (ContactCardGetResponse, SessionState, State, Language, Error) {
return get(j, "GetContactCards", NS_CONTACTS,
func(accountId string, ids []string) ContactCardGetCommand {
return ContactCardGetCommand{AccountId: accountId, Ids: contactIds}
},
ContactCardGetResponse{},
identity1,
- accountId, session, ctx, logger, acceptLanguage, contactIds,
+ accountId, contactIds,
+ ctx,
)
}
@@ -26,11 +18,10 @@ type ContactCardChanges = ChangesTemplate[ContactCard]
// Retrieve the changes in Contact Cards since a given State.
// @api:tags contact,changes
-func (j *Client) GetContactCardChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (ContactCardChanges, SessionState, State, Language, Error) {
+func (j *Client) GetContactCardChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (ContactCardChanges, SessionState, State, Language, Error) {
return changes(j, "GetContactCardChanges", NS_CONTACTS,
func() ContactCardChangesCommand {
- return ContactCardChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return ContactCardChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
ContactCardChangesResponse{},
func(path string, rof string) ContactCardGetRefCommand {
@@ -54,128 +45,72 @@ func (j *Client) GetContactCardChanges(accountId string, session *Session, ctx c
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) QueryContactCards(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, //NOSONAR
+type ContactCardSearchResults SearchResultsTemplate[ContactCard]
+
+var _ SearchResults[ContactCard] = ContactCardSearchResults{}
+
+func (r ContactCardSearchResults) GetResults() []ContactCard { return r.Results }
+func (r ContactCardSearchResults) GetCanCalculateChanges() bool { return r.CanCalculateChanges }
+func (r ContactCardSearchResults) GetPosition() uint { return r.Position }
+func (r ContactCardSearchResults) GetLimit() uint { return r.Limit }
+func (r ContactCardSearchResults) GetTotal() *uint { return r.Total }
+
+func (j *Client) QueryContactCards(accountIds []string,
filter ContactCardFilterElement, sortBy []ContactCardComparator,
- position uint, limit uint) (map[string][]ContactCard, SessionState, State, Language, Error) {
- logger = j.logger("QueryContactCards", session, logger)
-
- uniqueAccountIds := structs.Uniq(accountIds)
-
- if sortBy == nil {
- sortBy = []ContactCardComparator{{Property: ContactCardPropertyUpdated, IsAscending: false}}
- }
-
- invocations := make([]Invocation, len(uniqueAccountIds)*2)
- for i, accountId := range uniqueAccountIds {
- query := ContactCardQueryCommand{
- AccountId: accountId,
- Filter: filter,
- Sort: sortBy,
- }
- if limit > 0 {
- query.Limit = limit
- }
- if position > 0 {
- query.Position = position
- }
- invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(ContactCardGetRefCommand{
- AccountId: accountId,
- IdsRef: &ResultReference{
- Name: CommandContactCardQuery,
- Path: "/ids/*",
- ResultOf: mcid(accountId, "0"),
- },
- }, mcid(accountId, "1"))
- }
- cmd, err := j.request(session, logger, NS_CONTACTS, invocations...)
- if err != nil {
- return nil, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]ContactCard, State, Error) {
- resp := map[string][]ContactCard{}
- stateByAccountId := map[string]State{}
- for _, accountId := range uniqueAccountIds {
- var response ContactCardGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, mcid(accountId, "1"), &response)
- if err != nil {
- return nil, "", err
+ position int, limit uint, calculateTotal bool,
+ ctx Context) (map[string]ContactCardSearchResults, SessionState, State, Language, Error) {
+ return queryN(j, "QueryContactCards", NS_CONTACTS,
+ []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: uintPtr(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}}
+ },
+ func(query ContactCardQueryResponse, get ContactCardGetResponse) ContactCardSearchResults {
+ return ContactCardSearchResults{
+ Results: get.List,
+ CanCalculateChanges: query.CanCalculateChanges,
+ Position: query.Position,
+ Total: uintPtrIf(query.Total, calculateTotal),
+ Limit: query.Limit,
}
- if len(response.NotFound) > 0 {
- // TODO what to do when there are not-found emails here? potentially nothing, they could have been deleted between query and get?
- }
- resp[accountId] = response.List
- stateByAccountId[accountId] = response.State
- }
- return resp, squashState(stateByAccountId), nil
- })
-}
-
-func (j *Client) CreateContactCard(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create ContactCard) (*ContactCard, SessionState, State, Language, Error) {
- logger = j.logger("CreateContactCard", session, logger)
-
- cmd, err := j.request(session, logger, NS_CONTACTS,
- invocation(ContactCardSetCommand{
- AccountId: accountId,
- Create: map[string]ContactCard{
- "c": create,
- },
- }, "0"),
- invocation(ContactCardGetCommand{
- AccountId: accountId,
- Ids: []string{"#c"},
- }, "1"),
+ },
+ accountIds,
+ filter, sortBy, limit, position, ctx,
)
- if err != nil {
- return nil, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*ContactCard, State, Error) {
- var setResponse ContactCardSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandContactCardSet, "0", &setResponse)
- if err != nil {
- return nil, "", err
- }
-
- setErr, notok := setResponse.NotCreated["c"]
- if notok {
- logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
- return nil, "", setErrorError(setErr, EmailType)
- }
-
- if created, ok := setResponse.Created["c"]; !ok || created == nil {
- berr := fmt.Errorf("failed to find %s in %s response", ContactCardType, string(CommandContactCardSet))
- logger.Error().Err(berr)
- return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
- }
-
- var getResponse ContactCardGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, "1", &getResponse)
- if err != nil {
- return nil, "", err
- }
-
- if len(getResponse.List) < 1 {
- berr := fmt.Errorf("failed to find %s in %s response", ContactCardType, string(CommandContactCardSet))
- logger.Error().Err(berr)
- return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
- }
-
- return &getResponse.List[0], setResponse.NewState, nil
- })
}
-func (j *Client) DeleteContactCard(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+func (j *Client) CreateContactCard(accountId string, contact ContactCard, ctx Context) (*ContactCard, SessionState, State, Language, Error) {
+ return create(j, "CreateContactCard", NS_CONTACTS,
+ func(accountId string, create map[string]ContactCard) ContactCardSetCommand {
+ return ContactCardSetCommand{AccountId: accountId, Create: create}
+ },
+ func(accountId string, ids string) ContactCardGetCommand {
+ return ContactCardGetCommand{AccountId: accountId, Ids: []string{ids}}
+ },
+ func(resp ContactCardSetResponse) map[string]*ContactCard {
+ return resp.Created
+ },
+ func(resp ContactCardGetResponse) []ContactCard {
+ return resp.List
+ },
+ accountId, contact,
+ ctx,
+ )
+}
+
+func (j *Client) DeleteContactCard(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
return destroy(j, "DeleteContactCard", NS_CONTACTS,
func(accountId string, destroy []string) ContactCardSetCommand {
return ContactCardSetCommand{AccountId: accountId, Destroy: destroy}
},
ContactCardSetResponse{},
- accountId, destroyIds, session, ctx, logger, acceptLanguage,
+ accountId, destroyIds,
+ ctx,
)
}
diff --git a/pkg/jmap/api_email.go b/pkg/jmap/api_email.go
index c7cda8fc82..d46da23d8f 100644
--- a/pkg/jmap/api_email.go
+++ b/pkg/jmap/api_email.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"encoding/base64"
"fmt"
"time"
@@ -27,8 +26,11 @@ type getEmailsResult struct {
}
// Retrieve specific Emails by their id.
-func (j *Client) GetEmails(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string, fetchBodies bool, maxBodyValueBytes uint, markAsSeen bool, withThreads bool) ([]Email, []string, SessionState, State, Language, Error) { //NOSONAR
- logger = j.logger("GetEmails", session, logger)
+func (j *Client) GetEmails(accountId string, ids []string, //NOSONAR
+ fetchBodies bool, maxBodyValueBytes uint, markAsSeen bool, withThreads bool,
+ ctx Context) ([]Email, []string, SessionState, State, Language, Error) {
+ logger := j.logger("GetEmails", ctx)
+ ctx = ctx.WithLogger(logger)
get := EmailGetCommand{AccountId: accountId, Ids: ids, FetchAllBodyValues: fetchBodies}
if maxBodyValueBytes > 0 {
@@ -57,14 +59,14 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
methodCalls = append(methodCalls, invocation(threads, "2"))
}
- cmd, err := j.request(session, logger, NS_MAIL, methodCalls...)
+ cmd, err := j.request(ctx, NS_MAIL, methodCalls...)
if err != nil {
return nil, nil, "", "", "", err
}
- result, sessionState, state, language, gwerr := command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (getEmailsResult, State, Error) {
+ result, sessionState, state, language, gwerr := command(j, ctx, cmd, func(body *Response) (getEmailsResult, State, Error) {
if markAsSeen {
var markResponse EmailSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &markResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailSet, "0", &markResponse)
if err != nil {
return getEmailsResult{}, "", err
}
@@ -74,13 +76,13 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
}
}
var response EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &response)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailGet, "1", &response)
if err != nil {
return getEmailsResult{}, "", err
}
if withThreads {
var threads ThreadGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, "2", &threads)
+ err = retrieveResponseMatchParameters(ctx, body, CommandThreadGet, "2", &threads)
if err != nil {
return getEmailsResult{}, "", err
}
@@ -91,17 +93,18 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
return result.emails, result.notFound, sessionState, state, language, gwerr
}
-func (j *Client) GetEmailBlobId(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (string, SessionState, State, Language, Error) {
- logger = j.logger("GetEmailBlobId", session, logger)
+func (j *Client) GetEmailBlobId(accountId string, id string, ctx Context) (string, SessionState, State, Language, Error) {
+ logger := j.logger("GetEmailBlobId", ctx)
+ ctx = ctx.WithLogger(logger)
get := EmailGetCommand{AccountId: accountId, Ids: []string{id}, FetchAllBodyValues: false, Properties: []string{"blobId"}}
- cmd, err := j.request(session, logger, NS_MAIL, invocation(get, "0"))
+ cmd, err := j.request(ctx, NS_MAIL, invocation(get, "0"))
if err != nil {
return "", "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (string, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (string, State, Error) {
var response EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "0", &response)
+ err = retrieveGet(ctx, body, get, "0", &response)
if err != nil {
return "", "", err
}
@@ -114,10 +117,13 @@ func (j *Client) GetEmailBlobId(accountId string, session *Session, ctx context.
}
// Retrieve all the Emails in a given Mailbox by its id.
-func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, offset int, limit uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool) (Emails, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("GetAllEmailsInMailbox", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOSONAR
+ offset int, limit uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool,
+ ctx Context) (Emails, 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)
})
+ ctx = ctx.WithLogger(logger)
query := EmailQueryCommand{
AccountId: accountId,
@@ -147,8 +153,9 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
invocation(get, "1"),
}
+ threads := ThreadGetRefCommand{}
if withThreads {
- threads := ThreadGetRefCommand{
+ threads = ThreadGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
ResultOf: "1",
@@ -159,19 +166,19 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
invocations = append(invocations, invocation(threads, "2"))
}
- cmd, err := j.request(session, logger, NS_MAIL, invocations...)
+ cmd, err := j.request(ctx, NS_MAIL, invocations...)
if err != nil {
return Emails{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Emails, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (Emails, State, Error) {
var queryResponse EmailQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, "0", &queryResponse)
+ err = retrieveQuery(ctx, body, query, "0", &queryResponse)
if err != nil {
return Emails{}, "", err
}
var getResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &getResponse)
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
if err != nil {
logger.Error().Err(err).Send()
return Emails{}, "", err
@@ -179,7 +186,7 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
if withThreads {
var thread ThreadGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, "2", &thread)
+ err = retrieveGet(ctx, body, threads, "2", &thread)
if err != nil {
return Emails{}, "", err
}
@@ -206,10 +213,13 @@ type EmailChanges struct {
// Retrieve the changes in Emails since a given State.
// @api:tags email,changes
-func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (EmailChanges, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("GetEmailChanges", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) GetEmailChanges(accountId string,
+ sinceState State, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint,
+ ctx Context) (EmailChanges, SessionState, State, Language, Error) { //NOSONAR
+ logger := j.loggerParams("GetEmailChanges", ctx, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies).Str(logSinceState, string(sinceState))
})
+ ctx = ctx.WithLogger(logger)
changes := EmailChangesCommand{
AccountId: accountId,
@@ -236,7 +246,7 @@ func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context
getUpdated.MaxBodyValueBytes = maxBodyValueBytes
}
- cmd, err := j.request(session, logger, NS_MAIL,
+ cmd, err := j.request(ctx, NS_MAIL,
invocation(changes, "0"),
invocation(getCreated, "1"),
invocation(getUpdated, "2"),
@@ -245,22 +255,22 @@ func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context
return EmailChanges{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (EmailChanges, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (EmailChanges, State, Error) {
var changesResponse EmailChangesResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailChanges, "0", &changesResponse)
+ err = retrieveChanges(ctx, body, changes, "0", &changesResponse)
if err != nil {
return EmailChanges{}, "", err
}
var createdResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &createdResponse)
+ err = retrieveGet(ctx, body, getCreated, "1", &createdResponse)
if err != nil {
logger.Error().Err(err).Send()
return EmailChanges{}, "", err
}
var updatedResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "2", &updatedResponse)
+ err = retrieveGet(ctx, body, getUpdated, "2", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return EmailChanges{}, "", err
@@ -291,10 +301,13 @@ type EmailSnippetQueryResult struct {
QueryState State `json:"queryState"`
}
-func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset int, limit uint) (map[string]EmailSnippetQueryResult, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("QueryEmailSnippets", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) QueryEmailSnippets(accountIds []string, //NOSONAR
+ filter EmailFilterElement, offset int, limit uint,
+ ctx Context) (map[string]EmailSnippetQueryResult, SessionState, State, Language, Error) {
+ logger := j.loggerParams("QueryEmailSnippets", ctx, func(z zerolog.Context) zerolog.Context {
return z.Uint(logLimit, limit).Int(logOffset, offset)
})
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
invocations := make([]Invocation, len(uniqueAccountIds)*3)
@@ -340,28 +353,28 @@ func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterEleme
invocations[i*3+2] = invocation(snippet, mcid(accountId, "2"))
}
- cmd, err := j.request(session, logger, NS_MAIL, invocations...)
+ cmd, err := j.request(ctx, NS_MAIL, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailSnippetQueryResult, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]EmailSnippetQueryResult, State, Error) {
results := make(map[string]EmailSnippetQueryResult, len(uniqueAccountIds))
for _, accountId := range uniqueAccountIds {
var queryResponse EmailQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
if err != nil {
return nil, "", err
}
var mailResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &mailResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailGet, mcid(accountId, "1"), &mailResponse)
if err != nil {
return nil, "", err
}
var snippetResponse SearchSnippetGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandSearchSnippetGet, mcid(accountId, "2"), &snippetResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandSearchSnippetGet, mcid(accountId, "2"), &snippetResponse)
if err != nil {
return nil, "", err
}
@@ -407,10 +420,13 @@ type EmailQueryResult struct {
QueryState State `json:"queryState"`
}
-func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset int, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryResult, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("QueryEmails", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) QueryEmails(accountIds []string,
+ filter EmailFilterElement, offset 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)
})
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
invocations := make([]Invocation, len(uniqueAccountIds)*2)
@@ -444,22 +460,22 @@ func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, ses
invocations[i*2+1] = invocation(mails, mcid(accountId, "1"))
}
- cmd, err := j.request(session, logger, NS_MAIL, invocations...)
+ cmd, err := j.request(ctx, NS_MAIL, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryResult, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]EmailQueryResult, State, Error) {
results := make(map[string]EmailQueryResult, len(uniqueAccountIds))
for _, accountId := range uniqueAccountIds {
var queryResponse EmailQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
if err != nil {
return nil, "", err
}
var emailsResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &emailsResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailGet, mcid(accountId, "1"), &emailsResponse)
if err != nil {
return nil, "", err
}
@@ -489,10 +505,13 @@ type EmailQueryWithSnippetsResult struct {
QueryState State `json:"queryState"`
}
-func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset int, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryWithSnippetsResult, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("QueryEmailsWithSnippets", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) QueryEmailsWithSnippets(accountIds []string, //NOSONAR
+ filter EmailFilterElement, offset int, limit uint, 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)
})
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
invocations := make([]Invocation, len(uniqueAccountIds)*3)
@@ -536,28 +555,28 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilter
invocations[i*3+2] = invocation(mails, mcid(accountId, "2"))
}
- cmd, err := j.request(session, logger, NS_MAIL, invocations...)
+ cmd, err := j.request(ctx, NS_MAIL, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryWithSnippetsResult, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]EmailQueryWithSnippetsResult, State, Error) {
result := make(map[string]EmailQueryWithSnippetsResult, len(uniqueAccountIds))
for _, accountId := range uniqueAccountIds {
var queryResponse EmailQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
if err != nil {
return nil, "", err
}
var snippetResponse SearchSnippetGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandSearchSnippetGet, mcid(accountId, "1"), &snippetResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandSearchSnippetGet, mcid(accountId, "1"), &snippetResponse)
if err != nil {
return nil, "", err
}
var emailsResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "2"), &emailsResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailGet, mcid(accountId, "2"), &emailsResponse)
if err != nil {
return nil, "", err
}
@@ -602,7 +621,7 @@ type UploadedEmail struct {
Sha512 string `json:"sha:512"`
}
-func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (UploadedEmail, SessionState, State, Language, Error) {
+func (j *Client) ImportEmail(accountId string, data []byte, ctx Context) (UploadedEmail, SessionState, State, Language, Error) {
encoded := base64.StdEncoding.EncodeToString(data)
upload := BlobUploadCommand{
@@ -627,7 +646,7 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
Properties: []string{BlobPropertyDigestSha512},
}
- cmd, err := j.request(session, logger, NS_MAIL,
+ cmd, err := j.request(ctx, NS_MAIL,
invocation(upload, "0"),
invocation(getHash, "1"),
)
@@ -635,32 +654,32 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
return UploadedEmail{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedEmail, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (UploadedEmail, State, Error) {
var uploadResponse BlobUploadResponse
- err = retrieveResponseMatchParameters(logger, body, CommandBlobUpload, "0", &uploadResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandBlobUpload, "0", &uploadResponse)
if err != nil {
return UploadedEmail{}, "", err
}
var getResponse BlobGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandBlobGet, "1", &getResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandBlobGet, "1", &getResponse)
if err != nil {
- logger.Error().Err(err).Send()
+ ctx.Logger.Error().Err(err).Send()
return UploadedEmail{}, "", err
}
if len(uploadResponse.Created) != 1 {
- logger.Error().Msgf("%T.Created has %v elements instead of 1", uploadResponse, len(uploadResponse.Created))
+ ctx.Logger.Error().Msgf("%T.Created has %v elements instead of 1", uploadResponse, len(uploadResponse.Created))
return UploadedEmail{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
upload, ok := uploadResponse.Created["0"]
if !ok {
- logger.Error().Msgf("%T.Created has no element '0'", uploadResponse)
+ ctx.Logger.Error().Msgf("%T.Created has no element '0'", uploadResponse)
return UploadedEmail{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
if len(getResponse.List) != 1 {
- logger.Error().Msgf("%T.List has %v elements instead of 1", getResponse, len(getResponse.List))
+ ctx.Logger.Error().Msgf("%T.List has %v elements instead of 1", getResponse, len(getResponse.List))
return UploadedEmail{}, "", jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
get := getResponse.List[0]
@@ -675,7 +694,7 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
}
-func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (*Email, SessionState, State, Language, Error) {
+func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId string, ctx Context) (*Email, SessionState, State, Language, Error) {
set := EmailSetCommand{
AccountId: accountId,
Create: map[string]EmailCreate{
@@ -686,16 +705,16 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
set.Destroy = []string{replaceId}
}
- cmd, err := j.request(session, logger, NS_MAIL,
+ cmd, err := j.request(ctx, NS_MAIL,
invocation(set, "0"),
)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*Email, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (*Email, State, Error) {
var setResponse EmailSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailSet, "0", &setResponse)
if err != nil {
return nil, "", err
}
@@ -707,14 +726,14 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
setErr, notok := setResponse.NotCreated["c"]
if notok {
- logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
+ ctx.Logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
return nil, "", setErrorError(setErr, EmailType)
}
created, ok := setResponse.Created["c"]
if !ok {
berr := fmt.Errorf("failed to find %s in %s response", EmailType, string(CommandEmailSet))
- logger.Error().Err(berr)
+ ctx.Logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
@@ -730,20 +749,19 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
// To create drafts, use the CreateEmail function instead.
//
// To delete mails, use the DeleteEmails function instead.
-func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]*Email, SessionState, State, Language, Error) {
- cmd, err := j.request(session, logger, NS_MAIL,
- invocation(EmailSetCommand{
- AccountId: accountId,
- Update: updates,
- }, "0"),
- )
+func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate, ctx Context) (map[string]*Email, SessionState, State, Language, Error) {
+ set := EmailSetCommand{
+ AccountId: accountId,
+ Update: updates,
+ }
+ cmd, err := j.request(ctx, NS_MAIL, invocation(set, "0"))
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]*Email, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]*Email, State, Error) {
var setResponse EmailSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
+ err = retrieveSet(ctx, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
@@ -757,20 +775,19 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate,
})
}
-func (j *Client) DeleteEmails(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
- cmd, err := j.request(session, logger, NS_MAIL,
- invocation(EmailSetCommand{
- AccountId: accountId,
- Destroy: destroy,
- }, "0"),
- )
+func (j *Client) DeleteEmails(accountId string, destroy []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
+ set := EmailSetCommand{
+ AccountId: accountId,
+ Destroy: destroy,
+ }
+ cmd, err := j.request(ctx, NS_MAIL, invocation(set, "0"))
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]SetError, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]SetError, State, Error) {
var setResponse EmailSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
+ err = retrieveSet(ctx, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
@@ -807,8 +824,10 @@ type MoveMail struct {
ToMailboxId string
}
-func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, move *MoveMail, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (EmailSubmission, SessionState, State, Language, Error) { //NOSONAR
- logger = j.logger("SubmitEmail", session, logger)
+func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, move *MoveMail, //NOSONAR
+ ctx Context) (EmailSubmission, SessionState, State, Language, Error) {
+ logger := j.logger("SubmitEmail", ctx)
+ ctx = ctx.WithLogger(logger)
update := map[string]any{
EmailPropertyKeywords + "/" + JmapKeywordDraft: nil, // unmark as draft
@@ -821,7 +840,7 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
id := "s0"
- set := EmailSubmissionSetCommand{
+ submit := EmailSubmissionSetCommand{
AccountId: accountId,
Create: map[string]EmailSubmissionCreate{
id: {
@@ -840,17 +859,17 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
Ids: []string{"#" + id},
}
- cmd, err := j.request(session, logger, NS_MAIL_SUBMISSION,
- invocation(set, "0"),
+ cmd, err := j.request(ctx, NS_MAIL_SUBMISSION,
+ invocation(submit, "0"),
invocation(get, "1"),
)
if err != nil {
return EmailSubmission{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (EmailSubmission, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (EmailSubmission, State, Error) {
var submissionResponse EmailSubmissionSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionSet, "0", &submissionResponse)
+ err = retrieveSet(ctx, body, submit, "0", &submissionResponse)
if err != nil {
return EmailSubmission{}, "", err
}
@@ -866,14 +885,14 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
// The response to this MUST be returned after the EmailSubmission/set response."
// from an example in the spec, it has the same tag as the EmailSubmission/set command ("0" in this case)
var setResponse EmailSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailSet, "0", &setResponse)
if err != nil {
return EmailSubmission{}, "", err
}
if len(setResponse.Updated) == 1 {
var getResponse EmailSubmissionGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "1", &getResponse)
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
if err != nil {
return EmailSubmission{}, "", err
}
@@ -898,20 +917,22 @@ type emailSubmissionResult struct {
notFound []string
}
-func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]EmailSubmission, []string, SessionState, State, Language, Error) {
- logger = j.logger("GetEmailSubmissionStatus", session, logger)
+func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []string, ctx Context) (map[string]EmailSubmission, []string, SessionState, State, Language, Error) {
+ logger := j.logger("GetEmailSubmissionStatus", ctx)
+ ctx = ctx.WithLogger(logger)
- cmd, err := j.request(session, logger, NS_MAIL_SUBMISSION, invocation(EmailSubmissionGetCommand{
+ get := EmailSubmissionGetCommand{
AccountId: accountId,
Ids: submissionIds,
- }, "0"))
+ }
+ cmd, err := j.request(ctx, NS_MAIL_SUBMISSION, invocation(get, "0"))
if err != nil {
return nil, nil, "", "", "", err
}
- result, sessionState, state, lang, err := command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (emailSubmissionResult, State, Error) {
+ result, sessionState, state, lang, err := command(j, ctx, cmd, func(body *Response) (emailSubmissionResult, State, Error) {
var response EmailSubmissionGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "0", &response)
+ err = retrieveGet(ctx, body, get, "0", &response)
if err != nil {
return emailSubmissionResult{}, "", err
}
@@ -925,34 +946,41 @@ func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []stri
return result.submissions, result.notFound, sessionState, state, lang, err
}
-func (j *Client) EmailsInThread(accountId string, threadId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, fetchBodies bool, maxBodyValueBytes uint) ([]Email, SessionState, State, Language, Error) { //NOSONAR
- logger = j.loggerParams("EmailsInThread", session, logger, func(z zerolog.Context) zerolog.Context {
+func (j *Client) EmailsInThread(accountId string, threadId string,
+ fetchBodies bool, maxBodyValueBytes uint,
+ ctx Context) ([]Email, SessionState, State, Language, Error) { //NOSONAR
+ logger := j.loggerParams("EmailsInThread", ctx, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies).Str("threadId", log.SafeString(threadId))
})
+ ctx = ctx.WithLogger(logger)
- cmd, err := j.request(session, logger, NS_MAIL,
- invocation(ThreadGetCommand{
- AccountId: accountId,
- Ids: []string{threadId},
- }, "0"),
- invocation(EmailGetRefCommand{
- AccountId: accountId,
- IdsRef: &ResultReference{
- ResultOf: "0",
- Name: CommandThreadGet,
- Path: "/list/*/emailIds",
- },
- FetchAllBodyValues: fetchBodies,
- MaxBodyValueBytes: maxBodyValueBytes,
- }, "1"),
+ thread := ThreadGetCommand{
+ AccountId: accountId,
+ Ids: []string{threadId},
+ }
+
+ get := EmailGetRefCommand{
+ AccountId: accountId,
+ IdsRef: &ResultReference{
+ ResultOf: "0",
+ Name: CommandThreadGet,
+ Path: "/list/*/emailIds",
+ },
+ FetchAllBodyValues: fetchBodies,
+ MaxBodyValueBytes: maxBodyValueBytes,
+ }
+
+ cmd, err := j.request(ctx, NS_MAIL,
+ invocation(thread, "0"),
+ invocation(get, "1"),
)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Email, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) ([]Email, State, Error) {
var emailsResponse EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &emailsResponse)
+ err = retrieveGet(ctx, body, get, "1", &emailsResponse)
if err != nil {
return nil, "", err
}
@@ -987,8 +1015,11 @@ var EmailSummaryProperties = []string{
EmailPropertyPreview,
}
-func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter EmailFilterElement, limit uint, withThreads bool) (map[string]EmailsSummary, SessionState, State, Language, Error) { //NOSONAR
- logger = j.logger("QueryEmailSummaries", session, logger)
+func (j *Client) QueryEmailSummaries(accountIds []string, //NOSONAR
+ filter EmailFilterElement, limit uint, withThreads bool,
+ ctx Context) (map[string]EmailsSummary, SessionState, State, Language, Error) {
+ logger := j.logger("QueryEmailSummaries", ctx)
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
@@ -1029,22 +1060,22 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
}, mcid(accountId, "2"))
}
}
- cmd, err := j.request(session, logger, NS_MAIL, invocations...)
+ cmd, err := j.request(ctx, NS_MAIL, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailsSummary, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]EmailsSummary, State, Error) {
resp := map[string]EmailsSummary{}
for _, accountId := range uniqueAccountIds {
var queryResponse EmailQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
if err != nil {
return nil, "", err
}
var response EmailGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &response)
+ err = retrieveResponseMatchParameters(ctx, body, CommandEmailGet, mcid(accountId, "1"), &response)
if err != nil {
return nil, "", err
}
@@ -1053,7 +1084,7 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
}
if withThreads {
var thread ThreadGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, mcid(accountId, "2"), &thread)
+ err = retrieveResponseMatchParameters(ctx, body, CommandThreadGet, mcid(accountId, "2"), &thread)
if err != nil {
return nil, "", err
}
@@ -1076,11 +1107,11 @@ type EmailSubmissionChanges = ChangesTemplate[EmailSubmission]
// Retrieve the changes in Email Submissions since a given State.
// @api:tags email,changes
-func (j *Client) GetEmailSubmissionChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (EmailSubmissionChanges, SessionState, State, Language, Error) {
+func (j *Client) GetEmailSubmissionChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (EmailSubmissionChanges, SessionState, State, Language, Error) {
return changes(j, "GetEmailSubmissionChanges", NS_MAIL_SUBMISSION,
func() EmailSubmissionChangesCommand {
- return EmailSubmissionChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return EmailSubmissionChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
EmailSubmissionChangesResponse{},
func(path string, rof string) EmailSubmissionGetRefCommand {
@@ -1104,7 +1135,7 @@ func (j *Client) GetEmailSubmissionChanges(accountId string, session *Session, c
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
diff --git a/pkg/jmap/api_identity.go b/pkg/jmap/api_identity.go
index 0ce515658c..12df2eb1a3 100644
--- a/pkg/jmap/api_identity.go
+++ b/pkg/jmap/api_identity.go
@@ -1,36 +1,36 @@
package jmap
import (
- "context"
"strconv"
- "github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/structs"
)
var NS_IDENTITY = ns(JmapMail)
-func (j *Client) GetAllIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) ([]Identity, SessionState, State, Language, Error) {
+func (j *Client) GetAllIdentities(accountId string, ctx Context) ([]Identity, SessionState, State, Language, Error) {
return getA(j, "GetAllIdentities", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId}
},
IdentityGetResponse{},
- accountId, session, ctx, logger, acceptLanguage, []string{},
+ accountId, []string{},
+ ctx,
)
}
-func (j *Client) GetIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identityIds []string) ([]Identity, SessionState, State, Language, Error) {
+func (j *Client) GetIdentities(accountId string, identityIds []string, ctx Context) ([]Identity, SessionState, State, Language, Error) {
return getA(j, "GetIdentities", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId, Ids: ids}
},
IdentityGetResponse{},
- accountId, session, ctx, logger, acceptLanguage, identityIds,
+ accountId, identityIds,
+ ctx,
)
}
-func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]Identity, SessionState, State, Language, Error) {
+func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, ctx Context) (map[string][]Identity, SessionState, State, Language, Error) {
return getN(j, "GetIdentitiesForAllAccounts", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId}
@@ -38,7 +38,8 @@ func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Sessi
IdentityGetResponse{},
func(resp IdentityGetResponse) []Identity { return resp.List },
identity1,
- accountIds, session, ctx, logger, acceptLanguage, []string{},
+ accountIds, []string{},
+ ctx,
)
}
@@ -48,10 +49,11 @@ type IdentitiesAndMailboxesGetResponse struct {
Mailboxes []Mailbox `json:"mailboxes"`
}
-func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (IdentitiesAndMailboxesGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds []string, ctx Context) (IdentitiesAndMailboxesGetResponse, SessionState, State, Language, Error) {
uniqueAccountIds := structs.Uniq(accountIds)
- logger = j.logger("GetIdentitiesAndMailboxes", session, logger)
+ logger := j.logger("GetIdentitiesAndMailboxes", ctx)
+ ctx = ctx.WithLogger(logger)
calls := make([]Invocation, len(uniqueAccountIds)+1)
calls[0] = invocation(MailboxGetCommand{AccountId: mailboxAccountId}, "0")
@@ -59,17 +61,17 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
calls[i+1] = invocation(IdentityGetCommand{AccountId: accountId}, strconv.Itoa(i+1))
}
- cmd, err := j.request(session, logger, NS_IDENTITY, calls...)
+ cmd, err := j.request(ctx, NS_IDENTITY, calls...)
if err != nil {
return IdentitiesAndMailboxesGetResponse{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentitiesAndMailboxesGetResponse, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (IdentitiesAndMailboxesGetResponse, State, Error) {
identities := make(map[string][]Identity, len(uniqueAccountIds))
stateByAccountId := make(map[string]State, len(uniqueAccountIds))
notFound := []string{}
for i, accountId := range uniqueAccountIds {
var response IdentityGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, strconv.Itoa(i+1), &response)
+ err = retrieveResponseMatchParameters(ctx, body, CommandIdentityGet, strconv.Itoa(i+1), &response)
if err != nil {
return IdentitiesAndMailboxesGetResponse{}, "", err
} else {
@@ -80,7 +82,7 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
}
var mailboxResponse MailboxGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, "0", &mailboxResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, "0", &mailboxResponse)
if err != nil {
return IdentitiesAndMailboxesGetResponse{}, "", err
}
@@ -93,91 +95,60 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
})
}
-func (j *Client) CreateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (Identity, SessionState, State, Language, Error) {
- logger = j.logger("CreateIdentity", session, logger)
- cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
- AccountId: accountId,
- Create: map[string]Identity{
- "c": identity,
+func (j *Client) CreateIdentity(accountId string, identity IdentityChange, ctx Context) (*Identity, SessionState, State, Language, Error) {
+ return create(j, "CreateIdentity", NS_IDENTITY,
+ func(accountId string, create map[string]IdentityChange) IdentitySetCommand {
+ return IdentitySetCommand{AccountId: accountId, Create: create}
},
- }, "0"))
- if err != nil {
- return Identity{}, "", "", "", err
- }
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identity, State, Error) {
- var response IdentitySetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
- if err != nil {
- return Identity{}, response.NewState, err
- }
- setErr, notok := response.NotCreated["c"]
- if notok {
- logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr) //NOSONAR
- return Identity{}, "", setErrorError(setErr, IdentityType)
- }
- return response.Created["c"], response.NewState, nil
- })
+ func(accountId string, ids string) IdentityGetCommand {
+ return IdentityGetCommand{AccountId: accountId, Ids: []string{ids}}
+ },
+ func(resp IdentitySetResponse) map[string]*Identity {
+ return resp.Created
+ },
+ func(resp IdentityGetResponse) []Identity {
+ return resp.List
+ },
+ accountId, identity,
+ ctx,
+ )
}
-func (j *Client) UpdateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (Identity, SessionState, State, Language, Error) {
- logger = j.logger("UpdateIdentity", session, logger)
- cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
- AccountId: accountId,
- Update: map[string]PatchObject{
- "c": identity.AsPatch(),
+func (j *Client) UpdateIdentity(accountId string, id string, changes IdentityChange, ctx Context) (Identity, SessionState, State, Language, Error) {
+ return update(j, "UpdateIdentity", NS_IDENTITY,
+ func(update map[string]PatchObject) IdentitySetCommand {
+ return IdentitySetCommand{AccountId: accountId, Update: update}
},
- }, "0"))
- if err != nil {
- return Identity{}, "", "", "", err
- }
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identity, State, Error) {
- var response IdentitySetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
- if err != nil {
- return Identity{}, response.NewState, err
- }
- setErr, notok := response.NotCreated["c"]
- if notok {
- logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr)
- return Identity{}, "", setErrorError(setErr, IdentityType)
- }
- return response.Created["c"], response.NewState, nil
- })
+ func(id string) IdentityGetCommand {
+ return IdentityGetCommand{AccountId: accountId, Ids: []string{id}}
+ },
+ func(resp IdentitySetResponse) map[string]SetError { return resp.NotUpdated },
+ func(resp IdentityGetResponse) Identity { return resp.List[0] },
+ id, changes,
+ ctx,
+ )
}
-func (j *Client) DeleteIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) ([]string, SessionState, State, Language, Error) {
- logger = j.logger("DeleteIdentity", session, logger)
- cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
- AccountId: accountId,
- Destroy: ids,
- }, "0"))
- if err != nil {
- return nil, "", "", "", err
- }
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]string, State, Error) {
- var response IdentitySetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
- if err != nil {
- return nil, "", err
- }
- for _, setErr := range response.NotDestroyed {
- // TODO only returning the first error here, we should probably aggregate them instead
- logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr)
- return nil, "", setErrorError(setErr, IdentityType)
- }
- return response.Destroyed, response.NewState, nil
- })
+func (j *Client) DeleteIdentity(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
+ return destroy(j, "DeleteIdentity", NS_IDENTITY,
+ func(accountId string, destroy []string) IdentitySetCommand {
+ return IdentitySetCommand{AccountId: accountId, Destroy: destroyIds}
+ },
+ IdentitySetResponse{},
+ accountId, destroyIds,
+ ctx,
+ )
}
type IdentityChanges = ChangesTemplate[Identity]
// Retrieve the changes in Email Identities since a given State.
// @api:tags email,changes
-func (j *Client) GetIdentityChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (IdentityChanges, SessionState, State, Language, Error) {
+func (j *Client) GetIdentityChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (IdentityChanges, SessionState, State, Language, Error) {
return changes(j, "GetIdentityChanges", NS_IDENTITY,
func() IdentityChangesCommand {
- return IdentityChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return IdentityChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
IdentityChangesResponse{},
func(path string, rof string) IdentityGetRefCommand {
@@ -201,6 +172,6 @@ func (j *Client) GetIdentityChanges(accountId string, session *Session, ctx cont
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
diff --git a/pkg/jmap/api_mailbox.go b/pkg/jmap/api_mailbox.go
index e96cbc1cea..7e845cf9c7 100644
--- a/pkg/jmap/api_mailbox.go
+++ b/pkg/jmap/api_mailbox.go
@@ -1,17 +1,14 @@
package jmap
import (
- "context"
- "fmt"
"slices"
- "github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/structs"
)
var NS_MAILBOX = ns(JmapMail)
-func (j *Client) GetMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (MailboxGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetMailbox(accountId string, ids []string, ctx Context) (MailboxGetResponse, SessionState, State, Language, Error) {
/*
return get(j, "GetMailbox", NS_MAILBOX,
func(accountId string, ids []string) MailboxGetCommand {
@@ -23,10 +20,10 @@ func (j *Client) GetMailbox(accountId string, session *Session, ctx context.Cont
)
*/
- return fget[Mailboxes](MAILBOX, j, "GetMailbox", accountId, ids, session, ctx, logger, acceptLanguage)
+ return fget[Mailboxes](MAILBOX, j, "GetMailbox", accountId, ids, ctx)
}
-func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]Mailbox, SessionState, State, Language, Error) {
+func (j *Client) GetAllMailboxes(accountIds []string, ctx Context) (map[string][]Mailbox, SessionState, State, Language, Error) {
/*
return getAN(j, "GetAllMailboxes", NS_MAILBOX,
func(accountId string, ids []string) MailboxGetCommand {
@@ -37,11 +34,12 @@ func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx cont
accountIds, session, ctx, logger, acceptLanguage, []string{},
)
*/
- return fgetAN[Mailboxes](MAILBOX, j, "GetAllMailboxes", identity1, accountIds, []string{}, session, ctx, logger, acceptLanguage)
+ return fgetAN[Mailboxes](MAILBOX, j, "GetAllMailboxes", identity1, accountIds, []string{}, ctx)
}
-func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter MailboxFilterElement) (map[string][]Mailbox, SessionState, State, Language, Error) {
- logger = j.logger("SearchMailboxes", session, logger)
+func (j *Client) SearchMailboxes(accountIds []string, filter MailboxFilterElement, ctx Context) (map[string][]Mailbox, SessionState, State, Language, Error) {
+ logger := j.logger("SearchMailboxes", ctx)
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
@@ -57,17 +55,17 @@ func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx cont
},
}, mcid(accountId, "1"))
}
- cmd, err := j.request(session, logger, NS_MAILBOX, invocations...)
+ cmd, err := j.request(ctx, NS_MAILBOX, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]Mailbox, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string][]Mailbox, State, Error) {
resp := map[string][]Mailbox{}
stateByAccountid := map[string]State{}
for _, accountId := range uniqueAccountIds {
var response MailboxGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "1"), &response)
+ err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, mcid(accountId, "1"), &response)
if err != nil {
return nil, "", err
}
@@ -79,8 +77,9 @@ func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx cont
})
}
-func (j *Client) SearchMailboxIdsPerRole(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, roles []string) (map[string]map[string]string, SessionState, State, Language, Error) { //NOSONAR
- logger = j.logger("SearchMailboxIdsPerRole", session, logger)
+func (j *Client) SearchMailboxIdsPerRole(accountIds []string, roles []string, ctx Context) (map[string]map[string]string, SessionState, State, Language, Error) { //NOSONAR
+ logger := j.logger("SearchMailboxIdsPerRole", ctx)
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
@@ -90,19 +89,19 @@ func (j *Client) SearchMailboxIdsPerRole(accountIds []string, session *Session,
invocations[i*len(roles)+j] = invocation(MailboxQueryCommand{AccountId: accountId, Filter: MailboxFilterCondition{Role: role}}, mcid(accountId, role))
}
}
- cmd, err := j.request(session, logger, NS_MAILBOX, invocations...)
+ cmd, err := j.request(ctx, NS_MAILBOX, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]map[string]string, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]map[string]string, State, Error) {
resp := map[string]map[string]string{}
stateByAccountid := map[string]State{}
for _, accountId := range uniqueAccountIds {
mailboxIdsByRole := map[string]string{}
for _, role := range roles {
var response MailboxQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxQuery, mcid(accountId, role), &response)
+ err = retrieveResponseMatchParameters(ctx, body, CommandMailboxQuery, mcid(accountId, role), &response)
if err != nil {
return nil, "", err
}
@@ -134,10 +133,11 @@ func newMailboxChanges(oldState, newState State, hasMoreChanges bool, created, u
// Retrieve Mailbox changes since a given state.
// @apidoc mailboxes,changes
-func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
+func (j *Client) GetMailboxChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (MailboxChanges, SessionState, State, Language, Error) {
return changesA(j, "GetMailboxChanges", NS_MAILBOX,
func() MailboxChangesCommand {
- return MailboxChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return MailboxChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
MailboxChangesResponse{},
MailboxGetResponse{},
@@ -152,17 +152,19 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
}
},
newMailboxChanges,
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
// Retrieve Mailbox changes of multiple Accounts.
// @api:tags email,changes
-func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]State, maxChanges uint) (map[string]MailboxChanges, SessionState, State, Language, Error) { //NOSONAR
+func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, //NOSONAR
+ sinceStateMap map[string]State, maxChanges uint,
+ ctx Context) (map[string]MailboxChanges, SessionState, State, Language, Error) {
return changesN(j, "GetMailboxChangesForMultipleAccounts", NS_MAILBOX,
accountIds, sinceStateMap,
func(accountId string, state State) MailboxChangesCommand {
- return MailboxChangesCommand{AccountId: accountId, SinceState: state, MaxChanges: posUIntPtr(maxChanges)}
+ return MailboxChangesCommand{AccountId: accountId, SinceState: state, MaxChanges: uintPtr(maxChanges)}
},
MailboxChangesResponse{},
func(accountId string, path string, ref string) MailboxGetRefCommand {
@@ -172,12 +174,13 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
newMailboxChanges,
identity1,
func(resp MailboxGetResponse) State { return resp.State },
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]string, SessionState, State, Language, Error) {
- logger = j.logger("GetMailboxRolesForMultipleAccounts", session, logger)
+func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, ctx Context) (map[string][]string, SessionState, State, Language, Error) {
+ logger := j.logger("GetMailboxRolesForMultipleAccounts", ctx)
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
n := len(uniqueAccountIds)
@@ -205,17 +208,17 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session
}, mcid(accountId, "1"))
}
- cmd, err := j.request(session, logger, NS_MAILBOX, invocations...)
+ cmd, err := j.request(ctx, NS_MAILBOX, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]string, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string][]string, State, Error) {
resp := make(map[string][]string, n)
stateByAccountId := make(map[string]State, n)
for _, accountId := range uniqueAccountIds {
var getResponse MailboxGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "1"), &getResponse)
+ err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, mcid(accountId, "1"), &getResponse)
if err != nil {
return nil, "", err
}
@@ -231,8 +234,9 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session
})
}
-func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]string, SessionState, State, Language, Error) {
- logger = j.logger("GetInboxNameForMultipleAccounts", session, logger)
+func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, ctx Context) (map[string]string, SessionState, State, Language, Error) {
+ logger := j.logger("GetInboxNameForMultipleAccounts", ctx)
+ ctx = ctx.WithLogger(logger)
uniqueAccountIds := structs.Uniq(accountIds)
n := len(uniqueAccountIds)
@@ -250,17 +254,17 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *S
}, mcid(accountId, "0"))
}
- cmd, err := j.request(session, logger, NS_MAILBOX, invocations...)
+ cmd, err := j.request(ctx, NS_MAILBOX, invocations...)
if err != nil {
return nil, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]string, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (map[string]string, State, Error) {
resp := make(map[string]string, n)
stateByAccountId := make(map[string]State, n)
for _, accountId := range uniqueAccountIds {
var r MailboxQueryResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "0"), &r)
+ err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, mcid(accountId, "0"), &r)
if err != nil {
return nil, "", err
}
@@ -280,89 +284,48 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *S
})
}
-func (j *Client) UpdateMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, ifInState string, update MailboxChange) (Mailbox, SessionState, State, Language, Error) { //NOSONAR
- logger = j.logger("UpdateMailbox", session, logger)
- cmd, err := j.request(session, logger, NS_MAILBOX, invocation(MailboxSetCommand{
- AccountId: accountId,
- IfInState: ifInState,
- Update: map[string]PatchObject{
- mailboxId: update.AsPatch(),
+func (j *Client) UpdateMailbox(accountId string, mailboxId string, change MailboxChange, //NOSONAR
+ ctx Context) (Mailbox, SessionState, State, Language, Error) {
+ return update(j, "UpdateMailbox", NS_MAILBOX,
+ func(update map[string]PatchObject) MailboxSetCommand {
+ return MailboxSetCommand{AccountId: accountId, Update: update}
},
- }, "0"))
- if err != nil {
- return Mailbox{}, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Mailbox, State, Error) {
- var setResp MailboxSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxSet, "0", &setResp)
- if err != nil {
- return Mailbox{}, "", err
- }
- setErr, notok := setResp.NotUpdated["u"]
- if notok {
- logger.Error().Msgf("%T.NotUpdated returned an error %v", setResp, setErr)
- return Mailbox{}, "", setErrorError(setErr, MailboxType)
- }
- return setResp.Updated["c"], setResp.NewState, nil
- })
-}
-
-func (j *Client) CreateMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ifInState string, create MailboxChange) (Mailbox, SessionState, State, Language, Error) {
- logger = j.logger("CreateMailbox", session, logger)
- cmd, err := j.request(session, logger, NS_MAILBOX, invocation(MailboxSetCommand{
- AccountId: accountId,
- IfInState: ifInState,
- Create: map[string]MailboxChange{
- "c": create,
+ func(id string) MailboxGetCommand {
+ return MailboxGetCommand{AccountId: accountId, Ids: []string{id}}
},
- }, "0"))
- if err != nil {
- return Mailbox{}, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Mailbox, State, Error) {
- var setResp MailboxSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandMailboxSet, "0", &setResp)
- if err != nil {
- return Mailbox{}, "", err
- }
- setErr, notok := setResp.NotCreated["c"]
- if notok {
- logger.Error().Msgf("%T.NotCreated returned an error %v", setResp, setErr)
- return Mailbox{}, "", setErrorError(setErr, MailboxType)
- }
- if mailbox, ok := setResp.Created["c"]; ok {
- return mailbox, setResp.NewState, nil
- } else {
- return Mailbox{}, "", jmapError(fmt.Errorf("failed to find created %T in response", Mailbox{}), JmapErrorMissingCreatedObject)
- }
- })
+ func(resp MailboxSetResponse) map[string]SetError { return resp.NotUpdated },
+ func(resp MailboxGetResponse) Mailbox { return resp.List[0] },
+ mailboxId, change,
+ ctx,
+ )
}
-func (j *Client) DeleteMailboxes(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ifInState string, mailboxIds []string) ([]string, SessionState, State, Language, Error) {
- logger = j.logger("DeleteMailbox", session, logger)
- set := MailboxSetCommand{
- AccountId: accountId,
- IfInState: ifInState,
- Destroy: mailboxIds,
- }
- cmd, err := j.request(session, logger, NS_MAILBOX, invocation(set, "0"))
- if err != nil {
- return nil, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]string, State, Error) {
- var setResp MailboxSetResponse
- err = retrieveSet(logger, body, set, "0", &setResp)
- if err != nil {
- return nil, "", err
- }
- setErr, notok := setResp.NotDestroyed["u"]
- if notok {
- logger.Error().Msgf("%T.NotDestroyed returned an error %v", setResp, setErr)
- return nil, "", setErrorError(setErr, MailboxType)
- }
- return setResp.Destroyed, setResp.NewState, nil
- })
+func (j *Client) CreateMailbox(accountId string, mailbox MailboxChange, ctx Context) (*Mailbox, SessionState, State, Language, Error) {
+ return create(j, "CreateMailbox", NS_MAILBOX,
+ func(accountId string, create map[string]MailboxChange) MailboxSetCommand {
+ return MailboxSetCommand{AccountId: accountId, Create: create}
+ },
+ func(accountId string, ids string) MailboxGetCommand {
+ return MailboxGetCommand{AccountId: accountId, Ids: []string{ids}}
+ },
+ func(resp MailboxSetResponse) map[string]*Mailbox {
+ return resp.Created
+ },
+ func(resp MailboxGetResponse) []Mailbox {
+ return resp.List
+ },
+ accountId, mailbox,
+ ctx,
+ )
+}
+
+func (j *Client) DeleteMailboxes(accountId string, destroyIds []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
+ return destroy(j, "DeleteMailboxes", NS_MAILBOX,
+ func(accountId string, destroy []string) MailboxSetCommand {
+ return MailboxSetCommand{AccountId: accountId, Destroy: destroyIds}
+ },
+ MailboxSetResponse{},
+ accountId, destroyIds,
+ ctx,
+ )
}
diff --git a/pkg/jmap/api_objects.go b/pkg/jmap/api_objects.go
index 6e07bb1b6a..1d8c7a0d1a 100644
--- a/pkg/jmap/api_objects.go
+++ b/pkg/jmap/api_objects.go
@@ -1,8 +1,6 @@
package jmap
import (
- "context"
-
"github.com/opencloud-eu/opencloud/pkg/log"
)
@@ -22,14 +20,15 @@ type Objects struct {
// Retrieve objects of all types by their identifiers in a single batch.
// @api:tags changes
-func (j *Client) GetObjects(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, //NOSONAR
+func (j *Client) GetObjects(accountId string, //NOSONAR
mailboxIds []string, emailIds []string,
addressbookIds []string, contactIds []string,
calendarIds []string, eventIds []string,
quotaIds []string, identityIds []string,
emailSubmissionIds []string,
+ ctx Context,
) (Objects, SessionState, State, Language, Error) {
- l := j.logger("GetObjects", session, logger).With()
+ l := j.logger("GetObjects", ctx).With()
if len(mailboxIds) > 0 {
l = l.Array("mailboxIds", log.SafeStringArray(mailboxIds))
}
@@ -57,7 +56,8 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
if len(emailSubmissionIds) > 0 {
l = l.Array("emailSubmissionIds", log.SafeStringArray(emailSubmissionIds))
}
- logger = log.From(l)
+ logger := log.From(l)
+ ctx = ctx.WithLogger(logger)
methodCalls := []Invocation{}
if len(mailboxIds) > 0 {
@@ -88,17 +88,17 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
methodCalls = append(methodCalls, invocation(EmailSubmissionGetCommand{AccountId: accountId, Ids: emailSubmissionIds}, "emailSubmissionIds"))
}
- cmd, err := j.request(session, logger, NS_OBJECTS, methodCalls...)
+ cmd, err := j.request(ctx, NS_OBJECTS, methodCalls...)
if err != nil {
return Objects{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Objects, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (Objects, State, Error) {
objs := Objects{}
states := map[string]State{}
var mailboxes MailboxGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandMailboxGet, "mailboxes", &mailboxes); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandMailboxGet, "mailboxes", &mailboxes); err != nil {
return Objects{}, "", err
} else if ok {
objs.Mailboxes = &mailboxes
@@ -106,7 +106,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var emails EmailGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandEmailGet, "emails", &emails); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandEmailGet, "emails", &emails); err != nil {
return Objects{}, "", err
} else if ok {
objs.Emails = &emails
@@ -114,7 +114,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var calendars CalendarGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandCalendarGet, "calendars", &calendars); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandCalendarGet, "calendars", &calendars); err != nil {
return Objects{}, "", err
} else if ok {
objs.Calendars = &calendars
@@ -122,7 +122,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var events CalendarEventGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandCalendarEventGet, "events", &events); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandCalendarEventGet, "events", &events); err != nil {
return Objects{}, "", err
} else if ok {
objs.Events = &events
@@ -130,7 +130,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var addressbooks AddressBookGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandAddressBookGet, "addressbooks", &addressbooks); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandAddressBookGet, "addressbooks", &addressbooks); err != nil {
return Objects{}, "", err
} else if ok {
objs.Addressbooks = &addressbooks
@@ -138,7 +138,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var contacts ContactCardGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandContactCardGet, "contacts", &contacts); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandContactCardGet, "contacts", &contacts); err != nil {
return Objects{}, "", err
} else if ok {
objs.Contacts = &contacts
@@ -146,7 +146,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var quotas QuotaGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandQuotaGet, "quotas", "as); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandQuotaGet, "quotas", "as); err != nil {
return Objects{}, "", err
} else if ok {
objs.Quotas = "as
@@ -154,7 +154,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var identities IdentityGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandIdentityGet, "identities", &identities); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandIdentityGet, "identities", &identities); err != nil {
return Objects{}, "", err
} else if ok {
objs.Identities = &identities
@@ -162,7 +162,7 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
}
var submissions EmailSubmissionGetResponse
- if ok, err := tryRetrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "submissions", &submissions); err != nil {
+ if ok, err := tryRetrieveResponseMatchParameters(ctx, body, CommandEmailSubmissionGet, "submissions", &submissions); err != nil {
return Objects{}, "", err
} else if ok {
objs.EmailSubmissions = &submissions
diff --git a/pkg/jmap/api_principal.go b/pkg/jmap/api_principal.go
index 9eb42df7e0..512c0559df 100644
--- a/pkg/jmap/api_principal.go
+++ b/pkg/jmap/api_principal.go
@@ -1,37 +1,50 @@
package jmap
-import (
- "context"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
-)
-
var NS_PRINCIPALS = ns(JmapPrincipals)
-type PrincipalsResponse struct {
- Principals []Principal `json:"principals"`
- NotFound []string `json:"notFound,omitempty"`
-}
-
-func (j *Client) GetPrincipals(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (PrincipalsResponse, SessionState, State, Language, Error) {
- logger = j.logger("GetPrincipals", session, logger)
-
- cmd, err := j.request(session, logger, NS_PRINCIPALS,
- invocation(PrincipalGetCommand{AccountId: accountId, Ids: ids}, "0"),
+func (j *Client) GetPrincipals(accountId string, ids []string, ctx Context) (PrincipalGetResponse, SessionState, State, Language, Error) {
+ return get(j, "GetPrincipals", NS_PRINCIPALS,
+ func(accountId string, ids []string) PrincipalGetCommand {
+ return PrincipalGetCommand{AccountId: accountId, Ids: ids}
+ },
+ PrincipalGetResponse{},
+ identity1,
+ accountId, ids,
+ ctx,
+ )
+}
+
+type PrincipalSearchResults SearchResultsTemplate[Principal]
+
+var _ SearchResults[Principal] = PrincipalSearchResults{}
+
+func (r PrincipalSearchResults) GetResults() []Principal { return r.Results }
+func (r PrincipalSearchResults) GetCanCalculateChanges() bool { return r.CanCalculateChanges }
+func (r PrincipalSearchResults) GetPosition() uint { return r.Position }
+func (r PrincipalSearchResults) GetLimit() uint { return r.Limit }
+func (r PrincipalSearchResults) GetTotal() *uint { return r.Total }
+
+func (j *Client) QueryPrincipals(accountId string,
+ filter PrincipalFilterElement, sortBy []PrincipalComparator,
+ position uint, limit uint, calculateTotal bool,
+ ctx Context) (PrincipalSearchResults, SessionState, State, Language, Error) {
+ return query(j, "QueryPrincipals", NS_PRINCIPALS,
+ []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(cmd Command, path string, rof string) PrincipalGetRefCommand {
+ return PrincipalGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}}
+ },
+ func(query PrincipalQueryResponse, get PrincipalGetResponse) PrincipalSearchResults {
+ return PrincipalSearchResults{
+ Results: get.List,
+ CanCalculateChanges: query.CanCalculateChanges,
+ Position: query.Position,
+ Total: uintPtrIf(query.Total, calculateTotal),
+ Limit: query.Limit,
+ }
+ },
+ filter, sortBy, limit, position, ctx,
)
- if err != nil {
- return PrincipalsResponse{}, "", "", "", err
- }
-
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (PrincipalsResponse, State, Error) {
- var response PrincipalGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandPrincipalGet, "0", &response)
- if err != nil {
- return PrincipalsResponse{}, response.State, err
- }
- return PrincipalsResponse{
- Principals: response.List,
- NotFound: response.NotFound,
- }, response.State, nil
- })
}
diff --git a/pkg/jmap/api_quota.go b/pkg/jmap/api_quota.go
index 1f8c8deba2..4aca032636 100644
--- a/pkg/jmap/api_quota.go
+++ b/pkg/jmap/api_quota.go
@@ -1,14 +1,8 @@
package jmap
-import (
- "context"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
-)
-
var NS_QUOTA = ns(JmapQuota)
-func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]QuotaGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetQuotas(accountIds []string, ctx Context) (map[string]QuotaGetResponse, SessionState, State, Language, Error) {
return getN(j, "GetQuotas", NS_QUOTA,
func(accountId string, ids []string) QuotaGetCommand {
return QuotaGetCommand{AccountId: accountId}
@@ -16,7 +10,8 @@ func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Co
QuotaGetResponse{},
identity1,
identity1,
- accountIds, session, ctx, logger, acceptLanguage, []string{},
+ accountIds, []string{},
+ ctx,
)
}
@@ -24,11 +19,11 @@ type QuotaChanges = ChangesTemplate[Quota]
// Retrieve the changes in Quotas since a given State.
// @api:tags quota,changes
-func (j *Client) GetQuotaChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (QuotaChanges, SessionState, State, Language, Error) {
+func (j *Client) GetQuotaChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (QuotaChanges, SessionState, State, Language, Error) {
return changesA(j, "GetQuotaChanges", NS_QUOTA,
func() QuotaChangesCommand {
- return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
QuotaChangesResponse{},
QuotaGetResponse{},
@@ -52,15 +47,15 @@ func (j *Client) GetQuotaChanges(accountId string, session *Session, ctx context
Destroyed: destroyed,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
-func (j *Client) GetQuotaUsageChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
- acceptLanguage string, sinceState State, maxChanges uint) (QuotaChanges, SessionState, State, Language, Error) {
+func (j *Client) GetQuotaUsageChanges(accountId string, sinceState State, maxChanges uint,
+ ctx Context) (QuotaChanges, SessionState, State, Language, Error) {
return updates(j, "GetQuotaUsageChanges", NS_QUOTA,
func() QuotaChangesCommand {
- return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
+ return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)}
},
QuotaChangesResponse{},
func(path string, rof string) QuotaGetRefCommand {
@@ -87,6 +82,6 @@ func (j *Client) GetQuotaUsageChanges(accountId string, session *Session, ctx co
Updated: updated,
}
},
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
diff --git a/pkg/jmap/api_vacation.go b/pkg/jmap/api_vacation.go
index f513ec0ff6..6b1ae9acaf 100644
--- a/pkg/jmap/api_vacation.go
+++ b/pkg/jmap/api_vacation.go
@@ -1,11 +1,8 @@
package jmap
import (
- "context"
"fmt"
"time"
-
- "github.com/opencloud-eu/opencloud/pkg/log"
)
var NS_VACATION = ns(JmapVacationResponse)
@@ -14,19 +11,20 @@ const (
vacationResponseId = "singleton"
)
-func (j *Client) GetVacationResponse(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponseGetResponse, SessionState, State, Language, Error) {
+func (j *Client) GetVacationResponse(accountId string, ctx Context) (VacationResponseGetResponse, SessionState, State, Language, Error) {
return get(j, "GetVacationResponse", NS_VACATION,
func(accountId string, ids []string) VacationResponseGetCommand {
return VacationResponseGetCommand{AccountId: accountId}
},
VacationResponseGetResponse{},
identity1,
- accountId, session, ctx, logger, acceptLanguage, []string{},
+ accountId, []string{},
+ ctx,
)
}
// Same as VacationResponse but without the id.
-type VacationResponsePayload struct {
+type VacationResponseChange struct {
// Should a vacation response be sent if a message arrives between the "fromDate" and "toDate"?
IsEnabled bool `json:"isEnabled"`
// If "isEnabled" is true, messages that arrive on or after this date-time (but before the "toDate" if defined) should receive the
@@ -49,33 +47,39 @@ type VacationResponsePayload struct {
HtmlBody string `json:"htmlBody,omitempty"`
}
-func (j *Client) SetVacationResponse(accountId string, vacation VacationResponsePayload, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponse, SessionState, State, Language, Error) {
- logger = j.logger("SetVacationResponse", session, logger)
+func (j *Client) SetVacationResponse(accountId string, vacation VacationResponseChange,
+ ctx Context) (VacationResponse, SessionState, State, Language, Error) {
+ logger := j.logger("SetVacationResponse", ctx)
+ ctx = ctx.WithLogger(logger)
- cmd, err := j.request(session, logger, NS_VACATION,
- invocation(VacationResponseSetCommand{
- AccountId: accountId,
- Create: map[string]VacationResponse{
- vacationResponseId: {
- IsEnabled: vacation.IsEnabled,
- FromDate: vacation.FromDate,
- ToDate: vacation.ToDate,
- Subject: vacation.Subject,
- TextBody: vacation.TextBody,
- HtmlBody: vacation.HtmlBody,
- },
+ set := VacationResponseSetCommand{
+ AccountId: accountId,
+ Create: map[string]VacationResponse{
+ vacationResponseId: {
+ IsEnabled: vacation.IsEnabled,
+ FromDate: vacation.FromDate,
+ ToDate: vacation.ToDate,
+ Subject: vacation.Subject,
+ TextBody: vacation.TextBody,
+ HtmlBody: vacation.HtmlBody,
},
- }, "0"),
+ },
+ }
+
+ get := VacationResponseGetCommand{AccountId: accountId}
+
+ cmd, err := j.request(ctx, NS_VACATION,
+ invocation(set, "0"),
// chain a second request to get the current complete VacationResponse object
// after performing the changes, as that makes for a better API
- invocation(VacationResponseGetCommand{AccountId: accountId}, "1"),
+ invocation(get, "1"),
)
if err != nil {
return VacationResponse{}, "", "", "", err
}
- return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (VacationResponse, State, Error) {
+ return command(j, ctx, cmd, func(body *Response) (VacationResponse, State, Error) {
var setResponse VacationResponseSetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandVacationResponseSet, "0", &setResponse)
+ err = retrieveSet(ctx, body, set, "0", &setResponse)
if err != nil {
return VacationResponse{}, "", err
}
@@ -88,7 +92,7 @@ func (j *Client) SetVacationResponse(accountId string, vacation VacationResponse
}
var getResponse VacationResponseGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandVacationResponseGet, "1", &getResponse)
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
if err != nil {
return VacationResponse{}, "", err
}
diff --git a/pkg/jmap/client.go b/pkg/jmap/client.go
index 81b05bfccf..e7380b8139 100644
--- a/pkg/jmap/client.go
+++ b/pkg/jmap/client.go
@@ -22,13 +22,27 @@ type Client struct {
WsPushListener
}
+type ApiSupplier interface {
+ Api() ApiClient
+}
+
+type Hooks interface {
+ OnSessionOutdated(session *Session, newState SessionState)
+}
+
var _ io.Closer = &Client{}
var _ WsPushListener = &Client{}
+var _ ApiSupplier = &Client{}
+var _ Hooks = &Client{}
func (j *Client) Close() error {
return errors.Join(j.api.Close(), j.session.Close(), j.blob.Close(), j.ws.Close())
}
+func (j *Client) Api() ApiClient {
+ return j.api
+}
+
func NewClient(session SessionClient, api ApiClient, blob BlobClient, ws WsClientFactory) Client {
return Client{
session: session,
@@ -44,7 +58,7 @@ func (j *Client) AddSessionEventListener(listener SessionEventListener) {
j.sessionEventListeners.add(listener)
}
-func (j *Client) onSessionOutdated(session *Session, newSessionState SessionState) {
+func (j *Client) OnSessionOutdated(session *Session, newSessionState SessionState) {
j.sessionEventListeners.signal(func(listener SessionEventListener) {
listener.OnSessionOutdated(session, newSessionState)
})
@@ -65,25 +79,25 @@ func (j *Client) FetchSession(ctx context.Context, sessionUrl *url.URL, username
return newSession(wk)
}
-func (j *Client) logger(operation string, _ *Session, logger *log.Logger) *log.Logger {
- l := logger.With().Str(logOperation, operation)
+func (j *Client) logger(operation string, ctx Context) *log.Logger {
+ l := ctx.Logger.With().Str(logOperation, operation)
return log.From(l)
}
-func (j *Client) loggerParams(operation string, _ *Session, logger *log.Logger, params func(zerolog.Context) zerolog.Context) *log.Logger {
- l := logger.With().Str(logOperation, operation)
+func (j *Client) loggerParams(operation string, ctx Context, params func(zerolog.Context) zerolog.Context) *log.Logger {
+ l := ctx.Logger.With().Str(logOperation, operation)
if params != nil {
l = params(l)
}
return log.From(l)
}
-func (j *Client) maxCallsCheck(calls int, session *Session, logger *log.Logger) Error {
- if calls > session.Capabilities.Core.MaxCallsInRequest {
- logger.Error().
- Int("max-calls-in-request", session.Capabilities.Core.MaxCallsInRequest).
+func (j *Client) maxCallsCheck(calls int, ctx Context) Error {
+ if calls > ctx.Session.Capabilities.Core.MaxCallsInRequest {
+ ctx.Logger.Error().
+ Int("max-calls-in-request", ctx.Session.Capabilities.Core.MaxCallsInRequest).
Int("calls-in-request", calls).
- Msgf("number of calls in request payload (%d) exceeds the allowed maximum (%d)", session.Capabilities.Core.MaxCallsInRequest, calls)
+ Msgf("number of calls in request payload (%d) exceeds the allowed maximum (%d)", ctx.Session.Capabilities.Core.MaxCallsInRequest, calls)
return jmapError(errTooManyMethodCalls, JmapErrorTooManyMethodCalls)
}
return nil
@@ -92,8 +106,8 @@ func (j *Client) maxCallsCheck(calls int, session *Session, logger *log.Logger)
// Construct a Request from the given list of Invocation objects.
//
// If an issue occurs, then it is logged prior to returning it.
-func (j *Client) request(session *Session, logger *log.Logger, using []JmapNamespace, methodCalls ...Invocation) (Request, Error) {
- err := j.maxCallsCheck(len(methodCalls), session, logger)
+func (j *Client) request(ctx Context, using []JmapNamespace, methodCalls ...Invocation) (Request, Error) {
+ err := j.maxCallsCheck(len(methodCalls), ctx)
if err != nil {
return Request{}, err
}
diff --git a/pkg/jmap/http.go b/pkg/jmap/http.go
index 89b3a8086b..ec337291a8 100644
--- a/pkg/jmap/http.go
+++ b/pkg/jmap/http.go
@@ -217,7 +217,12 @@ func (h *HttpJmapClient) GetSession(ctx context.Context, sessionUrl *url.URL, us
return data, nil
}
-func (h *HttpJmapClient) Command(ctx context.Context, logger *log.Logger, session *Session, request Request, acceptLanguage string) ([]byte, Language, Error) { //NOSONAR
+func (h *HttpJmapClient) Command(request Request, ctx Context) ([]byte, Language, Error) { //NOSONAR
+ session := ctx.Session
+ logger := ctx.Logger
+ acceptLanguage := ctx.AcceptLanguage
+ cotx := ctx.Context
+
jmapUrl := session.JmapUrl.String()
endpoint := session.JmapEndpoint
logger = log.From(logger.With().Str(logEndpoint, endpoint))
@@ -228,7 +233,7 @@ func (h *HttpJmapClient) Command(ctx context.Context, logger *log.Logger, sessio
return nil, "", jmapError(err, JmapErrorEncodingRequestBody)
}
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, jmapUrl, bytes.NewBuffer(bodyBytes))
+ req, err := http.NewRequestWithContext(cotx, http.MethodPost, jmapUrl, bytes.NewBuffer(bodyBytes))
if err != nil {
logger.Error().Err(err).Msgf("failed to create POST request for %v", jmapUrl)
return nil, "", jmapError(err, JmapErrorCreatingRequest)
@@ -249,7 +254,7 @@ func (h *HttpJmapClient) Command(ctx context.Context, logger *log.Logger, sessio
logger.Trace().Str(logEndpoint, endpoint).Str(logProto, logProtoJmap).Str(logType, logTypeRequest).Msg(string(requestBytes))
}
}
- if err := h.auth(ctx, session.Username, logger, req); err != nil {
+ if err := h.auth(cotx, session.Username, logger, req); err != nil {
return nil, "", err
}
@@ -295,10 +300,15 @@ func (h *HttpJmapClient) Command(ctx context.Context, logger *log.Logger, sessio
return body, language, nil
}
-func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, session *Session, uploadUrl string, endpoint string, contentType string, acceptLanguage string, body io.Reader) (UploadedBlob, Language, Error) { //NOSONAR
+func (h *HttpJmapClient) UploadBinary(uploadUrl string, endpoint string, contentType string, body io.Reader, ctx Context) (UploadedBlob, Language, Error) { //NOSONAR
+ session := ctx.Session
+ logger := ctx.Logger
+ acceptLanguage := ctx.AcceptLanguage
+ cotx := ctx.Context
+
logger = log.From(logger.With().Str(logEndpoint, endpoint))
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadUrl, body)
+ req, err := http.NewRequestWithContext(cotx, http.MethodPost, uploadUrl, body)
if err != nil {
logger.Error().Err(err).Msgf("failed to create POST request for %v", uploadUrl)
return UploadedBlob{}, "", jmapError(err, JmapErrorCreatingRequest)
@@ -315,7 +325,7 @@ func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, s
}
}
- if err := h.auth(ctx, session.Username, logger, req); err != nil {
+ if err := h.auth(cotx, session.Username, logger, req); err != nil {
return UploadedBlob{}, "", err
}
@@ -357,8 +367,6 @@ func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, s
return UploadedBlob{}, language, jmapError(err, JmapErrorServerResponse)
}
- logger.Trace()
-
var result UploadedBlob
err = json.Unmarshal(responseBody, &result)
if err != nil {
@@ -370,10 +378,15 @@ func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, s
return result, language, nil
}
-func (h *HttpJmapClient) DownloadBinary(ctx context.Context, logger *log.Logger, session *Session, downloadUrl string, endpoint string, acceptLanguage string) (*BlobDownload, Language, Error) { //NOSONAR
+func (h *HttpJmapClient) DownloadBinary(downloadUrl string, endpoint string, ctx Context) (*BlobDownload, Language, Error) { //NOSONAR
+ session := ctx.Session
+ logger := ctx.Logger
+ acceptLanguage := ctx.AcceptLanguage
+ cotx := ctx.Context
+
logger = log.From(logger.With().Str(logEndpoint, endpoint))
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadUrl, nil)
+ req, err := http.NewRequestWithContext(cotx, http.MethodGet, downloadUrl, nil)
if err != nil {
logger.Error().Err(err).Msgf("failed to create GET request for %v", downloadUrl)
return nil, "", jmapError(err, JmapErrorCreatingRequest)
@@ -389,7 +402,7 @@ func (h *HttpJmapClient) DownloadBinary(ctx context.Context, logger *log.Logger,
}
}
- if err := h.auth(ctx, session.Username, logger, req); err != nil {
+ if err := h.auth(cotx, session.Username, logger, req); err != nil {
return nil, "", err
}
diff --git a/pkg/jmap/integration_contact_test.go b/pkg/jmap/integration_contact_test.go
index 37d60bef65..ef9cef8f6e 100644
--- a/pkg/jmap/integration_contact_test.go
+++ b/pkg/jmap/integration_contact_test.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
golog "log"
"math/rand"
"regexp"
@@ -20,7 +19,6 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/brianvoe/gofakeit/v7"
"github.com/opencloud-eu/opencloud/pkg/jscontact"
- "github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/structs"
)
@@ -45,17 +43,17 @@ func TestAddressBooks(t *testing.T) {
func(session *Session) string { return session.PrimaryAccounts.Contacts },
list,
getid,
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (AddressBookGetResponse, SessionState, State, Language, Error) {
- return s.client.GetAddressbooks(accountId, session, ctx, logger, acceptLanguage, ids)
+ func(s *StalwartTest, accountId string, ids []string, ctx Context) (AddressBookGetResponse, SessionState, State, Language, Error) {
+ return s.client.GetAddressbooks(accountId, ids, ctx)
},
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string, change AddressBookChange) (AddressBook, SessionState, State, Language, Error) { //NOSONAR
- return s.client.UpdateAddressBook(accountId, session, ctx, logger, acceptLanguage, id, change)
+ func(s *StalwartTest, accountId string, id string, change AddressBookChange, ctx Context) (AddressBook, SessionState, State, Language, Error) { //NOSONAR
+ return s.client.UpdateAddressBook(accountId, id, change, ctx)
},
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (map[string]SetError, SessionState, State, Language, Error) { //NOSONAR
- return s.client.DeleteAddressBook(accountId, ids, session, ctx, logger, acceptLanguage)
+ func(s *StalwartTest, accountId string, ids []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) { //NOSONAR
+ return s.client.DeleteAddressBook(accountId, ids, ctx)
},
- func(s *StalwartTest, t *testing.T, accountId string, count uint, session *Session, user User, principalIds []string) (AddressBookBoxes, []AddressBook, SessionState, State, error) {
- return s.fillAddressBook(t, accountId, count, session, user, principalIds)
+ func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, user User, principalIds []string) (AddressBookBoxes, []AddressBook, SessionState, State, error) {
+ return s.fillAddressBook(t, accountId, count, ctx, user, principalIds)
},
func(orig AddressBook) AddressBookChange {
return AddressBookChange{
@@ -86,6 +84,7 @@ func TestContacts(t *testing.T) {
user := pickUser()
session := s.Session(user.name)
+ ctx := s.Context(session)
accountId, addressbookId, expectedContactCardsById, boxes, err := s.fillContacts(t, count, session, user)
require.NoError(err)
@@ -99,15 +98,19 @@ func TestContacts(t *testing.T) {
{Property: ContactCardPropertyCreated, IsAscending: true},
}
- contactsByAccount, _, _, _, err := s.client.QueryContactCards([]string{accountId}, session, t.Context(), s.logger, "", filter, sortBy, 0, 0)
+ contactsByAccount, _, _, _, err := s.client.QueryContactCards([]string{accountId}, filter, sortBy, 0, 0, true, ctx)
require.NoError(err)
require.Len(contactsByAccount, 1)
require.Contains(contactsByAccount, accountId)
- contacts := contactsByAccount[accountId]
- require.Len(contacts, int(count))
+ results := contactsByAccount[accountId]
+ require.Len(results.Results, int(count))
+ require.Equal(uint(0), results.Limit)
+ require.Equal(uint(0), results.Position)
+ require.Equal(uint(0), results.Total)
+ require.Equal(true, results.CanCalculateChanges)
- for _, actual := range contacts {
+ for _, actual := range results.Results {
expected, ok := expectedContactCardsById[actual.Id]
require.True(ok, "failed to find created contact by its id")
matchContact(t, actual, expected)
@@ -115,13 +118,13 @@ func TestContacts(t *testing.T) {
// retrieve all objects at once
{
- ids := structs.Map(contacts, func(c ContactCard) string { return c.Id })
- fetched, _, _, _, err := s.client.GetContactCards(accountId, session, t.Context(), s.logger, "", ids)
+ ids := structs.Map(results.Results, func(c ContactCard) string { return c.Id })
+ fetched, _, _, _, err := s.client.GetContactCards(accountId, ids, ctx)
require.NoError(err)
require.Empty(fetched.NotFound)
require.Len(fetched.List, len(ids))
byId := structs.Index(fetched.List, func(r ContactCard) string { return r.Id })
- for _, actual := range contacts {
+ for _, actual := range results.Results {
expected, ok := byId[actual.Id]
require.True(ok, "failed to find created contact by its id")
matchContact(t, actual, expected)
@@ -129,8 +132,8 @@ func TestContacts(t *testing.T) {
}
// retrieve each object one by one
- for _, actual := range contacts {
- fetched, _, _, _, err := s.client.GetContactCards(accountId, session, t.Context(), s.logger, "", []string{actual.Id})
+ for _, actual := range results.Results {
+ fetched, _, _, _, err := s.client.GetContactCards(accountId, []string{actual.Id}, ctx)
require.NoError(err)
require.Len(fetched.List, 1)
matchContact(t, fetched.List[0], actual)
@@ -169,7 +172,7 @@ func (s *StalwartTest) fillAddressBook( //NOSONAR
t *testing.T,
accountId string,
count uint,
- session *Session,
+ ctx Context,
_ User,
principalIds []string,
) (AddressBookBoxes, []AddressBook, SessionState, State, error) {
@@ -192,7 +195,7 @@ func (s *StalwartTest) fillAddressBook( //NOSONAR
IsSubscribed: &subscribed,
}
if i%2 == 0 {
- abook.SortOrder = posUIntPtr(gofakeit.Uint())
+ abook.SortOrder = uintPtr(gofakeit.Uint())
boxes.sortOrdered = true
}
var sharing *AddressBookRights = nil
@@ -218,7 +221,7 @@ func (s *StalwartTest) fillAddressBook( //NOSONAR
abook.ShareWith = m
}
- a, sessionState, state, _, err := s.client.CreateAddressBook(accountId, session, s.ctx, s.logger, "", abook)
+ a, sessionState, state, _, err := s.client.CreateAddressBook(accountId, abook, ctx)
if err != nil {
return boxes, created, ss, as, err
}
diff --git a/pkg/jmap/integration_email_test.go b/pkg/jmap/integration_email_test.go
index 1a8c992703..3e14d1a356 100644
--- a/pkg/jmap/integration_email_test.go
+++ b/pkg/jmap/integration_email_test.go
@@ -40,10 +40,11 @@ func TestEmails(t *testing.T) {
user := pickUser()
session := s.Session(user.name)
+ ctx := s.Context(session)
accountId := session.PrimaryAccounts.Mail
- inboxId, inboxFolder := s.findInbox(t, accountId, session)
+ inboxId, inboxFolder := s.findInbox(t, accountId, ctx)
var threads int = 0
var mails []filledMail = nil
@@ -55,7 +56,7 @@ func TestEmails(t *testing.T) {
{
{
- resp, sessionState, _, _, err := s.client.GetAllIdentities(accountId, session, s.ctx, s.logger, "")
+ resp, sessionState, _, _, err := s.client.GetAllIdentities(accountId, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.Len(resp, 1)
@@ -64,7 +65,7 @@ func TestEmails(t *testing.T) {
}
{
- respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, session, s.ctx, s.logger, "")
+ respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.Len(respByAccountId, 1)
@@ -80,7 +81,7 @@ func TestEmails(t *testing.T) {
}
{
- resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, session, s.ctx, s.logger, "", inboxId, 0, 0, true, false, 0, true)
+ resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, 0, true, false, 0, true, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
@@ -94,7 +95,7 @@ func TestEmails(t *testing.T) {
}
{
- resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, session, s.ctx, s.logger, "", inboxId, 0, 0, false, false, 0, true)
+ resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, inboxId, 0, 0, false, false, 0, true, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
@@ -122,6 +123,7 @@ func TestSendingEmails(t *testing.T) {
from := pickUser()
session := s.Session(from.name)
+ ctx := s.Context(session)
accountId := session.PrimaryAccounts.Mail
var to User
@@ -142,7 +144,7 @@ func TestSendingEmails(t *testing.T) {
var mailboxPerRole map[string]Mailbox
{
- mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{accountId}, session, s.ctx, s.logger, "")
+ mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{accountId}, ctx)
require.NoError(err)
mailboxPerRole = structs.Index(mailboxes[accountId], func(m Mailbox) string { return m.Role })
require.Contains(mailboxPerRole, JmapMailboxRoleInbox)
@@ -152,7 +154,7 @@ func TestSendingEmails(t *testing.T) {
}
{
roles := []string{JmapMailboxRoleDrafts, JmapMailboxRoleSent, JmapMailboxRoleInbox}
- m, _, _, _, err := s.client.SearchMailboxIdsPerRole([]string{accountId}, session, s.ctx, s.logger, "", roles)
+ m, _, _, _, err := s.client.SearchMailboxIdsPerRole([]string{accountId}, roles, ctx)
require.NoError(err)
require.Contains(m, accountId)
a := m[accountId]
@@ -166,7 +168,7 @@ func TestSendingEmails(t *testing.T) {
accountId string
session *Session
}{{toAccountId, toSession}, {ccAccountId, ccSession}} {
- mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{u.accountId}, u.session, s.ctx, s.logger, "")
+ mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{u.accountId}, ctx)
require.NoError(err)
for _, mailbox := range mailboxes[u.accountId] {
require.Equal(0, mailbox.TotalEmails)
@@ -180,7 +182,7 @@ func TestSendingEmails(t *testing.T) {
{
var identity Identity
{
- identities, _, _, _, err := s.client.GetAllIdentities(accountId, session, s.ctx, s.logger, "")
+ identities, _, _, _, err := s.client.GetAllIdentities(accountId, ctx)
require.NoError(err)
require.NotEmpty(identities)
identity = identities[0]
@@ -191,12 +193,12 @@ func TestSendingEmails(t *testing.T) {
Subject: subject,
MailboxIds: toBoolMapS(mailboxPerRole[JmapMailboxRoleDrafts].Id),
}
- created, _, _, _, err := s.client.CreateEmail(accountId, create, "", session, s.ctx, s.logger, "")
+ created, _, _, _, err := s.client.CreateEmail(accountId, create, "", ctx)
require.NoError(err)
require.NotEmpty(created.Id)
{
- emails, notFound, _, _, _, err := s.client.GetEmails(accountId, session, s.ctx, s.logger, "", []string{created.Id}, true, 0, false, false)
+ emails, notFound, _, _, _, err := s.client.GetEmails(accountId, []string{created.Id}, true, 0, false, false, ctx)
require.NoError(err)
require.Len(emails, 1)
require.Empty(notFound)
@@ -215,14 +217,14 @@ func TestSendingEmails(t *testing.T) {
Subject: subject,
MailboxIds: toBoolMapS(mailboxPerRole[JmapMailboxRoleDrafts].Id),
}
- updated, _, _, _, err := s.client.CreateEmail(accountId, update, created.Id, session, s.ctx, s.logger, "")
+ updated, _, _, _, err := s.client.CreateEmail(accountId, update, created.Id, ctx)
require.NoError(err)
require.NotEmpty(updated.Id)
require.NotEqual(created.Id, updated.Id)
var updatedMailboxId string
{
- emails, notFound, _, _, _, err := s.client.GetEmails(accountId, session, s.ctx, s.logger, "", []string{created.Id, updated.Id}, true, 0, false, false)
+ emails, notFound, _, _, _, err := s.client.GetEmails(accountId, []string{created.Id, updated.Id}, true, 0, false, false, ctx)
require.NoError(err)
require.Len(emails, 1)
require.Len(notFound, 1)
@@ -241,7 +243,7 @@ func TestSendingEmails(t *testing.T) {
ToMailboxId: mailboxPerRole[JmapMailboxRoleSent].Id,
}
- sub, _, _, _, err := s.client.SubmitEmail(accountId, identity.Id, updated.Id, &move, session, s.ctx, s.logger, "")
+ sub, _, _, _, err := s.client.SubmitEmail(accountId, identity.Id, updated.Id, &move, ctx)
require.NoError(err)
require.NotEmpty(sub.Id)
require.NotEmpty(sub.ThreadId)
@@ -272,7 +274,7 @@ func TestSendingEmails(t *testing.T) {
}
time.Sleep(1 * time.Second)
- subs, notFound, _, _, _, err := s.client.GetEmailSubmissionStatus(accountId, []string{sub.Id}, session, s.ctx, s.logger, "")
+ subs, notFound, _, _, _, err := s.client.GetEmailSubmissionStatus(accountId, []string{sub.Id}, ctx)
require.NoError(err)
require.Empty(notFound)
require.Contains(subs, sub.Id)
@@ -286,7 +288,7 @@ func TestSendingEmails(t *testing.T) {
accountId string
session *Session
}{{to, toAccountId, toSession}, {cc, ccAccountId, ccSession}} {
- mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{r.accountId}, r.session, s.ctx, s.logger, "")
+ mailboxes, _, _, _, err := s.client.GetAllMailboxes([]string{r.accountId}, ctx)
require.NoError(err)
inboxId := ""
for _, mailbox := range mailboxes[r.accountId] {
@@ -297,7 +299,7 @@ func TestSendingEmails(t *testing.T) {
}
require.NotEmpty(inboxId, "failed to find the Mailbox with the 'inbox' role for %v", r.user.name)
- emails, _, _, _, err := s.client.QueryEmails([]string{r.accountId}, EmailFilterCondition{InMailbox: inboxId}, r.session, s.ctx, s.logger, "", 0, 0, true, 0)
+ emails, _, _, _, err := s.client.QueryEmails([]string{r.accountId}, EmailFilterCondition{InMailbox: inboxId}, 0, 0, true, 0, ctx)
require.NoError(err)
require.Contains(emails, r.accountId)
require.Len(emails[r.accountId].Emails, 1)
@@ -354,11 +356,11 @@ func matchEmail(t *testing.T, actual Email, expected filledMail, hasBodies bool)
}
}
-func (s *StalwartTest) findInbox(t *testing.T, accountId string, session *Session) (string, string) {
+func (s *StalwartTest) findInbox(t *testing.T, accountId string, ctx Context) (string, string) {
require := require.New(t)
- respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, session, s.ctx, s.logger, "")
+ respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, ctx)
require.NoError(err)
- require.Equal(session.State, sessionState)
+ require.Equal(ctx.Session.State, sessionState)
require.Len(respByAccountId, 1)
require.Contains(respByAccountId, accountId)
resp := respByAccountId[accountId]
diff --git a/pkg/jmap/integration_event_test.go b/pkg/jmap/integration_event_test.go
index dc40cac92f..47a8a1623e 100644
--- a/pkg/jmap/integration_event_test.go
+++ b/pkg/jmap/integration_event_test.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"encoding/base64"
"encoding/json"
"fmt"
@@ -17,7 +16,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
- "github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/structs"
)
@@ -36,17 +34,17 @@ func TestCalendars(t *testing.T) { //NOSONAR
func(session *Session) string { return session.PrimaryAccounts.Calendars },
func(resp CalendarGetResponse) []Calendar { return resp.List },
func(obj Calendar) string { return obj.Id },
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (CalendarGetResponse, SessionState, State, Language, Error) {
- return s.client.GetCalendars(accountId, session, ctx, logger, acceptLanguage, ids)
+ func(s *StalwartTest, accountId string, ids []string, ctx Context) (CalendarGetResponse, SessionState, State, Language, Error) {
+ return s.client.GetCalendars(accountId, ids, ctx)
},
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string, change CalendarChange) (Calendar, SessionState, State, Language, Error) { //NOSONAR
- return s.client.UpdateCalendar(accountId, session, ctx, logger, acceptLanguage, id, change)
+ func(s *StalwartTest, accountId string, id string, change CalendarChange, ctx Context) (Calendar, SessionState, State, Language, Error) { //NOSONAR
+ return s.client.UpdateCalendar(accountId, id, change, ctx)
},
- func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (map[string]SetError, SessionState, State, Language, Error) { //NOSONAR
- return s.client.DeleteCalendar(accountId, ids, session, ctx, logger, acceptLanguage)
+ func(s *StalwartTest, accountId string, ids []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) { //NOSONAR
+ return s.client.DeleteCalendar(accountId, ids, ctx)
},
- func(s *StalwartTest, t *testing.T, accountId string, count uint, session *Session, user User, principalIds []string) (CalendarBoxes, []Calendar, SessionState, State, error) {
- return s.fillCalendar(t, accountId, count, session, user, principalIds)
+ func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, user User, principalIds []string) (CalendarBoxes, []Calendar, SessionState, State, error) {
+ return s.fillCalendar(t, accountId, count, ctx, user, principalIds)
},
func(orig Calendar) CalendarChange {
return CalendarChange{
@@ -77,6 +75,7 @@ func TestEvents(t *testing.T) {
user := pickUser()
session := s.Session(user.name)
+ ctx := s.Context(session)
accountId, calendarId, expectedEventsById, boxes, err := s.fillEvents(t, count, session, user)
require.NoError(err)
@@ -90,18 +89,49 @@ func TestEvents(t *testing.T) {
{Property: CalendarEventPropertyStart, IsAscending: true},
}
- contactsByAccount, _, _, _, err := s.client.QueryCalendarEvents([]string{accountId}, session, t.Context(), s.logger, "", filter, sortBy, 0, 0)
- require.NoError(err)
+ {
+ resultsByAccount, _, _, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, 0, 0, true, ctx)
+ require.NoError(err)
- require.Len(contactsByAccount, 1)
- require.Contains(contactsByAccount, accountId)
- contacts := contactsByAccount[accountId]
- require.Len(contacts, int(count))
+ require.Len(resultsByAccount, 1)
+ require.Contains(resultsByAccount, accountId)
+ results := resultsByAccount[accountId]
+ require.Len(results.Results, int(count))
+ require.Equal(uint(0), results.Limit)
+ require.Equal(uint(0), results.Position)
+ require.Equal(true, results.CanCalculateChanges)
+ require.NotNil(results.Total)
+ require.Equal(count, *results.Total)
- for _, actual := range contacts {
- expected, ok := expectedEventsById[actual.Id]
- require.True(ok, "failed to find created contact by its id")
- matchEvent(t, actual, expected)
+ for _, actual := range results.Results {
+ expected, ok := expectedEventsById[actual.Id]
+ require.True(ok, "failed to find created contact by its id")
+ matchEvent(t, actual, expected)
+ }
+ }
+
+ {
+ 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, _, _, _, err := s.client.QueryCalendarEvents([]string{accountId}, filter, sortBy, position, limit, true, ctx)
+ fmt.Printf("=== i=%d | limit=%d | remainder=%d | position=%d | limit=%d | results=%d\n", i, limit, remainder, position, limit, len(m[accountId].Results))
+ require.NoError(err)
+ require.Len(m, 1)
+ require.Contains(m, accountId)
+ results := m[accountId]
+ require.Equal(len(results.Results), int(page))
+ require.Equal(limit, results.Limit)
+ require.Equal(position, results.Position)
+ require.Equal(true, results.CanCalculateChanges)
+ require.NotNil(results.Total)
+ require.Equal(count, *results.Total)
+ remainder -= uint(len(results.Results))
+ }
}
exceptions := []string{}
@@ -127,7 +157,7 @@ func (s *StalwartTest) fillCalendar( //NOSONAR
t *testing.T,
accountId string,
count uint,
- session *Session,
+ ctx Context,
_ User,
principalIds []string,
) (CalendarBoxes, []Calendar, SessionState, State, error) {
@@ -180,7 +210,7 @@ func (s *StalwartTest) fillCalendar( //NOSONAR
},
}
if i%2 == 0 {
- cal.SortOrder = posUIntPtr(gofakeit.Uint())
+ cal.SortOrder = uintPtr(gofakeit.Uint())
boxes.sortOrdered = true
}
var sharing *CalendarRights = nil
@@ -233,7 +263,7 @@ func (s *StalwartTest) fillCalendar( //NOSONAR
cal.ShareWith = m
}
- a, sessionState, state, _, err := s.client.CreateCalendar(accountId, session, s.ctx, s.logger, "", cal)
+ a, sessionState, state, _, err := s.client.CreateCalendar(accountId, cal, ctx)
if err != nil {
return boxes, created, ss, as, err
}
diff --git a/pkg/jmap/integration_test.go b/pkg/jmap/integration_test.go
index 64ffc030a6..f277b44f91 100644
--- a/pkg/jmap/integration_test.go
+++ b/pkg/jmap/integration_test.go
@@ -175,6 +175,15 @@ func (s *StalwartTest) Close() error {
return nil
}
+func (s *StalwartTest) Context(session *Session) Context {
+ return Context{
+ Session: session,
+ Context: s.ctx,
+ Logger: s.logger,
+ AcceptLanguage: "",
+ }
+}
+
func (s *StalwartTest) Session(username string) *Session {
session, jerr := s.client.FetchSession(s.ctx, s.sessionUrl, username, s.logger)
require.NoError(s.t, jerr)
@@ -1218,10 +1227,10 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
acc func(session *Session) string,
obj func(RESP) []OBJ,
id func(OBJ) string,
- get func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *clog.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error),
- update func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *clog.Logger, acceptLanguage string, id string, change CHANGE) (OBJ, SessionState, State, Language, Error),
- destroy func(s *StalwartTest, accountId string, session *Session, ctx context.Context, logger *clog.Logger, acceptLanguage string, ids []string) (map[string]SetError, SessionState, State, Language, Error),
- fill func(s *StalwartTest, t *testing.T, accountId string, count uint, session *Session, _ User, principalIds []string) (BOXES, []OBJ, SessionState, State, error),
+ get func(s *StalwartTest, accountId string, ids []string, ctx Context) (RESP, SessionState, State, Language, Error),
+ update func(s *StalwartTest, accountId string, id string, change CHANGE, ctx Context) (OBJ, SessionState, State, Language, Error),
+ destroy func(s *StalwartTest, accountId string, ids []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error),
+ fill func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, _ User, principalIds []string) (BOXES, []OBJ, SessionState, State, error),
change func(OBJ) CHANGE,
checkChanged func(t *testing.T, orig OBJ, change CHANGE, changed OBJ),
) {
@@ -1233,16 +1242,17 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
user := pickUser()
session := s.Session(user.name)
+ ctx := s.Context(session)
accountId := acc(session)
// we first need to retrieve the list of all the Principals in order to be able to use and test sharing
principalIds := []string{}
{
- principals, _, _, _, err := s.client.GetPrincipals(accountId, session, s.ctx, s.logger, "", []string{})
+ principals, _, _, _, err := s.client.GetPrincipals(accountId, []string{}, ctx)
require.NoError(err)
- require.NotEmpty(principals.Principals)
- principalIds = structs.Map(principals.Principals, func(p Principal) string { return p.Id })
+ require.NotEmpty(principals.List)
+ principalIds = structs.Map(principals.List, func(p Principal) string { return p.Id })
}
ss := SessionState("")
@@ -1252,7 +1262,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
// from the tests below
defaultContainerId := ""
{
- resp, sessionState, state, _, err := get(s, accountId, session, s.ctx, s.logger, "", []string{})
+ resp, sessionState, state, _, err := get(s, accountId, []string{}, ctx)
require.NoError(err)
require.Empty(resp.GetNotFound())
objs := obj(resp)
@@ -1265,7 +1275,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
// we are going to create a random amount of objects
num := uint(5 + rand.Intn(30))
{
- boxes, all, sessionState, state, err := fill(s, t, accountId, num, session, user, principalIds)
+ boxes, all, sessionState, state, err := fill(s, t, accountId, num, ctx, user, principalIds)
require.NoError(err)
require.Len(all, int(num))
ss = sessionState
@@ -1273,7 +1283,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
{
// lets retrieve all the existing objects by passing an empty ID slice
- resp, sessionState, state, _, err := get(s, accountId, session, s.ctx, s.logger, "", []string{})
+ resp, sessionState, state, _, err := get(s, accountId, []string{}, ctx)
require.NoError(err)
require.Empty(resp.GetNotFound())
objs := obj(resp)
@@ -1300,7 +1310,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
// lets retrieve every object we created by its ID
for _, a := range all {
i := id(a)
- resp, sessionState, state, _, err := get(s, accountId, session, s.ctx, s.logger, "", []string{i})
+ resp, sessionState, state, _, err := get(s, accountId, []string{i}, ctx)
require.NoError(err)
require.Empty(resp.GetNotFound())
objs := obj(resp)
@@ -1314,7 +1324,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
for _, a := range all {
i := id(a)
ch := change(a)
- changed, sessionState, state, _, err := update(s, accountId, session, s.ctx, s.logger, "", i, ch)
+ changed, sessionState, state, _, err := update(s, accountId, i, ch, ctx)
require.NoError(err)
require.NotEqual(a, changed)
require.Equal(sessionState, ss)
@@ -1325,7 +1335,7 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](
// now lets delete each object that we created, all at once
ids := structs.Map(all, id)
{
- errMap, sessionState, state, _, err := destroy(s, accountId, session, s.ctx, s.logger, "", ids)
+ errMap, sessionState, state, _, err := destroy(s, accountId, ids, ctx)
require.NoError(err)
require.Empty(errMap)
require.Equal(sessionState, ss)
diff --git a/pkg/jmap/integration_ws_test.go b/pkg/jmap/integration_ws_test.go
index 9177d2152c..1a1825d1fb 100644
--- a/pkg/jmap/integration_ws_test.go
+++ b/pkg/jmap/integration_ws_test.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"sync"
"sync/atomic"
"testing"
@@ -61,7 +60,7 @@ func TestWs(t *testing.T) {
require := require.New(t)
- ctx := context.Background()
+ cotx := t.Context()
s, err := newStalwartTest(t)
require.NoError(err)
@@ -69,11 +68,12 @@ func TestWs(t *testing.T) {
user := pickUser()
session := s.Session(user.name)
+ ctx := s.Context(session)
mailAccountId := session.PrimaryAccounts.Mail
inboxFolder := ""
{
- _, inboxFolder = s.findInbox(t, mailAccountId, session)
+ _, inboxFolder = s.findInbox(t, mailAccountId, ctx)
}
l := &testWsPushListener{t: t, username: user.name, logger: s.logger, mailAccountId: mailAccountId}
@@ -90,7 +90,7 @@ func TestWs(t *testing.T) {
var initialState State
{
- changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", State(""), true, 0, 0)
+ changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, EmptyState, true, 0, 0, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEmpty(state)
@@ -104,7 +104,7 @@ func TestWs(t *testing.T) {
require.NotEmpty(initialState)
{
- changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, true, 0, 0)
+ changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, initialState, true, 0, 0, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.Equal(initialState, state)
@@ -114,7 +114,7 @@ func TestWs(t *testing.T) {
require.Empty(changes.Updated)
}
- wsc, err := s.client.EnablePushNotifications(ctx, initialState, func() (*Session, error) { return session, nil })
+ wsc, err := s.client.EnablePushNotifications(cotx, initialState, func() (*Session, error) { return session, nil })
require.NoError(err)
defer wsc.Close()
@@ -147,7 +147,7 @@ func TestWs(t *testing.T) {
}
var lastState State
{
- changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, true, 0, 0)
+ changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, initialState, true, 0, 0, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(initialState, state)
@@ -181,7 +181,7 @@ func TestWs(t *testing.T) {
l.m.Unlock()
}
{
- changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, true, 0, 0)
+ changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, lastState, true, 0, 0, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(lastState, state)
@@ -215,7 +215,7 @@ func TestWs(t *testing.T) {
l.m.Unlock()
}
{
- changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, true, 0, 0)
+ changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, lastState, true, 0, 0, ctx)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(lastState, state)
diff --git a/pkg/jmap/model.go b/pkg/jmap/model.go
index a09e59ffce..4b28edba03 100644
--- a/pkg/jmap/model.go
+++ b/pkg/jmap/model.go
@@ -1354,13 +1354,32 @@ type ChangesResponse[T Foo] interface {
GetMarker() T
}
-type QueryCommand interface {
+type QueryCommand[T Foo] interface {
JmapCommand
- GetResponse() QueryResponse
+ GetResponse() QueryResponse[T]
}
-type QueryResponse interface {
+type QueryResponse[T Foo] interface {
GetQueryState() State
+ GetMarker() T
+}
+
+type UploadCommand[T Foo] interface {
+ JmapCommand
+ GetResponse() UploadResponse[T]
+}
+
+type UploadResponse[T Foo] interface {
+ GetMarker() T
+}
+
+type ParseCommand[T Foo] interface {
+ JmapCommand
+ GetResponse() ParseResponse[T]
+}
+
+type ParseResponse[T Foo] interface {
+ GetMarker() T
}
type ChangesTemplate[T Foo] struct {
@@ -1372,6 +1391,22 @@ type ChangesTemplate[T Foo] struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+type SearchResultsTemplate[T Foo] struct {
+ Results []T `json:"results"`
+ CanCalculateChanges bool `json:"canCalculateChanges"`
+ Position uint `json:"position"`
+ Limit uint `json:"limit,omitzero"`
+ Total *uint `json:"total,omitzero"`
+}
+
+type SearchResults[T Foo] interface {
+ GetResults() []T
+ GetCanCalculateChanges() bool
+ GetPosition() uint
+ GetTotal() *uint
+ GetLimit() uint
+}
+
type FilterOperatorTerm string
const (
@@ -1656,8 +1691,8 @@ type MailboxSetResponse struct {
AccountId string `json:"accountId"`
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState,omitempty"`
- Created map[string]Mailbox `json:"created,omitempty"`
- Updated map[string]Mailbox `json:"updated,omitempty"`
+ Created map[string]*Mailbox `json:"created,omitempty"`
+ Updated map[string]*Mailbox `json:"updated,omitempty"`
Destroyed []string `json:"destroyed,omitempty"`
NotCreated map[string]SetError `json:"notCreated,omitempty"`
NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
@@ -1717,11 +1752,11 @@ type MailboxQueryCommand struct {
FilterAsTree bool `json:"filterAsTree,omitempty"`
}
-var _ QueryCommand = &MailboxQueryCommand{}
+var _ QueryCommand[Mailbox] = &MailboxQueryCommand{}
-func (c MailboxQueryCommand) GetCommand() Command { return CommandMailboxQuery }
-func (c MailboxQueryCommand) GetObjectType() ObjectType { return MailboxType }
-func (c MailboxQueryCommand) GetResponse() QueryResponse { return MailboxQueryResponse{} }
+func (c MailboxQueryCommand) GetCommand() Command { return CommandMailboxQuery }
+func (c MailboxQueryCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxQueryCommand) GetResponse() QueryResponse[Mailbox] { return MailboxQueryResponse{} }
type EmailFilterElement interface {
_isAnEmailFilterElement() // marker method
@@ -2013,11 +2048,11 @@ type EmailQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitempty"`
}
-var _ QueryCommand = &EmailQueryCommand{}
+var _ QueryCommand[Email] = &EmailQueryCommand{}
-func (c EmailQueryCommand) GetCommand() Command { return CommandEmailQuery }
-func (c EmailQueryCommand) GetObjectType() ObjectType { return MailboxType }
-func (c EmailQueryCommand) GetResponse() QueryResponse { return EmailQueryResponse{} }
+func (c EmailQueryCommand) GetCommand() Command { return CommandEmailQuery }
+func (c EmailQueryCommand) GetObjectType() ObjectType { return MailboxType }
+func (c EmailQueryCommand) GetResponse() QueryResponse[Email] { return EmailQueryResponse{} }
type EmailGetCommand struct {
// The ids of the Email objects to return.
@@ -3063,9 +3098,10 @@ type EmailQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
-var _ QueryResponse = &EmailQueryResponse{}
+var _ QueryResponse[Email] = &EmailQueryResponse{}
func (r EmailQueryResponse) GetQueryState() State { return r.QueryState }
+func (r EmailQueryResponse) GetMarker() Email { return Email{} }
type EmailGetResponse struct {
// The id of the account used for the call.
@@ -3284,9 +3320,10 @@ type MailboxQueryResponse struct {
Limit int `json:"limit,omitzero"`
}
-var _ QueryResponse = &MailboxQueryResponse{}
+var _ QueryResponse[Mailbox] = &MailboxQueryResponse{}
func (r MailboxQueryResponse) GetQueryState() State { return r.QueryState }
+func (r MailboxQueryResponse) GetMarker() Mailbox { return Mailbox{} }
type EmailCreate struct {
// The set of Mailbox ids this Email belongs to.
@@ -3727,11 +3764,11 @@ func (r IdentityChangesResponse) GetDestroyed() []string { return r.Destroyed }
func (r IdentityChangesResponse) GetMarker() Identity { return Identity{} }
type IdentitySetCommand struct {
- AccountId string `json:"accountId"`
- IfInState string `json:"ifInState,omitempty"`
- Create map[string]Identity `json:"create,omitempty"`
- Update map[string]PatchObject `json:"update,omitempty"`
- Destroy []string `json:"destroy,omitempty"`
+ AccountId string `json:"accountId"`
+ IfInState string `json:"ifInState,omitempty"`
+ Create map[string]IdentityChange `json:"create,omitempty"`
+ Update map[string]PatchObject `json:"update,omitempty"`
+ Destroy []string `json:"destroy,omitempty"`
}
var _ SetCommand[Identity] = &IdentitySetCommand{}
@@ -3741,15 +3778,15 @@ func (c IdentitySetCommand) GetObjectType() ObjectType { return Identit
func (c IdentitySetCommand) GetResponse() SetResponse[Identity] { return IdentitySetResponse{} }
type IdentitySetResponse struct {
- AccountId string `json:"accountId"`
- OldState State `json:"oldState,omitempty"`
- NewState State `json:"newState,omitempty"`
- Created map[string]Identity `json:"created,omitempty"`
- Updated map[string]Identity `json:"updated,omitempty"`
- Destroyed []string `json:"destroyed,omitempty"`
- NotCreated map[string]SetError `json:"notCreated,omitempty"`
- NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
- NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
+ AccountId string `json:"accountId"`
+ OldState State `json:"oldState,omitempty"`
+ NewState State `json:"newState,omitempty"`
+ Created map[string]*Identity `json:"created,omitempty"`
+ Updated map[string]*Identity `json:"updated,omitempty"`
+ Destroyed []string `json:"destroyed,omitempty"`
+ NotCreated map[string]SetError `json:"notCreated,omitempty"`
+ NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
+ NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
var _ SetResponse[Identity] = &IdentitySetResponse{}
@@ -3810,9 +3847,41 @@ var _ Idable = &Identity{}
func (f Identity) GetObjectType() ObjectType { return IdentityType }
func (f Identity) GetId() string { return f.Id }
-var _ Change = Identity{}
+type IdentityChange struct {
+ // The “From” name the client SHOULD use when creating a new Email from this Identity.
+ Name string `json:"name,omitempty" doc:"req"`
-func (i Identity) AsPatch() PatchObject {
+ // The “From” email address the client MUST use when creating a new Email from this Identity.
+ //
+ // If the mailbox part of the address (the section before the “@”) is the single character
+ // * (e.g., *@example.com) then the client may use any valid address ending in that domain
+ // (e.g., foo@example.com).
+ Email string `json:"email,omitempty"`
+
+ // The Reply-To value the client SHOULD set when creating a new Email from this Identity.
+ ReplyTo string `json:"replyTo,omitempty"`
+
+ // The Bcc value the client SHOULD set when creating a new Email from this Identity.
+ Bcc *[]EmailAddress `json:"bcc,omitempty"`
+
+ // A signature the client SHOULD insert into new plaintext messages that will be sent from
+ // this Identity.
+ //
+ // Clients MAY ignore this and/or combine this with a client-specific signature preference.
+ TextSignature *string `json:"textSignature,omitempty"`
+
+ // A signature the client SHOULD insert into new HTML messages that will be sent from this
+ // Identity.
+ //
+ // This text MUST be an HTML snippet to be inserted into the section of the HTML.
+ //
+ // Clients MAY ignore this and/or combine this with a client-specific signature preference.
+ HtmlSignature *string `json:"htmlSignature,omitempty"`
+}
+
+var _ Change = IdentityChange{}
+
+func (i IdentityChange) AsPatch() PatchObject {
p := PatchObject{}
if i.Name != "" {
p["name"] = i.Name
@@ -3985,10 +4054,11 @@ type BlobUploadCommand struct {
Create map[string]UploadObject `json:"create"`
}
-var _ JmapCommand = &BlobUploadCommand{}
+var _ UploadCommand[Blob] = &BlobUploadCommand{}
-func (c BlobUploadCommand) GetCommand() Command { return CommandBlobUpload }
-func (c BlobUploadCommand) GetObjectType() ObjectType { return BlobType }
+func (c BlobUploadCommand) GetCommand() Command { return CommandBlobUpload }
+func (c BlobUploadCommand) GetObjectType() ObjectType { return BlobType }
+func (c BlobUploadCommand) GetResponse() UploadResponse[Blob] { return BlobUploadResponse{} }
type BlobUploadCreateResult struct {
Id string `json:"id"`
@@ -4001,6 +4071,10 @@ type BlobUploadResponse struct {
Created map[string]BlobUploadCreateResult `json:"created"`
}
+var _ UploadResponse[Blob] = &BlobUploadResponse{}
+
+func (r BlobUploadResponse) GetMarker() Blob { return Blob{} }
+
const (
BlobPropertyDataAsText = "data:asText"
BlobPropertyDataAsBase64 = "data:asBase64"
@@ -5837,6 +5911,28 @@ var _ Idable = &Principal{}
func (f Principal) GetObjectType() ObjectType { return PrincipalType }
func (f Principal) GetId() string { return f.Id }
+const (
+ PrincipalPropertyId = "id"
+ PrincipalPropertyType = "type"
+ PrincipalPropertyName = "name"
+ PrincipalPropertyDescription = "description"
+ PrincipalPropertyEmail = "email"
+ PrincipalPropertyTimeZone = "timeZone"
+ PrincipalPropertyCapabilities = "capabilites"
+ PrincipalPropertyAccounts = "accounts"
+)
+
+var PrincipalProperties = []string{
+ PrincipalPropertyId,
+ PrincipalPropertyType,
+ PrincipalPropertyName,
+ PrincipalPropertyDescription,
+ PrincipalPropertyEmail,
+ PrincipalPropertyTimeZone,
+ PrincipalPropertyCapabilities,
+ PrincipalPropertyAccounts,
+}
+
type ShareChangePerson struct {
// The name of the person who made the change.
Name string `json:"name"`
@@ -6589,7 +6685,7 @@ type ContactCardQueryCommand 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.
//
@@ -6615,7 +6711,7 @@ type ContactCardQueryCommand struct {
// to the maximum; the new limit is returned with the response so the client is aware.
//
// If a negative value is given, the call MUST be rejected with an invalidArguments error.
- Limit uint `json:"limit,omitzero" doc:"opt"`
+ Limit *uint `json:"limit,omitzero" doc:"opt"`
// Does the client wish to know the total number of results in the query?
//
@@ -6624,11 +6720,13 @@ type ContactCardQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitzero"`
}
-var _ QueryCommand = &ContactCardQueryCommand{}
+var _ QueryCommand[ContactCard] = &ContactCardQueryCommand{}
-func (c ContactCardQueryCommand) GetCommand() Command { return CommandContactCardQuery }
-func (c ContactCardQueryCommand) GetObjectType() ObjectType { return ContactCardType }
-func (c ContactCardQueryCommand) GetResponse() QueryResponse { return ContactCardQueryResponse{} }
+func (c ContactCardQueryCommand) GetCommand() Command { return CommandContactCardQuery }
+func (c ContactCardQueryCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardQueryCommand) GetResponse() QueryResponse[ContactCard] {
+ return ContactCardQueryResponse{}
+}
type ContactCardQueryResponse struct {
// The id of the account used for the call.
@@ -6680,9 +6778,10 @@ type ContactCardQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
-var _ QueryResponse = &ContactCardQueryResponse{}
+var _ QueryResponse[ContactCard] = &ContactCardQueryResponse{}
-func (r ContactCardQueryResponse) GetQueryState() State { return r.QueryState }
+func (r ContactCardQueryResponse) GetQueryState() State { return r.QueryState }
+func (r ContactCardQueryResponse) GetMarker() ContactCard { return ContactCard{} }
type ContactCardGetCommand struct {
// The ids of the ContactCard objects to return.
@@ -6960,10 +7059,13 @@ type CalendarEventParseCommand struct {
Properties []string `json:"properties,omitempty"`
}
-var _ JmapCommand = &CalendarEventParseCommand{}
+var _ ParseCommand[CalendarEvent] = &CalendarEventParseCommand{}
func (c CalendarEventParseCommand) GetCommand() Command { return CommandCalendarEventParse }
func (c CalendarEventParseCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventParseCommand) GetResponse() ParseResponse[CalendarEvent] {
+ return CalendarEventParseResponse{}
+}
type CalendarEventParseResponse struct {
// The id of the account used for the call.
@@ -6981,6 +7083,10 @@ type CalendarEventParseResponse struct {
NotParsable []string `json:"notParsable,omitempty"`
}
+var _ ParseResponse[CalendarEvent] = &CalendarEventParseResponse{}
+
+func (r CalendarEventParseResponse) GetMarker() CalendarEvent { return CalendarEvent{} }
+
type CalendarGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
@@ -7309,7 +7415,7 @@ type CalendarEventQueryCommand 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,omitempty" doc:"opt"`
+ Position int `json:"position,omitempty" doc:"opt"`
// An Email id.
//
@@ -7335,7 +7441,7 @@ type CalendarEventQueryCommand struct {
// to the maximum; the new limit is returned with the response so the client is aware.
//
// If a negative value is given, the call MUST be rejected with an invalidArguments error.
- Limit uint `json:"limit,omitempty" doc:"opt" default:"0"`
+ Limit *uint `json:"limit,omitempty" doc:"opt" default:"0"`
// Does the client wish to know the total number of results in the query?
//
@@ -7344,11 +7450,13 @@ type CalendarEventQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitempty" doc:"opt" default:"false"`
}
-var _ QueryCommand = &CalendarEventQueryCommand{}
+var _ QueryCommand[CalendarEvent] = &CalendarEventQueryCommand{}
-func (c CalendarEventQueryCommand) GetCommand() Command { return CommandCalendarEventQuery }
-func (c CalendarEventQueryCommand) GetObjectType() ObjectType { return CalendarEventType }
-func (c CalendarEventQueryCommand) GetResponse() QueryResponse { return CalendarEventQueryResponse{} }
+func (c CalendarEventQueryCommand) GetCommand() Command { return CommandCalendarEventQuery }
+func (c CalendarEventQueryCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventQueryCommand) GetResponse() QueryResponse[CalendarEvent] {
+ return CalendarEventQueryResponse{}
+}
type CalendarEventQueryResponse struct {
// The id of the account used for the call.
@@ -7400,9 +7508,10 @@ type CalendarEventQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
-var _ QueryResponse = &CalendarEventQueryResponse{}
+var _ QueryResponse[CalendarEvent] = &CalendarEventQueryResponse{}
-func (r CalendarEventQueryResponse) GetQueryState() State { return r.QueryState }
+func (r CalendarEventQueryResponse) GetQueryState() State { return r.QueryState }
+func (r CalendarEventQueryResponse) GetMarker() CalendarEvent { return CalendarEvent{} }
type CalendarEventGetCommand struct {
// The ids of the CalendarEvent objects to return.
@@ -7771,11 +7880,60 @@ type PrincipalComparator struct {
}
type PrincipalQueryCommand struct {
- AccountId string `json:"accountId"`
- Filter PrincipalFilterElement `json:"filter,omitempty"`
- Sort []PrincipalComparator `json:"sort,omitempty"`
- SortAsTree bool `json:"sortAsTree,omitempty"`
- FilterAsTree bool `json:"filterAsTree,omitempty"`
+ AccountId string `json:"accountId"`
+ Filter PrincipalFilterElement `json:"filter,omitempty"`
+ Sort []PrincipalComparator `json:"sort,omitempty"`
+
+ // The zero-based index of the first id in the full list of results to return.
+ //
+ // If a negative value is given, it is an offset from the end of the list.
+ // Specifically, the negative value MUST be added to the total number of results given
+ // the filter, and if still negative, it’s clamped to 0. This is now the zero-based
+ // index of the first id to return.
+ //
+ // 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"`
+
+ // An Email id.
+ //
+ // If supplied, the position argument is ignored.
+ // The index of this id in the results will be used in combination with the anchorOffset
+ // argument to determine the index of the first result to return.
+ Anchor string `json:"anchor,omitempty" doc:"opt"`
+
+ // The index of the first result to return relative to the index of the anchor,
+ // if an anchor is given.
+ //
+ // This MAY be negative.
+ //
+ // 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"`
+
+ // The maximum number of results to return.
+ //
+ // If null, no limit presumed.
+ // The server MAY choose to enforce a maximum limit argument.
+ // In this case, if a greater value is given (or if it is null), the limit is clamped
+ // to the maximum; the new limit is returned with the response so the client is aware.
+ //
+ // If a negative value is given, the call MUST be rejected with an invalidArguments error.
+ Limit uint `json:"limit,omitzero" doc:"opt"`
+
+ // Does the client wish to know the total number of results in the query?
+ //
+ // This may be slow and expensive for servers to calculate, particularly with complex filters,
+ // so clients should take care to only request the total when needed.
+ CalculateTotal bool `json:"calculateTotal,omitzero"`
+}
+
+var _ QueryCommand[Principal] = PrincipalQueryCommand{}
+
+func (c PrincipalQueryCommand) GetCommand() Command { return CommandPrincipalQuery }
+func (c PrincipalQueryCommand) GetObjectType() ObjectType { return PrincipalType }
+func (c PrincipalQueryCommand) GetResponse() QueryResponse[Principal] {
+ return PrincipalQueryResponse{}
}
type PrincipalQueryResponse struct {
@@ -7796,38 +7954,39 @@ type PrincipalQueryResponse struct {
//
// Should a client receive back a response with a different queryState string to a previous call, it MUST either
// throw away the currently cached query and fetch it again (note, this does not require fetching the records
- // again, just the list of ids) or call Mailbox/queryChanges to get the difference.
+ // again, just the list of ids) or call Principal/queryChanges to get the difference.
QueryState State `json:"queryState"`
- // This is true if the server supports calling Mailbox/queryChanges with these filter/sort parameters.
+ // This is true if the server supports calling Principal/queryChanges with these filter/sort parameters.
//
- // Note, this does not guarantee that the Mailbox/queryChanges call will succeed, as it may only be possible for
+ // Note, this does not guarantee that the Principal/queryChanges call will succeed, as it may only be possible for
// a limited time afterwards due to server internal implementation details.
CanCalculateChanges bool `json:"canCalculateChanges"`
// The zero-based index of the first result in the ids array within the complete list of query results.
- Position int `json:"position"`
+ Position uint `json:"position"`
- // The list of ids for each Mailbox in the query results, starting at the index given by the position argument
+ // The list of ids for each Principal in the query results, starting at the index given by the position argument
// of this response and continuing until it hits the end of the results or reaches the limit number of ids.
//
// If position is >= total, this MUST be the empty list.
Ids []string `json:"ids"`
- // The total number of Mailbox in the results (given the filter) (only if requested).
+ // The total number of Principal in the results (given the filter) (only if requested).
//
// This argument MUST be omitted if the calculateTotal request argument is not true.
- Total int `json:"total,omitzero"`
+ Total uint `json:"total,omitzero"`
// The limit enforced by the server on the maximum number of results to return (if set by the server).
//
// This is only returned if the server set a limit or used a different limit than that given in the request.
- Limit int `json:"limit,omitzero"`
+ Limit uint `json:"limit,omitzero"`
}
-var _ QueryResponse = &PrincipalQueryResponse{}
+var _ QueryResponse[Principal] = &PrincipalQueryResponse{}
func (r PrincipalQueryResponse) GetQueryState() State { return r.QueryState }
+func (r PrincipalQueryResponse) GetMarker() Principal { return Principal{} }
type ErrorResponse struct {
Type string `json:"type"`
diff --git a/pkg/jmap/templates.go b/pkg/jmap/templates.go
index 94cacfe42b..345a005443 100644
--- a/pkg/jmap/templates.go
+++ b/pkg/jmap/templates.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"fmt"
"github.com/opencloud-eu/opencloud/pkg/log"
@@ -48,25 +47,28 @@ func (f Mailboxes) MapChanges(oldState, newState State, hasMoreChanges bool, cre
func fget[F Factory[T, GETREQ, GETRESP, CHANGES], T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], CHANGES any](f Factory[T, GETREQ, GETRESP, CHANGES], //NOSONAR
client *Client, name string,
accountId string, ids []string,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (GETRESP, SessionState, State, Language, Error) {
+ ctx Context) (GETRESP, SessionState, State, Language, Error) {
var getresp GETRESP
return get(client, name, f.Namespaces(),
f.CreateGetCommand,
getresp,
identity1,
- accountId, session, ctx, logger, acceptLanguage, ids,
+ accountId, ids,
+ ctx,
)
}
func fgetA[F Factory[T, GETREQ, GETRESP, CHANGES], T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], CHANGES any](f Factory[T, GETREQ, GETRESP, CHANGES], //NOSONAR
client *Client, name string,
accountId string, ids []string,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) ([]T, SessionState, State, Language, Error) {
+ ctx Context) ([]T, SessionState, State, Language, Error) {
var getresp GETRESP
return getA(client, name, f.Namespaces(),
f.CreateGetCommand,
getresp,
- accountId, session, ctx, logger, acceptLanguage, ids,
+ accountId,
+ ids,
+ ctx,
)
}
@@ -75,22 +77,20 @@ func get[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOSON
getCommandFactory func(string, []string) GETREQ,
_ GETRESP,
mapper func(GETRESP) RESP,
- accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ accountId string, ids []string, ctx Context) (RESP, SessionState, State, Language, Error) {
+ ctx = ctx.WithLogger(client.logger(name, ctx))
var zero RESP
get := getCommandFactory(accountId, ids)
- cmd, err := client.request(session, logger, using,
- invocation(get, "0"),
- )
+ cmd, err := client.request(ctx, using, invocation(get, "0"))
if err != nil {
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
var response GETRESP
- err = retrieveGet(logger, body, get, "0", &response)
+ err = retrieveGet(ctx, body, get, "0", &response)
if err != nil {
return zero, "", err
}
@@ -103,21 +103,22 @@ func getA[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T]]( //NOSONAR
client *Client, name string, using []JmapNamespace,
getCommandFactory func(string, []string) GETREQ,
resp GETRESP,
- accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) ([]T, SessionState, State, Language, Error) {
- return get(client, name, using, getCommandFactory, resp, func(r GETRESP) []T { return r.GetList() }, accountId, session, ctx, logger, acceptLanguage, ids)
+ accountId string, ids []string, ctx Context) ([]T, SessionState, State, Language, Error) {
+ return get(client, name, using, getCommandFactory, resp, func(r GETRESP) []T { return r.GetList() }, accountId, ids, ctx)
}
func fgetAN[F Factory[T, GETREQ, GETRESP, CHANGES], T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any, CHANGES any](f Factory[T, GETREQ, GETRESP, CHANGES], //NOSONAR
client *Client, name string,
respMapper func(map[string][]T) RESP,
accountIds []string, ids []string,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
+ ctx Context) (RESP, SessionState, State, Language, Error) {
var getresp GETRESP
return getAN(client, name, f.Namespaces(),
f.CreateGetCommand,
getresp,
respMapper,
- accountIds, session, ctx, logger, acceptLanguage, ids,
+ accountIds, ids,
+ ctx,
)
}
@@ -126,11 +127,12 @@ func getAN[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOS
getCommandFactory func(string, []string) GETREQ,
resp GETRESP,
respMapper func(map[string][]T) RESP,
- accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error) {
+ accountIds []string, ids []string, ctx Context) (RESP, SessionState, State, Language, Error) {
return getN(client, name, using, getCommandFactory, resp,
func(r GETRESP) []T { return r.GetList() },
respMapper,
- accountIds, session, ctx, logger, acceptLanguage, ids,
+ accountIds, ids,
+ ctx,
)
}
@@ -140,8 +142,9 @@ func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP an
_ GETRESP,
itemMapper func(GETRESP) ITEM,
respMapper func(map[string]ITEM) RESP,
- accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ accountIds []string, ids []string, ctx Context) (RESP, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
var zero RESP
@@ -155,17 +158,17 @@ func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP an
invocations[i] = invocation(get, mcid(accountId, "0"))
}
- cmd, err := client.request(session, logger, using, invocations...)
+ cmd, err := client.request(ctx, using, invocations...)
if err != nil {
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
result := map[string]ITEM{}
responses := map[string]GETRESP{}
for _, accountId := range uniqueAccountIds {
var resp GETRESP
- err = retrieveResponseMatchParameters(logger, body, c, mcid(accountId, "0"), &resp)
+ err = retrieveResponseMatchParameters(ctx, body, c, mcid(accountId, "0"), &resp)
if err != nil {
return zero, "", err
}
@@ -182,13 +185,15 @@ func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP Se
getCommandFactory func(string, string) GETREQ,
createdMapper func(SETRESP) map[string]*T,
listMapper func(GETRESP) []T,
- accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create C) (*T, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ accountId string, create C,
+ ctx Context) (*T, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
createMap := map[string]C{"c": create}
get := getCommandFactory(accountId, "#c")
set := setCommandFactory(accountId, createMap)
- cmd, err := client.request(session, logger, using,
+ cmd, err := client.request(ctx, using,
invocation(set, "0"),
invocation(get, "1"),
)
@@ -196,9 +201,9 @@ func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP Se
return nil, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*T, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (*T, State, Error) {
var setResponse SETRESP
- err = retrieveSet(logger, body, set, "0", &setResponse)
+ err = retrieveSet(ctx, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
@@ -218,7 +223,7 @@ func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP Se
}
var getResponse GETRESP
- err = retrieveGet(logger, body, get, "1", &getResponse)
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
if err != nil {
return nil, "", err
}
@@ -237,20 +242,21 @@ func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP Se
func destroy[T Foo, REQ SetCommand[T], RESP SetResponse[T]](client *Client, name string, using []JmapNamespace, //NOSONAR
setCommandFactory func(string, []string) REQ, _ RESP,
- accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ accountId string, destroy []string, ctx Context) (map[string]SetError, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
set := setCommandFactory(accountId, destroy)
- cmd, err := client.request(session, logger, using,
+ cmd, err := client.request(ctx, using,
invocation(set, "0"),
)
if err != nil {
return nil, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]SetError, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (map[string]SetError, State, Error) {
var setResponse RESP
- err = retrieveSet(logger, body, set, "0", &setResponse)
+ err = retrieveSet(ctx, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
@@ -265,12 +271,12 @@ func changesA[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGES
_ GETRESP,
getCommandFactory func(string, string) GETREQ,
respMapper func(State, State, bool, []T, []T, []string) RESP,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
+ ctx Context) (RESP, SessionState, State, Language, Error) {
return changes(client, name, using, changesCommandFactory, changesResp, getCommandFactory,
func(r GETRESP) []T { return r.GetList() },
respMapper,
- session, ctx, logger, acceptLanguage,
+ ctx,
)
}
@@ -281,15 +287,15 @@ func changes[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESR
getCommandFactory func(string, string) GETREQ,
getMapper func(GETRESP) []ITEM,
respMapper func(State, State, bool, []ITEM, []ITEM, []string) RESP,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ ctx Context) (RESP, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
var zero RESP
changes := changesCommandFactory()
getCreated := getCommandFactory("/created", "0") //NOSONAR
getUpdated := getCommandFactory("/updated", "0") //NOSONAR
- cmd, err := client.request(session, logger, using,
+ cmd, err := client.request(ctx.WithLogger(logger), using,
invocation(changes, "0"),
invocation(getCreated, "1"),
invocation(getUpdated, "2"),
@@ -298,22 +304,22 @@ func changes[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESR
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
var changesResponse CHANGESRESP
- err = retrieveChanges(logger, body, changes, "0", &changesResponse)
+ err = retrieveChanges(ctx, body, changes, "0", &changesResponse)
if err != nil {
return zero, "", err
}
var createdResponse GETRESP
- err = retrieveGet(logger, body, getCreated, "1", &createdResponse)
+ err = retrieveGet(ctx, body, getCreated, "1", &createdResponse)
if err != nil {
logger.Error().Err(err).Send()
return zero, "", err
}
var updatedResponse GETRESP
- err = retrieveGet(logger, body, getUpdated, "2", &updatedResponse)
+ err = retrieveGet(ctx, body, getUpdated, "2", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return zero, "", err
@@ -338,8 +344,8 @@ func changesN[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGES
changesItemMapper func(State, State, bool, []ITEM, []ITEM, []string) CHANGESITEM,
respMapper func(map[string]CHANGESITEM) RESP,
stateMapper func(GETRESP) State,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
- logger = client.loggerParams(name, session, logger, func(z zerolog.Context) zerolog.Context {
+ ctx Context) (RESP, SessionState, State, Language, Error) {
+ logger := client.loggerParams(name, ctx, func(z zerolog.Context) zerolog.Context {
sinceStateLogDict := zerolog.Dict()
for k, v := range sinceStateMap {
sinceStateLogDict.Str(log.SafeString(k), log.SafeString(string(v)))
@@ -377,29 +383,31 @@ func changesN[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGES
getCommand = getCreated.GetCommand()
}
- cmd, err := client.request(session, logger, using, invocations...)
+ ctx = ctx.WithLogger(logger)
+
+ cmd, err := client.request(ctx, using, invocations...)
if err != nil {
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
changesItemByAccount := make(map[string]CHANGESITEM, n)
stateByAccountId := make(map[string]State, n)
for _, accountId := range uniqueAccountIds {
var changesResponse CHANGESRESP
- err = retrieveResponseMatchParameters(logger, body, changesCommand, mcid(accountId, "0"), &changesResponse)
+ err = retrieveResponseMatchParameters(ctx, body, changesCommand, mcid(accountId, "0"), &changesResponse)
if err != nil {
return zero, "", err
}
var createdResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, mcid(accountId, "1"), &createdResponse)
+ err = retrieveResponseMatchParameters(ctx, body, getCommand, mcid(accountId, "1"), &createdResponse)
if err != nil {
return zero, "", err
}
var updatedResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, mcid(accountId, "2"), &updatedResponse)
+ err = retrieveResponseMatchParameters(ctx, body, getCommand, mcid(accountId, "2"), &updatedResponse)
if err != nil {
return zero, "", err
}
@@ -420,13 +428,14 @@ func updates[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESR
getCommandFactory func(string, string) GETREQ,
getMapper func(GETRESP) []ITEM,
respMapper func(State, State, bool, []ITEM) RESP,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ ctx Context) (RESP, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
var zero RESP
changes := changesCommandFactory()
getUpdated := getCommandFactory("/updated", "0") //NOSONAR
- cmd, err := client.request(session, logger, using,
+ cmd, err := client.request(ctx, using,
invocation(changes, "0"),
invocation(getUpdated, "1"),
)
@@ -434,15 +443,15 @@ func updates[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESR
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
var changesResponse CHANGESRESP
- err = retrieveChanges(logger, body, changes, "0", &changesResponse)
+ err = retrieveChanges(ctx, body, changes, "0", &changesResponse)
if err != nil {
return zero, "", err
}
var updatedResponse GETRESP
- err = retrieveGet(logger, body, getUpdated, "1", &updatedResponse)
+ err = retrieveGet(ctx, body, getUpdated, "1", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return zero, "", err
@@ -461,19 +470,20 @@ func update[T Foo, CHANGES Change, SET SetCommand[T], GET GetCommand[T], RESP an
notUpdatedExtractor func(SETRESP) map[string]SetError,
objExtractor func(GETRESP) RESP,
id string, changes CHANGES,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
+ ctx Context) (RESP, SessionState, State, Language, Error) {
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
update := setCommandFactory(map[string]PatchObject{id: changes.AsPatch()})
get := getCommandFactory(id)
- cmd, err := client.request(session, logger, using, invocation(update, "0"), invocation(get, "1"))
+ cmd, err := client.request(ctx, using, invocation(update, "0"), invocation(get, "1"))
var zero RESP
if err != nil {
return zero, "", "", "", err
}
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
var setResponse SETRESP
- err = retrieveSet(logger, body, update, "0", &setResponse)
+ err = retrieveSet(ctx, body, update, "0", &setResponse)
if err != nil {
return zero, setResponse.GetNewState(), err
}
@@ -484,10 +494,107 @@ func update[T Foo, CHANGES Change, SET SetCommand[T], GET GetCommand[T], RESP an
return zero, "", setErrorError(setErr, update.GetObjectType())
}
var getResponse GETRESP
- err = retrieveGet(logger, body, get, "1", &getResponse)
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
if err != nil {
return zero, setResponse.GetNewState(), err
}
return objExtractor(getResponse), setResponse.GetNewState(), nil
})
}
+
+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, using []JmapNamespace,
+ defaultSortBy []SORT,
+ queryCommandFactory func(filter FILTER, sortBy []SORT, limit uint, position 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,
+ ctx Context) (RESP, SessionState, State, Language, Error) {
+
+ logger := client.logger(name, ctx)
+ ctx = ctx.WithLogger(logger)
+
+ if sortBy == nil {
+ sortBy = defaultSortBy
+ }
+
+ query := queryCommandFactory(filter, sortBy, limit, position)
+ get := getCommandFactory(query.GetCommand(), "/ids/*", "0")
+
+ var zero RESP
+
+ cmd, err := client.request(ctx, using, invocation(query, "0"), invocation(get, "1"))
+ if err != nil {
+ return zero, "", "", "", err
+ }
+
+ return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) {
+ var queryResponse QUERYRESP
+ err = retrieveQuery(ctx, body, query, "0", &queryResponse)
+ if err != nil {
+ return zero, EmptyState, err
+ }
+ var getResponse GETRESP
+ err = retrieveGet(ctx, body, get, "1", &getResponse)
+ if err != nil {
+ return zero, EmptyState, err
+ }
+ return respMapper(queryResponse, getResponse), queryResponse.GetQueryState(), nil
+ })
+}
+
+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, using []JmapNamespace,
+ defaultSortBy []SORT,
+ queryCommandFactory func(accountId string, filter FILTER, sortBy []SORT, position int, limit 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,
+ ctx Context) (map[string]RESP, SessionState, State, Language, Error) {
+ uniqueAccountIds := structs.Uniq(accountIds)
+
+ if sortBy == nil {
+ sortBy = defaultSortBy
+ }
+
+ invocations := make([]Invocation, len(uniqueAccountIds)*2)
+ var g GET
+ var q QUERY
+ for i, accountId := range uniqueAccountIds {
+ query := queryCommandFactory(accountId, filter, sortBy, position, 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"))
+ q = query
+ g = get
+ }
+
+ cmd, err := client.request(ctx, NS_CALENDARS, invocations...)
+ if err != nil {
+ return nil, "", "", "", err
+ }
+
+ return command(client, ctx, cmd, func(body *Response) (map[string]RESP, State, Error) {
+ resp := map[string]RESP{}
+ stateByAccountId := map[string]State{}
+ for _, accountId := range uniqueAccountIds {
+ var queryResponse QUERYRESP
+ err = retrieveQuery(ctx, body, q, mcid(accountId, "0"), &queryResponse)
+ if err != nil {
+ return nil, "", err
+ }
+ var getResponse GETRESP
+ err = retrieveGet(ctx, body, g, mcid(accountId, "1"), &getResponse)
+ if err != nil {
+ return nil, "", err
+ }
+ if len(getResponse.GetNotFound()) > 0 {
+ // TODO what to do when there are not-found calendarevents here? potentially nothing, they could have been deleted between query and get?
+ }
+ resp[accountId] = respMapper(queryResponse, getResponse)
+ stateByAccountId[accountId] = getResponse.GetState()
+ }
+ return resp, squashState(stateByAccountId), nil
+ })
+}
diff --git a/pkg/jmap/tools.go b/pkg/jmap/tools.go
index 08790a8c3e..bf17907daa 100644
--- a/pkg/jmap/tools.go
+++ b/pkg/jmap/tools.go
@@ -1,7 +1,6 @@
package jmap
import (
- "context"
"encoding/json"
"errors"
"fmt"
@@ -13,7 +12,6 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
- "github.com/opencloud-eu/opencloud/pkg/log"
)
type eventListeners[T any] struct {
@@ -52,16 +50,19 @@ func mcid(accountId string, tag string) string {
return accountId + ":" + tag
}
-func command[T any](api ApiClient, //NOSONAR
- logger *log.Logger,
- ctx context.Context,
- session *Session,
- sessionOutdatedHandler func(session *Session, newState SessionState),
+type Cmdr interface {
+ ApiSupplier
+ Hooks
+}
+
+func command[T any](client Cmdr, //NOSONAR
+ ctx Context,
request Request,
- acceptLanguage string,
mapper func(body *Response) (T, State, Error)) (T, SessionState, State, Language, Error) {
- responseBody, language, jmapErr := api.Command(ctx, logger, session, request, acceptLanguage)
+ logger := ctx.Logger
+
+ responseBody, language, jmapErr := client.Api().Command(request, ctx)
if jmapErr != nil {
var zero T
return zero, "", "", language, jmapErr
@@ -75,10 +76,8 @@ func command[T any](api ApiClient, //NOSONAR
return zero, "", "", language, jmapError(err, JmapErrorDecodingResponseBody)
}
- if response.SessionState != session.State {
- if sessionOutdatedHandler != nil {
- sessionOutdatedHandler(session, response.SessionState)
- }
+ if response.SessionState != ctx.Session.State {
+ client.OnSessionOutdated(ctx.Session, response.SessionState)
}
// search for an "error" response
@@ -199,25 +198,25 @@ func retrieveResponseMatch(data *Response, command Command, tag string) (Invocat
return Invocation{}, false
}
-func retrieveResponseMatchParameters[T any](logger *log.Logger, data *Response, command Command, tag string, target *T) Error {
+func retrieveResponseMatchParameters[T any](ctx Context, data *Response, command Command, tag string, target *T) Error {
match, ok := retrieveResponseMatch(data, command, tag)
if !ok {
err := fmt.Errorf("failed to find JMAP response invocation match for command '%v' and tag '%v'", command, tag) // NOSONAR
- logger.Error().Msg(err.Error())
+ ctx.Logger.Error().Msg(err.Error())
return jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
params := match.Parameters
typedParams, ok := params.(T)
if !ok {
err := fmt.Errorf("JMAP response invocation matches command '%v' and tag '%v' but the type %T does not match the expected %T", command, tag, params, *target) // NOSONAR
- logger.Error().Msg(err.Error())
+ ctx.Logger.Error().Msg(err.Error())
return jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
*target = typedParams
return nil
}
-func tryRetrieveResponseMatchParameters[T any](logger *log.Logger, data *Response, command Command, tag string, target *T) (bool, Error) {
+func tryRetrieveResponseMatchParameters[T any](ctx Context, data *Response, command Command, tag string, target *T) (bool, Error) {
match, ok := retrieveResponseMatch(data, command, tag)
if !ok {
return false, nil
@@ -226,23 +225,35 @@ func tryRetrieveResponseMatchParameters[T any](logger *log.Logger, data *Respons
typedParams, ok := params.(T)
if !ok {
err := fmt.Errorf("JMAP response invocation matches command '%v' and tag '%v' but the type %T does not match the expected %T", command, tag, params, *target)
- logger.Error().Msg(err.Error())
+ ctx.Logger.Error().Msg(err.Error())
return true, jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
*target = typedParams
return true, nil
}
-func retrieveGet[T Foo, C GetCommand[T], R GetResponse[T]](logger *log.Logger, data *Response, command C, tag string, target *R) Error {
- return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+func retrieveGet[T Foo, C GetCommand[T], R GetResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
}
-func retrieveSet[T Foo, C SetCommand[T], R SetResponse[T]](logger *log.Logger, data *Response, command C, tag string, target *R) Error {
- return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+func retrieveSet[T Foo, C SetCommand[T], R SetResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
}
-func retrieveChanges[T Foo, C ChangesCommand[T], R ChangesResponse[T]](logger *log.Logger, data *Response, command C, tag string, target *R) Error {
- return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+func retrieveQuery[T Foo, C QueryCommand[T], R QueryResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
+}
+
+func retrieveChanges[T Foo, C ChangesCommand[T], R ChangesResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
+}
+
+func retrieveUpload[T Foo, C UploadCommand[T], R UploadResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
+}
+
+func retrieveParse[T Foo, C ParseCommand[T], R ParseResponse[T]](ctx Context, data *Response, command C, tag string, target *R) Error {
+ return retrieveResponseMatchParameters(ctx, data, command.GetCommand(), tag, target)
}
func (i *Invocation) MarshalJSON() ([]byte, error) {
@@ -396,7 +407,7 @@ 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 posUIntPtr(i uint) *uint {
+func uintPtr(i uint) *uint {
if i > 0 {
return &i
} else {
@@ -404,6 +415,14 @@ func posUIntPtr(i uint) *uint {
}
}
+func uintPtrIf(i uint, condition bool) *uint {
+ if condition {
+ return uintPtr(i)
+ } else {
+ return nil
+ }
+}
+
func ns(namespaces ...JmapNamespace) []JmapNamespace {
result := make([]JmapNamespace, len(namespaces)+1)
result[0] = JmapCore
diff --git a/pkg/jscontact/model.go b/pkg/jscontact/model.go
index 3b7b5e0976..43046e5445 100644
--- a/pkg/jscontact/model.go
+++ b/pkg/jscontact/model.go
@@ -2012,324 +2012,3 @@ type PersonalInfo struct {
// Note that succinct labels are best for proper display on small graphical interfaces and screens.
Label string `json:"label,omitempty"`
}
-
-// A ContactCard object contains information about a person, company, or other entity, or represents a group of such entities.
-//
-// It is a JSCard (JSContact) object, as defined in [RFC9553], with two additional properties.
-//
-// A contact card with a `kind` property equal to `group` represents a group of contacts.
-// Clients often present these separately from other contact cards.
-//
-// The `members` property, as defined in RFC XXX, Section XXX, contains a set of UIDs for other contacts that are the members
-// of this group.
-// Clients should consider the group to contain any `ContactCard` with a matching UID, from any account they have access to with
-// support for the `urn:ietf:params:jmap:contacts` capability.
-// UIDs that cannot be found SHOULD be ignored but preserved.
-//
-// For example, suppose a user adds contacts from a shared address book to their private group, then temporarily loses access to
-// this address book.
-// The UIDs cannot be resolved so the contacts will disappear from the group.
-// However, if they are given permission to access the data again the UIDs will be found and the contacts will reappear.
-type ContactCard struct {
- // The id of the Card (immutable; server-set).
- //
- // The id uniquely identifies a Card with a particular “uid” within a particular account.
- //
- // This is a JMAP extension and not part of [RFC9553].
- Id string `json:"id,omitempty" doc:"!request,req"`
-
- // The set of AddressBook ids this Card belongs to.
- //
- // A card MUST belong to at least one AddressBook at all times (until it is destroyed).
- //
- // The set is represented as an object, with each key being an AddressBook id.
- //
- // The value for each key in the object MUST be true.
- //
- // This is a JMAP extension and not part of [RFC9553].
- AddressBookIds map[string]bool `json:"addressBookIds,omitempty"`
-
- // The JSContact type of the Card object: the value MUST be "Card".
- Type TypeOfContactCard `json:"@type,omitempty"`
-
- // The JSContact version of this Card.
- //
- // The value MUST be one of the IANA-registered JSContact Version values for the version property.
- Version JSContactVersion `json:"version"`
-
- // The date and time when the Card was created (UTCDateTime).
- Created time.Time `json:"created,omitzero"`
-
- // The kind of the entity the Card represents (default: `individual`).
- //
- // Values are:
- // * `individual`: a single person
- // * `group`: a group of people or entities
- // * `org`: an organization
- // * `location`: a named location
- // * `device`: a device such as an appliance, a computer, or a network element
- // * `application`: a software application
- Kind ContactCardKind `json:"kind,omitempty"`
-
- // The language tag, as defined in [RFC5646].
- //
- // The language tag that best describes the language used for text in the Card, optionally including
- // additional information such as the script.
- //
- // Note that values MAY be localized in the `localizations` property.
- Language string `json:"language,omitempty"`
-
- // The set of Cards that are members of this group Card.
- //
- // Each key in the set is the uid property value of the member, and each boolean value MUST be `true`.
- // If this property is set, then the value of the kind property MUST be `group`.
- //
- // The opposite is not true. A group Card will usually contain the members property to specify the members
- // of the group, but it is not required to.
- //
- // A group Card without the members property can be considered an abstract grouping or one whose members
- // are known empirically (e.g., `IETF Participants`).
- Members map[string]bool `json:"members,omitempty"`
-
- // The identifier for the product that created the Card.
- //
- // If set, the value MUST be at least one character long.
- ProdId string `json:"prodId,omitempty"`
-
- // The set of Card objects that relate to the Card.
- //
- // The value is a map, where each key is the uid property value of the related Card, and the value
- // defines the relation
- //
- // ```json
- // {
- // "relatedTo": {
- // "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6": {
- // "relation": {"friend": true}
- // },
- // "8cacdfb7d1ffdb59@example.com": {
- // "relation": {}
- // }
- // }
- // }
- // ```
- RelatedTo map[string]Relation `json:"relatedTo,omitempty"`
-
- // An identifier that associates the object as the same across different systems, address books, and views.
- //
- // The value SHOULD be a URN [RFC8141], but for compatibility with [RFC6350], it MAY also be a URI [RFC3986]
- // or free-text value.
- //
- // The value of the URN SHOULD be in the "uuid" namespace [RFC9562].
- //
- // [RFC9562] describes multiple versions of Universally Unique IDentifiers (UUIDs); UUID version 4 is RECOMMENDED.
- Uid string `json:"uid,omitempty"`
-
- // The date and time when the data in the Card was last modified (UTCDateTime).
- Updated time.Time `json:"updated,omitzero"`
-
- // The name of the entity represented by the Card.
- //
- // This can be any type of name, e.g., it can, but need not, be the legal name of a person.
- Name *Name `json:"name,omitempty"`
-
- // The nicknames of the entity represented by the Card.
- Nicknames map[string]Nickname `json:"nicknames,omitempty"`
-
- // The company or organization names and units associated with the Card.
- Organizations map[string]Organization `json:"organizations,omitempty"`
-
- // The information that directs how to address, speak to, or refer to the entity that is represented by the Card.
- SpeakToAs *SpeakToAs `json:"speakToAs,omitempty"`
-
- // The job titles or functional positions of the entity represented by the Card.
- Titles map[string]Title `json:"titles,omitempty"`
-
- // The email addresses in which to contact the entity represented by the Card.
- Emails map[string]EmailAddress `json:"emails,omitempty"`
-
- // The online services that are associated with the entity represented by the Card.
- //
- // This can be messaging services, social media profiles, and other.
- OnlineServices map[string]OnlineService `json:"onlineServices,omitempty"`
-
- // The phone numbers by which to contact the entity represented by the Card.
- Phones map[string]Phone `json:"phones,omitempty"`
-
- // The preferred languages for contacting the entity associated with the Card.
- PreferredLanguages map[string]LanguagePref `json:"preferredLanguages,omitempty"`
-
- // The calendaring resources of the entity represented by the Card, such as to look up free-busy information.
- //
- // A Calendar object has all properties of the Resource data type, with the following additional definitions:
- // * The `@type` property value MUST be `Calendar`, if set
- // * The `kind` property is mandatory. Its enumerated values are:
- // * `calendar`: The resource is a calendar that contains entries such as calendar events or tasks
- // * `freeBusy`: The resource allows for free-busy lookups, for example, to schedule group events
- Calendars map[string]Calendar `json:"calendars,omitempty"`
-
- // The scheduling addresses by which the entity may receive calendar scheduling invitations.
- SchedulingAddresses map[string]SchedulingAddress `json:"schedulingAddresses,omitempty"`
-
- // The addresses of the entity represented by the Card, such as postal addresses or geographic locations.
- Addresses map[string]Address `json:"addresses,omitempty"`
-
- // The cryptographic resources such as public keys and certificates associated with the entity represented by the Card.
- //
- // A CryptoKey object has all properties of the `Resource` data type, with the following additional definition:
- // the `@type` property value MUST be `CryptoKey`, if set.
- //
- // The following example shows how to refer to an external cryptographic resource:
- // ```json
- // "cryptoKeys": {
- // "mykey1": {
- // "uri": "https://www.example.com/keys/jdoe.cer"
- // }
- // }
- // ```
- CryptoKeys map[string]CryptoKey `json:"cryptoKeys,omitempty"`
-
- // The directories containing information about the entity represented by the Card.
- //
- // A Directory object has all properties of the `Resource` data type, with the following additional definitions:
- // * The `@type` property value MUST be `Directory`, if set
- // * The `kind` property is mandatory; tts enumerated values are:
- // * `directory`: the resource is a directory service that the entity represented by the Card is a part of; this
- // typically is an organizational directory that also contains associated entities, e.g., co-workers and management
- // in a company directory
- // * `entry`: the resource is a directory entry of the entity represented by the Card; in contrast to the `directory`
- // type, this is the specific URI for the entity within a directory
- Directories map[string]Directory `json:"directories,omitempty"`
-
- // The links to resources that do not fit any of the other use-case-specific resource properties.
- //
- // A Link object has all properties of the `Resource` data type, with the following additional definitions:
- // * The `@type` property value MUST be `Link`, if set
- // * The `kind` property is optional; tts enumerated values are:
- // * `contact`: the resource is a URI by which the entity represented by the Card may be contacted;
- // this includes web forms or other media that require user interaction
- Links map[string]Link `json:"links,omitempty"`
-
- // The media resources such as photographs, avatars, or sounds that are associated with the entity represented by the Card.
- //
- // A Media object has all properties of the Resource data type, with the following additional definitions:
- // * the `@type` property value MUST be `Media`, if set
- // * the `kind` property is mandatory; its enumerated values are:
- // * `photo`: the resource is a photograph or avatar
- // * `sound`: the resource is audio media, e.g., to specify the proper pronunciation of the name property contents
- // * `logo`: the resource is a graphic image or logo associated with the entity represented by the Card
- Media map[string]Media `json:"media,omitempty"`
-
- // The property values localized to languages other than the main `language` of the Card.
- //
- // Localizations provide language-specific alternatives for existing property values and SHOULD NOT add new properties.
- //
- // The keys in the localizations property value are language tags [RFC5646]; the values are of type `PatchObject` and
- // localize the Card in that language tag.
- //
- // The paths in the `PatchObject` are relative to the Card that includes the localizations property.
- //
- // A patch MUST NOT target the localizations property.
- //
- // Conceptually, a Card is localized as follows:
- // * Determine the language tag in which the Card should be localized.
- // * If the localizations property includes a key for that language, obtain the PatchObject value;
- // if there is no such key, stop.
- // * Create a copy of the Card, but do not copy the localizations property.
- // * Apply all patches in the PatchObject to the copy of the Card.
- // * Optionally, set the language property in the copy of the Card.
- // * Use the patched copy of the Card as the localized variant of the original Card.
- //
- // A patch in the `PatchObject` may contain any value type.
- //
- // Its value MUST be a valid value according to the definition of the patched property.
- Localizations map[string]PatchObject `json:"localizations,omitempty"`
-
- // The memorable dates and events for the entity represented by the Card.
- Anniversaries map[string]Anniversary `json:"anniversaries,omitempty"`
-
- // The set of free-text keywords, also known as tags.
- //
- // Each key in the set is a keyword, and each boolean value MUST be `true`.
- Keywords map[string]bool `json:"keywords,omitempty"`
-
- // The free-text notes that are associated with the Card.
- Notes map[string]Note `json:"notes,omitempty"`
-
- // The personal information of the entity represented by the Card.
- PersonalInfo map[string]PersonalInfo `json:"personalInfo,omitempty"`
-}
-
-func (f ContactCard) GetId() string { return f.Id }
-
-const (
- ContactCardPropertyId = "id"
- ContactCardPropertyAddressBookIds = "addressBookIds"
- ContactCardPropertyType = "@type"
- ContactCardPropertyVersion = "version"
- ContactCardPropertyCreated = "created"
- ContactCardPropertyKind = "kind"
- ContactCardPropertyLanguage = "language"
- ContactCardPropertyMembers = "members"
- ContactCardPropertyProdId = "prodId"
- ContactCardPropertyRelatedTo = "relatedTo"
- ContactCardPropertyUid = "uid"
- ContactCardPropertyUpdated = "updated"
- ContactCardPropertyName = "name"
- ContactCardPropertyNicknames = "nicknames"
- ContactCardPropertyOrganizations = "organizations"
- ContactCardPropertySpeakToAs = "speakToAs"
- ContactCardPropertyTitles = "titles"
- ContactCardPropertyEmails = "emails"
- ContactCardPropertyOnlineServices = "onlineServices"
- ContactCardPropertyPhones = "phones"
- ContactCardPropertyPreferredLanguages = "preferredLanguages"
- ContactCardPropertyCalendars = "calendars"
- ContactCardPropertySchedulingAddresses = "schedulingAddresses"
- ContactCardPropertyAddresses = "addresses"
- ContactCardPropertyCryptoKeys = "cryptoKeys"
- ContactCardPropertyDirectories = "directories"
- ContactCardPropertyLinks = "links"
- ContactCardPropertyMedia = "media"
- ContactCardPropertyLocalizations = "localizations"
- ContactCardPropertyAnniversaries = "anniversaries"
- ContactCardPropertyKeywords = "keywords"
- ContactCardPropertyNotes = "notes"
- ContactCardPropertyPersonalInfo = "personalInfo"
-)
-
-var ContactCardProperties = []string{
- ContactCardPropertyId,
- ContactCardPropertyAddressBookIds,
- ContactCardPropertyType,
- ContactCardPropertyVersion,
- ContactCardPropertyCreated,
- ContactCardPropertyKind,
- ContactCardPropertyLanguage,
- ContactCardPropertyMembers,
- ContactCardPropertyProdId,
- ContactCardPropertyRelatedTo,
- ContactCardPropertyUid,
- ContactCardPropertyUpdated,
- ContactCardPropertyName,
- ContactCardPropertyNicknames,
- ContactCardPropertyOrganizations,
- ContactCardPropertySpeakToAs,
- ContactCardPropertyTitles,
- ContactCardPropertyEmails,
- ContactCardPropertyOnlineServices,
- ContactCardPropertyPhones,
- ContactCardPropertyPreferredLanguages,
- ContactCardPropertyCalendars,
- ContactCardPropertySchedulingAddresses,
- ContactCardPropertyAddresses,
- ContactCardPropertyCryptoKeys,
- ContactCardPropertyDirectories,
- ContactCardPropertyLinks,
- ContactCardPropertyMedia,
- ContactCardPropertyLocalizations,
- ContactCardPropertyAnniversaries,
- ContactCardPropertyKeywords,
- ContactCardPropertyNotes,
- ContactCardPropertyPersonalInfo,
-}
diff --git a/services/groupware/pkg/groupware/api_account.go b/services/groupware/pkg/groupware/api_account.go
index 96f5665c9c..0314bf1881 100644
--- a/services/groupware/pkg/groupware/api_account.go
+++ b/services/groupware/pkg/groupware/api_account.go
@@ -44,7 +44,7 @@ func (g *Groupware) GetAccounts(w http.ResponseWriter, r *http.Request) {
func (g *Groupware) GetAccountsWithTheirIdentities(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
allAccountIds := req.AllAccountIds()
- resp, sessionState, state, lang, err := g.jmap.GetIdentitiesForAllAccounts(allAccountIds, req.session, req.ctx, req.logger, req.language())
+ resp, sessionState, state, lang, err := g.jmap.GetIdentitiesForAllAccounts(allAccountIds, req.ctx)
if err != nil {
return req.jmapErrorN(allAccountIds, err, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_addressbooks.go b/services/groupware/pkg/groupware/api_addressbooks.go
index d98b20fa8d..6f169b81aa 100644
--- a/services/groupware/pkg/groupware/api_addressbooks.go
+++ b/services/groupware/pkg/groupware/api_addressbooks.go
@@ -15,7 +15,7 @@ func (g *Groupware) GetAddressbooks(w http.ResponseWriter, r *http.Request) {
return resp
}
- addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, req.session, req.ctx, req.logger, req.language(), nil)
+ addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, []string{}, req.ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -42,7 +42,7 @@ func (g *Groupware) GetAddressbook(w http.ResponseWriter, r *http.Request) {
l = l.Str(UriParamAddressBookId, log.SafeString(addressBookId))
logger := log.From(l)
- addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, req.session, req.ctx, logger, req.language(), []string{addressBookId})
+ addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, []string{addressBookId}, req.ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -84,8 +84,8 @@ func (g *Groupware) GetAddressBookChanges(w http.ResponseWriter, r *http.Request
}
logger := log.From(l)
-
- changes, sessionState, state, lang, jerr := g.jmap.GetAddressbookChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetAddressbookChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -110,7 +110,8 @@ func (g *Groupware) CreateAddressBook(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
- created, sessionState, state, lang, jerr := g.jmap.CreateAddressBook(accountId, req.session, req.ctx, logger, req.language(), create)
+ ctx := req.ctx.WithLogger(logger)
+ created, sessionState, state, lang, jerr := g.jmap.CreateAddressBook(accountId, create, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -133,8 +134,8 @@ func (g *Groupware) DeleteAddressBook(w http.ResponseWriter, r *http.Request) {
l.Str(UriParamAddressBookId, log.SafeString(addressBookId))
logger := log.From(l)
-
- deleted, sessionState, state, lang, jerr := g.jmap.DeleteAddressBook(accountId, []string{addressBookId}, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ deleted, sessionState, state, lang, jerr := g.jmap.DeleteAddressBook(accountId, []string{addressBookId}, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_blob.go b/services/groupware/pkg/groupware/api_blob.go
index 556e496684..133cd9c5ec 100644
--- a/services/groupware/pkg/groupware/api_blob.go
+++ b/services/groupware/pkg/groupware/api_blob.go
@@ -5,6 +5,7 @@ import (
"net/http"
"strconv"
+ "github.com/opencloud-eu/opencloud/pkg/jmap"
"github.com/opencloud-eu/opencloud/pkg/log"
)
@@ -27,8 +28,9 @@ func (g *Groupware) GetBlobMeta(w http.ResponseWriter, r *http.Request) {
l = l.Str(UriParamBlobId, blobId)
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- res, sessionState, state, lang, jerr := g.jmap.GetBlobMetadata(accountId, req.session, req.ctx, logger, req.language(), blobId)
+ res, sessionState, state, lang, jerr := g.jmap.GetBlobMetadata(accountId, blobId, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -57,8 +59,8 @@ func (g *Groupware) UploadBlob(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
-
- resp, lang, jerr := g.jmap.UploadBlobStream(accountId, req.session, req.ctx, logger, req.language(), contentType, body)
+ ctx := req.ctx.WithLogger(logger)
+ resp, lang, jerr := g.jmap.UploadBlobStream(accountId, contentType, body, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, req.session.State, lang)
}
@@ -85,21 +87,22 @@ func (g *Groupware) DownloadBlob(w http.ResponseWriter, r *http.Request) {
return gwerr
}
logger := log.From(req.logger.With().Str(logAccountId, accountId).Str(UriParamBlobId, blobId))
+ ctx := req.ctx.WithLogger(logger)
- return req.serveBlob(blobId, name, typ, logger, accountId, w)
+ return req.serveBlob(blobId, name, typ, ctx, accountId, w)
})
}
-func (r *Request) serveBlob(blobId string, name string, typ string, logger *log.Logger, accountId string, w http.ResponseWriter) *Error {
+func (r *Request) serveBlob(blobId string, name string, typ string, ctx jmap.Context, accountId string, w http.ResponseWriter) *Error {
if typ == "" {
typ = DefaultBlobDownloadType
}
- blob, lang, jerr := r.g.jmap.DownloadBlobStream(accountId, blobId, name, typ, r.session, r.ctx, logger, r.language())
+ blob, lang, jerr := r.g.jmap.DownloadBlobStream(accountId, blobId, name, typ, ctx)
if blob != nil && blob.Body != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
- logger.Error().Err(err).Msg("failed to close response body")
+ ctx.Logger.Error().Err(err).Msg("failed to close response body")
}
}(blob.Body)
}
diff --git a/services/groupware/pkg/groupware/api_calendars.go b/services/groupware/pkg/groupware/api_calendars.go
index 251dd46f24..d2a2af3dbd 100644
--- a/services/groupware/pkg/groupware/api_calendars.go
+++ b/services/groupware/pkg/groupware/api_calendars.go
@@ -15,7 +15,7 @@ func (g *Groupware) GetCalendars(w http.ResponseWriter, r *http.Request) {
return resp
}
- calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, req.session, req.ctx, req.logger, req.language(), nil)
+ calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, nil, req.ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -41,7 +41,7 @@ func (g *Groupware) GetCalendarById(w http.ResponseWriter, r *http.Request) {
l = l.Str(UriParamCalendarId, log.SafeString(calendarId))
logger := log.From(l)
- calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, req.session, req.ctx, logger, req.language(), []string{calendarId})
+ calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, single(calendarId), req.ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -83,8 +83,8 @@ func (g *Groupware) GetCalendarChanges(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
-
- changes, sessionState, state, lang, jerr := g.jmap.GetCalendarChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetCalendarChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -109,7 +109,8 @@ func (g *Groupware) CreateCalendar(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
- created, sessionState, state, lang, jerr := g.jmap.CreateCalendar(accountId, req.session, req.ctx, logger, req.language(), create)
+ ctx := req.ctx.WithLogger(logger)
+ created, sessionState, state, lang, jerr := g.jmap.CreateCalendar(accountId, create, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -132,8 +133,8 @@ func (g *Groupware) DeleteCalendar(w http.ResponseWriter, r *http.Request) {
l.Str(UriParamCalendarId, log.SafeString(calendarId))
logger := log.From(l)
-
- deleted, sessionState, state, lang, jerr := g.jmap.DeleteCalendar(accountId, []string{calendarId}, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ deleted, sessionState, state, lang, jerr := g.jmap.DeleteCalendar(accountId, single(calendarId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_changes.go b/services/groupware/pkg/groupware/api_changes.go
index 83d0ab8770..07e4b88335 100644
--- a/services/groupware/pkg/groupware/api_changes.go
+++ b/services/groupware/pkg/groupware/api_changes.go
@@ -77,7 +77,8 @@ func (g *Groupware) GetChanges(w http.ResponseWriter, r *http.Request) { //NOSON
}
logger := log.From(l)
- changes, sessionState, state, lang, jerr := g.jmap.GetChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_contacts.go b/services/groupware/pkg/groupware/api_contacts.go
index 8bbf61a4ad..b7aeedc1ca 100644
--- a/services/groupware/pkg/groupware/api_contacts.go
+++ b/services/groupware/pkg/groupware/api_contacts.go
@@ -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.parseUIntParam(QueryParamOffset, 0)
+ offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
if err != nil {
return req.errorN(accountIds, err)
}
if ok {
- l = l.Uint(QueryParamOffset, offset)
+ l = l.Int(QueryParamOffset, offset)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaults.contactLimit)
@@ -84,7 +84,8 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
}
logger := log.From(l)
- contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards(accountIds, req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
+ ctx := req.ctx.WithLogger(logger)
+ contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards(accountIds, filter, sortBy, offset, limit, true, ctx)
if jerr != nil {
return req.jmapErrorN(accountIds, jerr, sessionState, lang)
}
@@ -113,7 +114,8 @@ func (g *Groupware) GetContactById(w http.ResponseWriter, r *http.Request) {
l = l.Str(UriParamContactId, log.SafeString(contactId))
logger := log.From(l)
- contacts, sessionState, state, lang, jerr := g.jmap.GetContactCards(accountId, req.session, req.ctx, logger, req.language(), []string{contactId})
+ ctx := req.ctx.WithLogger(logger)
+ contacts, sessionState, state, lang, jerr := g.jmap.GetContactCards(accountId, single(contactId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -140,7 +142,8 @@ func (g *Groupware) GetAllContacts(w http.ResponseWriter, r *http.Request) {
l := req.logger.With()
logger := log.From(l)
- contacts, sessionState, state, lang, jerr := g.jmap.GetContactCards(accountId, req.session, req.ctx, logger, req.language(), []string{})
+ ctx := req.ctx.WithLogger(logger)
+ contacts, sessionState, state, lang, jerr := g.jmap.GetContactCards(accountId, []string{}, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -173,7 +176,8 @@ func (g *Groupware) GetContactsChanges(w http.ResponseWriter, r *http.Request) {
l = l.Str(HeaderParamSince, log.SafeString(string(sinceState)))
logger := log.From(l)
- changes, sessionState, state, lang, jerr := g.jmap.GetContactCardChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetContactCardChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -205,7 +209,8 @@ func (g *Groupware) CreateContact(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
- created, sessionState, state, lang, jerr := g.jmap.CreateContactCard(accountId, req.session, req.ctx, logger, req.language(), create)
+ ctx := req.ctx.WithLogger(logger)
+ created, sessionState, state, lang, jerr := g.jmap.CreateContactCard(accountId, create, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -228,8 +233,8 @@ func (g *Groupware) DeleteContact(w http.ResponseWriter, r *http.Request) {
l.Str(UriParamContactId, log.SafeString(contactId))
logger := log.From(l)
-
- deleted, sessionState, state, lang, jerr := g.jmap.DeleteContactCard(accountId, []string{contactId}, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ deleted, sessionState, state, lang, jerr := g.jmap.DeleteContactCard(accountId, single(contactId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_emails.go b/services/groupware/pkg/groupware/api_emails.go
index 47bf6d25f2..de781c39c3 100644
--- a/services/groupware/pkg/groupware/api_emails.go
+++ b/services/groupware/pkg/groupware/api_emails.go
@@ -48,8 +48,8 @@ func (g *Groupware) GetEmailChanges(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
-
- changes, sessionState, state, lang, jerr := g.jmap.GetEmailChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, true, g.config.maxBodyValueBytes, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetEmailChanges(accountId, sinceState, true, g.config.maxBodyValueBytes, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -98,12 +98,13 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
collapseThreads := false
fetchBodies := false
withThreads := true
- emails, sessionState, state, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, req.session, req.ctx, logger, req.language(), mailboxId, offset, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads)
+ emails, sessionState, state, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, mailboxId, offset, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -151,8 +152,8 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) { //NO
}
logger := log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId)).Str("id", log.SafeString(id)).Str("accept", log.SafeString(accept)))
-
- blobId, _, _, _, jerr := g.jmap.GetEmailBlobId(accountId, req.session, req.ctx, logger, req.language(), id)
+ ctx := req.ctx.WithLogger(logger)
+ blobId, _, _, _, jerr := g.jmap.GetEmailBlobId(accountId, id, ctx)
if jerr != nil {
return req.apiErrorFromJmap(req.observeJmapError(jerr))
}
@@ -165,7 +166,7 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) { //NO
if gwerr != nil {
return gwerr
}
- return req.serveBlob(blobId, name, typ, logger, accountId, w)
+ return req.serveBlob(blobId, name, typ, ctx, accountId, w)
}
})
} else {
@@ -195,8 +196,8 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) { //NO
if len(ids) == 1 {
logger := log.From(l.Str("id", log.SafeString(id)))
-
- emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.config.maxBodyValueBytes, markAsSeen, true)
+ ctx := req.ctx.WithLogger(logger)
+ emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, ids, true, g.config.maxBodyValueBytes, markAsSeen, true, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -211,8 +212,8 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) { //NO
}
} else {
logger := log.From(l.Array("ids", log.SafeStringArray(ids)))
-
- emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.config.maxBodyValueBytes, markAsSeen, false)
+ ctx := req.ctx.WithLogger(logger)
+ emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, ids, true, g.config.maxBodyValueBytes, markAsSeen, false, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -269,7 +270,8 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
}
logger := log.From(l)
- emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0, false, false)
+ ctx := req.ctx.WithLogger(logger)
+ emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, single(id), false, 0, false, false, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -305,8 +307,8 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
Str(UriParamEmailId, log.SafeString(id))
l = contextAppender(l)
logger := log.From(l)
-
- emails, _, _, _, lang, jerr := g.jmap.GetEmails(mailAccountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0, false, false)
+ ctx := req.ctx.WithLogger(logger)
+ emails, _, _, _, lang, jerr := g.jmap.GetEmails(mailAccountId, single(id), false, 0, false, false, ctx)
if jerr != nil {
return req.apiErrorFromJmap(req.observeJmapError(jerr))
}
@@ -329,7 +331,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
return nil
}
- blob, lang, jerr := g.jmap.DownloadBlobStream(blobAccountId, attachment.BlobId, attachment.Name, attachment.Type, req.session, req.ctx, logger, req.language())
+ blob, lang, jerr := g.jmap.DownloadBlobStream(blobAccountId, attachment.BlobId, attachment.Name, attachment.Type, ctx)
if blob != nil && blob.Body != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
@@ -390,8 +392,8 @@ func (g *Groupware) getEmailsSince(w http.ResponseWriter, r *http.Request, since
}
logger := log.From(l)
-
- changes, sessionState, state, lang, jerr := g.jmap.GetEmailChanges(accountId, req.session, req.ctx, logger, req.language(), since, true, g.config.maxBodyValueBytes, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetEmailChanges(accountId, since, true, g.config.maxBodyValueBytes, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -612,6 +614,7 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) { //NOSONA
return req.error(accountId, err)
}
logger = log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId)))
+ ctx := req.ctx.WithLogger(logger)
if !filter.IsNotEmpty() {
filter = nil
@@ -619,7 +622,7 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) { //NOSONA
fetchBodies := false
- resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets(single(accountId), filter, req.session, req.ctx, logger, req.language(), offset, limit, fetchBodies, g.config.maxBodyValueBytes)
+ resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets(single(accountId), filter, offset, limit, fetchBodies, g.config.maxBodyValueBytes, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -671,13 +674,14 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
return req.errorN(allAccountIds, err)
}
logger = log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(allAccountIds)))
+ ctx := req.ctx.WithLogger(logger)
if !filter.IsNotEmpty() {
filter = nil
}
if makesSnippets {
- resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSnippets(allAccountIds, filter, req.session, req.ctx, logger, req.language(), offset, limit)
+ resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSnippets(allAccountIds, filter, offset, limit, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
@@ -717,7 +721,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
} else {
withThreads := true
- resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, req.language(), filter, limit, withThreads)
+ resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, filter, limit, withThreads, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
@@ -762,8 +766,8 @@ var draftEmailAutoMailboxRolePrecedence = []string{
jmap.JmapMailboxRoleInbox, // but if there is none, we will use the Mailbox with the inbox role instead
}
-func findDraftsMailboxId(j *jmap.Client, accountId string, req Request, logger *log.Logger) (string, Response) {
- mailboxIdsPerAccountIds, sessionState, _, lang, jerr := j.SearchMailboxIdsPerRole(single(accountId), req.session, req.ctx, logger, req.language(), draftEmailAutoMailboxRolePrecedence)
+func findDraftsMailboxId(j *jmap.Client, accountId string, req Request, ctx jmap.Context) (string, Response) {
+ mailboxIdsPerAccountIds, sessionState, _, lang, jerr := j.SearchMailboxIdsPerRole(single(accountId), draftEmailAutoMailboxRolePrecedence, ctx)
if jerr != nil {
return "", req.jmapError(accountId, jerr, sessionState, lang)
} else {
@@ -785,8 +789,8 @@ var sentEmailAutoMailboxRolePrecedence = []string{
var draftAndSentMailboxRoles = structs.Uniq(structs.Concat(draftEmailAutoMailboxRolePrecedence, sentEmailAutoMailboxRolePrecedence))
-func findSentMailboxId(j *jmap.Client, accountId string, req Request, logger *log.Logger) (string, string, Response) { //NOSONAR
- mailboxIdsPerAccountIds, sessionState, _, lang, jerr := j.SearchMailboxIdsPerRole(single(accountId), req.session, req.ctx, logger, req.language(), draftAndSentMailboxRoles)
+func findSentMailboxId(j *jmap.Client, accountId string, req Request, ctx jmap.Context) (string, string, Response) { //NOSONAR
+ mailboxIdsPerAccountIds, sessionState, _, lang, jerr := j.SearchMailboxIdsPerRole(single(accountId), draftAndSentMailboxRoles, ctx)
if jerr != nil {
return "", "", req.jmapError(accountId, jerr, sessionState, lang)
} else {
@@ -823,6 +827,7 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, gwerr)
}
logger = log.From(logger.With().Str(logAccountId, log.SafeString(accountId)))
+ ctx := req.ctx.WithLogger(logger)
var body jmap.EmailCreate
err := req.body(&body)
@@ -831,7 +836,7 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
}
if len(body.MailboxIds) < 1 {
- mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, req, logger)
+ mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, req, ctx)
if mailboxId != "" {
body.MailboxIds[mailboxId] = true
} else {
@@ -839,7 +844,7 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
}
}
- created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, "", req.session, req.ctx, logger, req.language())
+ created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, "", ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -863,6 +868,7 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
}
logger = log.From(logger.With().Str(logAccountId, log.SafeString(accountId)))
+ ctx := req.ctx.WithLogger(logger)
var body jmap.EmailCreate
err = req.body(&body)
@@ -871,7 +877,7 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
}
if len(body.MailboxIds) < 1 {
- mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, req, logger)
+ mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, req, ctx)
if mailboxId != "" {
body.MailboxIds[mailboxId] = true
} else {
@@ -879,7 +885,7 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
}
}
- created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, replaceId, req.session, req.ctx, logger, req.language())
+ created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, replaceId, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -905,6 +911,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
l.Str(UriParamEmailId, log.SafeString(emailId))
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
var body map[string]any
err = req.body(&body)
@@ -916,7 +923,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
emailId: body,
}
- result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, updates, req.session, req.ctx, logger, req.language())
+ result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, updates, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -964,6 +971,7 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request)
l.Str(UriParamEmailId, log.SafeString(emailId))
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
var body emailKeywordUpdates
err = req.body(&body)
@@ -986,7 +994,7 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request)
emailId: patch,
}
- result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
+ result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1025,6 +1033,7 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) { /
l.Str(UriParamEmailId, log.SafeString(emailId))
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
var body []string
err = req.body(&body)
@@ -1044,7 +1053,7 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) { /
emailId: patch,
}
- result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
+ result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1087,6 +1096,7 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request)
l.Str(UriParamEmailId, log.SafeString(emailId))
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
var body []string
err = req.body(&body)
@@ -1106,7 +1116,7 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request)
emailId: patch,
}
- result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
+ result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1147,8 +1157,8 @@ func (g *Groupware) DeleteEmail(w http.ResponseWriter, r *http.Request) {
l.Str(UriParamEmailId, log.SafeString(emailId))
logger := log.From(l)
-
- resp, sessionState, state, lang, jerr := g.jmap.DeleteEmails(accountId, []string{emailId}, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ resp, sessionState, state, lang, jerr := g.jmap.DeleteEmails(accountId, single(emailId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1196,8 +1206,8 @@ func (g *Groupware) DeleteEmails(w http.ResponseWriter, r *http.Request) {
l.Array("emailIds", log.SafeStringArray(emailIds))
logger := log.From(l)
-
- resp, sessionState, state, lang, jerr := g.jmap.DeleteEmails(accountId, emailIds, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ resp, sessionState, state, lang, jerr := g.jmap.DeleteEmails(accountId, emailIds, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1245,7 +1255,8 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) { //NOSONA
moveToMailboxId, _ := req.getStringParam(QueryParamMoveToMailboxId, "")
if moveFromMailboxId == "" || moveToMailboxId == "" {
- draftsMailboxId, sentMailboxId, resp := findSentMailboxId(g.jmap, accountId, req, req.logger)
+ ctx := req.ctx.WithLogger(log.From(l))
+ draftsMailboxId, sentMailboxId, resp := findSentMailboxId(g.jmap, accountId, req, ctx)
if draftsMailboxId != "" && sentMailboxId != "" {
if moveFromMailboxId == "" {
moveFromMailboxId = draftsMailboxId
@@ -1265,8 +1276,8 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) { //NOSONA
}
logger := log.From(l)
-
- resp, sessionState, state, lang, jerr := g.jmap.SubmitEmail(accountId, identityId, emailId, move, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ resp, sessionState, state, lang, jerr := g.jmap.SubmitEmail(accountId, identityId, emailId, move, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -1366,10 +1377,11 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) { //N
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
reqId := req.GetRequestId()
getEmailsBefore := time.Now()
- emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, true, g.config.maxBodyValueBytes, false, false)
+ emails, _, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, single(id), true, g.config.maxBodyValueBytes, false, false, ctx)
getEmailsDuration := time.Since(getEmailsBefore)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
@@ -1394,7 +1406,8 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) { //N
g.job(logger, RelationTypeSameSender, func(jobId uint64, l *log.Logger) {
before := time.Now()
- resultsByAccountId, _, _, lang, jerr := g.jmap.QueryEmails(single(accountId), filter, req.session, bgctx, l, req.language(), 0, limit, false, g.config.maxBodyValueBytes)
+ ctx = ctx.WithLogger(logger).WithContext(bgctx)
+ resultsByAccountId, _, _, lang, jerr := g.jmap.QueryEmails(single(accountId), filter, 0, limit, false, g.config.maxBodyValueBytes, ctx)
if results, ok := resultsByAccountId[accountId]; ok {
duration := time.Since(before)
if jerr != nil {
@@ -1415,7 +1428,8 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) { //N
g.job(logger, RelationTypeSameThread, func(jobId uint64, l *log.Logger) {
before := time.Now()
- emails, _, _, lang, jerr := g.jmap.EmailsInThread(accountId, email.ThreadId, req.session, bgctx, l, req.language(), false, g.config.maxBodyValueBytes)
+ ctx = ctx.WithLogger(logger).WithContext(bgctx)
+ emails, _, _, lang, jerr := g.jmap.EmailsInThread(accountId, email.ThreadId, false, g.config.maxBodyValueBytes, ctx)
duration := time.Since(before)
if jerr != nil {
_ = req.observeJmapError(jerr)
@@ -1683,8 +1697,9 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- emailsSummariesByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, req.language(), filter, limit, true)
+ emailsSummariesByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, filter, limit, true, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_events.go b/services/groupware/pkg/groupware/api_events.go
index fd4feb9301..431febd46b 100644
--- a/services/groupware/pkg/groupware/api_events.go
+++ b/services/groupware/pkg/groupware/api_events.go
@@ -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.parseUIntParam(QueryParamOffset, 0)
+ offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
if err != nil {
return req.error(accountId, err)
}
if ok {
- l = l.Uint(QueryParamOffset, offset)
+ l = l.Int(QueryParamOffset, offset)
}
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaults.contactLimit)
@@ -46,7 +46,8 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
sortBy := []jmap.CalendarEventComparator{{Property: jmap.CalendarEventPropertyUpdated, IsAscending: false}}
logger := log.From(l)
- eventsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryCalendarEvents(single(accountId), req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
+ ctx := req.ctx.WithLogger(logger)
+ eventsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryCalendarEvents(single(accountId), filter, sortBy, offset, limit, true, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -82,7 +83,8 @@ func (g *Groupware) GetEventChanges(w http.ResponseWriter, r *http.Request) {
l = l.Str(HeaderParamSince, log.SafeString(string(sinceState)))
logger := log.From(l)
- changes, sessionState, state, lang, jerr := g.jmap.GetCalendarEventChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetCalendarEventChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -108,7 +110,8 @@ func (g *Groupware) CreateCalendarEvent(w http.ResponseWriter, r *http.Request)
}
logger := log.From(l)
- created, sessionState, state, lang, jerr := g.jmap.CreateCalendarEvent(accountId, req.session, req.ctx, logger, req.language(), create)
+ ctx := req.ctx.WithLogger(logger)
+ created, sessionState, state, lang, jerr := g.jmap.CreateCalendarEvent(accountId, create, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -131,8 +134,8 @@ func (g *Groupware) DeleteCalendarEvent(w http.ResponseWriter, r *http.Request)
l.Str(UriParamEventId, log.SafeString(eventId))
logger := log.From(l)
-
- deleted, sessionState, state, lang, jerr := g.jmap.DeleteCalendarEvent(accountId, []string{eventId}, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ deleted, sessionState, state, lang, jerr := g.jmap.DeleteCalendarEvent(accountId, single(eventId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -174,8 +177,8 @@ func (g *Groupware) ParseIcalBlob(w http.ResponseWriter, r *http.Request) {
blobIds := strings.Split(blobId, ",")
l := req.logger.With().Array(UriParamBlobId, log.SafeStringArray(blobIds))
logger := log.From(l)
-
- resp, sessionState, state, lang, jerr := g.jmap.ParseICalendarBlob(accountId, req.session, req.ctx, logger, req.language(), blobIds)
+ ctx := req.ctx.WithLogger(logger)
+ resp, sessionState, state, lang, jerr := g.jmap.ParseICalendarBlob(accountId, blobIds, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_identity.go b/services/groupware/pkg/groupware/api_identity.go
index 0ef0a2be6f..bdbff5fa39 100644
--- a/services/groupware/pkg/groupware/api_identity.go
+++ b/services/groupware/pkg/groupware/api_identity.go
@@ -7,7 +7,6 @@ import (
"github.com/opencloud-eu/opencloud/pkg/jmap"
"github.com/opencloud-eu/opencloud/pkg/log"
- "github.com/opencloud-eu/opencloud/pkg/structs"
)
// Get the list of identities that are associated with an account.
@@ -18,7 +17,8 @@ func (g *Groupware) GetIdentities(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
- res, sessionState, state, lang, jerr := g.jmap.GetAllIdentities(accountId, req.session, req.ctx, logger, req.language())
+ ctx := req.ctx.WithLogger(logger)
+ res, sessionState, state, lang, jerr := g.jmap.GetAllIdentities(accountId, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -37,7 +37,8 @@ func (g *Groupware) GetIdentityById(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId).Str(logIdentityId, id))
- res, sessionState, state, lang, jerr := g.jmap.GetIdentities(accountId, req.session, req.ctx, logger, req.language(), []string{id})
+ ctx := req.ctx.WithLogger(logger)
+ res, sessionState, state, lang, jerr := g.jmap.GetIdentities(accountId, single(id), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -56,14 +57,15 @@ func (g *Groupware) AddIdentity(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ ctx := req.ctx.WithLogger(logger)
- var identity jmap.Identity
+ var identity jmap.IdentityChange
err = req.body(&identity)
if err != nil {
return req.error(accountId, err)
}
- created, sessionState, state, lang, jerr := g.jmap.CreateIdentity(accountId, req.session, req.ctx, logger, req.language(), identity)
+ created, sessionState, state, lang, jerr := g.jmap.CreateIdentity(accountId, identity, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -77,15 +79,21 @@ func (g *Groupware) ModifyIdentity(w http.ResponseWriter, r *http.Request) {
if err != nil {
return req.error(accountId, err)
}
- logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ id, err := req.PathParamDoc(UriParamIdentityId, "The unique identifier of the Identity to modify")
+ if err != nil {
+ return req.error(accountId, err)
+ }
- var identity jmap.Identity
+ logger := log.From(req.logger.With().Str(logAccountId, accountId).Str(UriParamIdentityId, log.SafeString(id)))
+ ctx := req.ctx.WithLogger(logger)
+
+ var identity jmap.IdentityChange
err = req.body(&identity)
if err != nil {
return req.error(accountId, err)
}
- updated, sessionState, state, lang, jerr := g.jmap.UpdateIdentity(accountId, req.session, req.ctx, logger, req.language(), identity)
+ updated, sessionState, state, lang, jerr := g.jmap.UpdateIdentity(accountId, id, identity, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -100,7 +108,6 @@ func (g *Groupware) DeleteIdentity(w http.ResponseWriter, r *http.Request) {
if err != nil {
return req.error(accountId, err)
}
- logger := log.From(req.logger.With().Str(logAccountId, accountId))
id, err := req.PathParam(UriParamIdentityId)
if err != nil {
@@ -111,18 +118,19 @@ func (g *Groupware) DeleteIdentity(w http.ResponseWriter, r *http.Request) {
return req.parameterErrorResponse(single(accountId), UriParamIdentityId, fmt.Sprintf("Invalid value for path parameter '%v': '%s': %s", UriParamIdentityId, log.SafeString(id), "empty list of identity ids"))
}
- deletion, sessionState, state, lang, jerr := g.jmap.DeleteIdentity(accountId, req.session, req.ctx, logger, req.language(), ids)
+ logger := log.From(req.logger.With().Str(logAccountId, accountId).Array(UriParamIdentityId, log.SafeStringArray(ids)))
+ ctx := req.ctx.WithLogger(logger)
+
+ notDeleted, sessionState, state, lang, jerr := g.jmap.DeleteIdentity(accountId, ids, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
- notDeletedIds := structs.Missing(ids, deletion)
- if len(notDeletedIds) == 0 {
+ if len(notDeleted) == 0 {
return req.noContent(accountId, sessionState, IdentityResponseObjectType, state)
} else {
- logger.Error().Array("not-deleted", log.SafeStringArray(notDeletedIds)).Msgf("failed to delete %d identities", len(notDeletedIds))
- return req.errorS(accountId, req.apiError(&ErrorFailedToDeleteSomeIdentities,
- withMeta(map[string]any{"ids": notDeletedIds})), sessionState)
+ logger.Error().Msgf("failed to delete %d identities", len(notDeleted))
+ return req.errorS(accountId, req.apiError(&ErrorFailedToDeleteSomeIdentities), sessionState)
}
})
}
@@ -150,7 +158,8 @@ func (g *Groupware) GetIdentityChanges(w http.ResponseWriter, r *http.Request) {
l = l.Str(HeaderParamSince, log.SafeString(string(sinceState)))
logger := log.From(l)
- changes, sessionState, state, lang, jerr := g.jmap.GetIdentityChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ ctx := req.ctx.WithLogger(logger)
+ changes, sessionState, state, lang, jerr := g.jmap.GetIdentityChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_index.go b/services/groupware/pkg/groupware/api_index.go
index 015152054d..b17b0718b9 100644
--- a/services/groupware/pkg/groupware/api_index.go
+++ b/services/groupware/pkg/groupware/api_index.go
@@ -148,7 +148,7 @@ func (g *Groupware) Index(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountIds := req.AllAccountIds()
- boot, sessionState, state, lang, err := g.jmap.GetBootstrap(accountIds, req.session, req.ctx, req.logger, req.language())
+ boot, sessionState, state, lang, err := g.jmap.GetBootstrap(accountIds, req.ctx)
if err != nil {
return req.jmapErrorN(accountIds, err, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_mailbox.go b/services/groupware/pkg/groupware/api_mailbox.go
index ed30ce3e84..13801cdf53 100644
--- a/services/groupware/pkg/groupware/api_mailbox.go
+++ b/services/groupware/pkg/groupware/api_mailbox.go
@@ -30,7 +30,7 @@ func (g *Groupware) GetMailbox(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
- mailboxes, sessionState, state, lang, jerr := g.jmap.GetMailbox(accountId, req.session, req.ctx, req.logger, req.language(), []string{mailboxId})
+ mailboxes, sessionState, state, lang, jerr := g.jmap.GetMailbox(accountId, single(mailboxId), req.ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -83,9 +83,10 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) { //NOS
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ ctx := req.ctx.WithLogger(logger)
if hasCriteria {
- mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(single(accountId), req.session, req.ctx, logger, req.language(), filter)
+ mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(single(accountId), filter, ctx)
if err != nil {
return req.jmapError(accountId, err, sessionState, lang)
}
@@ -96,7 +97,7 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) { //NOS
return req.notFound(accountId, sessionState, MailboxResponseObjectType, state)
}
} else {
- mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(single(accountId), req.session, req.ctx, logger, req.language())
+ mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(single(accountId), ctx)
if err != nil {
return req.jmapError(accountId, err, sessionState, lang)
}
@@ -117,6 +118,7 @@ func (g *Groupware) GetMailboxesForAllAccounts(w http.ResponseWriter, r *http.Re
return req.noopN(nil) // when the user has no accounts
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)))
+ ctx := req.ctx.WithLogger(logger)
var filter jmap.MailboxFilterCondition
hasCriteria := false
@@ -134,13 +136,13 @@ func (g *Groupware) GetMailboxesForAllAccounts(w http.ResponseWriter, r *http.Re
}
if hasCriteria {
- mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, req.language(), filter)
+ mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(accountIds, filter, ctx)
if err != nil {
return req.jmapErrorN(accountIds, err, sessionState, lang)
}
return req.respondN(accountIds, sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state)
} else {
- mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(accountIds, req.session, req.ctx, logger, req.language())
+ mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(accountIds, ctx)
if err != nil {
return req.jmapErrorN(accountIds, err, sessionState, lang)
}
@@ -163,12 +165,13 @@ func (g *Groupware) GetMailboxByRoleForAllAccounts(w http.ResponseWriter, r *htt
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)).Str("role", role))
+ ctx := req.ctx.WithLogger(logger)
filter := jmap.MailboxFilterCondition{
Role: role,
}
- mailboxesByAccountId, sessionState, state, lang, jerr := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, req.language(), filter)
+ mailboxesByAccountId, sessionState, state, lang, jerr := g.jmap.SearchMailboxes(accountIds, filter, ctx)
if jerr != nil {
return req.jmapErrorN(accountIds, jerr, sessionState, lang)
}
@@ -202,6 +205,7 @@ func (g *Groupware) GetMailboxChanges(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
// As for Emails and Contacts, one would expect this request without any prior state to
// be usable to list all the objects that currently exist, but that is not the case for
@@ -216,7 +220,7 @@ func (g *Groupware) GetMailboxChanges(w http.ResponseWriter, r *http.Request) {
)))
}
- changes, sessionState, state, lang, jerr := g.jmap.GetMailboxChanges(accountId, req.session, req.ctx, logger, req.language(), sinceState, maxChanges)
+ changes, sessionState, state, lang, jerr := g.jmap.GetMailboxChanges(accountId, sinceState, maxChanges, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -254,9 +258,10 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
sinceStateMap := structs.MapValues(sinceStateStrMap, toState)
- changesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language(), sinceStateMap, maxChanges)
+ changesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, sinceStateMap, maxChanges, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
@@ -273,8 +278,9 @@ func (g *Groupware) GetMailboxRoles(w http.ResponseWriter, r *http.Request) {
allAccountIds := req.AllAccountIds()
l.Array(logAccountId, log.SafeStringArray(allAccountIds))
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- rolesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxRolesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language())
+ rolesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxRolesForMultipleAccounts(allAccountIds, ctx)
if jerr != nil {
return req.jmapErrorN(allAccountIds, jerr, sessionState, lang)
}
@@ -305,8 +311,9 @@ func (g *Groupware) UpdateMailbox(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- updated, sessionState, state, lang, jerr := g.jmap.UpdateMailbox(accountId, req.session, req.ctx, logger, req.language(), mailboxId, "", body)
+ updated, sessionState, state, lang, jerr := g.jmap.UpdateMailbox(accountId, mailboxId, body, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -330,8 +337,9 @@ func (g *Groupware) CreateMailbox(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- created, sessionState, state, lang, jerr := g.jmap.CreateMailbox(accountId, req.session, req.ctx, logger, req.language(), "", body)
+ created, sessionState, state, lang, jerr := g.jmap.CreateMailbox(accountId, body, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -365,8 +373,9 @@ func (g *Groupware) DeleteMailbox(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(l)
+ ctx := req.ctx.WithLogger(logger)
- deleted, sessionState, state, lang, jerr := g.jmap.DeleteMailboxes(accountId, req.session, req.ctx, logger, req.language(), "", mailboxIds)
+ deleted, sessionState, state, lang, jerr := g.jmap.DeleteMailboxes(accountId, mailboxIds, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_objects.go b/services/groupware/pkg/groupware/api_objects.go
index cead96685b..7af25a0f39 100644
--- a/services/groupware/pkg/groupware/api_objects.go
+++ b/services/groupware/pkg/groupware/api_objects.go
@@ -120,8 +120,11 @@ func (g *Groupware) GetObjects(w http.ResponseWriter, r *http.Request) { //NOSON
}
logger := log.From(l)
- objs, sessionState, state, lang, jerr := g.jmap.GetObjects(accountId, req.session, req.ctx, logger, req.language(),
- mailboxIds, emailIds, addressbookIds, contactIds, calendarIds, eventIds, quotaIds, identityIds, emailSubmissionIds)
+ ctx := req.ctx.WithLogger(logger)
+ objs, sessionState, state, lang, jerr := g.jmap.GetObjects(accountId,
+ mailboxIds, emailIds, addressbookIds, contactIds, calendarIds, eventIds, quotaIds, identityIds, emailSubmissionIds,
+ ctx,
+ )
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_quota.go b/services/groupware/pkg/groupware/api_quota.go
index f1ac84c169..5772ba1888 100644
--- a/services/groupware/pkg/groupware/api_quota.go
+++ b/services/groupware/pkg/groupware/api_quota.go
@@ -19,8 +19,9 @@ func (g *Groupware) GetQuota(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ ctx := req.ctx.WithLogger(logger)
- res, sessionState, state, lang, jerr := g.jmap.GetQuotas(single(accountId), req.session, req.ctx, logger, req.language())
+ res, sessionState, state, lang, jerr := g.jmap.GetQuotas(single(accountId), ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -47,8 +48,9 @@ func (g *Groupware) GetQuotaForAllAccounts(w http.ResponseWriter, r *http.Reques
return req.noopN(accountIds) // user has no accounts
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)))
+ ctx := req.ctx.WithLogger(logger)
- res, sessionState, state, lang, jerr := g.jmap.GetQuotas(accountIds, req.session, req.ctx, logger, req.language())
+ res, sessionState, state, lang, jerr := g.jmap.GetQuotas(accountIds, ctx)
if jerr != nil {
return req.jmapErrorN(accountIds, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/api_vacation.go b/services/groupware/pkg/groupware/api_vacation.go
index 10627e2f9c..1cd99307d0 100644
--- a/services/groupware/pkg/groupware/api_vacation.go
+++ b/services/groupware/pkg/groupware/api_vacation.go
@@ -20,8 +20,9 @@ func (g *Groupware) GetVacation(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ ctx := req.ctx.WithLogger(logger)
- res, sessionState, state, lang, jerr := g.jmap.GetVacationResponse(accountId, req.session, req.ctx, logger, req.language())
+ res, sessionState, state, lang, jerr := g.jmap.GetVacationResponse(accountId, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
@@ -40,14 +41,15 @@ func (g *Groupware) SetVacation(w http.ResponseWriter, r *http.Request) {
return req.error(accountId, err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
+ ctx := req.ctx.WithLogger(logger)
- var body jmap.VacationResponsePayload
+ var body jmap.VacationResponseChange
err = req.body(&body)
if err != nil {
return req.error(accountId, err)
}
- res, sessionState, state, lang, jerr := g.jmap.SetVacationResponse(accountId, body, req.session, req.ctx, logger, req.language())
+ res, sessionState, state, lang, jerr := g.jmap.SetVacationResponse(accountId, body, ctx)
if jerr != nil {
return req.jmapError(accountId, jerr, sessionState, lang)
}
diff --git a/services/groupware/pkg/groupware/error.go b/services/groupware/pkg/groupware/error.go
index 199a0d6631..9a45694164 100644
--- a/services/groupware/pkg/groupware/error.go
+++ b/services/groupware/pkg/groupware/error.go
@@ -632,7 +632,7 @@ func errorId(r *http.Request, ctx context.Context) string {
}
func (r *Request) errorId() string {
- return errorId(r.r, r.ctx)
+ return errorId(r.r, r.cotx)
}
func apiError(id string, gwerr GroupwareError, options ...ErrorOpt) *Error {
diff --git a/services/groupware/pkg/groupware/framework.go b/services/groupware/pkg/groupware/framework.go
index 1869759098..703a35fd8b 100644
--- a/services/groupware/pkg/groupware/framework.go
+++ b/services/groupware/pkg/groupware/framework.go
@@ -582,6 +582,15 @@ func (g *Groupware) serveError(w http.ResponseWriter, r *http.Request, error *Er
}
}
+func newContext(session *jmap.Session, cotx context.Context, logger *log.Logger, acceptLanguage string) jmap.Context {
+ return jmap.Context{
+ Session: session,
+ Context: cotx,
+ Logger: logger,
+ AcceptLanguage: acceptLanguage,
+ }
+}
+
// Execute a closure with a JMAP Session.
//
// Returns
@@ -589,23 +598,23 @@ func (g *Groupware) serveError(w http.ResponseWriter, r *http.Request, error *Er
// - if an error occurs, after which timestamp a retry is allowed
// - whether the request was sent to the server or not
func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler func(r Request) Response) (Response, time.Time, bool) {
- ctx := r.Context()
- sl := g.logger.SubloggerWithRequestID(ctx)
+ cotx := r.Context()
+ sl := g.logger.SubloggerWithRequestID(cotx)
logger := &sl
// retrieve the current user from the inbound request
var user user
{
var err error
- user, err = g.userProvider.GetUser(r, ctx, logger)
+ user, err = g.userProvider.GetUser(r, cotx, logger)
if err != nil {
g.metrics.AuthenticationFailureCounter.Inc()
- g.serveError(w, r, apiError(errorId(r, ctx), ErrorInvalidAuthentication), time.Time{})
+ g.serveError(w, r, apiError(errorId(r, cotx), ErrorInvalidAuthentication), time.Time{})
return Response{}, time.Time{}, false
}
if user == nil {
g.metrics.AuthenticationFailureCounter.Inc()
- g.serveError(w, r, apiError(errorId(r, ctx), ErrorMissingAuthentication), time.Time{})
+ g.serveError(w, r, apiError(errorId(r, cotx), ErrorMissingAuthentication), time.Time{})
return Response{}, time.Time{}, false
}
@@ -615,10 +624,10 @@ func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler
// retrieve a JMAP Session for that user
var session jmap.Session
{
- s, ok, gwerr, retryAfter := g.session(ctx, user, logger)
+ s, ok, gwerr, retryAfter := g.session(cotx, user, logger)
if gwerr != nil {
g.metrics.SessionFailureCounter.Inc()
- errorId := errorId(r, ctx)
+ errorId := errorId(r, cotx)
logger.Error().Str("code", gwerr.Code).Str("error", gwerr.Title).Str("detail", gwerr.Detail).Str(logErrorId, errorId).Msg("failed to determine JMAP session")
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
return Response{}, retryAfter, false
@@ -628,7 +637,7 @@ func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler
} else {
// no session = authentication failed
g.metrics.SessionFailureCounter.Inc()
- errorId := errorId(r, ctx)
+ errorId := errorId(r, cotx)
logger.Error().Str(logErrorId, errorId).Msg("could not authenticate, failed to find Session")
gwerr = &ErrorInvalidAuthentication
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
@@ -638,11 +647,16 @@ func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler
decoratedLogger := decorateLogger(logger, session)
+ language := r.Header.Get("Accept-Language")
+
+ ctx := newContext(&session, cotx, decoratedLogger, language)
+
// build the Request object
req := Request{
g: g,
user: user,
r: r,
+ cotx: cotx,
ctx: ctx,
logger: decoratedLogger,
session: &session,
@@ -735,32 +749,32 @@ func (g *Groupware) respond(w http.ResponseWriter, r *http.Request, handler func
}
func (g *Groupware) stream(w http.ResponseWriter, r *http.Request, handler func(r Request, w http.ResponseWriter) *Error) {
- ctx := r.Context()
- sl := g.logger.SubloggerWithRequestID(ctx)
+ cotx := r.Context()
+ sl := g.logger.SubloggerWithRequestID(cotx)
logger := &sl
- user, err := g.userProvider.GetUser(r, ctx, logger)
+ user, err := g.userProvider.GetUser(r, cotx, logger)
if err != nil {
- g.serveError(w, r, apiError(errorId(r, ctx), ErrorInvalidAuthentication), time.Time{})
+ g.serveError(w, r, apiError(errorId(r, cotx), ErrorInvalidAuthentication), time.Time{})
return
}
if user == nil {
- g.serveError(w, r, apiError(errorId(r, ctx), ErrorMissingAuthentication), time.Time{})
+ g.serveError(w, r, apiError(errorId(r, cotx), ErrorMissingAuthentication), time.Time{})
return
}
logger = log.From(logger.With().Str(logUserId, log.SafeString(user.GetId())))
- session, ok, gwerr, retryAfter := g.session(ctx, user, logger)
+ session, ok, gwerr, retryAfter := g.session(cotx, user, logger)
if gwerr != nil {
- errorId := errorId(r, ctx)
+ errorId := errorId(r, cotx)
logger.Error().Str("code", gwerr.Code).Str("error", gwerr.Title).Str("detail", gwerr.Detail).Str(logErrorId, errorId).Msg("failed to determine JMAP session")
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
return
}
if !ok {
// no session = authentication failed
- errorId := errorId(r, ctx)
+ errorId := errorId(r, cotx)
logger.Error().Str(logErrorId, errorId).Msg("could not authenticate, failed to find Session")
gwerr = &ErrorInvalidAuthentication
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
@@ -769,13 +783,18 @@ func (g *Groupware) stream(w http.ResponseWriter, r *http.Request, handler func(
decoratedLogger := decorateLogger(logger, session)
+ language := r.Header.Get("Accept-Language")
+
+ ctx := newContext(&session, cotx, decoratedLogger, language)
+
req := Request{
g: g,
user: user,
r: r,
- ctx: ctx,
+ cotx: cotx,
logger: decoratedLogger,
session: &session,
+ ctx: ctx,
}
apierr := handler(req, w)
diff --git a/services/groupware/pkg/groupware/request.go b/services/groupware/pkg/groupware/request.go
index b648fd17c4..9fd18ae55f 100644
--- a/services/groupware/pkg/groupware/request.go
+++ b/services/groupware/pkg/groupware/request.go
@@ -39,9 +39,10 @@ type Request struct {
g *Groupware
user user
r *http.Request
- ctx context.Context
+ cotx context.Context
logger *log.Logger
session *jmap.Session
+ ctx jmap.Context
}
func isDefaultAccountId(accountId string) bool {
@@ -57,11 +58,11 @@ func (r *Request) GetUser() user {
}
func (r *Request) GetRequestId() string {
- return chimiddleware.GetReqID(r.ctx)
+ return chimiddleware.GetReqID(r.cotx)
}
func (r *Request) GetTraceId() string {
- return groupwaremiddleware.GetTraceID(r.ctx)
+ return groupwaremiddleware.GetTraceID(r.cotx)
}
var (
diff --git a/services/groupware/pkg/groupware/request_test.go b/services/groupware/pkg/groupware/request_test.go
index 277e379b3c..69d40cb51f 100644
--- a/services/groupware/pkg/groupware/request_test.go
+++ b/services/groupware/pkg/groupware/request_test.go
@@ -1,7 +1,6 @@
package groupware
import (
- "context"
"fmt"
"net/http"
"net/url"
@@ -12,8 +11,8 @@ import (
func TestParseSort(t *testing.T) {
req := Request{
- r: &http.Request{},
- ctx: context.Background(),
+ r: &http.Request{},
+ cotx: t.Context(),
}
require := require.New(t)
{
@@ -84,7 +83,7 @@ func TestParseMap(t *testing.T) {
{
u, err := url.Parse(tt.uri)
require.NoError(err)
- req = Request{r: &http.Request{URL: u}, ctx: context.Background()}
+ req = Request{r: &http.Request{URL: u}, cotx: t.Context()}
}
res, ok, err := req.parseMapParam("map")
require.Nil(err)