groupware: add markAsSeen=true to mark an email as $seen before it is

retrieved
This commit is contained in:
Pascal Bleser
2025-10-21 10:16:50 +02:00
parent f4ed735441
commit fa6b695f24
3 changed files with 53 additions and 11 deletions

View File

@@ -32,22 +32,47 @@ type Emails struct {
}
// Retrieve specific Emails by their id.
func (j *Client) GetEmails(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string, fetchBodies bool, maxBodyValueBytes uint) (Emails, SessionState, Language, Error) {
func (j *Client) GetEmails(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string, fetchBodies bool, maxBodyValueBytes uint, markAsSeen bool) (Emails, SessionState, Language, Error) {
logger = j.logger("GetEmails", session, logger)
get := EmailGetCommand{AccountId: accountId, Ids: ids, FetchAllBodyValues: fetchBodies}
if maxBodyValueBytes > 0 {
get.MaxBodyValueBytes = maxBodyValueBytes
}
invokeGet := invocation(CommandEmailGet, get, "1")
cmd, err := j.request(session, logger, invocation(CommandEmailGet, get, "0"))
methodCalls := []Invocation{invokeGet}
if markAsSeen {
patch := map[string]bool{
JmapKeywordSeen: true,
}
updates := make(map[string]EmailUpdate, len(ids))
for _, id := range ids {
updates[id] = EmailUpdate{"keywords": patch}
}
mark := EmailSetCommand{AccountId: accountId, Update: updates}
methodCalls = []Invocation{invocation(CommandEmailSet, mark, "0"), invokeGet}
}
cmd, err := j.request(session, logger, methodCalls...)
if err != nil {
logger.Error().Err(err).Send()
return Emails{}, "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
}
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Emails, Error) {
if markAsSeen {
var markResponse EmailSetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &markResponse)
if err != nil {
return Emails{}, err
}
for _, seterr := range markResponse.NotUpdated {
// TODO we don't have a way to compose multiple set errors yet
return Emails{}, setErrorError(seterr, EmailType)
}
}
var response EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "0", &response)
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &response)
if err != nil {
return Emails{}, err
}

View File

@@ -149,6 +149,14 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
return err
}
_, ok, err := req.parseBoolParam(QueryParamMarkAsSeen, false)
if err != nil {
return err
}
if ok {
return req.parameterError(QueryParamMarkAsSeen, fmt.Sprintf("when the Accept header is set to '%s', the API does not support setting %s", accept, QueryParamMarkAsSeen))
}
logger := log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId)).Str("id", log.SafeString(id)).Str("accept", log.SafeString(accept)))
blobId, _, _, jerr := g.jmap.GetEmailBlobId(accountId, req.session, req.ctx, logger, req.language(), id)
@@ -178,11 +186,19 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
return errorResponse(err)
}
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
if len(ids) == 1 {
l = l.Str("id", log.SafeString(id))
logger := log.From(l)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes)
markAsSeen, ok, err := req.parseBoolParam(QueryParamMarkAsSeen, false)
if err != nil {
return errorResponse(err)
}
if ok {
l = l.Bool(QueryParamMarkAsSeen, markAsSeen)
}
if len(ids) == 1 {
logger := log.From(l.Str("id", log.SafeString(id)))
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes, markAsSeen)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -194,7 +210,7 @@ func (g *Groupware) GetEmailsById(w http.ResponseWriter, r *http.Request) {
} else {
logger := log.From(l.Array("ids", log.SafeStringArray(ids)))
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), ids, true, g.maxBodyValueBytes, markAsSeen)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -240,7 +256,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
}
l := req.logger.With().Str(logAccountId, log.SafeString(accountId))
logger := log.From(l)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0)
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0, false)
if jerr != nil {
return req.errorResponseFromJmap(jerr)
}
@@ -265,7 +281,7 @@ func (g *Groupware) GetEmailAttachments(w http.ResponseWriter, r *http.Request)
l = contextAppender(l)
logger := log.From(l)
emails, _, lang, jerr := g.jmap.GetEmails(mailAccountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0)
emails, _, lang, jerr := g.jmap.GetEmails(mailAccountId, req.session, req.ctx, logger, req.language(), []string{id}, false, 0, false)
if jerr != nil {
return req.apiErrorFromJmap(req.observeJmapError(jerr))
}
@@ -1319,7 +1335,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) {
reqId := req.GetRequestId()
getEmailsBefore := time.Now()
emails, sessionState, lang, jerr := g.jmap.GetEmails(accountId, req.session, req.ctx, logger, req.language(), []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, false)
getEmailsDuration := time.Since(getEmailsBefore)
if jerr != nil {
return req.errorResponseFromJmap(jerr)

View File

@@ -53,6 +53,7 @@ const (
QueryParamAttachmentBlobId = "blobId"
QueryParamSeen = "seen"
QueryParamUndesirable = "undesirable"
QueryParamMarkAsSeen = "markAsSeen"
HeaderSince = "if-none-match"
)