groupware: refactor response objects to take a list of accountIds

This commit is contained in:
Pascal Bleser
2025-12-04 09:38:00 +01:00
parent a65fec417f
commit 196ee7b3e4
15 changed files with 409 additions and 362 deletions

View File

@@ -31,9 +31,9 @@ func (g *Groupware) GetAccount(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, account, err := req.GetAccountForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
return etagResponse(accountId, account, req.session.State, AccountResponseObjectType, jmap.State(req.session.State), "")
return etagResponse(single(accountId), account, req.session.State, AccountResponseObjectType, jmap.State(req.session.State), "")
})
}
@@ -66,7 +66,7 @@ func (g *Groupware) GetAccounts(w http.ResponseWriter, r *http.Request) {
}
// sort on accountId to have a stable order that remains the same with every query
slices.SortFunc(list, func(a, b AccountWithId) int { return strings.Compare(a.AccountId, b.AccountId) })
return etagResponse(joinAccountIds(structs.Map(list, func(a AccountWithId) string { return a.AccountId })), list, req.session.State, AccountResponseObjectType, jmap.State(req.session.State), "")
return etagResponse(structs.Map(list, func(a AccountWithId) string { return a.AccountId }), list, req.session.State, AccountResponseObjectType, jmap.State(req.session.State), "")
})
}
@@ -75,7 +75,7 @@ func (g *Groupware) GetAccountsWithTheirIdentities(w http.ResponseWriter, r *htt
allAccountIds := req.AllAccountIds()
resp, sessionState, state, lang, err := g.jmap.GetIdentitiesForAllAccounts(allAccountIds, req.session, req.ctx, req.logger, req.language())
if err != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), err)
return req.errorResponseFromJmap(allAccountIds, err)
}
list := make([]AccountWithIdAndIdentities, len(req.session.Accounts))
i := 0
@@ -94,7 +94,7 @@ func (g *Groupware) GetAccountsWithTheirIdentities(w http.ResponseWriter, r *htt
}
// sort on accountId to have a stable order that remains the same with every query
slices.SortFunc(list, func(a, b AccountWithIdAndIdentities) int { return strings.Compare(a.AccountId, b.AccountId) })
return etagResponse(joinAccountIds(structs.Map(list, func(a AccountWithIdAndIdentities) string { return a.AccountId })), list, sessionState, AccountResponseObjectType, state, lang)
return etagResponse(structs.Map(list, func(a AccountWithIdAndIdentities) string { return a.AccountId }), list, sessionState, AccountResponseObjectType, state, lang)
})
}

View File

@@ -18,13 +18,13 @@ func (g *Groupware) GetBlobMeta(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForBlob()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l := req.logger.With().Str(logAccountId, accountId)
blobId := chi.URLParam(req.r, UriParamBlobId)
if blobId == "" {
return req.parameterErrorResponse(accountId, UriParamBlobId, fmt.Sprintf("Invalid value for path parameter '%v': empty", UriParamBlobId))
return req.parameterErrorResponse(single(accountId), UriParamBlobId, fmt.Sprintf("Invalid value for path parameter '%v': empty", UriParamBlobId))
}
l = l.Str(UriParamBlobId, blobId)
@@ -32,13 +32,13 @@ func (g *Groupware) GetBlobMeta(w http.ResponseWriter, r *http.Request) {
res, sessionState, state, lang, jerr := g.jmap.GetBlobMetadata(accountId, req.session, req.ctx, logger, req.language(), blobId)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
blob := res
if blob == nil {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
return etagResponse(accountId, res, sessionState, BlobResponseObjectType, state, lang)
return etagResponse(single(accountId), res, sessionState, BlobResponseObjectType, state, lang)
})
}
@@ -57,16 +57,16 @@ func (g *Groupware) UploadBlob(w http.ResponseWriter, r *http.Request) {
accountId, err := req.GetAccountIdForBlob()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(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)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return response(accountId, resp, req.session.State, lang)
return response(single(accountId), resp, req.session.State, lang)
})
}

View File

@@ -34,10 +34,10 @@ func (g *Groupware) GetCalendars(w http.ResponseWriter, r *http.Request) {
calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, req.session, req.ctx, req.logger, req.language(), nil)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, calendars, sessionState, CalendarResponseObjectType, state, lang)
return etagResponse(single(accountId), calendars, sessionState, CalendarResponseObjectType, state, lang)
})
}
@@ -74,13 +74,13 @@ func (g *Groupware) GetCalendarById(w http.ResponseWriter, r *http.Request) {
logger := log.From(l)
calendars, sessionState, state, lang, jerr := g.jmap.GetCalendars(accountId, req.session, req.ctx, logger, req.language(), []string{calendarId})
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(calendars.NotFound) > 0 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
} else {
return etagResponse(accountId, calendars.Calendars[0], sessionState, CalendarResponseObjectType, state, lang)
return etagResponse(single(accountId), calendars.Calendars[0], sessionState, CalendarResponseObjectType, state, lang)
}
})
}
@@ -115,7 +115,7 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
offset, ok, err := req.parseUIntParam(QueryParamOffset, 0)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamOffset, offset)
@@ -123,7 +123,7 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaultContactLimit)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamLimit, limit)
@@ -135,15 +135,15 @@ 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([]string{accountId}, req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
eventsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryCalendarEvents(single(accountId), req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if events, ok := eventsByAccountId[accountId]; ok {
return etagResponse(accountId, events, sessionState, EventResponseObjectType, state, lang)
return etagResponse(single(accountId), events, sessionState, EventResponseObjectType, state, lang)
} else {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
})
}
@@ -163,15 +163,15 @@ func (g *Groupware) CreateCalendarEvent(w http.ResponseWriter, r *http.Request)
var create jmap.CalendarEvent
err := req.body(&create)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(l)
created, sessionState, state, lang, jerr := g.jmap.CreateCalendarEvent(accountId, req.session, req.ctx, logger, req.language(), create)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, EventResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, EventResponseObjectType, state, lang)
})
}
@@ -191,25 +191,25 @@ func (g *Groupware) DeleteCalendarEvent(w http.ResponseWriter, r *http.Request)
deleted, sessionState, state, _, jerr := g.jmap.DeleteCalendarEvent(accountId, []string{eventId}, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
for _, e := range deleted {
desc := e.Description
if desc != "" {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteContact,
withDetail(e.Description),
), sessionState)
} else {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteContact,
), sessionState)
}
}
return noContentResponseWithEtag(accountId, sessionState, EventResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, EventResponseObjectType, state)
})
}
@@ -217,7 +217,7 @@ func (g *Groupware) ParseIcalBlob(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForBlob()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
blobId := chi.URLParam(r, UriParamBlobId)
@@ -228,8 +228,8 @@ func (g *Groupware) ParseIcalBlob(w http.ResponseWriter, r *http.Request) {
resp, sessionState, state, lang, jerr := g.jmap.ParseICalendarBlob(accountId, req.session, req.ctx, logger, req.language(), blobIds)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, resp, sessionState, EventResponseObjectType, state, lang)
return etagResponse(single(accountId), resp, sessionState, EventResponseObjectType, state, lang)
})
}

View File

@@ -34,10 +34,10 @@ func (g *Groupware) GetAddressbooks(w http.ResponseWriter, r *http.Request) {
addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, req.session, req.ctx, req.logger, req.language(), nil)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, addressbooks, sessionState, AddressBookResponseObjectType, state, lang)
return etagResponse(single(accountId), addressbooks, sessionState, AddressBookResponseObjectType, state, lang)
})
}
@@ -74,13 +74,13 @@ func (g *Groupware) GetAddressbook(w http.ResponseWriter, r *http.Request) {
logger := log.From(l)
addressbooks, sessionState, state, lang, jerr := g.jmap.GetAddressbooks(accountId, req.session, req.ctx, logger, req.language(), []string{addressBookId})
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(addressbooks.NotFound) > 0 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
} else {
return etagResponse(accountId, addressbooks, sessionState, AddressBookResponseObjectType, state, lang)
return etagResponse(single(accountId), addressbooks, sessionState, AddressBookResponseObjectType, state, lang)
}
})
}
@@ -115,7 +115,7 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
offset, ok, err := req.parseUIntParam(QueryParamOffset, 0)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamOffset, offset)
@@ -123,7 +123,7 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaultContactLimit)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamLimit, limit)
@@ -135,15 +135,15 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
sortBy := []jmap.ContactCardComparator{{Property: jscontact.ContactCardPropertyUpdated, IsAscending: false}}
logger := log.From(l)
contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards([]string{accountId}, req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
contactsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryContactCards(single(accountId), req.session, req.ctx, logger, req.language(), filter, sortBy, offset, limit)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if contacts, ok := contactsByAccountId[accountId]; ok {
return etagResponse(accountId, contacts, sessionState, ContactResponseObjectType, state, lang)
return etagResponse(single(accountId), contacts, sessionState, ContactResponseObjectType, state, lang)
} else {
return etagNotFoundResponse(accountId, sessionState, ContactResponseObjectType, state, lang)
return etagNotFoundResponse(single(accountId), sessionState, ContactResponseObjectType, state, lang)
}
})
}
@@ -163,13 +163,13 @@ func (g *Groupware) GetContactById(w http.ResponseWriter, r *http.Request) {
logger := log.From(l)
contactsById, sessionState, state, lang, jerr := g.jmap.GetContactCardsById(accountId, req.session, req.ctx, logger, req.language(), []string{contactId})
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if contact, ok := contactsById[contactId]; ok {
return etagResponse(accountId, contact, sessionState, ContactResponseObjectType, state, lang)
return etagResponse(single(accountId), contact, sessionState, ContactResponseObjectType, state, lang)
} else {
return etagNotFoundResponse(accountId, sessionState, ContactResponseObjectType, state, lang)
return etagNotFoundResponse(single(accountId), sessionState, ContactResponseObjectType, state, lang)
}
})
}
@@ -189,15 +189,15 @@ func (g *Groupware) CreateContact(w http.ResponseWriter, r *http.Request) {
var create jscontact.ContactCard
err := req.body(&create)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(l)
created, sessionState, state, lang, jerr := g.jmap.CreateContactCard(accountId, req.session, req.ctx, logger, req.language(), create)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, ContactResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, ContactResponseObjectType, state, lang)
})
}
@@ -216,24 +216,24 @@ func (g *Groupware) DeleteContact(w http.ResponseWriter, r *http.Request) {
deleted, sessionState, state, _, jerr := g.jmap.DeleteContactCard(accountId, []string{contactId}, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
for _, e := range deleted {
desc := e.Description
if desc != "" {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteContact,
withDetail(e.Description),
), sessionState)
} else {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteContact,
), sessionState)
}
}
return noContentResponseWithEtag(accountId, sessionState, ContactResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, ContactResponseObjectType, state)
})
}

View File

@@ -69,21 +69,21 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if mailboxId == "" {
return req.parameterErrorResponse(accountId, UriParamMailboxId, fmt.Sprintf("Missing required mailbox ID path parameter '%v'", UriParamMailboxId))
return req.parameterErrorResponse(single(accountId), UriParamMailboxId, fmt.Sprintf("Missing required mailbox ID path parameter '%v'", UriParamMailboxId))
}
logger := log.From(req.logger.With().Str(HeaderSince, log.SafeString(since)).Str(logAccountId, log.SafeString(accountId)))
changes, sessionState, state, lang, jerr := g.jmap.GetMailboxChanges(accountId, req.session, req.ctx, logger, req.language(), mailboxId, since, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, changes, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), changes, sessionState, EmailResponseObjectType, state, lang)
})
} else {
g.respond(w, r, func(req Request) Response {
@@ -91,17 +91,17 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, accountId)
if mailboxId == "" {
return req.parameterErrorResponse(accountId, UriParamMailboxId, fmt.Sprintf("Missing required mailbox ID path parameter '%v'", UriParamMailboxId))
return req.parameterErrorResponse(single(accountId), UriParamMailboxId, fmt.Sprintf("Missing required mailbox ID path parameter '%v'", UriParamMailboxId))
}
offset, ok, err := req.parseIntParam(QueryParamOffset, 0)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Int(QueryParamOffset, offset)
@@ -109,7 +109,7 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
limit, ok, err := req.parseUIntParam(QueryParamLimit, g.defaultEmailLimit)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamLimit, limit)
@@ -123,12 +123,12 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
emails, sessionState, state, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, req.session, req.ctx, logger, req.language(), mailboxId, offset, limit, collapseThreads, fetchBodies, g.maxBodyValueBytes, withThreads)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
sanitized, err := req.sanitizeEmails(emails.Emails)
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
safe := jmap.Emails{
@@ -138,7 +138,7 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
Offset: emails.Offset,
}
return etagResponse(accountId, safe, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), safe, sessionState, EmailResponseObjectType, state, lang)
})
}
}
@@ -189,17 +189,17 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
if len(ids) < 1 {
return req.parameterErrorResponse(accountId, UriParamEmailId, fmt.Sprintf("Invalid value for path parameter '%v': '%s': %s", UriParamEmailId, log.SafeString(id), "empty list of mail ids"))
return req.parameterErrorResponse(single(accountId), UriParamEmailId, fmt.Sprintf("Invalid value for path parameter '%v': '%s': %s", UriParamEmailId, log.SafeString(id), "empty list of mail ids"))
}
markAsSeen, ok, err := req.parseBoolParam(QueryParamMarkAsSeen, false)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Bool(QueryParamMarkAsSeen, markAsSeen)
@@ -210,32 +210,32 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
emails, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes, markAsSeen, true)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(emails) < 1 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
} else {
sanitized, err := req.sanitizeEmail(emails[0])
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
return etagResponse(accountId, sanitized, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), sanitized, sessionState, EmailResponseObjectType, state, lang)
}
} 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.maxBodyValueBytes, markAsSeen, false)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(emails) < 1 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
} else {
sanitized, err := req.sanitizeEmails(emails)
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
return etagResponse(accountId, sanitized, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), sanitized, sessionState, EmailResponseObjectType, state, lang)
}
}
})
@@ -270,22 +270,22 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
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)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(emails) < 1 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
email, err := req.sanitizeEmail(emails[0])
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
return etagResponse(accountId, email.Attachments, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), email.Attachments, sessionState, EmailResponseObjectType, state, lang)
})
} else {
g.stream(w, r, func(req Request, w http.ResponseWriter) *Error {
@@ -373,13 +373,13 @@ func (g *Groupware) getEmailsSince(w http.ResponseWriter, r *http.Request, since
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, log.SafeString(accountId))
maxChanges, ok, err := req.parseUIntParam(QueryParamMaxChanges, 0)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamMaxChanges, maxChanges)
@@ -389,10 +389,10 @@ func (g *Groupware) getEmailsSince(w http.ResponseWriter, r *http.Request, since
changes, sessionState, state, lang, jerr := g.jmap.GetEmailsSince(accountId, req.session, req.ctx, logger, req.language(), since, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, changes, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), changes, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -595,12 +595,12 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
ok, filter, makesSnippets, offset, limit, logger, err := g.buildFilter(req)
if !ok {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger = log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId)))
@@ -610,9 +610,9 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) {
fetchBodies := false
resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets([]string{accountId}, filter, req.session, req.ctx, logger, req.language(), offset, limit, fetchBodies, g.maxBodyValueBytes)
resultsByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailsWithSnippets(single(accountId), filter, req.session, req.ctx, logger, req.language(), offset, limit, fetchBodies, g.maxBodyValueBytes)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if results, ok := resultsByAccount[accountId]; ok {
@@ -632,7 +632,7 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) {
}
sanitized, err := req.sanitizeEmail(result.Email)
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
flattened[i] = EmailWithSnippets{
Email: sanitized,
@@ -640,14 +640,14 @@ func (g *Groupware) GetEmails(w http.ResponseWriter, r *http.Request) {
}
}
return etagResponse(accountId, EmailWithSnippetsSearchResults{
return etagResponse(single(accountId), EmailWithSnippetsSearchResults{
Results: flattened,
Total: results.Total,
Limit: results.Limit,
QueryState: results.QueryState,
}, sessionState, EmailResponseObjectType, state, lang)
} else {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
})
}
@@ -659,7 +659,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
ok, filter, makesSnippets, offset, limit, logger, err := g.buildFilter(req)
if !ok {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
logger = log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(allAccountIds)))
@@ -670,7 +670,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
if makesSnippets {
resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSnippets(allAccountIds, filter, req.session, req.ctx, logger, req.language(), offset, limit)
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), jerr)
return req.errorResponseFromJmap(allAccountIds, jerr)
}
var totalOverAllAccounts uint = 0
@@ -697,7 +697,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
// TODO offset and limit over the aggregated results by account
return etagResponse(joinAccountIds(allAccountIds), EmailSearchSnippetsResults{
return etagResponse(allAccountIds, EmailSearchSnippetsResults{
Results: flattened,
Total: totalOverAllAccounts,
Limit: limit,
@@ -708,7 +708,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
resultsByAccountId, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, req.language(), filter, limit, withThreads)
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), jerr)
return req.errorResponseFromJmap(allAccountIds, jerr)
}
var totalAcrossAllAccounts uint = 0
@@ -734,7 +734,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque
// TODO offset and limit over the aggregated results by account
return etagResponse(joinAccountIds(allAccountIds), EmailSearchResults{
return etagResponse(allAccountIds, EmailSearchResults{
Results: flattened,
Total: totalAcrossAllAccounts,
Limit: limit,
@@ -750,22 +750,22 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
logger = log.From(logger.With().Str(logAccountId, log.SafeString(accountId)))
var body jmap.EmailCreate
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, "", req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -775,7 +775,7 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
replaceId := chi.URLParam(r, UriParamEmailId)
@@ -785,15 +785,15 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
var body jmap.EmailCreate
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, replaceId, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -814,7 +814,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -823,7 +823,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
var body map[string]any
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
updates := map[string]jmap.EmailUpdate{
@@ -832,20 +832,20 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, updates, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if result == nil {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
"An internal API behaved unexpectedly: missing Email update response from JMAP endpoint")))
}
updatedEmail, ok := result[emailId]
if !ok {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
"An internal API behaved unexpectedly: wrong Email update ID response from JMAP endpoint")))
}
return etagResponse(accountId, updatedEmail, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), updatedEmail, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -867,7 +867,7 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request)
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -876,11 +876,11 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request)
var body emailKeywordUpdates
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if body.IsEmpty() {
return noContentResponse(accountId, req.session.State)
return noContentResponse(single(accountId), req.session.State)
}
patch := jmap.EmailUpdate{}
@@ -896,20 +896,20 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request)
result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if result == nil {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
"An internal API behaved unexpectedly: missing Email update response from JMAP endpoint")))
}
updatedEmail, ok := result[emailId]
if !ok {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
"An internal API behaved unexpectedly: wrong Email update ID response from JMAP endpoint")))
}
return etagResponse(accountId, updatedEmail, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), updatedEmail, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -931,7 +931,7 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -940,11 +940,11 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) {
var body []string
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if len(body) < 1 {
return noContentResponse(accountId, req.session.State)
return noContentResponse(single(accountId), req.session.State)
}
patch := jmap.EmailUpdate{}
@@ -957,23 +957,23 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) {
result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if result == nil {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
"An internal API behaved unexpectedly: missing Email update response from JMAP endpoint")))
}
updatedEmail, ok := result[emailId]
if !ok {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
"An internal API behaved unexpectedly: wrong Email update ID response from JMAP endpoint")))
}
if updatedEmail == nil {
return noContentResponseWithEtag(accountId, sessionState, EmailResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, EmailResponseObjectType, state)
} else {
return etagResponse(accountId, updatedEmail, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), updatedEmail, sessionState, EmailResponseObjectType, state, lang)
}
})
}
@@ -996,7 +996,7 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request)
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -1005,11 +1005,11 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request)
var body []string
err := req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if len(body) < 1 {
return noContentResponse(accountId, req.session.State)
return noContentResponse(single(accountId), req.session.State)
}
patch := jmap.EmailUpdate{}
@@ -1022,23 +1022,23 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request)
result, sessionState, state, lang, jerr := g.jmap.UpdateEmails(accountId, patches, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if result == nil {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Missing Email Update Response",
"An internal API behaved unexpectedly: missing Email update response from JMAP endpoint")))
}
updatedEmail, ok := result[emailId]
if !ok {
return errorResponse(accountId, apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
return errorResponse(single(accountId), apiError(req.errorId(), ErrorApiInconsistency, withTitle("API Inconsistency: Wrong Email Update Response ID",
"An internal API behaved unexpectedly: wrong Email update ID response from JMAP endpoint")))
}
if updatedEmail == nil {
return noContentResponseWithEtag(accountId, sessionState, EmailResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, EmailResponseObjectType, state)
} else {
return etagResponse(accountId, updatedEmail, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), updatedEmail, sessionState, EmailResponseObjectType, state, lang)
}
})
}
@@ -1061,7 +1061,7 @@ func (g *Groupware) DeleteEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -1069,25 +1069,25 @@ func (g *Groupware) DeleteEmail(w http.ResponseWriter, r *http.Request) {
resp, sessionState, state, _, jerr := g.jmap.DeleteEmails(accountId, []string{emailId}, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
for _, e := range resp {
desc := e.Description
if desc != "" {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteEmail,
withDetail(e.Description),
), sessionState)
} else {
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteEmail,
), sessionState)
}
}
return noContentResponseWithEtag(accountId, sessionState, EmailResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, EmailResponseObjectType, state)
})
}
@@ -1117,14 +1117,14 @@ func (g *Groupware) DeleteEmails(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
var emailIds []string
err := req.body(&emailIds)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l.Array("emailIds", log.SafeStringArray(emailIds))
@@ -1133,7 +1133,7 @@ func (g *Groupware) DeleteEmails(w http.ResponseWriter, r *http.Request) {
resp, sessionState, state, _, jerr := g.jmap.DeleteEmails(accountId, emailIds, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(resp) > 0 {
@@ -1141,13 +1141,13 @@ func (g *Groupware) DeleteEmails(w http.ResponseWriter, r *http.Request) {
for emailId, e := range resp {
meta[emailId] = e.Description
}
return errorResponseWithSessionState(accountId, apiError(
return errorResponseWithSessionState(single(accountId), apiError(
req.errorId(),
ErrorFailedToDeleteEmail,
withMeta(meta),
), sessionState)
}
return noContentResponseWithEtag(accountId, sessionState, EmailResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, EmailResponseObjectType, state)
})
}
@@ -1157,7 +1157,7 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l.Str(logAccountId, accountId)
@@ -1166,7 +1166,7 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) {
identityId, err := req.getMandatoryStringParam(QueryParamIdentityId)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l.Str(QueryParamIdentityId, log.SafeString(identityId))
@@ -1186,7 +1186,7 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) {
}
// only one is set
msg := fmt.Sprintf("Missing required value for query parameter '%v'", missing)
return errorResponse(accountId, req.observedParameterError(ErrorMissingMandatoryRequestParameter,
return errorResponse(single(accountId), req.observedParameterError(ErrorMissingMandatoryRequestParameter,
withDetail(msg),
withSource(&ErrorSource{Parameter: missing})))
}
@@ -1196,10 +1196,10 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) {
resp, sessionState, state, lang, jerr := g.jmap.SubmitEmail(accountId, identityId, emailId, move, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, resp, sessionState, EmailResponseObjectType, state, lang)
return etagResponse(single(accountId), resp, sessionState, EmailResponseObjectType, state, lang)
})
}
@@ -1261,13 +1261,13 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
accountId, gwerr := req.GetAccountIdForMail()
if gwerr != nil {
return errorResponse(accountId, gwerr)
return errorResponse(single(accountId), gwerr)
}
l = l.Str(logAccountId, log.SafeString(accountId))
limit, ok, err := req.parseUIntParam(QueryParamLimit, 10) // TODO configurable default limit
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint("limit", limit)
@@ -1275,7 +1275,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
days, ok, err := req.parseUIntParam(QueryParamDays, 5) // TODO configurable default days
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint("days", days)
@@ -1288,13 +1288,13 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
emails, sessionState, state, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, true, g.maxBodyValueBytes, false, false)
getEmailsDuration := time.Since(getEmailsBefore)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(emails) < 1 {
req.observe(g.metrics.EmailByIdDuration.WithLabelValues(req.session.JmapEndpoint, metrics.Values.Result.NotFound), getEmailsDuration.Seconds())
logger.Trace().Msg("failed to find any emails matching id") // the id is already in the log field
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
} else {
req.observe(g.metrics.EmailByIdDuration.WithLabelValues(req.session.JmapEndpoint, metrics.Values.Result.Found), getEmailsDuration.Seconds())
}
@@ -1310,7 +1310,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
g.job(logger, RelationTypeSameSender, func(jobId uint64, l *log.Logger) {
before := time.Now()
resultsByAccountId, _, _, lang, jerr := g.jmap.QueryEmails([]string{accountId}, filter, req.session, bgctx, l, req.language(), 0, limit, false, g.maxBodyValueBytes)
resultsByAccountId, _, _, lang, jerr := g.jmap.QueryEmails(single(accountId), filter, req.session, bgctx, l, req.language(), 0, limit, false, g.maxBodyValueBytes)
if results, ok := resultsByAccountId[accountId]; ok {
duration := time.Since(before)
if jerr != nil {
@@ -1350,9 +1350,9 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
sanitized, err := req.sanitizeEmail(email)
if err != nil {
return errorResponseWithSessionState(accountId, err, sessionState)
return errorResponseWithSessionState(single(accountId), err, sessionState)
}
return etagResponse(accountId, AboutEmailResponse{
return etagResponse(single(accountId), AboutEmailResponse{
Email: sanitized,
RequestId: reqId,
}, sessionState, EmailResponseObjectType, state, lang)
@@ -1625,7 +1625,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
limit, ok, err := req.parseUIntParam(QueryParamLimit, 10) // TODO from configuration
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if ok {
l = l.Uint(QueryParamLimit, limit)
@@ -1633,7 +1633,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
offset, ok, err := req.parseUIntParam(QueryParamOffset, 0)
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if offset > 0 {
return notImplementesResponse()
@@ -1644,7 +1644,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
seen, ok, err := req.parseBoolParam(QueryParamSeen, false)
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if ok {
l = l.Bool(QueryParamSeen, seen)
@@ -1652,7 +1652,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
undesirable, ok, err := req.parseBoolParam(QueryParamUndesirable, false)
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if ok {
l = l.Bool(QueryParamUndesirable, undesirable)
@@ -1674,7 +1674,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
emailsSummariesByAccount, sessionState, state, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, req.language(), filter, limit, true)
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), jerr)
return req.errorResponseFromJmap(allAccountIds, jerr)
}
// sort in memory to respect the overall limit
@@ -1698,7 +1698,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
summaries[i] = summarizeEmail(all[i].accountId, all[i].email)
}
return etagResponse(joinAccountIds(allAccountIds), EmailSummaries{
return etagResponse(allAccountIds, EmailSummaries{
Emails: summaries,
Total: total,
Limit: limit,

View File

@@ -31,14 +31,14 @@ func (g *Groupware) GetIdentities(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(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())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, res, sessionState, IdentityResponseObjectType, state, lang)
return etagResponse(single(accountId), res, sessionState, IdentityResponseObjectType, state, lang)
})
}
@@ -46,18 +46,18 @@ func (g *Groupware) GetIdentityById(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
id := chi.URLParam(r, UriParamIdentityId)
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})
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(res) < 1 {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
return etagResponse(accountId, res[0], sessionState, IdentityResponseObjectType, state, lang)
return etagResponse(single(accountId), res[0], sessionState, IdentityResponseObjectType, state, lang)
})
}
@@ -65,21 +65,21 @@ func (g *Groupware) AddIdentity(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
var identity jmap.Identity
err = req.body(&identity)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
created, sessionState, state, lang, jerr := g.jmap.CreateIdentity(accountId, req.session, req.ctx, logger, req.language(), identity)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, IdentityResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, IdentityResponseObjectType, state, lang)
})
}
@@ -87,21 +87,21 @@ func (g *Groupware) ModifyIdentity(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
var identity jmap.Identity
err = req.body(&identity)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
updated, sessionState, state, lang, jerr := g.jmap.UpdateIdentity(accountId, req.session, req.ctx, logger, req.language(), identity)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, updated, sessionState, IdentityResponseObjectType, state, lang)
return etagResponse(single(accountId), updated, sessionState, IdentityResponseObjectType, state, lang)
})
}
@@ -109,27 +109,27 @@ func (g *Groupware) DeleteIdentity(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
id := chi.URLParam(r, UriParamIdentityId)
ids := strings.Split(id, ",")
if len(ids) < 1 {
return req.parameterErrorResponse(accountId, UriParamEmailId, fmt.Sprintf("Invalid value for path parameter '%v': '%s': %s", UriParamIdentityId, log.SafeString(id), "empty list of identity ids"))
return req.parameterErrorResponse(single(accountId), UriParamEmailId, fmt.Sprintf("Invalid value for path parameter '%v': '%s': %s", UriParamIdentityId, log.SafeString(id), "empty list of identity ids"))
}
deletion, sessionState, state, _, jerr := g.jmap.DeleteIdentity(accountId, req.session, req.ctx, logger, req.language(), ids)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
notDeletedIds := structs.Missing(ids, deletion)
if len(notDeletedIds) == 0 {
return noContentResponseWithEtag(accountId, sessionState, IdentityResponseObjectType, state)
return noContentResponseWithEtag(single(accountId), sessionState, IdentityResponseObjectType, state)
} else {
logger.Error().Array("not-deleted", log.SafeStringArray(notDeletedIds)).Msgf("failed to delete %d identities", len(notDeletedIds))
return errorResponseWithSessionState(accountId, req.apiError(&ErrorFailedToDeleteSomeIdentities,
return errorResponseWithSessionState(single(accountId), req.apiError(&ErrorFailedToDeleteSomeIdentities,
withMeta(map[string]any{"ids": notDeletedIds})), sessionState)
}
})

View File

@@ -163,10 +163,10 @@ func (g *Groupware) Index(w http.ResponseWriter, r *http.Request) {
boot, sessionState, state, lang, err := g.jmap.GetBootstrap(accountIds, req.session, req.ctx, req.logger, req.language())
if err != nil {
return req.errorResponseFromJmap(joinAccountIds(accountIds), err)
return req.errorResponseFromJmap(accountIds, err)
}
return etagResponse(joinAccountIds(accountIds), IndexResponse{
return etagResponse(accountIds, IndexResponse{
Version: Version,
Capabilities: Capabilities,
Limits: buildIndexLimits(req.session),

View File

@@ -39,18 +39,18 @@ func (g *Groupware) GetMailbox(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
mailboxes, sessionState, state, lang, jerr := g.jmap.GetMailbox(accountId, req.session, req.ctx, req.logger, req.language(), []string{mailboxId})
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
if len(mailboxes.Mailboxes) == 1 {
return etagResponse(accountId, mailboxes.Mailboxes[0], sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), mailboxes.Mailboxes[0], sessionState, MailboxResponseObjectType, state, lang)
} else {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
})
}
@@ -110,12 +110,12 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
subscribed, set, err := req.parseBoolParam(QueryParamMailboxSearchSubscribed, false)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if set {
filter.IsSubscribed = &subscribed
@@ -125,25 +125,25 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) {
logger := log.From(req.logger.With().Str(logAccountId, accountId))
if hasCriteria {
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes([]string{accountId}, req.session, req.ctx, logger, req.language(), filter)
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(single(accountId), req.session, req.ctx, logger, req.language(), filter)
if err != nil {
return req.errorResponseFromJmap(accountId, err)
return req.errorResponseFromJmap(single(accountId), err)
}
if mailboxes, ok := mailboxesByAccountId[accountId]; ok {
return etagResponse(accountId, sortMailboxSlice(mailboxes), sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), sortMailboxSlice(mailboxes), sessionState, MailboxResponseObjectType, state, lang)
} else {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
} else {
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes([]string{accountId}, req.session, req.ctx, logger, req.language())
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(single(accountId), req.session, req.ctx, logger, req.language())
if err != nil {
return req.errorResponseFromJmap(accountId, err)
return req.errorResponseFromJmap(single(accountId), err)
}
if mailboxes, ok := mailboxesByAccountId[accountId]; ok {
return etagResponse(accountId, sortMailboxSlice(mailboxes), sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), sortMailboxSlice(mailboxes), sessionState, MailboxResponseObjectType, state, lang)
} else {
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
}
}
})
@@ -179,13 +179,13 @@ func (g *Groupware) GetMailboxesForAllAccounts(w http.ResponseWriter, r *http.Re
g.respond(w, r, func(req Request) Response {
accountIds := req.AllAccountIds()
if len(accountIds) < 1 {
return noContentResponse("", "")
return noContentResponse(nil, "")
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)))
subscribed, set, err := req.parseBoolParam(QueryParamMailboxSearchSubscribed, false)
if err != nil {
return errorResponse(joinAccountIds(accountIds), err)
return errorResponse(accountIds, err)
}
if set {
filter.IsSubscribed = &subscribed
@@ -195,15 +195,15 @@ 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)
if err != nil {
return req.errorResponseFromJmap(joinAccountIds(accountIds), err)
return req.errorResponseFromJmap(accountIds, err)
}
return etagResponse(joinAccountIds(accountIds), sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(accountIds, sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
} else {
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.GetAllMailboxes(accountIds, req.session, req.ctx, logger, req.language())
if err != nil {
return req.errorResponseFromJmap(joinAccountIds(accountIds), err)
return req.errorResponseFromJmap(accountIds, err)
}
return etagResponse(joinAccountIds(accountIds), sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(accountIds, sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
}
})
}
@@ -213,7 +213,7 @@ func (g *Groupware) GetMailboxByRoleForAllAccounts(w http.ResponseWriter, r *htt
g.respond(w, r, func(req Request) Response {
accountIds := req.AllAccountIds()
if len(accountIds) < 1 {
return noContentResponse("", "")
return noContentResponse(nil, "")
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)).Str("role", role))
@@ -223,9 +223,9 @@ func (g *Groupware) GetMailboxByRoleForAllAccounts(w http.ResponseWriter, r *htt
mailboxesByAccountId, sessionState, state, lang, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, req.language(), filter)
if err != nil {
return req.errorResponseFromJmap(joinAccountIds(accountIds), err)
return req.errorResponseFromJmap(accountIds, err)
}
return etagResponse(joinAccountIds(accountIds), sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(accountIds, sortMailboxesMap(mailboxesByAccountId), sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -253,13 +253,13 @@ func (g *Groupware) GetMailboxChanges(w http.ResponseWriter, r *http.Request) {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, accountId)
maxChanges, ok, err := req.parseUIntParam(QueryParamMaxChanges, 0)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
if ok {
l = l.Uint(QueryParamMaxChanges, maxChanges)
@@ -269,10 +269,10 @@ 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(), mailboxId, sinceState, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, changes, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), changes, sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -300,7 +300,7 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht
sinceStateMap, ok, err := req.parseMapParam(QueryParamSince)
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if ok {
dict := zerolog.Dict()
@@ -312,7 +312,7 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht
maxChanges, ok, err := req.parseUIntParam(QueryParamMaxChanges, 0)
if err != nil {
return errorResponse(joinAccountIds(allAccountIds), err)
return errorResponse(allAccountIds, err)
}
if ok {
l = l.Uint(QueryParamMaxChanges, maxChanges)
@@ -322,10 +322,10 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht
changesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language(), sinceStateMap, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), jerr)
return req.errorResponseFromJmap(allAccountIds, jerr)
}
return etagResponse(joinAccountIds(allAccountIds), changesByAccountId, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(allAccountIds, changesByAccountId, sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -338,10 +338,10 @@ func (g *Groupware) GetMailboxRoles(w http.ResponseWriter, r *http.Request) {
rolesByAccountId, sessionState, state, lang, jerr := g.jmap.GetMailboxRolesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(allAccountIds), jerr)
return req.errorResponseFromJmap(allAccountIds, jerr)
}
return etagResponse(joinAccountIds(allAccountIds), rolesByAccountId, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(allAccountIds, rolesByAccountId, sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -353,23 +353,23 @@ func (g *Groupware) UpdateMailbox(w http.ResponseWriter, r *http.Request) {
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, accountId)
var body jmap.MailboxChange
err = req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(l)
updated, sessionState, state, lang, jerr := g.jmap.UpdateMailbox(accountId, req.session, req.ctx, logger, req.language(), mailboxId, "", body)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, updated, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), updated, sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -378,23 +378,23 @@ func (g *Groupware) CreateMailbox(w http.ResponseWriter, r *http.Request) {
l := req.logger.With()
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, accountId)
var body jmap.MailboxChange
err = req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(l)
created, sessionState, state, lang, jerr := g.jmap.CreateMailbox(accountId, req.session, req.ctx, logger, req.language(), "", body)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, created, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), created, sessionState, MailboxResponseObjectType, state, lang)
})
}
@@ -406,12 +406,12 @@ func (g *Groupware) DeleteMailbox(w http.ResponseWriter, r *http.Request) {
l := req.logger.With()
accountId, err := req.GetAccountIdForMail()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
l = l.Str(logAccountId, accountId)
if len(mailboxIds) < 1 {
return noContentResponse(accountId, req.session.State)
return noContentResponse(single(accountId), req.session.State)
}
l = l.Array(UriParamMailboxId, log.SafeStringArray(mailboxIds))
@@ -419,10 +419,10 @@ func (g *Groupware) DeleteMailbox(w http.ResponseWriter, r *http.Request) {
deleted, sessionState, state, lang, jerr := g.jmap.DeleteMailboxes(accountId, req.session, req.ctx, logger, req.language(), "", mailboxIds)
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, deleted, sessionState, MailboxResponseObjectType, state, lang)
return etagResponse(single(accountId), deleted, sessionState, MailboxResponseObjectType, state, lang)
})
}

View File

@@ -26,18 +26,18 @@ func (g *Groupware) GetQuota(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForQuota()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, state, lang, jerr := g.jmap.GetQuotas([]string{accountId}, req.session, req.ctx, logger, req.language())
res, sessionState, state, lang, jerr := g.jmap.GetQuotas(single(accountId), req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
for _, v := range res {
return etagResponse(accountId, v.List, sessionState, QuotaResponseObjectType, state, lang)
return etagResponse(single(accountId), v.List, sessionState, QuotaResponseObjectType, state, lang)
}
return notFoundResponse(accountId, sessionState)
return notFoundResponse(single(accountId), sessionState)
})
}
@@ -65,13 +65,13 @@ func (g *Groupware) GetQuotaForAllAccounts(w http.ResponseWriter, r *http.Reques
g.respond(w, r, func(req Request) Response {
accountIds := req.AllAccountIds()
if len(accountIds) < 1 {
return noContentResponse(joinAccountIds(accountIds), "")
return noContentResponse(accountIds, "")
}
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)))
res, sessionState, state, lang, jerr := g.jmap.GetQuotas(accountIds, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(joinAccountIds(accountIds), jerr)
return req.errorResponseFromJmap(accountIds, jerr)
}
result := make(map[string]AccountQuota, len(res))
@@ -81,6 +81,6 @@ func (g *Groupware) GetQuotaForAllAccounts(w http.ResponseWriter, r *http.Reques
Quotas: accountQuotas.List,
}
}
return etagResponse(joinAccountIds(accountIds), result, sessionState, QuotaResponseObjectType, state, lang)
return etagResponse(accountIds, result, sessionState, QuotaResponseObjectType, state, lang)
})
}

View File

@@ -31,7 +31,7 @@ func (g *Groupware) GetTaskLists(w http.ResponseWriter, r *http.Request) {
}
var _ string = accountId
return etagResponse(accountId, AllTaskLists, req.session.State, TaskListResponseObjectType, TaskListsState, "")
return etagResponse(single(accountId), AllTaskLists, req.session.State, TaskListResponseObjectType, TaskListsState, "")
})
}
@@ -65,10 +65,10 @@ func (g *Groupware) GetTaskListById(w http.ResponseWriter, r *http.Request) {
// TODO replace with proper implementation
for _, tasklist := range AllTaskLists {
if tasklist.Id == tasklistId {
return response(accountId, tasklist, req.session.State, "")
return response(single(accountId), tasklist, req.session.State, "")
}
}
return etagNotFoundResponse(accountId, req.session.State, TaskListResponseObjectType, TaskListsState, "")
return etagNotFoundResponse(single(accountId), req.session.State, TaskListResponseObjectType, TaskListsState, "")
})
}
@@ -100,8 +100,8 @@ func (g *Groupware) GetTasksInTaskList(w http.ResponseWriter, r *http.Request) {
// TODO replace with proper implementation
tasks, ok := TaskMapByTaskListId[tasklistId]
if !ok {
return notFoundResponse(accountId, req.session.State)
return notFoundResponse(single(accountId), req.session.State)
}
return etagResponse(accountId, tasks, req.session.State, TaskResponseObjectType, TaskState, "")
return etagResponse(single(accountId), tasks, req.session.State, TaskResponseObjectType, TaskState, "")
})
}

View File

@@ -33,15 +33,15 @@ func (g *Groupware) GetVacation(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForVacationResponse()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, state, lang, jerr := g.jmap.GetVacationResponse(accountId, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, res, sessionState, VacationResponseResponseObjectType, state, lang)
return etagResponse(single(accountId), res, sessionState, VacationResponseResponseObjectType, state, lang)
})
}
@@ -69,21 +69,21 @@ func (g *Groupware) SetVacation(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountId, err := req.GetAccountIdForVacationResponse()
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
var body jmap.VacationResponsePayload
err = req.body(&body)
if err != nil {
return errorResponse(accountId, err)
return errorResponse(single(accountId), err)
}
res, sessionState, state, lang, jerr := g.jmap.SetVacationResponse(accountId, body, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(accountId, jerr)
return req.errorResponseFromJmap(single(accountId), jerr)
}
return etagResponse(accountId, res, sessionState, VacationResponseResponseObjectType, state, lang)
return etagResponse(single(accountId), res, sessionState, VacationResponseResponseObjectType, state, lang)
})
}

View File

@@ -629,6 +629,6 @@ func errorResponses(errors ...Error) ErrorResponse {
return ErrorResponse{Errors: errors}
}
func (r Request) errorResponseFromJmap(accountId string, err jmap.Error) Response {
return errorResponse(accountId, r.apiErrorFromJmap(r.observeJmapError(err)))
func (r Request) errorResponseFromJmap(accountIds []string, err jmap.Error) Response {
return errorResponse(accountIds, r.apiErrorFromJmap(r.observeJmapError(err)))
}

View File

@@ -207,42 +207,54 @@ func NewGroupware(config *config.Config, logger *log.Logger, mux *chi.Mux, prome
m := metrics.New(prometheusRegistry, logger)
// TODO add timeouts and other meaningful configuration settings for the HTTP client
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
httpTransport.ResponseHeaderTimeout = responseHeaderTimeout
if insecureTls {
tlsConfig := &tls.Config{InsecureSkipVerify: true}
httpTransport.TLSClientConfig = tlsConfig
}
httpClient := *http.DefaultClient
httpClient.Transport = httpTransport
userProvider := newRevaContextUsernameProvider()
jmapMetricsAdapter := groupwareHttpJmapApiClientMetricsRecorder{m: m}
api := jmap.NewHttpJmapClient(
&httpClient,
masterUsername,
masterPassword,
jmapMetricsAdapter,
)
var jmapClient jmap.Client
{
var api *jmap.HttpJmapClient
{
// TODO add timeouts and other meaningful configuration settings for the HTTP client
var httpClient http.Client
{
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
httpTransport.ResponseHeaderTimeout = responseHeaderTimeout
if insecureTls {
tlsConfig := &tls.Config{InsecureSkipVerify: true}
httpTransport.TLSClientConfig = tlsConfig
}
httpClient = *http.DefaultClient
httpClient.Transport = httpTransport
}
wsDialer := &websocket.Dialer{
HandshakeTimeout: wsHandshakeTimeout,
}
if insecureTls {
wsDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
api = jmap.NewHttpJmapClient(
&httpClient,
masterUsername,
masterPassword,
jmapMetricsAdapter,
)
}
wsf, err := jmap.NewHttpWsClientFactory(wsDialer, masterUsername, masterPassword, logger, jmapMetricsAdapter)
if err != nil {
logger.Error().Err(err).Msg("failed to create websocket client")
return nil, GroupwareInitializationError{Message: "failed to create websocket client", Err: err}
}
var wsf *jmap.HttpWsClientFactory
{
wsDialer := &websocket.Dialer{
HandshakeTimeout: wsHandshakeTimeout,
}
if insecureTls {
wsDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
// api implements all three interfaces:
jmapClient := jmap.NewClient(api, api, api, wsf)
wsf, err = jmap.NewHttpWsClientFactory(wsDialer, masterUsername, masterPassword, logger, jmapMetricsAdapter)
if err != nil {
logger.Error().Err(err).Msg("failed to create websocket client")
return nil, GroupwareInitializationError{Message: "failed to create websocket client", Err: err}
}
}
// api implements all three interfaces:
jmapClient = jmap.NewClient(api, api, api, wsf)
}
sessionCacheBuilder := newSessionCacheBuilder(
sessionUrl,
@@ -456,9 +468,20 @@ func (g *Groupware) ServeSSE(w http.ResponseWriter, r *http.Request) {
})
}
// Provide a JMAP Session for the
func (g *Groupware) session(user user, _ *log.Logger) (jmap.Session, bool, *GroupwareError, time.Time) {
s := g.sessionCache.Get(user.GetUsername())
// Provide a JMAP Session for the given user
func (g *Groupware) session(user user, logger *log.Logger) (jmap.Session, bool, *GroupwareError, time.Time) {
if user == nil {
logger.Warn().Msg("user is nil")
return jmap.Session{}, false, nil, time.Time{}
}
name := user.GetUsername()
if name == "" {
logger.Warn().Msg("user has an empty username")
return jmap.Session{}, false, nil, time.Time{}
}
// first look into the session cache
s := g.sessionCache.Get(name)
if s != nil {
if s.Success() {
return s.Get(), true, nil, s.Until()
@@ -466,7 +489,8 @@ func (g *Groupware) session(user user, _ *log.Logger) (jmap.Session, bool, *Grou
return jmap.Session{}, false, s.Error(), s.Until()
}
}
// not sure this should happen:
// not sure this should/could happen:
logger.Warn().Msg("session cache returned nil")
return jmap.Session{}, false, nil, time.Time{}
}
@@ -521,44 +545,63 @@ func (g *Groupware) serveError(w http.ResponseWriter, r *http.Request, error *Er
render.Render(w, r, errorResponses(*error))
}
// Execute a closure with a JMAP Session.
//
// Returns
// - a Response object
// - 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)
logger := &sl
user, err := g.userProvider.GetUser(r, ctx, logger)
if err != nil {
g.metrics.AuthenticationFailureCounter.Inc()
g.serveError(w, r, apiError(errorId(r, ctx), 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{})
return Response{}, time.Time{}, false
// retrieve the current user from the inbound request
var user user
{
var err error
user, err = g.userProvider.GetUser(r, ctx, logger)
if err != nil {
g.metrics.AuthenticationFailureCounter.Inc()
g.serveError(w, r, apiError(errorId(r, ctx), 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{})
return Response{}, time.Time{}, false
}
logger = log.From(logger.With().Str(logUserId, log.SafeString(user.GetId())))
}
logger = log.From(logger.With().Str(logUserId, log.SafeString(user.GetId())))
// retrieve a JMAP Session for that user
var session jmap.Session
{
s, ok, gwerr, retryAfter := g.session(user, logger)
if gwerr != nil {
g.metrics.SessionFailureCounter.Inc()
errorId := errorId(r, ctx)
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
}
if ok {
session = s
} else {
// no session = authentication failed
g.metrics.SessionFailureCounter.Inc()
errorId := errorId(r, ctx)
logger.Error().Str(logErrorId, errorId).Msg("could not authenticate, failed to find Session")
gwerr = &ErrorInvalidAuthentication
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
return Response{}, retryAfter, false
}
}
session, ok, gwerr, retryAfter := g.session(user, logger)
if gwerr != nil {
g.metrics.SessionFailureCounter.Inc()
errorId := errorId(r, ctx)
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
}
if !ok {
// no session = authentication failed
g.metrics.SessionFailureCounter.Inc()
errorId := errorId(r, ctx)
logger.Error().Str(logErrorId, errorId).Msg("could not authenticate, failed to find Session")
gwerr = &ErrorInvalidAuthentication
g.serveError(w, r, apiError(errorId, *gwerr), retryAfter)
return Response{}, retryAfter, false
}
decoratedLogger := decorateLogger(logger, session)
// build the Request object
req := Request{
g: g,
user: user,
@@ -568,7 +611,10 @@ func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler
session: &session,
}
// perform the actual request using the closure that was passed in
response := handler(req)
// return the result of that closure execution
return response, time.Time{}, true
}
@@ -577,8 +623,10 @@ const (
StateResponseHeader = "State"
ObjectTypeResponseHeader = "Object-Type"
AccountIdResponseHeader = "Account-Id"
AccountIdsResponseHeader = "Account-Ids"
)
// Send the Response object as an HTTP response.
func (g *Groupware) sendResponse(w http.ResponseWriter, r *http.Request, response Response) {
if response.err != nil {
g.log(response.err)
@@ -600,9 +648,9 @@ func (g *Groupware) sendResponse(w http.ResponseWriter, r *http.Request, respons
{
etag := string(response.etag)
if etag != "" {
challenge := r.Header.Get("if-none-match")
quotedEtag := "\"" + etag + "\""
notModified = challenge != "" && (challenge == etag || challenge == quotedEtag)
challenge := r.Header.Get("if-none-match") // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-None-Match
quotedEtag := "\"" + etag + "\"" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag#etag_value
notModified = challenge != "" && (challenge == etag || challenge == quotedEtag) // be a bit flexible/permissive here with the quoting
w.Header().Add("ETag", quotedEtag)
w.Header().Add(StateResponseHeader, etag)
}
@@ -613,8 +661,17 @@ func (g *Groupware) sendResponse(w http.ResponseWriter, r *http.Request, respons
w.Header().Add(ObjectTypeResponseHeader, ot)
}
}
if response.accountId != "" {
w.Header().Add(AccountIdResponseHeader, response.accountId)
switch len(response.accountIds) {
case 0:
break
case 1:
w.Header().Add(AccountIdResponseHeader, response.accountIds[0])
default:
c := make([]string, len(response.accountIds))
copy(c, response.accountIds)
slices.Sort(c)
value := strings.Join(c, ",")
w.Header().Add(AccountIdsResponseHeader, value)
}
if notModified {
@@ -714,16 +771,6 @@ func (g *Groupware) MethodNotAllowed(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}
func joinAccountIds(accountIds []string) string {
switch len(accountIds) {
case 0:
return ""
case 1:
return accountIds[0]
default:
c := make([]string, len(accountIds))
copy(c, accountIds)
slices.Sort(c)
return strings.Join(c, ",")
}
func single[S any](s S) []S {
return []S{s}
}

View File

@@ -170,8 +170,8 @@ func (r Request) parameterError(param string, detail string) *Error {
withSource(&ErrorSource{Parameter: param}))
}
func (r Request) parameterErrorResponse(accountId string, param string, detail string) Response {
return errorResponse(accountId, r.parameterError(param, detail))
func (r Request) parameterErrorResponse(accountIds []string, param string, detail string) Response {
return errorResponse(accountIds, r.parameterError(param, detail))
}
func (r Request) getStringParam(param string, defaultValue string) (string, bool) {
@@ -355,7 +355,7 @@ func (r Request) observeJmapError(jerr jmap.Error) jmap.Error {
func (r Request) needTask(accountId string) (bool, Response) {
if !IgnoreSessionCapabilityChecks {
if r.session.Capabilities.Tasks == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingTasksSessionCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingTasksSessionCapability), r.session.State)
}
}
return true, Response{}
@@ -367,11 +367,11 @@ func (r Request) needTaskForAccount(accountId string) (bool, Response) {
}
account, ok := r.session.Accounts[accountId]
if !ok {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorAccountNotFound), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorAccountNotFound), r.session.State)
}
if !IgnoreSessionCapabilityChecks {
if account.AccountCapabilities.Tasks == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingTasksAccountCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingTasksAccountCapability), r.session.State)
}
}
return true, Response{}
@@ -380,7 +380,7 @@ func (r Request) needTaskForAccount(accountId string) (bool, Response) {
func (r Request) needTaskWithAccount() (bool, string, Response) {
accountId, err := r.GetAccountIdForTask()
if err != nil {
return false, "", errorResponse(accountId, err)
return false, "", errorResponse(single(accountId), err)
}
if !IgnoreSessionCapabilityChecks {
if ok, resp := r.needTaskForAccount(accountId); !ok {
@@ -393,7 +393,7 @@ func (r Request) needTaskWithAccount() (bool, string, Response) {
func (r Request) needCalendar(accountId string) (bool, Response) {
if !IgnoreSessionCapabilityChecks {
if r.session.Capabilities.Calendars == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingCalendarsSessionCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingCalendarsSessionCapability), r.session.State)
}
}
return true, Response{}
@@ -405,11 +405,11 @@ func (r Request) needCalendarForAccount(accountId string) (bool, Response) {
}
account, ok := r.session.Accounts[accountId]
if !ok {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorAccountNotFound), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorAccountNotFound), r.session.State)
}
if !IgnoreSessionCapabilityChecks {
if account.AccountCapabilities.Calendars == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingCalendarsAccountCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingCalendarsAccountCapability), r.session.State)
}
}
return true, Response{}
@@ -418,7 +418,7 @@ func (r Request) needCalendarForAccount(accountId string) (bool, Response) {
func (r Request) needCalendarWithAccount() (bool, string, Response) {
accountId, err := r.GetAccountIdForCalendar()
if err != nil {
return false, "", errorResponse(accountId, err)
return false, "", errorResponse(single(accountId), err)
}
if !IgnoreSessionCapabilityChecks {
if ok, resp := r.needCalendarForAccount(accountId); !ok {
@@ -430,7 +430,7 @@ func (r Request) needCalendarWithAccount() (bool, string, Response) {
func (r Request) needContact(accountId string) (bool, Response) {
if r.session.Capabilities.Contacts == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingContactsSessionCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingContactsSessionCapability), r.session.State)
}
return true, Response{}
}
@@ -441,10 +441,10 @@ func (r Request) needContactForAccount(accountId string) (bool, Response) {
}
account, ok := r.session.Accounts[accountId]
if !ok {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorAccountNotFound), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorAccountNotFound), r.session.State)
}
if account.AccountCapabilities.Contacts == nil {
return false, errorResponseWithSessionState(accountId, r.apiError(&ErrorMissingContactsAccountCapability), r.session.State)
return false, errorResponseWithSessionState(single(accountId), r.apiError(&ErrorMissingContactsAccountCapability), r.session.State)
}
return true, Response{}
}
@@ -452,7 +452,7 @@ func (r Request) needContactForAccount(accountId string) (bool, Response) {
func (r Request) needContactWithAccount() (bool, string, Response) {
accountId, err := r.GetAccountIdForContact()
if err != nil {
return false, "", errorResponse(accountId, err)
return false, "", errorResponse(single(accountId), err)
}
if ok, resp := r.needContactForAccount(accountId); !ok {
return false, accountId, resp

View File

@@ -31,14 +31,14 @@ type Response struct {
err *Error
etag jmap.State
objectType ResponseObjectType
accountId string
accountIds []string
sessionState jmap.SessionState
contentLanguage jmap.Language
}
func errorResponse(accountId string, err *Error) Response {
func errorResponse(accountIds []string, err *Error) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
err: err,
etag: "",
@@ -46,9 +46,9 @@ func errorResponse(accountId string, err *Error) Response {
}
}
func errorResponseWithSessionState(accountId string, err *Error, sessionState jmap.SessionState) Response {
func errorResponseWithSessionState(accountIds []string, err *Error, sessionState jmap.SessionState) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
err: err,
etag: "",
@@ -56,9 +56,9 @@ func errorResponseWithSessionState(accountId string, err *Error, sessionState jm
}
}
func response(accountId string, body any, sessionState jmap.SessionState, contentLanguage jmap.Language) Response {
func response(accountIds []string, body any, sessionState jmap.SessionState, contentLanguage jmap.Language) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: body,
err: nil,
etag: jmap.State(sessionState),
@@ -67,9 +67,9 @@ func response(accountId string, body any, sessionState jmap.SessionState, conten
}
}
func etagResponse(accountId string, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response {
func etagResponse(accountIds []string, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: body,
err: nil,
etag: etag,
@@ -92,9 +92,9 @@ func etagOnlyResponse(body any, etag jmap.State, objectType ResponseObjectType,
}
*/
func noContentResponse(accountId string, sessionState jmap.SessionState) Response {
func noContentResponse(accountIds []string, sessionState jmap.SessionState) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
status: http.StatusNoContent,
err: nil,
@@ -103,9 +103,9 @@ func noContentResponse(accountId string, sessionState jmap.SessionState) Respons
}
}
func noContentResponseWithEtag(accountId string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response {
func noContentResponseWithEtag(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
status: http.StatusNoContent,
err: nil,
@@ -139,9 +139,9 @@ func timeoutResponse(sessionState jmap.SessionState) Response {
}
*/
func notFoundResponse(accountId string, sessionState jmap.SessionState) Response {
func notFoundResponse(accountIds []string, sessionState jmap.SessionState) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
status: http.StatusNotFound,
err: nil,
@@ -150,9 +150,9 @@ func notFoundResponse(accountId string, sessionState jmap.SessionState) Response
}
}
func etagNotFoundResponse(accountId string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response {
func etagNotFoundResponse(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response {
return Response{
accountId: accountId,
accountIds: accountIds,
body: nil,
status: http.StatusNotFound,
err: nil,