mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-07-13 09:02:09 -04:00
groupware: implement Mailbox modification endpoints + refactor ETag/state in the framework
* add endpoints for Mailboxes:
- PATCH mailboxes/{id}
- DELETE mailboxes/{id}
- POST mailboxes
* refactor the pkg/jmap and groupware framework to systematically
return a jmap.State out-of-band of the per-method payloads, since
they are almost always present in JMAP responses, which lead to the
artificial creation of a lot of composed struct types just to also
return the State; on the downside, it adds yet another return
parameter
This commit is contained in:
@@ -9,12 +9,7 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
)
|
||||
|
||||
type BlobResponse struct {
|
||||
Blob *Blob `json:"blob,omitempty"`
|
||||
State State `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) GetBlobMetadata(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (BlobResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetBlobMetadata(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (*Blob, SessionState, State, Language, Error) {
|
||||
cmd, jerr := j.request(session, logger,
|
||||
invocation(CommandBlobGet, BlobGetCommand{
|
||||
AccountId: accountId,
|
||||
@@ -24,22 +19,22 @@ func (j *Client) GetBlobMetadata(accountId string, session *Session, ctx context
|
||||
}, "0"),
|
||||
)
|
||||
if jerr != nil {
|
||||
return BlobResponse{}, "", "", jerr
|
||||
return nil, "", "", "", jerr
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (BlobResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*Blob, State, Error) {
|
||||
var response BlobGetResponse
|
||||
err := retrieveResponseMatchParameters(logger, body, CommandBlobGet, "0", &response)
|
||||
if err != nil {
|
||||
return BlobResponse{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(response.List) != 1 {
|
||||
logger.Error().Msgf("%T.List has %v entries instead of 1", response, len(response.List))
|
||||
return BlobResponse{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return nil, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
get := response.List[0]
|
||||
return BlobResponse{Blob: &get, State: response.State}, nil
|
||||
return &get, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,7 +43,6 @@ type UploadedBlob struct {
|
||||
Size int `json:"size"`
|
||||
Type string `json:"type"`
|
||||
Sha512 string `json:"sha:512"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) UploadBlobStream(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, contentType string, body io.Reader) (UploadedBlob, Language, Error) {
|
||||
@@ -70,7 +64,7 @@ func (j *Client) DownloadBlobStream(accountId string, blobId string, name string
|
||||
return j.blob.DownloadBinary(ctx, logger, session, downloadUrl, session.DownloadEndpoint, acceptLanguage)
|
||||
}
|
||||
|
||||
func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte, contentType string) (UploadedBlob, SessionState, Language, Error) {
|
||||
func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte, contentType string) (UploadedBlob, SessionState, State, Language, Error) {
|
||||
encoded := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
upload := BlobUploadCommand{
|
||||
@@ -100,35 +94,35 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
|
||||
invocation(CommandBlobGet, getHash, "1"),
|
||||
)
|
||||
if jerr != nil {
|
||||
return UploadedBlob{}, "", "", jerr
|
||||
return UploadedBlob{}, "", "", "", jerr
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedBlob, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedBlob, State, Error) {
|
||||
var uploadResponse BlobUploadResponse
|
||||
err := retrieveResponseMatchParameters(logger, body, CommandBlobUpload, "0", &uploadResponse)
|
||||
if err != nil {
|
||||
return UploadedBlob{}, err
|
||||
return UploadedBlob{}, "", err
|
||||
}
|
||||
|
||||
var getResponse BlobGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandBlobGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return UploadedBlob{}, err
|
||||
return UploadedBlob{}, "", err
|
||||
}
|
||||
|
||||
if len(uploadResponse.Created) != 1 {
|
||||
logger.Error().Msgf("%T.Created has %v entries instead of 1", uploadResponse, len(uploadResponse.Created))
|
||||
return UploadedBlob{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedBlob{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
upload, ok := uploadResponse.Created["0"]
|
||||
if !ok {
|
||||
logger.Error().Msgf("%T.Created has no item '0'", uploadResponse)
|
||||
return UploadedBlob{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedBlob{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
logger.Error().Msgf("%T.List has %v entries instead of 1", getResponse, len(getResponse.List))
|
||||
return UploadedBlob{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedBlob{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
get := getResponse.List[0]
|
||||
|
||||
@@ -137,8 +131,7 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
|
||||
Size: upload.Size,
|
||||
Type: upload.Type,
|
||||
Sha512: get.DigestSha512,
|
||||
State: getResponse.State,
|
||||
}, nil
|
||||
}, getResponse.State, nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type AccountBootstrapResult struct {
|
||||
Quotas []Quota `json:"quotas,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]AccountBootstrapResult, SessionState, Language, Error) {
|
||||
func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]AccountBootstrapResult, SessionState, State, Language, Error) {
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
|
||||
logger = j.logger("GetBootstrap", session, logger)
|
||||
@@ -25,26 +25,30 @@ func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context
|
||||
|
||||
cmd, err := j.request(session, logger, calls...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]AccountBootstrapResult, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]AccountBootstrapResult, State, Error) {
|
||||
identityPerAccount := map[string][]Identity{}
|
||||
quotaPerAccount := map[string][]Quota{}
|
||||
identityStatesPerAccount := map[string]State{}
|
||||
quotaStatesPerAccount := map[string]State{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var identityResponse IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, mcid(accountId, "I"), &identityResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
} else {
|
||||
identityPerAccount[accountId] = identityResponse.List
|
||||
identityStatesPerAccount[accountId] = identityResponse.State
|
||||
}
|
||||
|
||||
var quotaResponse QuotaGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandQuotaGet, mcid(accountId, "Q"), "aResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
} else {
|
||||
quotaPerAccount[accountId] = quotaResponse.List
|
||||
quotaStatesPerAccount[accountId] = quotaResponse.State
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +69,7 @@ func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context
|
||||
r.Quotas = value
|
||||
result[accountId] = r
|
||||
}
|
||||
return result, nil
|
||||
|
||||
return result, squashStateMaps(identityStatesPerAccount, quotaStatesPerAccount), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
)
|
||||
|
||||
func (j *Client) ParseICalendarBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, blobIds []string) (CalendarEventParseResponse, SessionState, Language, Error) {
|
||||
func (j *Client) ParseICalendarBlob(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, blobIds []string) (CalendarEventParseResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("ParseICalendarBlob", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
invocation(CommandCalendarEventParse, CalendarEventParseCommand{AccountId: accountId, BlobIDs: blobIds}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return CalendarEventParseResponse{}, "", "", err
|
||||
return CalendarEventParseResponse{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (CalendarEventParseResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (CalendarEventParseResponse, State, Error) {
|
||||
var response CalendarEventParseResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandCalendarEventParse, "0", &response)
|
||||
if err != nil {
|
||||
return CalendarEventParseResponse{}, err
|
||||
return CalendarEventParseResponse{}, "", err
|
||||
}
|
||||
return response, nil
|
||||
return response, "", nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,36 +12,34 @@ import (
|
||||
type AddressBooksResponse struct {
|
||||
AddressBooks []AddressBook `json:"addressbooks"`
|
||||
NotFound []string `json:"notFound,omitempty"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) GetAddressbooks(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (AddressBooksResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetAddressbooks(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (AddressBooksResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetAddressbooks", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
invocation(CommandAddressBookGet, AddressBookGetCommand{AccountId: accountId, Ids: ids}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return AddressBooksResponse{}, "", "", err
|
||||
return AddressBooksResponse{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (AddressBooksResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (AddressBooksResponse, State, Error) {
|
||||
var response AddressBookGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandAddressBookGet, "0", &response)
|
||||
if err != nil {
|
||||
return AddressBooksResponse{}, err
|
||||
return AddressBooksResponse{}, response.State, err
|
||||
}
|
||||
return AddressBooksResponse{
|
||||
AddressBooks: response.List,
|
||||
NotFound: response.NotFound,
|
||||
State: response.State,
|
||||
}, nil
|
||||
}, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) QueryContactCards(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string,
|
||||
filter ContactCardFilterElement, sortBy []ContactCardComparator,
|
||||
position uint, limit uint) (map[string][]jscontact.ContactCard, SessionState, Language, Error) {
|
||||
position uint, limit uint) (map[string][]jscontact.ContactCard, SessionState, State, Language, Error) {
|
||||
logger = j.logger("QueryContactCards", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
@@ -75,32 +73,29 @@ func (j *Client) QueryContactCards(accountIds []string, session *Session, ctx co
|
||||
}
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]jscontact.ContactCard, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]jscontact.ContactCard, State, Error) {
|
||||
resp := map[string][]jscontact.ContactCard{}
|
||||
stateByAccountId := map[string]State{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var response ContactCardGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, mcid(accountId, "1"), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
if len(response.NotFound) > 0 {
|
||||
// TODO what to do when there are not-found emails here? potentially nothing, they could have been deleted between query and get?
|
||||
}
|
||||
resp[accountId] = response.List
|
||||
stateByAccountId[accountId] = response.State
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
type CreatedContactCard struct {
|
||||
ContactCard *jscontact.ContactCard `json:"contactCard"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) CreateContactCard(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create jscontact.ContactCard) (CreatedContactCard, SessionState, Language, Error) {
|
||||
func (j *Client) CreateContactCard(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create jscontact.ContactCard) (*jscontact.ContactCard, SessionState, State, Language, Error) {
|
||||
logger = j.logger("CreateContactCard", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
@@ -116,53 +111,45 @@ func (j *Client) CreateContactCard(accountId string, session *Session, ctx conte
|
||||
}, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return CreatedContactCard{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (CreatedContactCard, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*jscontact.ContactCard, State, Error) {
|
||||
var setResponse ContactCardSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandContactCardSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return CreatedContactCard{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
setErr, notok := setResponse.NotCreated["c"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
|
||||
return CreatedContactCard{}, setErrorError(setErr, EmailType)
|
||||
return nil, "", setErrorError(setErr, EmailType)
|
||||
}
|
||||
|
||||
if created, ok := setResponse.Created["c"]; !ok || created == nil {
|
||||
berr := fmt.Errorf("failed to find %s in %s response", string(ContactCardType), string(CommandContactCardSet))
|
||||
logger.Error().Err(berr)
|
||||
return CreatedContactCard{}, simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
return nil, "", simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
var getResponse ContactCardGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return CreatedContactCard{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(getResponse.List) < 1 {
|
||||
berr := fmt.Errorf("failed to find %s in %s response", string(ContactCardType), string(CommandContactCardSet))
|
||||
logger.Error().Err(berr)
|
||||
return CreatedContactCard{}, simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
return nil, "", simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
return CreatedContactCard{
|
||||
ContactCard: &getResponse.List[0],
|
||||
State: setResponse.NewState,
|
||||
}, nil
|
||||
return &getResponse.List[0], setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
type DeletedContactCards struct {
|
||||
State State `json:"state"`
|
||||
NotDestroyed map[string]SetError `json:"notDestroyed"`
|
||||
}
|
||||
|
||||
func (j *Client) DeleteContactCard(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (DeletedContactCards, SessionState, Language, Error) {
|
||||
func (j *Client) DeleteContactCard(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
|
||||
logger = j.logger("DeleteContactCard", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
@@ -172,18 +159,15 @@ func (j *Client) DeleteContactCard(accountId string, destroy []string, session *
|
||||
}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return DeletedContactCards{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (DeletedContactCards, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]SetError, State, Error) {
|
||||
var setResponse ContactCardSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandContactCardSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return DeletedContactCards{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
return DeletedContactCards{
|
||||
State: setResponse.NewState,
|
||||
NotDestroyed: setResponse.NotDestroyed,
|
||||
}, nil
|
||||
return setResponse.NotDestroyed, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@ type Emails struct {
|
||||
Total uint `json:"total,omitzero"`
|
||||
Limit uint `json:"limit,omitzero"`
|
||||
Offset uint `json:"offset,omitzero"`
|
||||
State State `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// Retrieve specific Emails by their id.
|
||||
func (j *Client) GetEmails(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string, fetchBodies bool, maxBodyValueBytes uint, markAsSeen bool, withThreads bool) (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, withThreads bool) ([]Email, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetEmails", session, logger)
|
||||
|
||||
get := EmailGetCommand{AccountId: accountId, Ids: ids, FetchAllBodyValues: fetchBodies}
|
||||
@@ -53,62 +52,62 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
|
||||
cmd, err := j.request(session, logger, methodCalls...)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return Emails{}, "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
return nil, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Emails, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Email, State, Error) {
|
||||
if markAsSeen {
|
||||
var markResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &markResponse)
|
||||
if err != nil {
|
||||
return Emails{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
for _, seterr := range markResponse.NotUpdated {
|
||||
// TODO we don't have a way to compose multiple set errors yet
|
||||
return Emails{}, setErrorError(seterr, EmailType)
|
||||
return nil, "", setErrorError(seterr, EmailType)
|
||||
}
|
||||
}
|
||||
var response EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &response)
|
||||
if err != nil {
|
||||
return Emails{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
if withThreads {
|
||||
var threads ThreadGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, "2", &threads)
|
||||
if err != nil {
|
||||
return Emails{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
setThreadSize(&threads, response.List)
|
||||
}
|
||||
return Emails{Emails: response.List, State: response.State}, nil
|
||||
return response.List, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) GetEmailBlobId(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (string, SessionState, Language, Error) {
|
||||
func (j *Client) GetEmailBlobId(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string) (string, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetEmailBlobId", session, logger)
|
||||
|
||||
get := EmailGetCommand{AccountId: accountId, Ids: []string{id}, FetchAllBodyValues: false, Properties: []string{"blobId"}}
|
||||
cmd, err := j.request(session, logger, invocation(CommandEmailGet, get, "0"))
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
return "", "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (string, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (string, State, Error) {
|
||||
var response EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "0", &response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
if len(response.List) != 1 {
|
||||
return "", nil
|
||||
return "", "", nil
|
||||
}
|
||||
email := response.List[0]
|
||||
return email.BlobId, nil
|
||||
return email.BlobId, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve all the Emails in a given Mailbox by its id.
|
||||
func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, offset uint, limit uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool) (Emails, SessionState, Language, Error) {
|
||||
func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, offset uint, limit uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool) (Emails, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("GetAllEmailsInMailbox", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies).Uint(logOffset, offset).Uint(logLimit, limit)
|
||||
})
|
||||
@@ -155,27 +154,27 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return Emails{}, "", "", err
|
||||
return Emails{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Emails, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Emails, State, Error) {
|
||||
var queryResponse EmailQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, "0", &queryResponse)
|
||||
if err != nil {
|
||||
return Emails{}, err
|
||||
return Emails{}, "", err
|
||||
}
|
||||
var getResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return Emails{}, err
|
||||
return Emails{}, "", err
|
||||
}
|
||||
|
||||
if withThreads {
|
||||
var thread ThreadGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, "2", &thread)
|
||||
if err != nil {
|
||||
return Emails{}, err
|
||||
return Emails{}, "", err
|
||||
}
|
||||
setThreadSize(&thread, getResponse.List)
|
||||
}
|
||||
@@ -185,12 +184,12 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
|
||||
Total: queryResponse.Total,
|
||||
Limit: queryResponse.Limit,
|
||||
Offset: queryResponse.Position,
|
||||
}, nil
|
||||
}, queryResponse.QueryState, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Get all the Emails that have been created, updated or deleted since a given state.
|
||||
func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, Language, Error) {
|
||||
func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("GetEmailsSince", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies).Str(logSinceState, sinceState)
|
||||
})
|
||||
@@ -226,28 +225,28 @@ func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.
|
||||
invocation(CommandEmailGet, getUpdated, "2"),
|
||||
)
|
||||
if err != nil {
|
||||
return MailboxChanges{}, "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
return MailboxChanges{}, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxChanges, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxChanges, State, Error) {
|
||||
var changesResponse EmailChangesResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailChanges, "0", &changesResponse)
|
||||
if err != nil {
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
var createdResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &createdResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
var updatedResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "2", &updatedResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
return MailboxChanges{
|
||||
@@ -256,8 +255,7 @@ func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.
|
||||
NewState: changesResponse.NewState,
|
||||
Created: createdResponse.List,
|
||||
Updated: createdResponse.List,
|
||||
State: updatedResponse.State,
|
||||
}, nil
|
||||
}, updatedResponse.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -275,7 +273,7 @@ type EmailSnippetQueryResult struct {
|
||||
QueryState State `json:"queryState"`
|
||||
}
|
||||
|
||||
func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint) (map[string]EmailSnippetQueryResult, SessionState, Language, Error) {
|
||||
func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint) (map[string]EmailSnippetQueryResult, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("QueryEmailSnippets", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Uint(logLimit, limit).Uint(logOffset, offset)
|
||||
})
|
||||
@@ -326,28 +324,28 @@ func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterEleme
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailSnippetQueryResult, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailSnippetQueryResult, State, Error) {
|
||||
results := make(map[string]EmailSnippetQueryResult, len(uniqueAccountIds))
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var queryResponse EmailQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var mailResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &mailResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var snippetResponse SearchSnippetGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandSearchSnippetGet, mcid(accountId, "2"), &snippetResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
mailResponseById := structs.Index(mailResponse.List, func(e Email) string { return e.Id })
|
||||
@@ -379,7 +377,7 @@ func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterEleme
|
||||
QueryState: queryResponse.QueryState,
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
return results, squashStateFunc(results, func(r EmailSnippetQueryResult) State { return r.QueryState }), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -391,7 +389,7 @@ type EmailQueryResult struct {
|
||||
QueryState State `json:"queryState"`
|
||||
}
|
||||
|
||||
func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryResult, SessionState, Language, Error) {
|
||||
func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryResult, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("QueryEmails", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies)
|
||||
})
|
||||
@@ -430,22 +428,22 @@ func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, ses
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryResult, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryResult, State, Error) {
|
||||
results := make(map[string]EmailQueryResult, len(uniqueAccountIds))
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var queryResponse EmailQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var emailsResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &emailsResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
results[accountId] = EmailQueryResult{
|
||||
@@ -456,7 +454,7 @@ func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, ses
|
||||
QueryState: queryResponse.QueryState,
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
return results, squashStateFunc(results, func(r EmailQueryResult) State { return r.QueryState }), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -473,7 +471,7 @@ type EmailQueryWithSnippetsResult struct {
|
||||
QueryState State `json:"queryState"`
|
||||
}
|
||||
|
||||
func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryWithSnippetsResult, SessionState, Language, Error) {
|
||||
func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilterElement, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, offset uint, limit uint, fetchBodies bool, maxBodyValueBytes uint) (map[string]EmailQueryWithSnippetsResult, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("QueryEmailsWithSnippets", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies)
|
||||
})
|
||||
@@ -523,28 +521,28 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilter
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return nil, "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
return nil, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryWithSnippetsResult, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailQueryWithSnippetsResult, State, Error) {
|
||||
result := make(map[string]EmailQueryWithSnippetsResult, len(uniqueAccountIds))
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var queryResponse EmailQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var snippetResponse SearchSnippetGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandSearchSnippetGet, mcid(accountId, "1"), &snippetResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var emailsResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "2"), &emailsResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
snippetsById := map[string][]SearchSnippet{}
|
||||
@@ -576,7 +574,7 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilter
|
||||
QueryState: queryResponse.QueryState,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
return result, squashStateFunc(result, func(r EmailQueryWithSnippetsResult) State { return r.QueryState }), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -587,7 +585,7 @@ type UploadedEmail struct {
|
||||
Sha512 string `json:"sha:512"`
|
||||
}
|
||||
|
||||
func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (UploadedEmail, SessionState, Language, Error) {
|
||||
func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (UploadedEmail, SessionState, State, Language, Error) {
|
||||
encoded := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
upload := BlobUploadCommand{
|
||||
@@ -617,36 +615,36 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
|
||||
invocation(CommandBlobGet, getHash, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return UploadedEmail{}, "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
return UploadedEmail{}, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedEmail, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedEmail, State, Error) {
|
||||
var uploadResponse BlobUploadResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandBlobUpload, "0", &uploadResponse)
|
||||
if err != nil {
|
||||
return UploadedEmail{}, err
|
||||
return UploadedEmail{}, "", err
|
||||
}
|
||||
|
||||
var getResponse BlobGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandBlobGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return UploadedEmail{}, err
|
||||
return UploadedEmail{}, "", err
|
||||
}
|
||||
|
||||
if len(uploadResponse.Created) != 1 {
|
||||
logger.Error().Msgf("%T.Created has %v elements instead of 1", uploadResponse, len(uploadResponse.Created))
|
||||
return UploadedEmail{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedEmail{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
upload, ok := uploadResponse.Created["0"]
|
||||
if !ok {
|
||||
logger.Error().Msgf("%T.Created has no element '0'", uploadResponse)
|
||||
return UploadedEmail{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedEmail{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
logger.Error().Msgf("%T.List has %v elements instead of 1", getResponse, len(getResponse.List))
|
||||
return UploadedEmail{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
return UploadedEmail{}, "", simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
get := getResponse.List[0]
|
||||
|
||||
@@ -655,17 +653,12 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
|
||||
Size: upload.Size,
|
||||
Type: upload.Type,
|
||||
Sha512: get.DigestSha512,
|
||||
}, nil
|
||||
}, State(get.DigestSha256), nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
type CreatedEmail struct {
|
||||
Email *Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (CreatedEmail, SessionState, Language, Error) {
|
||||
func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (*Email, SessionState, State, Language, Error) {
|
||||
set := EmailSetCommand{
|
||||
AccountId: accountId,
|
||||
Create: map[string]EmailCreate{
|
||||
@@ -680,14 +673,14 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
|
||||
invocation(CommandEmailSet, set, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return CreatedEmail{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (CreatedEmail, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*Email, State, Error) {
|
||||
var setResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return CreatedEmail{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(setResponse.NotCreated) > 0 {
|
||||
@@ -698,28 +691,20 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
|
||||
setErr, notok := setResponse.NotCreated["c"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
|
||||
return CreatedEmail{}, setErrorError(setErr, EmailType)
|
||||
return nil, "", setErrorError(setErr, EmailType)
|
||||
}
|
||||
|
||||
created, ok := setResponse.Created["c"]
|
||||
if !ok {
|
||||
berr := fmt.Errorf("failed to find %s in %s response", string(EmailType), string(CommandEmailSet))
|
||||
logger.Error().Err(berr)
|
||||
return CreatedEmail{}, simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
return nil, "", simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
return CreatedEmail{
|
||||
Email: created,
|
||||
State: setResponse.NewState,
|
||||
}, nil
|
||||
return created, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
type UpdatedEmails struct {
|
||||
Updated map[string]*Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
// The Email/set method encompasses:
|
||||
// - Changing the keywords of an Email (e.g., unread/flagged status)
|
||||
// - Adding/removing an Email to/from Mailboxes (moving a message)
|
||||
@@ -728,7 +713,7 @@ type UpdatedEmails struct {
|
||||
// To create drafts, use the CreateEmail function instead.
|
||||
//
|
||||
// To delete mails, use the DeleteEmails function instead.
|
||||
func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (UpdatedEmails, SessionState, Language, Error) {
|
||||
func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]*Email, SessionState, State, Language, Error) {
|
||||
cmd, err := j.request(session, logger,
|
||||
invocation(CommandEmailSet, EmailSetCommand{
|
||||
AccountId: accountId,
|
||||
@@ -736,32 +721,24 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate,
|
||||
}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return UpdatedEmails{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UpdatedEmails, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]*Email, State, Error) {
|
||||
var setResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return UpdatedEmails{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
if len(setResponse.NotUpdated) != len(updates) {
|
||||
// error occured
|
||||
// TODO(pbleser-oc) handle submission errors
|
||||
}
|
||||
return UpdatedEmails{
|
||||
Updated: setResponse.Updated,
|
||||
State: setResponse.NewState,
|
||||
}, nil
|
||||
return setResponse.Updated, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
type DeletedEmails struct {
|
||||
State State `json:"state"`
|
||||
NotDestroyed map[string]SetError `json:"notDestroyed"`
|
||||
}
|
||||
|
||||
func (j *Client) DeleteEmails(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (DeletedEmails, SessionState, Language, Error) {
|
||||
func (j *Client) DeleteEmails(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
|
||||
cmd, err := j.request(session, logger,
|
||||
invocation(CommandEmailSet, EmailSetCommand{
|
||||
AccountId: accountId,
|
||||
@@ -769,25 +746,21 @@ func (j *Client) DeleteEmails(accountId string, destroy []string, session *Sessi
|
||||
}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return DeletedEmails{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (DeletedEmails, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]SetError, State, Error) {
|
||||
var setResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return DeletedEmails{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
return DeletedEmails{
|
||||
State: setResponse.NewState,
|
||||
NotDestroyed: setResponse.NotDestroyed,
|
||||
}, nil
|
||||
return setResponse.NotDestroyed, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
type SubmittedEmail struct {
|
||||
Id string `json:"id"`
|
||||
State State `json:"state"`
|
||||
SendAt time.Time `json:"sendAt,omitzero"`
|
||||
ThreadId string `json:"threadId,omitempty"`
|
||||
UndoStatus EmailSubmissionUndoStatus `json:"undoStatus,omitempty"`
|
||||
@@ -810,7 +783,7 @@ type SubmittedEmail struct {
|
||||
MdnBlobIds []string `json:"mdnBlobIds,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (SubmittedEmail, SessionState, Language, Error) {
|
||||
func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (SubmittedEmail, SessionState, State, Language, Error) {
|
||||
set := EmailSubmissionSetCommand{
|
||||
AccountId: accountId,
|
||||
Create: map[string]EmailSubmissionCreate{
|
||||
@@ -840,14 +813,14 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
|
||||
invocation(CommandEmailSubmissionGet, get, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, "", "", err
|
||||
return SubmittedEmail{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (SubmittedEmail, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (SubmittedEmail, State, Error) {
|
||||
var submissionResponse EmailSubmissionSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionSet, "0", &submissionResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, err
|
||||
return SubmittedEmail{}, "", err
|
||||
}
|
||||
|
||||
if len(submissionResponse.NotCreated) > 0 {
|
||||
@@ -863,13 +836,13 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
|
||||
var setResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, err
|
||||
return SubmittedEmail{}, "", err
|
||||
}
|
||||
|
||||
var getResponse EmailSubmissionGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, err
|
||||
return SubmittedEmail{}, "", err
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
@@ -881,18 +854,17 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
|
||||
|
||||
return SubmittedEmail{
|
||||
Id: submission.Id,
|
||||
State: setResponse.NewState,
|
||||
SendAt: submission.SendAt,
|
||||
ThreadId: submission.ThreadId,
|
||||
UndoStatus: submission.UndoStatus,
|
||||
Envelope: submission.Envelope,
|
||||
DsnBlobIds: submission.DsnBlobIds,
|
||||
MdnBlobIds: submission.MdnBlobIds,
|
||||
}, nil
|
||||
}, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) EmailsInThread(accountId string, threadId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, fetchBodies bool, maxBodyValueBytes uint) ([]Email, SessionState, Language, Error) {
|
||||
func (j *Client) EmailsInThread(accountId string, threadId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, fetchBodies bool, maxBodyValueBytes uint) ([]Email, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("EmailsInThread", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies).Str("threadId", log.SafeString(threadId))
|
||||
})
|
||||
@@ -914,16 +886,16 @@ func (j *Client) EmailsInThread(accountId string, threadId string, session *Sess
|
||||
}, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return []Email{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Email, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Email, State, Error) {
|
||||
var emailsResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &emailsResponse)
|
||||
if err != nil {
|
||||
return []Email{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
return emailsResponse.List, nil
|
||||
return emailsResponse.List, emailsResponse.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -954,7 +926,7 @@ var EmailSummaryProperties = []string{
|
||||
EmailPropertyPreview,
|
||||
}
|
||||
|
||||
func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter EmailFilterElement, limit uint, withThreads bool) (map[string]EmailsSummary, SessionState, Language, Error) {
|
||||
func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter EmailFilterElement, limit uint, withThreads bool) (map[string]EmailsSummary, SessionState, State, Language, Error) {
|
||||
logger = j.logger("QueryEmailSummaries", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
@@ -995,22 +967,22 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
|
||||
}
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailsSummary, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]EmailsSummary, State, Error) {
|
||||
resp := map[string]EmailsSummary{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var queryResponse EmailQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var response EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
if len(response.NotFound) > 0 {
|
||||
// TODO what to do when there are not-found emails here? potentially nothing, they could have been deleted between query and get?
|
||||
@@ -1019,7 +991,7 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
|
||||
var thread ThreadGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandThreadGet, mcid(accountId, "2"), &thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
setThreadSize(&thread, response.List)
|
||||
}
|
||||
@@ -1032,7 +1004,7 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
|
||||
State: response.State,
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashStateFunc(resp, func(s EmailsSummary) State { return s.State }), nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -8,60 +8,46 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
)
|
||||
|
||||
type Identities struct {
|
||||
Identities []Identity `json:"identities"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) GetAllIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (Identities, SessionState, Language, Error) {
|
||||
func (j *Client) GetAllIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) ([]Identity, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetAllIdentities", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId}, "0"))
|
||||
if err != nil {
|
||||
return Identities{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identities, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Identity, State, Error) {
|
||||
var response IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, "0", &response)
|
||||
if err != nil {
|
||||
return Identities{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
return Identities{
|
||||
Identities: response.List,
|
||||
State: response.State,
|
||||
}, nil
|
||||
return response.List, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) GetIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identityIds []string) (Identities, SessionState, Language, Error) {
|
||||
func (j *Client) GetIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identityIds []string) ([]Identity, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetIdentities", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId, Ids: identityIds}, "0"))
|
||||
if err != nil {
|
||||
return Identities{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identities, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]Identity, State, Error) {
|
||||
var response IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, "0", &response)
|
||||
if err != nil {
|
||||
return Identities{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
return Identities{
|
||||
Identities: response.List,
|
||||
State: response.State,
|
||||
}, nil
|
||||
return response.List, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
type IdentitiesGetResponse struct {
|
||||
Identities map[string][]Identity `json:"identities,omitempty"`
|
||||
NotFound []string `json:"notFound,omitempty"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (IdentitiesGetResponse, SessionState, Language, Error) {
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
|
||||
func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (IdentitiesGetResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetIdentitiesForAllAccounts", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
calls := make([]Invocation, len(uniqueAccountIds))
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
calls[i] = invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId}, strconv.Itoa(i))
|
||||
@@ -69,40 +55,38 @@ func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Sessi
|
||||
|
||||
cmd, err := j.request(session, logger, calls...)
|
||||
if err != nil {
|
||||
return IdentitiesGetResponse{}, "", "", err
|
||||
return IdentitiesGetResponse{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentitiesGetResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentitiesGetResponse, State, Error) {
|
||||
identities := make(map[string][]Identity, len(uniqueAccountIds))
|
||||
var lastState State
|
||||
stateByAccountId := make(map[string]State, len(uniqueAccountIds))
|
||||
notFound := []string{}
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
var response IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, strconv.Itoa(i), &response)
|
||||
if err != nil {
|
||||
return IdentitiesGetResponse{}, err
|
||||
return IdentitiesGetResponse{}, "", err
|
||||
} else {
|
||||
identities[accountId] = response.List
|
||||
}
|
||||
lastState = response.State
|
||||
stateByAccountId[accountId] = response.State
|
||||
notFound = append(notFound, response.NotFound...)
|
||||
}
|
||||
|
||||
return IdentitiesGetResponse{
|
||||
Identities: identities,
|
||||
NotFound: structs.Uniq(notFound),
|
||||
State: lastState,
|
||||
}, nil
|
||||
}, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
type IdentitiesAndMailboxesGetResponse struct {
|
||||
Identities map[string][]Identity `json:"identities,omitempty"`
|
||||
NotFound []string `json:"notFound,omitempty"`
|
||||
State State `json:"state"`
|
||||
Mailboxes []Mailbox `json:"mailboxes"`
|
||||
}
|
||||
|
||||
func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (IdentitiesAndMailboxesGetResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (IdentitiesAndMailboxesGetResponse, SessionState, State, Language, Error) {
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
|
||||
logger = j.logger("GetIdentitiesAndMailboxes", session, logger)
|
||||
@@ -115,40 +99,39 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
|
||||
|
||||
cmd, err := j.request(session, logger, calls...)
|
||||
if err != nil {
|
||||
return IdentitiesAndMailboxesGetResponse{}, "", "", err
|
||||
return IdentitiesAndMailboxesGetResponse{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentitiesAndMailboxesGetResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentitiesAndMailboxesGetResponse, State, Error) {
|
||||
identities := make(map[string][]Identity, len(uniqueAccountIds))
|
||||
var lastState State
|
||||
stateByAccountId := make(map[string]State, len(uniqueAccountIds))
|
||||
notFound := []string{}
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
var response IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentityGet, strconv.Itoa(i+1), &response)
|
||||
if err != nil {
|
||||
return IdentitiesAndMailboxesGetResponse{}, err
|
||||
return IdentitiesAndMailboxesGetResponse{}, "", err
|
||||
} else {
|
||||
identities[accountId] = response.List
|
||||
}
|
||||
lastState = response.State
|
||||
stateByAccountId[accountId] = response.State
|
||||
notFound = append(notFound, response.NotFound...)
|
||||
}
|
||||
|
||||
var mailboxResponse MailboxGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, "0", &mailboxResponse)
|
||||
if err != nil {
|
||||
return IdentitiesAndMailboxesGetResponse{}, err
|
||||
return IdentitiesAndMailboxesGetResponse{}, "", err
|
||||
}
|
||||
|
||||
return IdentitiesAndMailboxesGetResponse{
|
||||
Identities: identities,
|
||||
NotFound: structs.Uniq(notFound),
|
||||
State: lastState,
|
||||
Mailboxes: mailboxResponse.List,
|
||||
}, nil
|
||||
}, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) CreateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (State, SessionState, Language, Error) {
|
||||
func (j *Client) CreateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (Identity, SessionState, State, Language, Error) {
|
||||
logger = j.logger("CreateIdentity", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandIdentitySet, IdentitySetCommand{
|
||||
AccountId: accountId,
|
||||
@@ -157,24 +140,24 @@ func (j *Client) CreateIdentity(accountId string, session *Session, ctx context.
|
||||
},
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return Identity{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (State, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identity, State, Error) {
|
||||
var response IdentitySetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
|
||||
if err != nil {
|
||||
return response.NewState, err
|
||||
return Identity{}, response.NewState, err
|
||||
}
|
||||
setErr, notok := response.NotCreated["c"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr)
|
||||
return "", setErrorError(setErr, IdentityType)
|
||||
return Identity{}, "", setErrorError(setErr, IdentityType)
|
||||
}
|
||||
return response.NewState, nil
|
||||
return response.Created["c"], response.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) UpdateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (State, SessionState, Language, Error) {
|
||||
func (j *Client) UpdateIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identity Identity) (Identity, SessionState, State, Language, Error) {
|
||||
logger = j.logger("UpdateIdentity", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandIdentitySet, IdentitySetCommand{
|
||||
AccountId: accountId,
|
||||
@@ -183,48 +166,43 @@ func (j *Client) UpdateIdentity(accountId string, session *Session, ctx context.
|
||||
},
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return Identity{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (State, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Identity, State, Error) {
|
||||
var response IdentitySetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
|
||||
if err != nil {
|
||||
return response.NewState, err
|
||||
return Identity{}, response.NewState, err
|
||||
}
|
||||
setErr, notok := response.NotCreated["c"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr)
|
||||
return "", setErrorError(setErr, IdentityType)
|
||||
return Identity{}, "", setErrorError(setErr, IdentityType)
|
||||
}
|
||||
return response.NewState, nil
|
||||
return response.Created["c"], response.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
type IdentityDeletion struct {
|
||||
Destroyed []string `json:"destroyed"`
|
||||
NewState State `json:"newState,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) DeleteIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (IdentityDeletion, SessionState, Language, Error) {
|
||||
func (j *Client) DeleteIdentity(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) ([]string, SessionState, State, Language, Error) {
|
||||
logger = j.logger("DeleteIdentity", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandIdentitySet, IdentitySetCommand{
|
||||
AccountId: accountId,
|
||||
Destroy: ids,
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return IdentityDeletion{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (IdentityDeletion, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]string, State, Error) {
|
||||
var response IdentitySetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandIdentitySet, "0", &response)
|
||||
if err != nil {
|
||||
return IdentityDeletion{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
for _, setErr := range response.NotDestroyed {
|
||||
// TODO only returning the first error here, we should probably aggregate them instead
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", response, setErr)
|
||||
return IdentityDeletion{}, setErrorError(setErr, IdentityType)
|
||||
return nil, "", setErrorError(setErr, IdentityType)
|
||||
}
|
||||
return IdentityDeletion{Destroyed: response.Destroyed, NewState: response.NewState}, nil
|
||||
return response.Destroyed, response.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package jmap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
@@ -12,41 +13,39 @@ import (
|
||||
type MailboxesResponse struct {
|
||||
Mailboxes []Mailbox `json:"mailboxes"`
|
||||
NotFound []any `json:"notFound"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
// https://jmap.io/spec-mail.html#mailboxget
|
||||
func (j *Client) GetMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (MailboxesResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (MailboxesResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetMailbox", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
invocation(CommandMailboxGet, MailboxGetCommand{AccountId: accountId, Ids: ids}, "0"),
|
||||
)
|
||||
if err != nil {
|
||||
return MailboxesResponse{}, "", "", err
|
||||
return MailboxesResponse{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxesResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxesResponse, State, Error) {
|
||||
var response MailboxGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, "0", &response)
|
||||
if err != nil {
|
||||
return MailboxesResponse{}, err
|
||||
return MailboxesResponse{}, "", err
|
||||
}
|
||||
return MailboxesResponse{
|
||||
Mailboxes: response.List,
|
||||
NotFound: response.NotFound,
|
||||
State: response.State,
|
||||
}, nil
|
||||
}, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]Mailboxes, SessionState, Language, Error) {
|
||||
func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]Mailbox, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetAllMailboxes", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
n := len(uniqueAccountIds)
|
||||
if n < 1 {
|
||||
return nil, "", "", nil
|
||||
return nil, "", "", "", nil
|
||||
}
|
||||
|
||||
invocations := make([]Invocation, n)
|
||||
@@ -56,35 +55,27 @@ func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx cont
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]Mailboxes, Error) {
|
||||
resp := map[string]Mailboxes{}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]Mailbox, State, Error) {
|
||||
resp := map[string][]Mailbox{}
|
||||
stateByAccountid := map[string]State{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var response MailboxGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "0"), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
resp[accountId] = Mailboxes{
|
||||
Mailboxes: response.List,
|
||||
State: response.State,
|
||||
}
|
||||
resp[accountId] = response.List
|
||||
stateByAccountid[accountId] = response.State
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountid), nil
|
||||
})
|
||||
}
|
||||
|
||||
type Mailboxes struct {
|
||||
// The list of mailboxes that were found using the specified search criteria.
|
||||
Mailboxes []Mailbox `json:"mailboxes,omitempty"`
|
||||
// The state of the search.
|
||||
State State `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter MailboxFilterElement) (map[string]Mailboxes, SessionState, Language, Error) {
|
||||
func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, filter MailboxFilterElement) (map[string][]Mailbox, SessionState, State, Language, Error) {
|
||||
logger = j.logger("SearchMailboxes", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
@@ -103,21 +94,23 @@ func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx cont
|
||||
}
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return map[string]Mailboxes{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]Mailboxes, Error) {
|
||||
resp := map[string]Mailboxes{}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]Mailbox, State, Error) {
|
||||
resp := map[string][]Mailbox{}
|
||||
stateByAccountid := map[string]State{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var response MailboxGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "1"), &response)
|
||||
if err != nil {
|
||||
return map[string]Mailboxes{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
resp[accountId] = Mailboxes{Mailboxes: response.List, State: response.State}
|
||||
resp[accountId] = response.List
|
||||
stateByAccountid[accountId] = response.State
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountid), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,11 +120,10 @@ type MailboxChanges struct {
|
||||
NewState State `json:"newState"`
|
||||
Created []Email `json:"created,omitempty"`
|
||||
Updated []Email `json:"updated,omitempty"`
|
||||
State State `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// Retrieve Email changes in a given Mailbox since a given state.
|
||||
func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, sinceState string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, Language, Error) {
|
||||
func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, sinceState string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("GetMailboxChanges", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
return z.Bool(logFetchBodies, fetchBodies).Str(logSinceState, sinceState)
|
||||
})
|
||||
@@ -167,28 +159,28 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
|
||||
invocation(CommandEmailGet, getUpdated, "2"),
|
||||
)
|
||||
if err != nil {
|
||||
return MailboxChanges{}, "", "", err
|
||||
return MailboxChanges{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxChanges, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxChanges, State, Error) {
|
||||
var mailboxResponse MailboxChangesResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxChanges, "0", &mailboxResponse)
|
||||
if err != nil {
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
var createdResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &createdResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
var updatedResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "2", &updatedResponse)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
return MailboxChanges{}, err
|
||||
return MailboxChanges{}, "", err
|
||||
}
|
||||
|
||||
return MailboxChanges{
|
||||
@@ -197,13 +189,12 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
|
||||
NewState: mailboxResponse.NewState,
|
||||
Created: createdResponse.List,
|
||||
Updated: createdResponse.List,
|
||||
State: createdResponse.State,
|
||||
}, nil
|
||||
}, createdResponse.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve Email changes in Mailboxes of multiple Accounts.
|
||||
func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (map[string]MailboxChanges, SessionState, Language, Error) {
|
||||
func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (map[string]MailboxChanges, SessionState, State, Language, Error) {
|
||||
logger = j.loggerParams("GetMailboxChangesForMultipleAccounts", session, logger, func(z zerolog.Context) zerolog.Context {
|
||||
sinceStateLogDict := zerolog.Dict()
|
||||
for k, v := range sinceStateMap {
|
||||
@@ -215,7 +206,7 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
n := len(uniqueAccountIds)
|
||||
if n < 1 {
|
||||
return map[string]MailboxChanges{}, "", "", nil
|
||||
return map[string]MailboxChanges{}, "", "", "", nil
|
||||
}
|
||||
|
||||
invocations := make([]Invocation, n*3)
|
||||
@@ -257,28 +248,29 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return map[string]MailboxChanges{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]MailboxChanges, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]MailboxChanges, State, Error) {
|
||||
resp := make(map[string]MailboxChanges, n)
|
||||
stateByAccountId := make(map[string]State, n)
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var mailboxResponse MailboxChangesResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxChanges, mcid(accountId, "0"), &mailboxResponse)
|
||||
if err != nil {
|
||||
return map[string]MailboxChanges{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var createdResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &createdResponse)
|
||||
if err != nil {
|
||||
return map[string]MailboxChanges{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var updatedResponse EmailGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "2"), &updatedResponse)
|
||||
if err != nil {
|
||||
return map[string]MailboxChanges{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
resp[accountId] = MailboxChanges{
|
||||
@@ -287,21 +279,21 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
|
||||
NewState: mailboxResponse.NewState,
|
||||
Created: createdResponse.List,
|
||||
Updated: createdResponse.List,
|
||||
State: createdResponse.State,
|
||||
}
|
||||
stateByAccountId[accountId] = createdResponse.State
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]string, SessionState, Language, Error) {
|
||||
func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]string, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetMailboxRolesForMultipleAccounts", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
n := len(uniqueAccountIds)
|
||||
if n < 1 {
|
||||
return map[string][]string{}, "", "", nil
|
||||
return nil, "", "", "", nil
|
||||
}
|
||||
|
||||
t := true
|
||||
@@ -326,16 +318,17 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return map[string][]string{}, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]string, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string][]string, State, Error) {
|
||||
resp := make(map[string][]string, n)
|
||||
stateByAccountId := make(map[string]State, n)
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var getResponse MailboxGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "1"), &getResponse)
|
||||
if err != nil {
|
||||
return map[string][]string{}, err
|
||||
return nil, "", err
|
||||
}
|
||||
roles := make([]string, len(getResponse.List))
|
||||
for i, mailbox := range getResponse.List {
|
||||
@@ -343,18 +336,19 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session
|
||||
}
|
||||
slices.Sort(roles)
|
||||
resp[accountId] = roles
|
||||
stateByAccountId[accountId] = getResponse.State
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]string, SessionState, Language, Error) {
|
||||
func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]string, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetInboxNameForMultipleAccounts", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
n := len(uniqueAccountIds)
|
||||
if n < 1 {
|
||||
return nil, "", "", nil
|
||||
return nil, "", "", "", nil
|
||||
}
|
||||
|
||||
invocations := make([]Invocation, n*2)
|
||||
@@ -369,27 +363,116 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *S
|
||||
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]string, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]string, State, Error) {
|
||||
resp := make(map[string]string, n)
|
||||
stateByAccountId := make(map[string]State, n)
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var r MailboxQueryResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "0"), &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
switch len(r.Ids) {
|
||||
case 0:
|
||||
// skip: account has no inbox?
|
||||
case 1:
|
||||
resp[accountId] = r.Ids[0]
|
||||
stateByAccountId[accountId] = r.QueryState
|
||||
default:
|
||||
logger.Warn().Msgf("multiple ids for mailbox role='%v' for accountId='%v'", JmapMailboxRoleInbox, accountId)
|
||||
resp[accountId] = r.Ids[0]
|
||||
stateByAccountId[accountId] = r.QueryState
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
return resp, squashState(stateByAccountId), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) UpdateMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, ifInState string, update MailboxChange) (Mailbox, SessionState, State, Language, Error) {
|
||||
logger = j.logger("UpdateMailbox", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandMailboxSet, MailboxSetCommand{
|
||||
AccountId: accountId,
|
||||
IfInState: ifInState,
|
||||
Update: map[string]PatchObject{
|
||||
mailboxId: update.AsPatch(),
|
||||
},
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return Mailbox{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Mailbox, State, Error) {
|
||||
var setResp MailboxSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxSet, "0", &setResp)
|
||||
if err != nil {
|
||||
return Mailbox{}, "", err
|
||||
}
|
||||
setErr, notok := setResp.NotUpdated["u"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotUpdated returned an error %v", setResp, setErr)
|
||||
return Mailbox{}, "", setErrorError(setErr, MailboxType)
|
||||
}
|
||||
return setResp.Updated["c"], setResp.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) CreateMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ifInState string, create MailboxChange) (Mailbox, SessionState, State, Language, Error) {
|
||||
logger = j.logger("CreateMailbox", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandMailboxSet, MailboxSetCommand{
|
||||
AccountId: accountId,
|
||||
IfInState: ifInState,
|
||||
Create: map[string]MailboxChange{
|
||||
"c": create,
|
||||
},
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return Mailbox{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (Mailbox, State, Error) {
|
||||
var setResp MailboxSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxSet, "0", &setResp)
|
||||
if err != nil {
|
||||
return Mailbox{}, "", err
|
||||
}
|
||||
setErr, notok := setResp.NotCreated["c"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotCreated returned an error %v", setResp, setErr)
|
||||
return Mailbox{}, "", setErrorError(setErr, MailboxType)
|
||||
}
|
||||
if mailbox, ok := setResp.Created["c"]; ok {
|
||||
return mailbox, setResp.NewState, nil
|
||||
} else {
|
||||
return Mailbox{}, "", simpleError(fmt.Errorf("failed to find created %T in response", Mailbox{}), JmapErrorMissingCreatedObject)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Client) DeleteMailboxes(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ifInState string, mailboxIds []string) ([]string, SessionState, State, Language, Error) {
|
||||
logger = j.logger("DeleteMailbox", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandMailboxSet, MailboxSetCommand{
|
||||
AccountId: accountId,
|
||||
IfInState: ifInState,
|
||||
Destroy: mailboxIds,
|
||||
}, "0"))
|
||||
if err != nil {
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]string, State, Error) {
|
||||
var setResp MailboxSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandMailboxSet, "0", &setResp)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
setErr, notok := setResp.NotUpdated["u"]
|
||||
if notok {
|
||||
logger.Error().Msgf("%T.NotUpdated returned an error %v", setResp, setErr)
|
||||
return nil, "", setErrorError(setErr, MailboxType)
|
||||
}
|
||||
return setResp.Destroyed, setResp.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
)
|
||||
|
||||
func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]QuotaGetResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]QuotaGetResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetQuotas", session, logger)
|
||||
|
||||
uniqueAccountIds := structs.Uniq(accountIds)
|
||||
@@ -18,18 +18,18 @@ func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Co
|
||||
}
|
||||
cmd, err := j.request(session, logger, invocations...)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
return nil, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]QuotaGetResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]QuotaGetResponse, State, Error) {
|
||||
result := map[string]QuotaGetResponse{}
|
||||
for _, accountId := range uniqueAccountIds {
|
||||
var response QuotaGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandQuotaGet, mcid(accountId, "0"), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
result[accountId] = response
|
||||
}
|
||||
return result, nil
|
||||
return result, squashStateFunc(result, func(q QuotaGetResponse) State { return q.State }), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@ const (
|
||||
)
|
||||
|
||||
// https://jmap.io/spec-mail.html#vacationresponseget
|
||||
func (j *Client) GetVacationResponse(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponseGetResponse, SessionState, Language, Error) {
|
||||
func (j *Client) GetVacationResponse(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponseGetResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("GetVacationResponse", session, logger)
|
||||
cmd, err := j.request(session, logger, invocation(CommandVacationResponseGet, VacationResponseGetCommand{AccountId: accountId}, "0"))
|
||||
if err != nil {
|
||||
return VacationResponseGetResponse{}, "", "", err
|
||||
return VacationResponseGetResponse{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (VacationResponseGetResponse, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (VacationResponseGetResponse, State, Error) {
|
||||
var response VacationResponseGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandVacationResponseGet, "0", &response)
|
||||
if err != nil {
|
||||
return VacationResponseGetResponse{}, err
|
||||
return VacationResponseGetResponse{}, "", err
|
||||
}
|
||||
return response, nil
|
||||
return response, response.State, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -53,12 +53,7 @@ type VacationResponsePayload struct {
|
||||
HtmlBody string `json:"htmlBody,omitempty"`
|
||||
}
|
||||
|
||||
type VacationResponseChange struct {
|
||||
VacationResponse VacationResponse `json:"vacationResponse"`
|
||||
ResponseState State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) SetVacationResponse(accountId string, vacation VacationResponsePayload, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponseChange, SessionState, Language, Error) {
|
||||
func (j *Client) SetVacationResponse(accountId string, vacation VacationResponsePayload, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponse, SessionState, State, Language, Error) {
|
||||
logger = j.logger("SetVacationResponse", session, logger)
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
@@ -80,37 +75,34 @@ func (j *Client) SetVacationResponse(accountId string, vacation VacationResponse
|
||||
invocation(CommandVacationResponseGet, VacationResponseGetCommand{AccountId: accountId}, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return VacationResponseChange{}, "", "", err
|
||||
return VacationResponse{}, "", "", "", err
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (VacationResponseChange, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (VacationResponse, State, Error) {
|
||||
var setResponse VacationResponseSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandVacationResponseSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return VacationResponseChange{}, err
|
||||
return VacationResponse{}, "", err
|
||||
}
|
||||
|
||||
setErr, notok := setResponse.NotCreated[vacationResponseId]
|
||||
if notok {
|
||||
// this means that the VacationResponse was not updated
|
||||
logger.Error().Msgf("%T.NotCreated contains an error: %v", setResponse, setErr)
|
||||
return VacationResponseChange{}, setErrorError(setErr, VacationResponseType)
|
||||
return VacationResponse{}, "", setErrorError(setErr, VacationResponseType)
|
||||
}
|
||||
|
||||
var getResponse VacationResponseGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandVacationResponseGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return VacationResponseChange{}, err
|
||||
return VacationResponse{}, "", err
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
berr := fmt.Errorf("failed to find %s in %s response", string(VacationResponseType), string(CommandVacationResponseGet))
|
||||
logger.Error().Msg(berr.Error())
|
||||
return VacationResponseChange{}, simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
return VacationResponse{}, "", simpleError(berr, JmapErrorInvalidJmapResponsePayload)
|
||||
}
|
||||
|
||||
return VacationResponseChange{
|
||||
VacationResponse: getResponse.List[0],
|
||||
ResponseState: setResponse.NewState,
|
||||
}, nil
|
||||
return getResponse.List[0], setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ const (
|
||||
JmapErrorWssFailedToSendWebSocketPushDisable
|
||||
JmapErrorWssFailedToClose
|
||||
JmapErrorWssFailedToRetrieveSession
|
||||
JmapErrorMissingCreatedObject
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -689,7 +689,7 @@ func TestEmails(t *testing.T) {
|
||||
var inboxFolder string
|
||||
var inboxId string
|
||||
{
|
||||
respByAccountId, sessionState, _, err := s.client.GetAllMailboxes([]string{accountId}, s.session, s.ctx, s.logger, "")
|
||||
respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, s.session, s.ctx, s.logger, "")
|
||||
require.NoError(err)
|
||||
require.Equal(s.session.State, sessionState)
|
||||
require.Len(respByAccountId, 1)
|
||||
@@ -698,7 +698,7 @@ func TestEmails(t *testing.T) {
|
||||
|
||||
mailboxesNameByRole := map[string]string{}
|
||||
mailboxesUnreadByRole := map[string]int{}
|
||||
for _, m := range resp.Mailboxes {
|
||||
for _, m := range resp {
|
||||
if m.Role != "" {
|
||||
mailboxesNameByRole[m.Role] = m.Name
|
||||
mailboxesUnreadByRole[m.Role] = m.UnreadEmails
|
||||
@@ -708,7 +708,7 @@ func TestEmails(t *testing.T) {
|
||||
require.Contains(mailboxesUnreadByRole, "inbox")
|
||||
require.Zero(mailboxesUnreadByRole["inbox"])
|
||||
|
||||
inboxId = mailboxId("inbox", resp.Mailboxes)
|
||||
inboxId = mailboxId("inbox", resp)
|
||||
require.NotEmpty(inboxId)
|
||||
inboxFolder = mailboxesNameByRole["inbox"]
|
||||
require.NotEmpty(inboxFolder)
|
||||
@@ -724,23 +724,23 @@ func TestEmails(t *testing.T) {
|
||||
|
||||
{
|
||||
{
|
||||
resp, sessionState, _, err := s.client.GetAllIdentities(accountId, s.session, s.ctx, s.logger, "")
|
||||
resp, sessionState, _, _, err := s.client.GetAllIdentities(accountId, s.session, s.ctx, s.logger, "")
|
||||
require.NoError(err)
|
||||
require.Equal(s.session.State, sessionState)
|
||||
require.Len(resp.Identities, 1)
|
||||
require.Equal(s.userEmail, resp.Identities[0].Email)
|
||||
require.Equal(s.userPersonName, resp.Identities[0].Name)
|
||||
require.Len(resp, 1)
|
||||
require.Equal(s.userEmail, resp[0].Email)
|
||||
require.Equal(s.userPersonName, resp[0].Name)
|
||||
}
|
||||
|
||||
{
|
||||
respByAccountId, sessionState, _, err := s.client.GetAllMailboxes([]string{accountId}, s.session, s.ctx, s.logger, "")
|
||||
respByAccountId, sessionState, _, _, err := s.client.GetAllMailboxes([]string{accountId}, s.session, s.ctx, s.logger, "")
|
||||
require.NoError(err)
|
||||
require.Equal(s.session.State, sessionState)
|
||||
require.Len(respByAccountId, 1)
|
||||
require.Contains(respByAccountId, accountId)
|
||||
resp := respByAccountId[accountId]
|
||||
mailboxesUnreadByRole := map[string]int{}
|
||||
for _, m := range resp.Mailboxes {
|
||||
for _, m := range resp {
|
||||
if m.Role != "" {
|
||||
mailboxesUnreadByRole[m.Role] = m.UnreadEmails
|
||||
}
|
||||
@@ -749,7 +749,7 @@ func TestEmails(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
resp, sessionState, _, err := s.client.GetAllEmailsInMailbox(accountId, s.session, s.ctx, s.logger, "", inboxId, 0, 0, true, false, 0, true)
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, s.session, s.ctx, s.logger, "", inboxId, 0, 0, true, false, 0, true)
|
||||
require.NoError(err)
|
||||
require.Equal(s.session.State, sessionState)
|
||||
|
||||
@@ -766,7 +766,7 @@ func TestEmails(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
resp, sessionState, _, err := s.client.GetAllEmailsInMailbox(accountId, s.session, s.ctx, s.logger, "", inboxId, 0, 0, false, false, 0, true)
|
||||
resp, sessionState, _, _, err := s.client.GetAllEmailsInMailbox(accountId, s.session, s.ctx, s.logger, "", inboxId, 0, 0, false, false, 0, true)
|
||||
require.NoError(err)
|
||||
require.Equal(s.session.State, sessionState)
|
||||
|
||||
|
||||
@@ -1201,7 +1201,7 @@ type Mailbox struct {
|
||||
// (that has the same parent) in any Mailbox listing in the client’s UI.
|
||||
// Mailboxes with equal order SHOULD be sorted in alphabetical order by name.
|
||||
// The sorting should take into account locale-specific character order convention.
|
||||
SortOrder int `json:"sortOrder,omitzero"`
|
||||
SortOrder *int `json:"sortOrder,omitempty"`
|
||||
|
||||
// The number of Emails in this Mailbox.
|
||||
TotalEmails int `json:"totalEmails"`
|
||||
@@ -1220,7 +1220,7 @@ type Mailbox struct {
|
||||
// These are backwards compatible with IMAP ACLs, as defined in [RFC4314].
|
||||
//
|
||||
// [RFC4314]: https://www.rfc-editor.org/rfc/rfc4314.html
|
||||
MyRights MailboxRights `json:"myRights,omitempty"`
|
||||
MyRights *MailboxRights `json:"myRights,omitempty"`
|
||||
|
||||
// Has the user indicated they wish to see this Mailbox in their client?
|
||||
//
|
||||
@@ -1241,7 +1241,96 @@ type Mailbox struct {
|
||||
// This property corresponds to IMAP [RFC3501] Mailbox subscriptions.
|
||||
//
|
||||
// [RFC3501]: https://www.rfc-editor.org/rfc/rfc3501.html
|
||||
IsSubscribed bool `json:"isSubscribed"`
|
||||
IsSubscribed *bool `json:"isSubscribed,omitempty"`
|
||||
}
|
||||
|
||||
type MailboxChange struct {
|
||||
// User-visible name for the Mailbox, e.g., “Inbox”.
|
||||
//
|
||||
// This MUST be a Net-Unicode string [@!RFC5198] of at least 1 character in length, subject to the maximum size
|
||||
// given in the capability object.
|
||||
//
|
||||
// There MUST NOT be two sibling Mailboxes with both the same parent and the same name.
|
||||
//
|
||||
// Servers MAY reject names that violate server policy (e.g., names containing a slash (/) or control characters).
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The Mailbox id for the parent of this Mailbox, or null if this Mailbox is at the top level.
|
||||
//
|
||||
// Mailboxes form acyclic graphs (forests) directed by the child-to-parent relationship. There MUST NOT be a loop.
|
||||
ParentId string `json:"parentId,omitempty"`
|
||||
|
||||
// Identifies Mailboxes that have a particular common purpose (e.g., the “inbox”), regardless of the name property
|
||||
// (which may be localised).
|
||||
//
|
||||
// This value is shared with IMAP (exposed in IMAP via the SPECIAL-USE extension [RFC6154]).
|
||||
// However, unlike in IMAP, a Mailbox MUST only have a single role, and there MUST NOT be two Mailboxes in the same
|
||||
// account with the same role.
|
||||
//
|
||||
// Servers providing IMAP access to the same data are encouraged to enforce these extra restrictions in IMAP as well.
|
||||
// Otherwise, modifying the IMAP attributes to ensure compliance when exposing the data over JMAP is implementation dependent.
|
||||
//
|
||||
// The value MUST be one of the Mailbox attribute names listed in the IANA IMAP Mailbox Name Attributes registry,
|
||||
// as established in [RFC8457], converted to lowercase. New roles may be established here in the future.
|
||||
//
|
||||
// An account is not required to have Mailboxes with any particular roles.
|
||||
//
|
||||
// [RFC6154]: https://www.rfc-editor.org/rfc/rfc6154.html
|
||||
// [RFC8457]: https://www.rfc-editor.org/rfc/rfc8457.html
|
||||
Role string `json:"role,omitempty"`
|
||||
|
||||
// Defines the sort order of Mailboxes when presented in the client’s UI, so it is consistent between devices.
|
||||
//
|
||||
// Default value: 0
|
||||
//
|
||||
// The number MUST be an integer in the range 0 <= sortOrder < 2^31.
|
||||
//
|
||||
// A Mailbox with a lower order should be displayed before a Mailbox with a higher order
|
||||
// (that has the same parent) in any Mailbox listing in the client’s UI.
|
||||
// Mailboxes with equal order SHOULD be sorted in alphabetical order by name.
|
||||
// The sorting should take into account locale-specific character order convention.
|
||||
SortOrder *int `json:"sortOrder,omitempty"`
|
||||
|
||||
// Has the user indicated they wish to see this Mailbox in their client?
|
||||
//
|
||||
// This SHOULD default to false for Mailboxes in shared accounts the user has access to and true
|
||||
// for any new Mailboxes created by the user themself.
|
||||
//
|
||||
// This MUST be stored separately per user where multiple users have access to a shared Mailbox.
|
||||
//
|
||||
// A user may have permission to access a large number of shared accounts, or a shared account with a very
|
||||
// large set of Mailboxes, but only be interested in the contents of a few of these.
|
||||
//
|
||||
// Clients may choose to only display Mailboxes where the isSubscribed property is set to true, and offer
|
||||
// a separate UI to allow the user to see and subscribe/unsubscribe from the full set of Mailboxes.
|
||||
//
|
||||
// However, clients MAY choose to ignore this property, either entirely for ease of implementation or just
|
||||
// for an account where isPersonal is true (indicating it is the user’s own rather than a shared account).
|
||||
//
|
||||
// This property corresponds to IMAP [RFC3501] Mailbox subscriptions.
|
||||
//
|
||||
// [RFC3501]: https://www.rfc-editor.org/rfc/rfc3501.html
|
||||
IsSubscribed *bool `json:"isSubscribed,omitempty"`
|
||||
}
|
||||
|
||||
func (m MailboxChange) AsPatch() PatchObject {
|
||||
p := PatchObject{}
|
||||
if m.Name != "" {
|
||||
p["name"] = m.Name
|
||||
}
|
||||
if m.ParentId != "" {
|
||||
p["parentId"] = m.ParentId
|
||||
}
|
||||
if m.Role != "" {
|
||||
p["role"] = m.Role
|
||||
}
|
||||
if m.SortOrder != nil {
|
||||
p["sortOrder"] = m.SortOrder
|
||||
}
|
||||
if m.IsSubscribed != nil {
|
||||
p["isSubscribed"] = m.IsSubscribed
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type MailboxGetCommand struct {
|
||||
@@ -1254,6 +1343,26 @@ type MailboxGetRefCommand struct {
|
||||
IdsRef *ResultReference `json:"#ids,omitempty"`
|
||||
}
|
||||
|
||||
type MailboxSetCommand struct {
|
||||
AccountId string `json:"accountId"`
|
||||
IfInState string `json:"ifInState,omitempty"`
|
||||
Create map[string]MailboxChange `json:"create,omitempty"`
|
||||
Update map[string]PatchObject `json:"update,omitempty"`
|
||||
Destroy []string `json:"destroy,omitempty"`
|
||||
}
|
||||
|
||||
type MailboxSetResponse struct {
|
||||
AccountId string `json:"accountId"`
|
||||
OldState State `json:"oldState,omitempty"`
|
||||
NewState State `json:"newState,omitempty"`
|
||||
Created map[string]Mailbox `json:"created,omitempty"`
|
||||
Updated map[string]Mailbox `json:"updated,omitempty"`
|
||||
Destroyed []string `json:"destroyed,omitempty"`
|
||||
NotCreated map[string]SetError `json:"notCreated,omitempty"`
|
||||
NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
|
||||
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
|
||||
}
|
||||
|
||||
type MailboxChangesCommand struct {
|
||||
// The id of the account to use.
|
||||
AccountId string `json:"accountId"`
|
||||
@@ -5251,6 +5360,7 @@ const (
|
||||
CommandEmailSubmissionSet Command = "EmailSubmission/set"
|
||||
CommandThreadGet Command = "Thread/get"
|
||||
CommandMailboxGet Command = "Mailbox/get"
|
||||
CommandMailboxSet Command = "Mailbox/set"
|
||||
CommandMailboxQuery Command = "Mailbox/query"
|
||||
CommandMailboxChanges Command = "Mailbox/changes"
|
||||
CommandIdentityGet Command = "Identity/get"
|
||||
@@ -5272,6 +5382,7 @@ var CommandResponseTypeMap = map[Command]func() any{
|
||||
CommandBlobUpload: func() any { return BlobUploadResponse{} },
|
||||
CommandMailboxQuery: func() any { return MailboxQueryResponse{} },
|
||||
CommandMailboxGet: func() any { return MailboxGetResponse{} },
|
||||
CommandMailboxSet: func() any { return MailboxSetResponse{} },
|
||||
CommandMailboxChanges: func() any { return MailboxChangesResponse{} },
|
||||
CommandEmailQuery: func() any { return EmailQueryResponse{} },
|
||||
CommandEmailChanges: func() any { return EmailChangesResponse{} },
|
||||
|
||||
@@ -231,15 +231,15 @@ func TestRequests(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
foldersByAccountId, sessionState, _, err := client.GetAllMailboxes([]string{"a"}, &session, ctx, &logger, "")
|
||||
foldersByAccountId, sessionState, _, _, err := client.GetAllMailboxes([]string{"a"}, &session, ctx, &logger, "")
|
||||
require.NoError(err)
|
||||
require.Len(foldersByAccountId, 1)
|
||||
require.Contains(foldersByAccountId, "a")
|
||||
folders := foldersByAccountId["a"]
|
||||
require.Len(folders.Mailboxes, 5)
|
||||
require.Len(folders, 5)
|
||||
require.NotEmpty(sessionState)
|
||||
|
||||
emails, sessionState, _, err := client.GetAllEmailsInMailbox("a", &session, ctx, &logger, "", "Inbox", 0, 0, false, true, 0, true)
|
||||
emails, sessionState, _, _, err := client.GetAllEmailsInMailbox("a", &session, ctx, &logger, "", "Inbox", 0, 0, false, true, 0, true)
|
||||
require.NoError(err)
|
||||
require.Len(emails.Emails, 3)
|
||||
require.NotEmpty(sessionState)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -57,12 +58,12 @@ func command[T any](api ApiClient,
|
||||
sessionOutdatedHandler func(session *Session, newState SessionState),
|
||||
request Request,
|
||||
acceptLanguage string,
|
||||
mapper func(body *Response) (T, Error)) (T, SessionState, Language, Error) {
|
||||
mapper func(body *Response) (T, State, Error)) (T, SessionState, State, Language, Error) {
|
||||
|
||||
responseBody, language, jmapErr := api.Command(ctx, logger, session, request, acceptLanguage)
|
||||
if jmapErr != nil {
|
||||
var zero T
|
||||
return zero, "", language, jmapErr
|
||||
return zero, "", "", language, jmapErr
|
||||
}
|
||||
|
||||
var response Response
|
||||
@@ -70,7 +71,7 @@ func command[T any](api ApiClient,
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msgf("failed to deserialize body JSON payload into a %T", response)
|
||||
var zero T
|
||||
return zero, "", language, SimpleError{code: JmapErrorDecodingResponseBody, err: err}
|
||||
return zero, "", "", language, SimpleError{code: JmapErrorDecodingResponseBody, err: err}
|
||||
}
|
||||
|
||||
if response.SessionState != session.State {
|
||||
@@ -117,21 +118,21 @@ func command[T any](api ApiClient,
|
||||
err = errors.New(msg)
|
||||
logger.Warn().Int("code", code).Str("type", errorParameters.Type).Msg(msg)
|
||||
var zero T
|
||||
return zero, response.SessionState, language, SimpleError{code: code, err: err}
|
||||
return zero, response.SessionState, "", language, SimpleError{code: code, err: err}
|
||||
} else {
|
||||
code := JmapErrorUnspecifiedType
|
||||
msg := fmt.Sprintf("found method level error in response '%v'", mr.Tag)
|
||||
err := errors.New(msg)
|
||||
logger.Warn().Int("code", code).Msg(msg)
|
||||
var zero T
|
||||
return zero, response.SessionState, language, SimpleError{code: code, err: err}
|
||||
return zero, response.SessionState, "", language, SimpleError{code: code, err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, jerr := mapper(&response)
|
||||
result, state, jerr := mapper(&response)
|
||||
sessionState := response.SessionState
|
||||
return result, sessionState, language, jerr
|
||||
return result, sessionState, state, language, jerr
|
||||
}
|
||||
|
||||
func mapstructStringToTimeHook() mapstructure.DecodeHookFunc {
|
||||
@@ -256,3 +257,74 @@ func (i *Invocation) UnmarshalJSON(bs []byte) error {
|
||||
i.Parameters = params
|
||||
return nil
|
||||
}
|
||||
|
||||
func squashState(all map[string]State) State {
|
||||
return squashStateFunc(all, func(s State) State { return s })
|
||||
}
|
||||
|
||||
func squashStateFunc[V any](all map[string]V, mapper func(V) State) State {
|
||||
n := len(all)
|
||||
if n == 0 {
|
||||
return State("")
|
||||
}
|
||||
if n == 1 {
|
||||
for _, v := range all {
|
||||
return mapper(v)
|
||||
}
|
||||
}
|
||||
|
||||
parts := make([]string, n)
|
||||
sortedKeys := make([]string, n)
|
||||
i := 0
|
||||
for k := range all {
|
||||
sortedKeys[i] = k
|
||||
i++
|
||||
}
|
||||
slices.Sort(sortedKeys)
|
||||
for i, k := range sortedKeys {
|
||||
if v, ok := all[k]; ok {
|
||||
parts[i] = k + ":" + string(mapper(v))
|
||||
} else {
|
||||
parts[i] = k + ":"
|
||||
}
|
||||
}
|
||||
return State(strings.Join(parts, ","))
|
||||
}
|
||||
|
||||
func squashStateMaps(first map[string]State, second map[string]State) State {
|
||||
return squashStateFunc(mapPairs(first, second), func(p pair[State, State]) State {
|
||||
if p.left != nil {
|
||||
if p.right != nil {
|
||||
return *p.left + ";" + *p.right
|
||||
} else {
|
||||
return *p.left + ";"
|
||||
}
|
||||
} else if p.right != nil {
|
||||
return ";" + *p.right
|
||||
} else {
|
||||
return ";"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type pair[L any, R any] struct {
|
||||
left *L
|
||||
right *R
|
||||
}
|
||||
|
||||
func mapPairs[K comparable, L, R any](left map[K]L, right map[K]R) map[K]pair[L, R] {
|
||||
result := map[K]pair[L, R]{}
|
||||
for k, l := range left {
|
||||
if r, ok := right[k]; ok {
|
||||
result[k] = pair[L, R]{left: &l, right: &r}
|
||||
} else {
|
||||
result[k] = pair[L, R]{left: &l, right: nil}
|
||||
}
|
||||
}
|
||||
for k, r := range right {
|
||||
if _, ok := left[k]; !ok {
|
||||
result[k] = pair[L, R]{left: nil, right: &r}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestDeserializeMailboxGetResponse(t *testing.T) {
|
||||
require.Equal(expected.unread, folder.UnreadThreads)
|
||||
require.Empty(folder.ParentId)
|
||||
require.Zero(folder.SortOrder)
|
||||
require.True(folder.IsSubscribed)
|
||||
require.Nil(folder.IsSubscribed)
|
||||
|
||||
require.True(folder.MyRights.MayReadItems)
|
||||
require.True(folder.MyRights.MayAddItems)
|
||||
|
||||
Reference in New Issue
Block a user