mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-06 04:11:21 -05:00
jmap: fix Email/set
This commit is contained in:
@@ -522,8 +522,8 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
|
||||
}
|
||||
|
||||
type CreatedEmail struct {
|
||||
Email Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
Email *Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
func (j *Client) CreateEmail(accountId string, email EmailCreate, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (CreatedEmail, SessionState, Language, Error) {
|
||||
@@ -572,8 +572,8 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, session *Sessi
|
||||
}
|
||||
|
||||
type UpdatedEmails struct {
|
||||
Updated map[string]Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
Updated map[string]*Email `json:"email"`
|
||||
State State `json:"state"`
|
||||
}
|
||||
|
||||
// The Email/set method encompasses:
|
||||
@@ -613,7 +613,8 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate,
|
||||
}
|
||||
|
||||
type DeletedEmails struct {
|
||||
State State `json:"state"`
|
||||
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) {
|
||||
@@ -633,11 +634,10 @@ func (j *Client) DeleteEmails(accountId string, destroy []string, session *Sessi
|
||||
if err != nil {
|
||||
return DeletedEmails{}, err
|
||||
}
|
||||
if len(setResponse.NotDestroyed) != len(destroy) {
|
||||
// error occured
|
||||
// TODO(pbleser-oc) handle submission errors
|
||||
}
|
||||
return DeletedEmails{State: setResponse.NewState}, nil
|
||||
return DeletedEmails{
|
||||
State: setResponse.NewState,
|
||||
NotDestroyed: setResponse.NotDestroyed,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ func (h *HttpJmapClient) GetSession(sessionUrl *url.URL, username string, logger
|
||||
}
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
h.listener.OnFailedRequestWithStatus(endpoint, res.StatusCode)
|
||||
logger.Error().Str(logHttpStatus, res.Status).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 200")
|
||||
logger.Error().Str(logHttpStatus, log.SafeString(res.Status)).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 200")
|
||||
return SessionResponse{}, SimpleError{code: JmapErrorServerResponse, err: fmt.Errorf("JMAP API response status is %v", res.Status)}
|
||||
}
|
||||
h.listener.OnSuccessfulRequest(endpoint, res.StatusCode)
|
||||
@@ -168,7 +168,7 @@ func (h *HttpJmapClient) GetSession(sessionUrl *url.URL, username string, logger
|
||||
var data SessionResponse
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
logger.Error().Str(logHttpUrl, sessionUrlStr).Err(err).Msg("failed to decode JSON payload from .well-known/jmap response")
|
||||
logger.Error().Str(logHttpUrl, log.SafeString(sessionUrlStr)).Err(err).Msg("failed to decode JSON payload from .well-known/jmap response")
|
||||
h.listener.OnResponseBodyUnmarshallingError(endpoint, err)
|
||||
return SessionResponse{}, SimpleError{code: JmapErrorDecodingResponseBody, err: err}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ func (h *HttpJmapClient) Command(ctx context.Context, logger *log.Logger, sessio
|
||||
language := Language(res.Header.Get("Content-Language"))
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
h.listener.OnFailedRequestWithStatus(endpoint, res.StatusCode)
|
||||
logger.Error().Str(logEndpoint, endpoint).Str(logHttpStatus, res.Status).Msg("HTTP response status code is not 2xx")
|
||||
logger.Error().Str(logEndpoint, endpoint).Str(logHttpStatus, log.SafeString(res.Status)).Msg("HTTP response status code is not 2xx")
|
||||
return nil, language, SimpleError{code: JmapErrorServerResponse, err: err}
|
||||
}
|
||||
if res.Body != nil {
|
||||
@@ -259,7 +259,7 @@ func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, s
|
||||
language := Language(res.Header.Get("Content-Language"))
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
h.listener.OnFailedRequestWithStatus(endpoint, res.StatusCode)
|
||||
logger.Error().Str(logHttpStatus, res.Status).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 2xx")
|
||||
logger.Error().Str(logHttpStatus, log.SafeString(res.Status)).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 2xx")
|
||||
return UploadedBlob{}, language, SimpleError{code: JmapErrorServerResponse, err: err}
|
||||
}
|
||||
if res.Body != nil {
|
||||
@@ -282,7 +282,7 @@ func (h *HttpJmapClient) UploadBinary(ctx context.Context, logger *log.Logger, s
|
||||
var result UploadedBlob
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
logger.Error().Str(logHttpUrl, uploadUrl).Err(err).Msg("failed to decode JSON payload from the upload response")
|
||||
logger.Error().Str(logHttpUrl, log.SafeString(uploadUrl)).Err(err).Msg("failed to decode JSON payload from the upload response")
|
||||
h.listener.OnResponseBodyUnmarshallingError(endpoint, err)
|
||||
return UploadedBlob{}, language, SimpleError{code: JmapErrorDecodingResponseBody, err: err}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ func (h *HttpJmapClient) DownloadBinary(ctx context.Context, logger *log.Logger,
|
||||
}
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
h.listener.OnFailedRequestWithStatus(endpoint, res.StatusCode)
|
||||
logger.Error().Str(logHttpStatus, res.Status).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 2xx")
|
||||
logger.Error().Str(logHttpStatus, log.SafeString(res.Status)).Int(logHttpStatusCode, res.StatusCode).Msg("HTTP response status code is not 2xx")
|
||||
return nil, language, SimpleError{code: JmapErrorServerResponse, err: err}
|
||||
}
|
||||
h.listener.OnSuccessfulRequest(endpoint, res.StatusCode)
|
||||
|
||||
@@ -152,7 +152,7 @@ type senderGenerator struct {
|
||||
senders []sender
|
||||
}
|
||||
|
||||
func newSenderGenerator(domain string, numSenders int) senderGenerator {
|
||||
func newSenderGenerator(numSenders int) senderGenerator {
|
||||
senders := make([]sender, numSenders)
|
||||
for i := range numSenders {
|
||||
person := gofakeit.Person()
|
||||
@@ -494,7 +494,7 @@ func (s *StalwartTest) fill(folder string, count int) ([]filledMail, int, error)
|
||||
bccName := "HR"
|
||||
bccAddress := fmt.Sprintf("corporate@%s", domain)
|
||||
|
||||
sg := newSenderGenerator(domain, senders)
|
||||
sg := newSenderGenerator(senders)
|
||||
thread := 0
|
||||
mails := make([]filledMail, count)
|
||||
for i := 0; i < count; thread++ {
|
||||
|
||||
@@ -2067,14 +2067,14 @@ type Email struct {
|
||||
// (as referenced by the blobId, i.e., the number of octets in the file the user would download).
|
||||
//
|
||||
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
|
||||
Size int `json:"size"`
|
||||
Size int `json:"size,omitzero"`
|
||||
|
||||
// The date the Email was received by the message store.
|
||||
//
|
||||
// This is the internal date in IMAP [RFC3501].
|
||||
//
|
||||
// [RFC3501]: https://www.rfc-editor.org/rfc/rfc3501.html
|
||||
ReceivedAt time.Time `json:"receivedAt,omitempty"`
|
||||
ReceivedAt time.Time `json:"receivedAt,omitzero"`
|
||||
|
||||
// This is a list of all header fields [RFC5322], in the same order they appear in the message.
|
||||
//
|
||||
@@ -2116,7 +2116,7 @@ type Email struct {
|
||||
Subject string `json:"subject,omitempty"`
|
||||
|
||||
// The value is identical to the value of header:Date:asDate.
|
||||
SentAt time.Time `json:"sentAt,omitempty"`
|
||||
SentAt time.Time `json:"sentAt,omitzero"`
|
||||
|
||||
// This is the full MIME structure of the message body, without recursing into message/rfc822 or message/global parts.
|
||||
//
|
||||
@@ -2817,10 +2817,56 @@ type EmailCreate struct {
|
||||
type EmailUpdate map[string]any
|
||||
|
||||
type EmailSetCommand struct {
|
||||
AccountId string `json:"accountId"`
|
||||
Create map[string]EmailCreate `json:"create,omitempty"`
|
||||
Update map[string]EmailUpdate `json:"update,omitempty"`
|
||||
Destroy []string `json:"destroy,omitempty"`
|
||||
// The id of the account to use.
|
||||
AccountId string `json:"accountId"`
|
||||
|
||||
// This is a state string as returned by the `Email/get` method.
|
||||
//
|
||||
// If supplied, the string must match the current state; otherwise, the method will be aborted and a
|
||||
// `stateMismatch` error returned.
|
||||
//
|
||||
// If null, any changes will be applied to the current state.
|
||||
IfInState string `json:"ifInState,omitempty"`
|
||||
|
||||
// A map of a creation id (a temporary id set by the client) to Email objects,
|
||||
// or null if no objects are to be created.
|
||||
//
|
||||
// The Email object type definition may define default values for properties.
|
||||
//
|
||||
// Any such property may be omitted by the client.
|
||||
//
|
||||
// The client MUST omit any properties that may only be set by the server.
|
||||
Create map[string]EmailCreate `json:"create,omitempty"`
|
||||
|
||||
// A map of an id to a `Patch` object to apply to the current Email object with that id,
|
||||
// or null if no objects are to be updated.
|
||||
//
|
||||
// A `PatchObject` is of type `String[*]` and represents an unordered set of patches.
|
||||
//
|
||||
// The keys are a path in JSON Pointer Format [@!RFC6901], with an implicit leading `/` (i.e., prefix each key
|
||||
// with `/` before applying the JSON Pointer evaluation algorithm).
|
||||
//
|
||||
// All paths MUST also conform to the following restrictions; if there is any violation, the update
|
||||
// MUST be rejected with an `invalidPatch` error:
|
||||
// !- The pointer MUST NOT reference inside an array (i.e., you MUST NOT insert/delete from an array; the array MUST be replaced in its entirety instead).
|
||||
// !- All parts prior to the last (i.e., the value after the final slash) MUST already exist on the object being patched.
|
||||
// !- There MUST NOT be two patches in the `PatchObject` where the pointer of one is the prefix of the pointer of the other, e.g., `"alerts/1/offset"` and `"alerts"`.
|
||||
//
|
||||
// The value associated with each pointer determines how to apply that patch:
|
||||
// !- If null, set to the default value if specified for this property; otherwise, remove the property from the patched object. If the key is not present in the parent, this a no-op.
|
||||
// ç- Anything else: The value to set for this property (this may be a replacement or addition to the object being patched).
|
||||
//
|
||||
// Any server-set properties MAY be included in the patch if their value is identical to the current server value
|
||||
// (before applying the patches to the object). Otherwise, the update MUST be rejected with an `invalidProperties` `SetError`.
|
||||
//
|
||||
// This patch definition is designed such that an entire Email object is also a valid `PatchObject`.
|
||||
//
|
||||
// The client may choose to optimise network usage by just sending the diff or may send the whole object; the server
|
||||
// processes it the same either way.
|
||||
Update map[string]EmailUpdate `json:"update,omitempty"`
|
||||
|
||||
// A list of ids for Email objects to permanently delete, or null if no objects are to be destroyed.
|
||||
Destroy []string `json:"destroy,omitempty"`
|
||||
}
|
||||
|
||||
type EmailSetResponse struct {
|
||||
@@ -2842,7 +2888,7 @@ type EmailSetResponse struct {
|
||||
// that were omitted by the client and thus set to a default by the server.
|
||||
//
|
||||
// This argument is null if no Email objects were successfully created.
|
||||
Created map[string]Email `json:"created,omitempty"`
|
||||
Created map[string]*Email `json:"created,omitempty"`
|
||||
|
||||
// The keys in this map are the ids of all Emails that were successfully updated.
|
||||
//
|
||||
@@ -2852,7 +2898,7 @@ type EmailSetResponse struct {
|
||||
// This lets the client know of any changes to server-set or computed properties.
|
||||
//
|
||||
// This argument is null if no Email objects were successfully updated.
|
||||
Updated map[string]Email `json:"updated,omitempty"`
|
||||
Updated map[string]*Email `json:"updated,omitempty"`
|
||||
|
||||
// A list of Email ids for records that were successfully destroyed, or null if none.
|
||||
Destroyed []string `json:"destroyed,omitempty"`
|
||||
@@ -4609,6 +4655,7 @@ var CommandResponseTypeMap = map[Command]func() any{
|
||||
CommandEmailQuery: func() any { return EmailQueryResponse{} },
|
||||
CommandEmailChanges: func() any { return EmailChangesResponse{} },
|
||||
CommandEmailGet: func() any { return EmailGetResponse{} },
|
||||
CommandEmailSet: func() any { return EmailSetResponse{} },
|
||||
CommandEmailSubmissionGet: func() any { return EmailSubmissionGetResponse{} },
|
||||
CommandEmailSubmissionSet: func() any { return EmailSubmissionSetResponse{} },
|
||||
CommandThreadGet: func() any { return ThreadGetResponse{} },
|
||||
|
||||
Reference in New Issue
Block a user