groupware: add quota API + add support for Accept-Language and Content-Language

This commit is contained in:
Pascal Bleser
2025-10-06 11:58:36 +02:00
parent 9f8672a68d
commit 9bfa73869f
29 changed files with 362 additions and 237 deletions
+6
View File
@@ -35,6 +35,9 @@ tags:
- name: task
x-displayName: Tasks
description: APIs about tasks
- name: quota
x-displayName: Quota
description: APIs about quotas
- name: vacation
x-displayName: Vacation Responses
description: APIs about vacation responses
@@ -63,6 +66,9 @@ x-tagGroups:
tags:
- tasklist
- task
- name: Quotas
tags:
- quota
components:
securitySchemes:
api:
@@ -32,7 +32,7 @@ func (g *Groupware) GetAccount(w http.ResponseWriter, r *http.Request) {
if err != nil {
return errorResponse(err)
}
return response(account, req.session.State)
return response(account, req.session.State, "")
})
}
@@ -54,7 +54,7 @@ type SwaggerGetAccountsResponse struct {
// 500: ErrorResponse500
func (g *Groupware) GetAccounts(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
return response(req.session.Accounts, req.session.State)
return response(req.session.Accounts, req.session.State, "")
})
}
@@ -107,7 +107,7 @@ func (g *Groupware) GetAccountBootstrap(w http.ResponseWriter, r *http.Request)
logger := log.From(req.logger.With().Str(logAccountId, mailAccountId))
accountIds := structs.Keys(req.session.Accounts)
resp, sessionState, jerr := g.jmap.GetIdentitiesAndMailboxes(mailAccountId, accountIds, req.session, req.ctx, logger)
resp, sessionState, lang, jerr := g.jmap.GetIdentitiesAndMailboxes(mailAccountId, accountIds, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -121,6 +121,6 @@ func (g *Groupware) GetAccountBootstrap(w http.ResponseWriter, r *http.Request)
Mailboxes: map[string][]jmap.Mailbox{
mailAccountId: resp.Mailboxes,
},
}, sessionState)
}, sessionState, lang)
})
}
@@ -28,7 +28,7 @@ func (g *Groupware) GetBlobMeta(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, jerr := g.jmap.GetBlobMetadata(accountId, req.session, req.ctx, logger, blobId)
res, sessionState, lang, jerr := g.jmap.GetBlobMetadata(accountId, req.session, req.ctx, logger, req.language(), blobId)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -38,9 +38,9 @@ func (g *Groupware) GetBlobMeta(w http.ResponseWriter, r *http.Request) {
}
digest := blob.Digest()
if digest != "" {
return etagResponse(res, sessionState, jmap.State(digest))
return etagResponse(res, sessionState, jmap.State(digest), lang)
} else {
return response(res, sessionState)
return response(res, sessionState, lang)
}
})
}
@@ -64,12 +64,12 @@ func (g *Groupware) UploadBlob(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
resp, jerr := g.jmap.UploadBlobStream(accountId, req.session, req.ctx, logger, contentType, body)
resp, lang, jerr := g.jmap.UploadBlobStream(accountId, req.session, req.ctx, logger, contentType, req.language(), body)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagOnlyResponse(resp, jmap.State(resp.Sha512))
return etagOnlyResponse(resp, jmap.State(resp.Sha512), lang)
})
}
@@ -89,7 +89,7 @@ func (g *Groupware) DownloadBlob(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
blob, jerr := g.jmap.DownloadBlobStream(accountId, blobId, name, typ, req.session, req.ctx, logger)
blob, lang, jerr := g.jmap.DownloadBlobStream(accountId, blobId, name, typ, req.session, req.ctx, logger, req.language())
if blob != nil && blob.Body != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
@@ -118,6 +118,9 @@ func (g *Groupware) DownloadBlob(w http.ResponseWriter, r *http.Request) {
if blob.Size >= 0 {
w.Header().Add("Content-Size", strconv.Itoa(blob.Size))
}
if lang != "" {
w.Header().Add("Content-Language", string(lang))
}
_, err := io.Copy(w, blob.Body)
if err != nil {
@@ -31,7 +31,7 @@ func (g *Groupware) GetCalendars(w http.ResponseWriter, r *http.Request) {
}
var _ string = accountId
return response(AllCalendars, req.session.State)
return response(AllCalendars, req.session.State, "")
})
}
@@ -65,7 +65,7 @@ func (g *Groupware) GetCalendarById(w http.ResponseWriter, r *http.Request) {
// TODO replace with proper implementation
for _, calendar := range AllCalendars {
if calendar.Id == calendarId {
return response(calendar, req.session.State)
return response(calendar, req.session.State, "")
}
}
return notFoundResponse(req.session.State)
@@ -102,6 +102,6 @@ func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request)
if !ok {
return notFoundResponse(req.session.State)
}
return response(events, req.session.State)
return response(events, req.session.State, "")
})
}
@@ -33,7 +33,7 @@ func (g *Groupware) GetAddressbooks(w http.ResponseWriter, r *http.Request) {
var _ string = accountId
// TODO replace with proper implementation
return response(AllAddressBooks, req.session.State)
return response(AllAddressBooks, req.session.State, "")
})
}
@@ -67,7 +67,7 @@ func (g *Groupware) GetAddressbook(w http.ResponseWriter, r *http.Request) {
// TODO replace with proper implementation
for _, ab := range AllAddressBooks {
if ab.Id == addressBookId {
return response(ab, req.session.State)
return response(ab, req.session.State, "")
}
}
return notFoundResponse(req.session.State)
@@ -104,6 +104,6 @@ func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Requ
if !ok {
return notFoundResponse(req.session.State)
}
return response(contactCards, req.session.State)
return response(contactCards, req.session.State, "")
})
}
@@ -77,12 +77,12 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
logger := log.From(req.logger.With().Str(HeaderSince, since).Str(logAccountId, accountId))
emails, sessionState, jerr := g.jmap.GetMailboxChanges(accountId, req.session, req.ctx, logger, mailboxId, since, true, g.maxBodyValueBytes, maxChanges)
emails, sessionState, 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(jerr)
}
return etagResponse(emails, sessionState, emails.State)
return etagResponse(emails, sessionState, emails.State, lang)
})
} else {
g.respond(w, r, func(req Request) Response {
@@ -114,12 +114,12 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request
logger := log.From(l)
emails, sessionState, jerr := g.jmap.GetAllEmailsInMailbox(accountId, req.session, req.ctx, logger, mailboxId, offset, limit, true, g.maxBodyValueBytes)
emails, sessionState, lang, jerr := g.jmap.GetAllEmailsInMailbox(accountId, req.session, req.ctx, logger, req.language(), mailboxId, offset, limit, true, g.maxBodyValueBytes)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(emails, sessionState, emails.State)
return etagResponse(emails, sessionState, emails.State, lang)
})
}
}
@@ -140,25 +140,25 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
if len(ids) == 1 {
logger := log.From(l.Str("id", log.SafeString(id)))
emails, sessionState, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, ids, true, g.maxBodyValueBytes)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
if len(emails.Emails) < 1 {
return notFoundResponse(sessionState)
} else {
return etagResponse(emails.Emails[0], sessionState, emails.State)
return etagResponse(emails.Emails[0], sessionState, emails.State, lang)
}
} else {
logger := log.From(l.Array("ids", log.SafeStringArray(ids)))
emails, sessionState, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, ids, true, g.maxBodyValueBytes)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
if len(emails.Emails) < 1 {
return notFoundResponse(sessionState)
} else {
return etagResponse(emails.Emails, sessionState, emails.State)
return etagResponse(emails.Emails, sessionState, emails.State, lang)
}
}
})
@@ -196,7 +196,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
}
l := req.logger.With().Str(logAccountId, accountId)
logger := log.From(l)
emails, sessionState, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, []string{id}, false, 0)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -204,7 +204,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
return notFoundResponse(sessionState)
}
email := emails.Emails[0]
return etagResponse(email.Attachments, sessionState, emails.State)
return etagResponse(email.Attachments, sessionState, emails.State, lang)
})
} else {
g.stream(w, r, func(req Request, w http.ResponseWriter) *Error {
@@ -221,7 +221,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
l = contextAppender(l)
logger := log.From(l)
emails, _, jerr := g.jmap.GetEmails(mailAccountId, req.session, req.ctx, logger, []string{id}, false, 0)
emails, _, lang, jerr := g.jmap.GetEmails(mailAccountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0)
if jerr != nil {
return req.apiErrorFromJmap(req.observeJmapError(jerr))
}
@@ -241,7 +241,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
return nil
}
blob, jerr := g.jmap.DownloadBlobStream(blobAccountId, attachment.BlobId, attachment.Name, attachment.Type, req.session, req.ctx, logger)
blob, lang, jerr := g.jmap.DownloadBlobStream(blobAccountId, attachment.BlobId, attachment.Name, attachment.Type, req.session, req.ctx, logger, req.language())
if blob != nil && blob.Body != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
@@ -270,7 +270,9 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
if blob.Size >= 0 {
w.Header().Add("Content-Size", strconv.Itoa(blob.Size))
}
if lang != "" {
w.Header().Add("Content-Language", string(lang))
}
_, err := io.Copy(w, blob.Body)
if err != nil {
return req.observedParameterError(ErrorStreamingResponse)
@@ -300,12 +302,12 @@ func (g *Groupware) getEmailsSince(w http.ResponseWriter, r *http.Request, since
logger := log.From(l)
emails, sessionState, jerr := g.jmap.GetEmailsSince(accountId, req.session, req.ctx, logger, since, true, g.maxBodyValueBytes, maxChanges)
emails, sessionState, lang, jerr := g.jmap.GetEmailsSince(accountId, req.session, req.ctx, logger, req.language(), since, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(emails, sessionState, emails.State)
return etagResponse(emails, sessionState, emails.State, lang)
})
}
@@ -497,7 +499,7 @@ func (g *Groupware) searchEmails(w http.ResponseWriter, r *http.Request) {
}
logger = log.From(logger.With().Str(logAccountId, accountId))
results, sessionState, jerr := g.jmap.QueryEmailsWithSnippets(accountId, filter, req.session, req.ctx, logger, offset, limit, fetchBodies, g.maxBodyValueBytes)
results, sessionState, lang, jerr := g.jmap.QueryEmailsWithSnippets(accountId, filter, req.session, req.ctx, logger, req.language(), offset, limit, fetchBodies, g.maxBodyValueBytes)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -522,7 +524,7 @@ func (g *Groupware) searchEmails(w http.ResponseWriter, r *http.Request) {
Total: results.Total,
Limit: results.Limit,
QueryState: results.QueryState,
}, sessionState, results.QueryState)
}, sessionState, results.QueryState, lang)
} else {
accountId, err := req.GetAccountIdForMail()
if err != nil {
@@ -530,7 +532,7 @@ func (g *Groupware) searchEmails(w http.ResponseWriter, r *http.Request) {
}
logger = log.From(logger.With().Str(logAccountId, accountId))
results, sessionState, jerr := g.jmap.QueryEmailSnippets(accountId, filter, req.session, req.ctx, logger, offset, limit)
results, sessionState, lang, jerr := g.jmap.QueryEmailSnippets(accountId, filter, req.session, req.ctx, logger, req.language(), offset, limit)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -540,7 +542,7 @@ func (g *Groupware) searchEmails(w http.ResponseWriter, r *http.Request) {
Total: results.Total,
Limit: results.Limit,
QueryState: results.QueryState,
}, sessionState, results.QueryState)
}, sessionState, results.QueryState, lang)
}
})
}
@@ -608,12 +610,12 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
BodyValues: body.BodyValues,
}
created, sessionState, jerr := g.jmap.CreateEmail(accountId, create, req.session, req.ctx, logger)
created, sessionState, lang, jerr := g.jmap.CreateEmail(accountId, create, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return response(created.Email, sessionState)
return response(created.Email, sessionState, lang)
})
}
@@ -642,7 +644,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
emailId: body,
}
result, sessionState, jerr := g.jmap.UpdateEmails(accountId, updates, req.session, req.ctx, logger)
result, sessionState, lang, jerr := g.jmap.UpdateEmails(accountId, updates, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -657,7 +659,7 @@ func (g *Groupware) UpdateEmail(w http.ResponseWriter, r *http.Request) {
"An internal API behaved unexpectedly: wrong Email update ID response from JMAP endpoint")))
}
return response(updatedEmail, sessionState)
return response(updatedEmail, sessionState, lang)
})
}
@@ -677,7 +679,7 @@ func (g *Groupware) DeleteEmail(w http.ResponseWriter, r *http.Request) {
logger := log.From(l)
_, sessionState, jerr := g.jmap.DeleteEmails(accountId, []string{emailId}, req.session, req.ctx, logger)
_, sessionState, _, jerr := g.jmap.DeleteEmails(accountId, []string{emailId}, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -687,14 +689,16 @@ func (g *Groupware) DeleteEmail(w http.ResponseWriter, r *http.Request) {
}
type AboutEmailsEvent struct {
Id string `json:"id"`
Source string `json:"source"`
Emails []jmap.Email `json:"emails"`
Id string `json:"id"`
Source string `json:"source"`
Emails []jmap.Email `json:"emails"`
Language jmap.Language `json:"lang"`
}
type AboutEmailResponse struct {
Email jmap.Email `json:"email"`
RequestId string `json:"requestId"`
Email jmap.Email `json:"email"`
RequestId string `json:"requestId"`
Language jmap.Language `json:"lang"`
}
func relatedEmails(email jmap.Email, beacon time.Time, days uint) jmap.EmailFilterElement {
@@ -766,7 +770,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
reqId := req.GetRequestId()
getEmailsBefore := time.Now()
emails, sessionState, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, []string{id}, true, g.maxBodyValueBytes)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, true, g.maxBodyValueBytes)
getEmailsDuration := time.Since(getEmailsBefore)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
@@ -791,7 +795,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
g.job(logger, RelationTypeSameSender, func(jobId uint64, l *log.Logger) {
before := time.Now()
results, _, jerr := g.jmap.QueryEmails(accountId, filter, req.session, bgctx, l, 0, limit, false, g.maxBodyValueBytes)
results, _, lang, jerr := g.jmap.QueryEmails(accountId, filter, req.session, bgctx, l, req.language(), 0, limit, false, g.maxBodyValueBytes)
duration := time.Since(before)
if jerr != nil {
req.observeJmapError(jerr)
@@ -801,14 +805,14 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
related := filterEmails(results.Emails, email)
l.Trace().Msgf("'%v' found %v other emails", RelationTypeSameSender, len(related))
if len(related) > 0 {
req.push(RelationEntityEmail, AboutEmailsEvent{Id: reqId, Emails: related, Source: RelationTypeSameSender})
req.push(RelationEntityEmail, AboutEmailsEvent{Id: reqId, Emails: related, Source: RelationTypeSameSender, Language: lang})
}
}
})
g.job(logger, RelationTypeSameThread, func(jobId uint64, l *log.Logger) {
before := time.Now()
emails, _, jerr := g.jmap.EmailsInThread(accountId, email.ThreadId, req.session, bgctx, l, false, g.maxBodyValueBytes)
emails, _, _, jerr := g.jmap.EmailsInThread(accountId, email.ThreadId, req.session, bgctx, l, req.language(), false, g.maxBodyValueBytes)
duration := time.Since(before)
if jerr != nil {
req.observeJmapError(jerr)
@@ -818,7 +822,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
related := filterEmails(emails, email)
l.Trace().Msgf("'%v' found %v other emails", RelationTypeSameThread, len(related))
if len(related) > 0 {
req.push(RelationEntityEmail, AboutEmailsEvent{Id: reqId, Emails: related, Source: RelationTypeSameThread})
req.push(RelationEntityEmail, AboutEmailsEvent{Id: reqId, Emails: related, Source: RelationTypeSameThread, Language: lang})
}
}
})
@@ -826,7 +830,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
return etagResponse(AboutEmailResponse{
Email: email,
RequestId: reqId,
}, sessionState, emails.State)
}, sessionState, emails.State, lang)
})
}
@@ -1113,7 +1117,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
logger := log.From(l)
emailsSummariesByAccount, sessionState, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, filter, limit)
emailsSummariesByAccount, sessionState, lang, jerr := g.jmap.QueryEmailSummaries(allAccountIds, req.session, req.ctx, logger, req.language(), filter, limit)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -1141,7 +1145,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter,
summaries[i] = summarizeEmail(all[i].accountId, all[i].email)
}
return response(summaries, sessionState)
return response(summaries, sessionState, lang)
})
}
@@ -32,10 +32,10 @@ func (g *Groupware) GetIdentities(w http.ResponseWriter, r *http.Request) {
return errorResponse(err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, jerr := g.jmap.GetIdentity(accountId, req.session, req.ctx, logger)
res, sessionState, lang, jerr := g.jmap.GetIdentity(accountId, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(res, sessionState, res.State)
return etagResponse(res, sessionState, res.State, lang)
})
}
@@ -152,7 +152,7 @@ func (g *Groupware) Index(w http.ResponseWriter, r *http.Request) {
g.respond(w, r, func(req Request) Response {
accountIds := structs.Keys(req.session.Accounts)
identitiesResponse, sessionState, err := g.jmap.GetIdentities(accountIds, req.session, req.ctx, req.logger)
identitiesResponse, sessionState, lang, err := g.jmap.GetIdentities(accountIds, req.session, req.ctx, req.logger, req.language())
if err != nil {
return req.errorResponseFromJmap(err)
}
@@ -163,7 +163,7 @@ func (g *Groupware) Index(w http.ResponseWriter, r *http.Request) {
Limits: buildIndexLimits(req.session),
Accounts: buildIndexAccount(req.session, identitiesResponse.Identities),
PrimaryAccounts: buildIndexPrimaryAccounts(req.session),
}, sessionState)
}, sessionState, lang)
})
}
@@ -41,13 +41,13 @@ func (g *Groupware) GetMailbox(w http.ResponseWriter, r *http.Request) {
return errorResponse(err)
}
mailboxes, sessionState, jerr := g.jmap.GetMailbox(accountId, req.session, req.ctx, req.logger, []string{mailboxId})
mailboxes, sessionState, lang, jerr := g.jmap.GetMailbox(accountId, req.session, req.ctx, req.logger, req.language(), []string{mailboxId})
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
if len(mailboxes.Mailboxes) == 1 {
return etagResponse(mailboxes.Mailboxes[0], sessionState, mailboxes.State)
return etagResponse(mailboxes.Mailboxes[0], sessionState, mailboxes.State, lang)
} else {
return notFoundResponse(sessionState)
}
@@ -123,25 +123,25 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) {
logger := log.From(req.logger.With().Str(logAccountId, accountId))
if hasCriteria {
mailboxesByAccountId, sessionState, err := g.jmap.SearchMailboxes([]string{accountId}, req.session, req.ctx, logger, filter)
mailboxesByAccountId, sessionState, lang, err := g.jmap.SearchMailboxes([]string{accountId}, req.session, req.ctx, logger, req.language(), filter)
if err != nil {
return req.errorResponseFromJmap(err)
}
mailboxes, ok := mailboxesByAccountId[accountId]
if ok {
return etagResponse(mailboxes.Mailboxes, sessionState, mailboxes.State)
return etagResponse(mailboxes.Mailboxes, sessionState, mailboxes.State, lang)
} else {
return notFoundResponse(sessionState)
}
} else {
mailboxesByAccountId, sessionState, err := g.jmap.GetAllMailboxes([]string{accountId}, req.session, req.ctx, logger)
mailboxesByAccountId, sessionState, lang, err := g.jmap.GetAllMailboxes([]string{accountId}, req.session, req.ctx, logger, req.language())
if err != nil {
return req.errorResponseFromJmap(err)
}
mailboxes, ok := mailboxesByAccountId[accountId]
if ok {
return etagResponse(mailboxes.Mailboxes, sessionState, mailboxes.State)
return etagResponse(mailboxes.Mailboxes, sessionState, mailboxes.State, lang)
} else {
return notFoundResponse(sessionState)
}
@@ -193,17 +193,17 @@ func (g *Groupware) GetMailboxesForAllAccounts(w http.ResponseWriter, r *http.Re
logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)))
if hasCriteria {
mailboxesByAccountId, sessionState, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, filter)
mailboxesByAccountId, sessionState, lang, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, req.language(), filter)
if err != nil {
return req.errorResponseFromJmap(err)
}
return response(mailboxesByAccountId, sessionState)
return response(mailboxesByAccountId, sessionState, lang)
} else {
mailboxesByAccountId, sessionState, err := g.jmap.GetAllMailboxes(accountIds, req.session, req.ctx, logger)
mailboxesByAccountId, sessionState, lang, err := g.jmap.GetAllMailboxes(accountIds, req.session, req.ctx, logger, req.language())
if err != nil {
return req.errorResponseFromJmap(err)
}
return response(mailboxesByAccountId, sessionState)
return response(mailboxesByAccountId, sessionState, lang)
}
})
}
@@ -221,11 +221,11 @@ func (g *Groupware) GetMailboxByRoleForAllAccounts(w http.ResponseWriter, r *htt
Role: role,
}
mailboxesByAccountId, sessionState, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, filter)
mailboxesByAccountId, sessionState, lang, err := g.jmap.SearchMailboxes(accountIds, req.session, req.ctx, logger, req.language(), filter)
if err != nil {
return req.errorResponseFromJmap(err)
}
return response(mailboxesByAccountId, sessionState)
return response(mailboxesByAccountId, sessionState, lang)
})
}
@@ -267,12 +267,12 @@ func (g *Groupware) GetMailboxChanges(w http.ResponseWriter, r *http.Request) {
logger := log.From(l)
changes, sessionState, jerr := g.jmap.GetMailboxChanges(accountId, req.session, req.ctx, logger, mailboxId, sinceState, true, g.maxBodyValueBytes, maxChanges)
changes, sessionState, 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(jerr)
}
return etagResponse(changes, sessionState, changes.State)
return etagResponse(changes, sessionState, changes.State, lang)
})
}
@@ -320,12 +320,12 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht
logger := log.From(l)
changesByAccountId, sessionState, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, sinceStateMap, true, g.maxBodyValueBytes, maxChanges)
changesByAccountId, sessionState, lang, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language(), sinceStateMap, true, g.maxBodyValueBytes, maxChanges)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return response(changesByAccountId, sessionState)
return response(changesByAccountId, sessionState, lang)
})
}
@@ -336,11 +336,11 @@ func (g *Groupware) GetMailboxRoles(w http.ResponseWriter, r *http.Request) {
l.Array(logAccountId, log.SafeStringArray(allAccountIds))
logger := log.From(l)
rolesByAccountId, sessionState, jerr := g.jmap.GetMailboxRolesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger)
rolesByAccountId, sessionState, lang, jerr := g.jmap.GetMailboxRolesForMultipleAccounts(allAccountIds, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return response(rolesByAccountId, sessionState)
return response(rolesByAccountId, sessionState, lang)
})
}
@@ -0,0 +1,39 @@
package groupware
import (
"net/http"
"github.com/opencloud-eu/opencloud/pkg/jmap"
"github.com/opencloud-eu/opencloud/pkg/log"
)
// When the request succeeds.
// swagger:response GetQuotaResponse200
type SwaggerGetQuotaResponse200 struct {
// in: body
Body []jmap.Quota
}
// swagger:route GET /groupware/accounts/{account}/quota quota getquota
// Get quota limits.
//
// responses:
//
// 200: GetQuotaResponse200
// 400: ErrorResponse400
// 500: ErrorResponse500
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(err)
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, lang, jerr := g.jmap.GetQuotas(accountId, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(res.List, sessionState, res.State, lang)
})
}
@@ -31,7 +31,7 @@ func (g *Groupware) GetTaskLists(w http.ResponseWriter, r *http.Request) {
}
var _ string = accountId
return response(AllTaskLists, req.session.State)
return response(AllTaskLists, req.session.State, "")
})
}
@@ -65,7 +65,7 @@ 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(tasklist, req.session.State)
return response(tasklist, req.session.State, "")
}
}
return notFoundResponse(req.session.State)
@@ -102,6 +102,6 @@ func (g *Groupware) GetTasksInTaskList(w http.ResponseWriter, r *http.Request) {
if !ok {
return notFoundResponse(req.session.State)
}
return response(tasks, req.session.State)
return response(tasks, req.session.State, "")
})
}
@@ -37,11 +37,11 @@ func (g *Groupware) GetVacation(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, jerr := g.jmap.GetVacationResponse(accountId, req.session, req.ctx, logger)
res, sessionState, lang, jerr := g.jmap.GetVacationResponse(accountId, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(res, sessionState, res.State)
return etagResponse(res, sessionState, res.State, lang)
})
}
@@ -79,11 +79,11 @@ func (g *Groupware) SetVacation(w http.ResponseWriter, r *http.Request) {
}
logger := log.From(req.logger.With().Str(logAccountId, accountId))
res, sessionState, jerr := g.jmap.SetVacationResponse(accountId, body, req.session, req.ctx, logger)
res, sessionState, lang, jerr := g.jmap.SetVacationResponse(accountId, body, req.session, req.ctx, logger, req.language())
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
return etagResponse(res, sessionState, res.ResponseState)
return etagResponse(res, sessionState, res.ResponseState, lang)
})
}
@@ -575,6 +575,10 @@ func (g *Groupware) sendResponse(w http.ResponseWriter, r *http.Request, respons
w.Header().Add("Session-State", string(sessionState))
}
if response.contentLanguage != "" {
w.Header().Add("Content-Language", string(response.contentLanguage))
}
notModified := false
if etag != "" {
challenge := r.Header.Get("if-none-match")
@@ -65,8 +65,8 @@ var (
errNoPrimaryAccountForTask = errors.New("no primary account for task")
errNoPrimaryAccountForCalendar = errors.New("no primary account for calendar")
errNoPrimaryAccountForContact = errors.New("no primary account for contact")
errNoPrimaryAccountForQuota = errors.New("no primary account for quota")
// errNoPrimaryAccountForSieve = errors.New("no primary account for sieve")
// errNoPrimaryAccountForQuota = errors.New("no primary account for quota")
// errNoPrimaryAccountForWebsocket = errors.New("no primary account for websocket")
)
@@ -109,6 +109,10 @@ func (r Request) GetAccountIdForVacationResponse() (string, *Error) {
return r.getAccountId(r.session.PrimaryAccounts.VacationResponse, errNoPrimaryAccountForVacationResponse)
}
func (r Request) GetAccountIdForQuota() (string, *Error) {
return r.getAccountId(r.session.PrimaryAccounts.Quota, errNoPrimaryAccountForQuota)
}
func (r Request) GetAccountIdForSubmission() (string, *Error) {
return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForSubmission)
}
@@ -280,6 +284,10 @@ func (r Request) body(target any) *Error {
return nil
}
func (r Request) language() string {
return r.r.Header.Get("Accept-Language")
}
func (r Request) observe(obs prometheus.Observer, value float64) {
metrics.WithExemplar(obs, value, r.GetRequestId(), r.GetTraceId())
}
@@ -7,11 +7,12 @@ import (
)
type Response struct {
body any
status int
err *Error
etag jmap.State
sessionState jmap.SessionState
body any
status int
err *Error
etag jmap.State
sessionState jmap.SessionState
contentLanguage jmap.Language
}
func errorResponse(err *Error) Response {
@@ -32,30 +33,33 @@ func errorResponseWithSessionState(err *Error, sessionState jmap.SessionState) R
}
}
func response(body any, sessionState jmap.SessionState) Response {
func response(body any, sessionState jmap.SessionState, contentLanguage jmap.Language) Response {
return Response{
body: body,
err: nil,
etag: jmap.State(sessionState),
sessionState: sessionState,
body: body,
err: nil,
etag: jmap.State(sessionState),
sessionState: sessionState,
contentLanguage: contentLanguage,
}
}
func etagResponse(body any, sessionState jmap.SessionState, etag jmap.State) Response {
func etagResponse(body any, sessionState jmap.SessionState, etag jmap.State, contentLanguage jmap.Language) Response {
return Response{
body: body,
err: nil,
etag: etag,
sessionState: sessionState,
body: body,
err: nil,
etag: etag,
sessionState: sessionState,
contentLanguage: contentLanguage,
}
}
func etagOnlyResponse(body any, etag jmap.State) Response {
func etagOnlyResponse(body any, etag jmap.State, contentLanguage jmap.Language) Response {
return Response{
body: body,
err: nil,
etag: etag,
sessionState: "",
body: body,
err: nil,
etag: etag,
sessionState: "",
contentLanguage: contentLanguage,
}
}
@@ -72,6 +72,7 @@ func (g *Groupware) Route(r chi.Router) {
r.Get("/identities", g.GetIdentities)
r.Get("/vacation", g.GetVacation)
r.Put("/vacation", g.SetVacation)
r.Get("/quota", g.GetQuota)
r.Route("/mailboxes", func(r chi.Router) {
r.Get("/", g.GetMailboxes) // ?name=&role=&subcribed=
r.Get("/{mailbox}", g.GetMailbox)