From 49c2425172d45215d0dfe4657f583d063c687f60 Mon Sep 17 00:00:00 2001
From: Pascal Bleser
Date: Tue, 7 Apr 2026 17:00:48 +0200
Subject: [PATCH] groupware: significant refactorings of the JMAP framework,
adding methods and more intelligence to the various request and response
types to improve the use of template functions, reducing the risks of typos
and copy/paste mistakes
---
pkg/jmap/api_addressbook.go | 75 +-
pkg/jmap/api_blob.go | 10 +-
pkg/jmap/api_bootstrap.go | 4 +-
pkg/jmap/api_calendar.go | 68 +-
pkg/jmap/api_changes.go | 18 +-
pkg/jmap/api_contact.go | 28 +-
pkg/jmap/api_email.go | 72 +-
pkg/jmap/api_identity.go | 30 +-
pkg/jmap/api_mailbox.go | 42 +-
pkg/jmap/api_objects.go | 18 +-
pkg/jmap/api_principal.go | 2 +-
pkg/jmap/api_quota.go | 20 +-
pkg/jmap/api_vacation.go | 10 +-
pkg/jmap/export_test.go | 68 ++
pkg/jmap/integration_contact_test.go | 112 ++-
pkg/jmap/integration_event_test.go | 4 +-
pkg/jmap/integration_test.go | 12 +-
pkg/jmap/integration_ws_test.go | 8 +-
pkg/jmap/model.go | 992 ++++++++++++++++++++++-----
pkg/jmap/model_test.go | 19 +
pkg/jmap/templates.go | 236 ++++---
pkg/jmap/tools.go | 24 +-
pkg/structs/structs.go | 9 +
23 files changed, 1365 insertions(+), 516 deletions(-)
create mode 100644 pkg/jmap/export_test.go
create mode 100644 pkg/jmap/model_test.go
diff --git a/pkg/jmap/api_addressbook.go b/pkg/jmap/api_addressbook.go
index beab91e810..650f5d41d1 100644
--- a/pkg/jmap/api_addressbook.go
+++ b/pkg/jmap/api_addressbook.go
@@ -14,26 +14,16 @@ type AddressBooksResponse struct {
}
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, NS_ADDRESSBOOKS,
- invocation(CommandAddressBookGet, AddressBookGetCommand{AccountId: accountId, Ids: ids}, "0"),
+ return get(j, "GetAddressbooks", NS_ADDRESSBOOKS,
+ func(accountId string, ids []string) AddressBookGetCommand {
+ return AddressBookGetCommand{AccountId: accountId, Ids: ids}
+ },
+ AddressBookGetResponse{},
+ func(resp AddressBookGetResponse) AddressBooksResponse {
+ return AddressBooksResponse{AddressBooks: resp.List, NotFound: resp.NotFound}
+ },
+ accountId, session, ctx, logger, acceptLanguage, ids,
)
- if err != nil {
- return AddressBooksResponse{}, "", "", "", err
- }
-
- 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{}, response.State, err
- }
- return AddressBooksResponse{
- AddressBooks: response.List,
- NotFound: response.NotFound,
- }, response.State, nil
- })
}
type AddressBookChanges struct {
@@ -48,11 +38,11 @@ type AddressBookChanges struct {
// Retrieve Address Book changes since a given state.
// @apidoc addressbook,changes
func (j *Client) GetAddressbookChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (AddressBookChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetAddressbookChanges", NS_ADDRESSBOOKS,
- CommandAddressBookChanges, CommandAddressBookGet,
+ return changes(j, "GetAddressbookChanges", NS_ADDRESSBOOKS,
func() AddressBookChangesCommand {
return AddressBookChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ AddressBookChangesResponse{},
func(path string, rof string) AddressBookGetRefCommand {
return AddressBookGetRefCommand{
AccountId: accountId,
@@ -63,9 +53,6 @@ func (j *Client) GetAddressbookChanges(accountId string, session *Session, ctx c
},
}
},
- func(resp AddressBookChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp AddressBookGetResponse) []AddressBook { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []AddressBook, destroyed []string) AddressBookChanges {
return AddressBookChanges{
@@ -77,42 +64,48 @@ func (j *Client) GetAddressbookChanges(accountId string, session *Session, ctx c
Destroyed: destroyed,
}
},
- func(resp AddressBookGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
-func (j *Client) CreateAddressBook(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create AddressBookChange) (*AddressBook, SessionState, State, Language, Error) {
- return createTemplate(j, "CreateAddressBook", NS_ADDRESSBOOKS, AddressBookType, CommandAddressBookSet, CommandAddressBookGet,
+func (j *Client) CreateAddressBook(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, addressbook AddressBookChange) (*AddressBook, SessionState, State, Language, Error) {
+ return create(j, "CreateAddressBook", NS_ADDRESSBOOKS,
func(accountId string, create map[string]AddressBookChange) AddressBookSetCommand {
return AddressBookSetCommand{AccountId: accountId, Create: create}
},
- func(accountId string, ref string) AddressBookGetCommand {
- return AddressBookGetCommand{AccountId: accountId, Ids: []string{ref}}
+ func(accountId string, ids string) AddressBookGetCommand {
+ return AddressBookGetCommand{AccountId: accountId, Ids: []string{ids}}
},
func(resp AddressBookSetResponse) map[string]*AddressBook {
return resp.Created
},
- func(resp AddressBookSetResponse) map[string]SetError {
- return resp.NotCreated
- },
func(resp AddressBookGetResponse) []AddressBook {
return resp.List
},
- func(resp AddressBookSetResponse) State {
- return resp.NewState
- },
- accountId, session, ctx, logger, acceptLanguage, create,
+ accountId, session, ctx, logger, acceptLanguage, addressbook,
)
}
-func (j *Client) DeleteAddressBook(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
- return deleteTemplate(j, "DeleteAddressBook", NS_ADDRESSBOOKS, CommandAddressBookSet,
+func (j *Client) DeleteAddressBook(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+ return destroy(j, "DeleteAddressBook", NS_ADDRESSBOOKS,
func(accountId string, destroy []string) AddressBookSetCommand {
return AddressBookSetCommand{AccountId: accountId, Destroy: destroy}
},
- func(resp AddressBookSetResponse) map[string]SetError { return resp.NotDestroyed },
- func(resp AddressBookSetResponse) State { return resp.NewState },
- accountId, destroy, session, ctx, logger, acceptLanguage,
+ AddressBookSetResponse{},
+ accountId, destroyIds, session, ctx, logger, acceptLanguage,
+ )
+}
+
+func (j *Client) UpdateAddressBook(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, id string, changes AddressBookChange) (AddressBook, SessionState, State, Language, Error) {
+ return update(j, "UpdateAddressBook", NS_ADDRESSBOOKS,
+ func(update map[string]PatchObject) AddressBookSetCommand {
+ return AddressBookSetCommand{AccountId: accountId, Update: update}
+ },
+ func(id string) AddressBookGetCommand {
+ return AddressBookGetCommand{AccountId: accountId, Ids: []string{id}}
+ },
+ func(resp AddressBookSetResponse) map[string]SetError { return resp.NotUpdated },
+ func(resp AddressBookGetResponse) AddressBook { return resp.List[0] },
+ id, changes, session, ctx, logger, acceptLanguage,
)
}
diff --git a/pkg/jmap/api_blob.go b/pkg/jmap/api_blob.go
index 40b6402f07..27b16cdb49 100644
--- a/pkg/jmap/api_blob.go
+++ b/pkg/jmap/api_blob.go
@@ -11,7 +11,7 @@ import (
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, ns(JmapBlob),
- invocation(CommandBlobGet, BlobGetCommand{
+ invocation(BlobGetCommand{
AccountId: accountId,
Ids: []string{id},
// add BlobPropertyData to retrieve the data
@@ -90,8 +90,8 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
}
cmd, jerr := j.request(session, logger, ns(JmapBlob),
- invocation(CommandBlobUpload, upload, "0"),
- invocation(CommandBlobGet, getHash, "1"),
+ invocation(upload, "0"),
+ invocation(getHash, "1"),
)
if jerr != nil {
return UploadedBlobWithHash{}, "", "", "", jerr
@@ -99,13 +99,13 @@ func (j *Client) UploadBlob(accountId string, session *Session, ctx context.Cont
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (UploadedBlobWithHash, State, Error) {
var uploadResponse BlobUploadResponse
- err := retrieveResponseMatchParameters(logger, body, CommandBlobUpload, "0", &uploadResponse)
+ err := retrieveResponseMatchParameters(logger, body, upload.GetCommand(), "0", &uploadResponse)
if err != nil {
return UploadedBlobWithHash{}, "", err
}
var getResponse BlobGetResponse
- err = retrieveResponseMatchParameters(logger, body, CommandBlobGet, "1", &getResponse)
+ err = retrieveResponseMatchParameters(logger, body, getHash.GetCommand(), "1", &getResponse)
if err != nil {
return UploadedBlobWithHash{}, "", err
}
diff --git a/pkg/jmap/api_bootstrap.go b/pkg/jmap/api_bootstrap.go
index 31c44e6e42..2101863af6 100644
--- a/pkg/jmap/api_bootstrap.go
+++ b/pkg/jmap/api_bootstrap.go
@@ -21,8 +21,8 @@ func (j *Client) GetBootstrap(accountIds []string, session *Session, ctx context
calls := make([]Invocation, len(uniqueAccountIds)*2)
for i, accountId := range uniqueAccountIds {
- calls[i*2+0] = invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId}, mcid(accountId, "I"))
- calls[i*2+1] = invocation(CommandQuotaGet, QuotaGetCommand{AccountId: accountId}, mcid(accountId, "Q"))
+ calls[i*2+0] = invocation(IdentityGetCommand{AccountId: accountId}, mcid(accountId, "I"))
+ calls[i*2+1] = invocation(QuotaGetCommand{AccountId: accountId}, mcid(accountId, "Q"))
}
cmd, err := j.request(session, logger, NS_MAIL_QUOTA, calls...)
diff --git a/pkg/jmap/api_calendar.go b/pkg/jmap/api_calendar.go
index 7260fda306..9f52663345 100644
--- a/pkg/jmap/api_calendar.go
+++ b/pkg/jmap/api_calendar.go
@@ -13,7 +13,7 @@ func (j *Client) ParseICalendarBlob(accountId string, session *Session, ctx cont
logger = j.logger("ParseICalendarBlob", session, logger)
cmd, err := j.request(session, logger, NS_CALENDARS,
- invocation(CommandCalendarEventParse, CalendarEventParseCommand{AccountId: accountId, BlobIds: blobIds}, "0"),
+ invocation(CalendarEventParseCommand{AccountId: accountId, BlobIds: blobIds}, "0"),
)
if err != nil {
return CalendarEventParseResponse{}, "", "", "", err
@@ -35,14 +35,14 @@ type CalendarsResponse struct {
}
func (j *Client) GetCalendars(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (CalendarsResponse, SessionState, State, Language, Error) {
- return getTemplate(j, "GetCalendars", NS_CALENDARS, CommandCalendarGet,
+ return get(j, "GetCalendars", NS_CALENDARS,
func(accountId string, ids []string) CalendarGetCommand {
return CalendarGetCommand{AccountId: accountId, Ids: ids}
},
+ CalendarGetResponse{},
func(resp CalendarGetResponse) CalendarsResponse {
return CalendarsResponse{Calendars: resp.List, NotFound: resp.NotFound}
},
- func(resp CalendarGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, ids,
)
}
@@ -59,11 +59,11 @@ type CalendarChanges struct {
// Retrieve Calendar changes since a given state.
// @apidoc calendar,changes
func (j *Client) GetCalendarChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (CalendarChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetCalendarChanges", NS_CALENDARS,
- CommandCalendarChanges, CommandCalendarGet,
+ return changes(j, "GetCalendarChanges", NS_CALENDARS,
func() CalendarChangesCommand {
return CalendarChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ CalendarChangesResponse{},
func(path string, rof string) CalendarGetRefCommand {
return CalendarGetRefCommand{
AccountId: accountId,
@@ -74,9 +74,6 @@ func (j *Client) GetCalendarChanges(accountId string, session *Session, ctx cont
},
}
},
- func(resp CalendarChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp CalendarGetResponse) []Calendar { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []Calendar, destroyed []string) CalendarChanges {
return CalendarChanges{
@@ -88,7 +85,6 @@ func (j *Client) GetCalendarChanges(accountId string, session *Session, ctx cont
Destroyed: destroyed,
}
},
- func(resp CalendarGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
@@ -117,8 +113,8 @@ func (j *Client) QueryCalendarEvents(accountIds []string, session *Session, ctx
if position > 0 {
query.Position = position
}
- invocations[i*2+0] = invocation(CommandCalendarEventQuery, query, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CommandCalendarEventGet, CalendarEventGetRefCommand{
+ invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
+ invocations[i*2+1] = invocation(CalendarEventGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
Name: CommandCalendarEventQuery,
@@ -165,11 +161,11 @@ type CalendarEventChanges struct {
// @api:tags event,changes
func (j *Client) GetCalendarEventChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (CalendarEventChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetCalendarEventChanges", NS_CALENDARS,
- CommandCalendarEventChanges, CommandCalendarEventGet,
+ return changes(j, "GetCalendarEventChanges", NS_CALENDARS,
func() CalendarEventChangesCommand {
return CalendarEventChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ CalendarEventChangesResponse{},
func(path string, rof string) CalendarEventGetRefCommand {
return CalendarEventGetRefCommand{
AccountId: accountId,
@@ -180,9 +176,6 @@ func (j *Client) GetCalendarEventChanges(accountId string, session *Session, ctx
},
}
},
- func(resp CalendarEventChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp CalendarEventGetResponse) []CalendarEvent { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []CalendarEvent, destroyed []string) CalendarEventChanges {
return CalendarEventChanges{
@@ -194,13 +187,12 @@ func (j *Client) GetCalendarEventChanges(accountId string, session *Session, ctx
Destroyed: destroyed,
}
},
- func(resp CalendarEventGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
-func (j *Client) CreateCalendarEvent(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create CalendarEvent) (*CalendarEvent, SessionState, State, Language, Error) {
- return createTemplate(j, "CreateCalendarEvent", NS_CALENDARS, CalendarEventType, CommandCalendarEventSet, CommandCalendarEventGet,
+func (j *Client) CreateCalendarEvent(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, event CalendarEvent) (*CalendarEvent, SessionState, State, Language, Error) {
+ return create(j, "CreateCalendarEvent", NS_CALENDARS,
func(accountId string, create map[string]CalendarEvent) CalendarEventSetCommand {
return CalendarEventSetCommand{AccountId: accountId, Create: create}
},
@@ -210,30 +202,23 @@ func (j *Client) CreateCalendarEvent(accountId string, session *Session, ctx con
func(resp CalendarEventSetResponse) map[string]*CalendarEvent {
return resp.Created
},
- func(resp CalendarEventSetResponse) map[string]SetError {
- return resp.NotCreated
- },
func(resp CalendarEventGetResponse) []CalendarEvent {
return resp.List
},
- func(resp CalendarEventSetResponse) State {
- return resp.NewState
- },
- accountId, session, ctx, logger, acceptLanguage, create)
+ accountId, session, ctx, logger, acceptLanguage, event)
}
-func (j *Client) DeleteCalendarEvent(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
- return deleteTemplate(j, "DeleteCalendarEvent", NS_CALENDARS, CommandCalendarEventSet,
+func (j *Client) DeleteCalendarEvent(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+ return destroy(j, "DeleteCalendarEvent", NS_CALENDARS,
func(accountId string, destroy []string) CalendarEventSetCommand {
return CalendarEventSetCommand{AccountId: accountId, Destroy: destroy}
},
- func(resp CalendarEventSetResponse) map[string]SetError { return resp.NotDestroyed },
- func(resp CalendarEventSetResponse) State { return resp.NewState },
- accountId, destroy, session, ctx, logger, acceptLanguage)
+ CalendarEventSetResponse{},
+ accountId, destroyIds, session, ctx, logger, acceptLanguage)
}
-func (j *Client) CreateCalendar(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create CalendarChange) (*Calendar, SessionState, State, Language, Error) {
- return createTemplate(j, "CreateCalendar", NS_CALENDARS, CalendarType, CommandAddressBookSet, CommandAddressBookGet,
+func (j *Client) CreateCalendar(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, calendar CalendarChange) (*Calendar, SessionState, State, Language, Error) {
+ return create(j, "CreateCalendar", NS_CALENDARS,
func(accountId string, create map[string]CalendarChange) CalendarSetCommand {
return CalendarSetCommand{AccountId: accountId, Create: create}
},
@@ -243,26 +228,19 @@ func (j *Client) CreateCalendar(accountId string, session *Session, ctx context.
func(resp CalendarSetResponse) map[string]*Calendar {
return resp.Created
},
- func(resp CalendarSetResponse) map[string]SetError {
- return resp.NotCreated
- },
func(resp CalendarGetResponse) []Calendar {
return resp.List
},
- func(resp CalendarSetResponse) State {
- return resp.NewState
- },
- accountId, session, ctx, logger, acceptLanguage, create,
+ accountId, session, ctx, logger, acceptLanguage, calendar,
)
}
-func (j *Client) DeleteCalendar(accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
- return deleteTemplate(j, "DeleteCalendar", NS_ADDRESSBOOKS, CommandAddressBookSet,
+func (j *Client) DeleteCalendar(accountId string, destroyIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
+ return destroy(j, "DeleteCalendar", NS_ADDRESSBOOKS,
func(accountId string, destroy []string) CalendarSetCommand {
return CalendarSetCommand{AccountId: accountId, Destroy: destroy}
},
- func(resp CalendarSetResponse) map[string]SetError { return resp.NotDestroyed },
- func(resp CalendarSetResponse) State { return resp.NewState },
- accountId, destroy, session, ctx, logger, acceptLanguage,
+ CalendarSetResponse{},
+ accountId, destroyIds, session, ctx, logger, acceptLanguage,
)
}
diff --git a/pkg/jmap/api_changes.go b/pkg/jmap/api_changes.go
index 369f79410d..95aef289db 100644
--- a/pkg/jmap/api_changes.go
+++ b/pkg/jmap/api_changes.go
@@ -81,31 +81,31 @@ func (j *Client) GetChanges(accountId string, session *Session, ctx context.Cont
methodCalls := []Invocation{}
if stateMap.Mailboxes != nil {
- methodCalls = append(methodCalls, invocation(CommandMailboxChanges, MailboxChangesCommand{AccountId: accountId, SinceState: *stateMap.Mailboxes, MaxChanges: posUIntPtr(maxChanges)}, "mailboxes"))
+ methodCalls = append(methodCalls, invocation(MailboxChangesCommand{AccountId: accountId, SinceState: *stateMap.Mailboxes, MaxChanges: posUIntPtr(maxChanges)}, "mailboxes"))
}
if stateMap.Emails != nil {
- methodCalls = append(methodCalls, invocation(CommandEmailChanges, EmailChangesCommand{AccountId: accountId, SinceState: *stateMap.Emails, MaxChanges: posUIntPtr(maxChanges)}, "emails"))
+ methodCalls = append(methodCalls, invocation(EmailChangesCommand{AccountId: accountId, SinceState: *stateMap.Emails, MaxChanges: posUIntPtr(maxChanges)}, "emails"))
}
if stateMap.Calendars != nil {
- methodCalls = append(methodCalls, invocation(CommandCalendarChanges, CalendarChangesCommand{AccountId: accountId, SinceState: *stateMap.Calendars, MaxChanges: posUIntPtr(maxChanges)}, "calendars"))
+ methodCalls = append(methodCalls, invocation(CalendarChangesCommand{AccountId: accountId, SinceState: *stateMap.Calendars, MaxChanges: posUIntPtr(maxChanges)}, "calendars"))
}
if stateMap.Events != nil {
- methodCalls = append(methodCalls, invocation(CommandCalendarEventChanges, CalendarEventChangesCommand{AccountId: accountId, SinceState: *stateMap.Events, MaxChanges: posUIntPtr(maxChanges)}, "events"))
+ methodCalls = append(methodCalls, invocation(CalendarEventChangesCommand{AccountId: accountId, SinceState: *stateMap.Events, MaxChanges: posUIntPtr(maxChanges)}, "events"))
}
if stateMap.Addressbooks != nil {
- methodCalls = append(methodCalls, invocation(CommandAddressBookChanges, AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: posUIntPtr(maxChanges)}, "addressbooks"))
+ methodCalls = append(methodCalls, invocation(AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: posUIntPtr(maxChanges)}, "addressbooks"))
}
if stateMap.Addressbooks != nil {
- methodCalls = append(methodCalls, invocation(CommandAddressBookChanges, AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: posUIntPtr(maxChanges)}, "addressbooks"))
+ methodCalls = append(methodCalls, invocation(AddressBookChangesCommand{AccountId: accountId, SinceState: *stateMap.Addressbooks, MaxChanges: posUIntPtr(maxChanges)}, "addressbooks"))
}
if stateMap.Contacts != nil {
- methodCalls = append(methodCalls, invocation(CommandContactCardChanges, ContactCardChangesCommand{AccountId: accountId, SinceState: *stateMap.Contacts, MaxChanges: posUIntPtr(maxChanges)}, "contacts"))
+ methodCalls = append(methodCalls, invocation(ContactCardChangesCommand{AccountId: accountId, SinceState: *stateMap.Contacts, MaxChanges: posUIntPtr(maxChanges)}, "contacts"))
}
if stateMap.Identities != nil {
- methodCalls = append(methodCalls, invocation(CommandIdentityChanges, IdentityChangesCommand{AccountId: accountId, SinceState: *stateMap.Identities, MaxChanges: posUIntPtr(maxChanges)}, "identities"))
+ methodCalls = append(methodCalls, invocation(IdentityChangesCommand{AccountId: accountId, SinceState: *stateMap.Identities, MaxChanges: posUIntPtr(maxChanges)}, "identities"))
}
if stateMap.EmailSubmissions != nil {
- methodCalls = append(methodCalls, invocation(CommandEmailSubmissionChanges, EmailSubmissionChangesCommand{AccountId: accountId, SinceState: *stateMap.EmailSubmissions, MaxChanges: posUIntPtr(maxChanges)}, "submissions"))
+ methodCalls = append(methodCalls, invocation(EmailSubmissionChangesCommand{AccountId: accountId, SinceState: *stateMap.EmailSubmissions, MaxChanges: posUIntPtr(maxChanges)}, "submissions"))
}
// if stateMap.Quotas != nil { methodCalls = append(methodCalls, invocation(CommandQuotaChanges, QuotaChangesCommand{AccountId: accountId, SinceState: *stateMap.Quotas, MaxChanges: posUIntPtr(maxChanges)}, "quotas")) }
diff --git a/pkg/jmap/api_contact.go b/pkg/jmap/api_contact.go
index d872a97f89..28d2397889 100644
--- a/pkg/jmap/api_contact.go
+++ b/pkg/jmap/api_contact.go
@@ -15,7 +15,7 @@ func (j *Client) GetContactCardsById(accountId string, session *Session, ctx con
acceptLanguage string, contactIds []string) (map[string]jscontact.ContactCard, SessionState, State, Language, Error) {
logger = j.logger("GetContactCardsById", session, logger)
- cmd, err := j.request(session, logger, NS_CONTACTS, invocation(CommandContactCardGet, ContactCardGetCommand{
+ cmd, err := j.request(session, logger, NS_CONTACTS, invocation(ContactCardGetCommand{
Ids: contactIds,
AccountId: accountId,
}, "0"))
@@ -39,12 +39,12 @@ func (j *Client) GetContactCardsById(accountId string, session *Session, ctx con
func (j *Client) GetContactCards(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, contactIds []string) ([]jscontact.ContactCard, SessionState, State, Language, Error) {
- return getTemplate(j, "GetContactCards", NS_CONTACTS, CommandContactCardGet,
+ return get(j, "GetContactCards", NS_CONTACTS,
func(accountId string, ids []string) ContactCardGetCommand {
return ContactCardGetCommand{AccountId: accountId, Ids: contactIds}
},
+ ContactCardGetResponse{},
func(resp ContactCardGetResponse) []jscontact.ContactCard { return resp.List },
- func(resp ContactCardGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, contactIds,
)
}
@@ -62,11 +62,11 @@ type ContactCardChanges struct {
// @api:tags contact,changes
func (j *Client) GetContactCardChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (ContactCardChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetContactCardChanges", NS_CONTACTS,
- CommandContactCardChanges, CommandContactCardGet,
+ return changes(j, "GetContactCardChanges", NS_CONTACTS,
func() ContactCardChangesCommand {
return ContactCardChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ ContactCardChangesResponse{},
func(path string, rof string) ContactCardGetRefCommand {
return ContactCardGetRefCommand{
AccountId: accountId,
@@ -77,9 +77,6 @@ func (j *Client) GetContactCardChanges(accountId string, session *Session, ctx c
},
}
},
- func(resp ContactCardChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp ContactCardGetResponse) []jscontact.ContactCard { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []jscontact.ContactCard, destroyed []string) ContactCardChanges {
return ContactCardChanges{
@@ -91,7 +88,6 @@ func (j *Client) GetContactCardChanges(accountId string, session *Session, ctx c
Destroyed: destroyed,
}
},
- func(resp ContactCardGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
@@ -120,8 +116,8 @@ func (j *Client) QueryContactCards(accountIds []string, session *Session, ctx co
if position > 0 {
query.Position = position
}
- invocations[i*2+0] = invocation(CommandContactCardQuery, query, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CommandContactCardGet, ContactCardGetRefCommand{
+ invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
+ invocations[i*2+1] = invocation(ContactCardGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
Name: CommandContactCardQuery,
@@ -158,13 +154,13 @@ func (j *Client) CreateContactCard(accountId string, session *Session, ctx conte
logger = j.logger("CreateContactCard", session, logger)
cmd, err := j.request(session, logger, NS_CONTACTS,
- invocation(CommandContactCardSet, ContactCardSetCommand{
+ invocation(ContactCardSetCommand{
AccountId: accountId,
Create: map[string]jscontact.ContactCard{
"c": create,
},
}, "0"),
- invocation(CommandContactCardGet, ContactCardGetCommand{
+ invocation(ContactCardGetCommand{
AccountId: accountId,
Ids: []string{"#c"},
}, "1"),
@@ -187,7 +183,7 @@ func (j *Client) CreateContactCard(accountId string, session *Session, ctx conte
}
if created, ok := setResponse.Created["c"]; !ok || created == nil {
- berr := fmt.Errorf("failed to find %s in %s response", string(ContactCardType), string(CommandContactCardSet))
+ berr := fmt.Errorf("failed to find %s in %s response", ContactCardType, string(CommandContactCardSet))
logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
@@ -199,7 +195,7 @@ func (j *Client) CreateContactCard(accountId string, session *Session, ctx conte
}
if len(getResponse.List) < 1 {
- berr := fmt.Errorf("failed to find %s in %s response", string(ContactCardType), string(CommandContactCardSet))
+ berr := fmt.Errorf("failed to find %s in %s response", ContactCardType, string(CommandContactCardSet))
logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
@@ -212,7 +208,7 @@ func (j *Client) DeleteContactCard(accountId string, destroy []string, session *
logger = j.logger("DeleteContactCard", session, logger)
cmd, err := j.request(session, logger, NS_CONTACTS,
- invocation(CommandContactCardSet, ContactCardSetCommand{
+ invocation(ContactCardSetCommand{
AccountId: accountId,
Destroy: destroy,
}, "0"),
diff --git a/pkg/jmap/api_email.go b/pkg/jmap/api_email.go
index 26261a64b9..8b286e6504 100644
--- a/pkg/jmap/api_email.go
+++ b/pkg/jmap/api_email.go
@@ -34,7 +34,7 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
if maxBodyValueBytes > 0 {
get.MaxBodyValueBytes = maxBodyValueBytes
}
- invokeGet := invocation(CommandEmailGet, get, "1")
+ invokeGet := invocation(get, "1")
methodCalls := []Invocation{invokeGet}
if markAsSeen {
@@ -43,7 +43,7 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
updates[id] = EmailUpdate{EmailPropertyKeywords + "/" + JmapKeywordSeen: true}
}
mark := EmailSetCommand{AccountId: accountId, Update: updates}
- methodCalls = []Invocation{invocation(CommandEmailSet, mark, "0"), invokeGet}
+ methodCalls = []Invocation{invocation(mark, "0"), invokeGet}
}
if withThreads {
threads := ThreadGetRefCommand{
@@ -54,7 +54,7 @@ func (j *Client) GetEmails(accountId string, session *Session, ctx context.Conte
Path: "/list/*/" + EmailPropertyThreadId, //NOSONAR
},
}
- methodCalls = append(methodCalls, invocation(CommandThreadGet, threads, "2"))
+ methodCalls = append(methodCalls, invocation(threads, "2"))
}
cmd, err := j.request(session, logger, NS_MAIL, methodCalls...)
@@ -95,7 +95,7 @@ func (j *Client) GetEmailBlobId(accountId string, session *Session, ctx context.
logger = j.logger("GetEmailBlobId", session, logger)
get := EmailGetCommand{AccountId: accountId, Ids: []string{id}, FetchAllBodyValues: false, Properties: []string{"blobId"}}
- cmd, err := j.request(session, logger, NS_MAIL, invocation(CommandEmailGet, get, "0"))
+ cmd, err := j.request(session, logger, NS_MAIL, invocation(get, "0"))
if err != nil {
return "", "", "", "", err
}
@@ -143,8 +143,8 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
}
invocations := []Invocation{
- invocation(CommandEmailQuery, query, "0"),
- invocation(CommandEmailGet, get, "1"),
+ invocation(query, "0"),
+ invocation(get, "1"),
}
if withThreads {
@@ -156,7 +156,7 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
Path: "/list/*/" + EmailPropertyThreadId,
},
}
- invocations = append(invocations, invocation(CommandThreadGet, threads, "2"))
+ invocations = append(invocations, invocation(threads, "2"))
}
cmd, err := j.request(session, logger, NS_MAIL, invocations...)
@@ -237,9 +237,9 @@ func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context
}
cmd, err := j.request(session, logger, NS_MAIL,
- invocation(CommandEmailChanges, changes, "0"),
- invocation(CommandEmailGet, getCreated, "1"),
- invocation(CommandEmailGet, getUpdated, "2"),
+ invocation(changes, "0"),
+ invocation(getCreated, "1"),
+ invocation(getUpdated, "2"),
)
if err != nil {
return EmailChanges{}, "", "", "", err
@@ -335,9 +335,9 @@ func (j *Client) QueryEmailSnippets(accountIds []string, filter EmailFilterEleme
},
}
- invocations[i*3+0] = invocation(CommandEmailQuery, query, mcid(accountId, "0"))
- invocations[i*3+1] = invocation(CommandEmailGet, mails, mcid(accountId, "1"))
- invocations[i*3+2] = invocation(CommandSearchSnippetGet, snippet, mcid(accountId, "2"))
+ invocations[i*3+0] = invocation(query, mcid(accountId, "0"))
+ invocations[i*3+1] = invocation(mails, mcid(accountId, "1"))
+ invocations[i*3+2] = invocation(snippet, mcid(accountId, "2"))
}
cmd, err := j.request(session, logger, NS_MAIL, invocations...)
@@ -440,8 +440,8 @@ func (j *Client) QueryEmails(accountIds []string, filter EmailFilterElement, ses
MaxBodyValueBytes: maxBodyValueBytes,
}
- invocations[i*2+0] = invocation(CommandEmailQuery, query, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CommandEmailGet, mails, mcid(accountId, "1"))
+ invocations[i*2+0] = invocation(query, mcid(accountId, "0"))
+ invocations[i*2+1] = invocation(mails, mcid(accountId, "1"))
}
cmd, err := j.request(session, logger, NS_MAIL, invocations...)
@@ -531,9 +531,9 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, filter EmailFilter
FetchAllBodyValues: fetchBodies,
MaxBodyValueBytes: maxBodyValueBytes,
}
- invocations[i*3+0] = invocation(CommandEmailQuery, query, mcid(accountId, "0"))
- invocations[i*3+1] = invocation(CommandSearchSnippetGet, snippet, mcid(accountId, "1"))
- invocations[i*3+2] = invocation(CommandEmailGet, mails, mcid(accountId, "2"))
+ invocations[i*3+0] = invocation(query, mcid(accountId, "0"))
+ invocations[i*3+1] = invocation(snippet, mcid(accountId, "1"))
+ invocations[i*3+2] = invocation(mails, mcid(accountId, "2"))
}
cmd, err := j.request(session, logger, NS_MAIL, invocations...)
@@ -628,8 +628,8 @@ func (j *Client) ImportEmail(accountId string, session *Session, ctx context.Con
}
cmd, err := j.request(session, logger, NS_MAIL,
- invocation(CommandBlobUpload, upload, "0"),
- invocation(CommandBlobGet, getHash, "1"),
+ invocation(upload, "0"),
+ invocation(getHash, "1"),
)
if err != nil {
return UploadedEmail{}, "", "", "", err
@@ -687,7 +687,7 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
}
cmd, err := j.request(session, logger, NS_MAIL,
- invocation(CommandEmailSet, set, "0"),
+ invocation(set, "0"),
)
if err != nil {
return nil, "", "", "", err
@@ -713,7 +713,7 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
created, ok := setResponse.Created["c"]
if !ok {
- berr := fmt.Errorf("failed to find %s in %s response", string(EmailType), string(CommandEmailSet))
+ berr := fmt.Errorf("failed to find %s in %s response", EmailType, string(CommandEmailSet))
logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
@@ -732,7 +732,7 @@ func (j *Client) CreateEmail(accountId string, email EmailCreate, replaceId stri
// 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) (map[string]*Email, SessionState, State, Language, Error) {
cmd, err := j.request(session, logger, NS_MAIL,
- invocation(CommandEmailSet, EmailSetCommand{
+ invocation(EmailSetCommand{
AccountId: accountId,
Update: updates,
}, "0"),
@@ -759,7 +759,7 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate,
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, NS_MAIL,
- invocation(CommandEmailSet, EmailSetCommand{
+ invocation(EmailSetCommand{
AccountId: accountId,
Destroy: destroy,
}, "0"),
@@ -841,8 +841,8 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
}
cmd, err := j.request(session, logger, NS_MAIL_SUBMISSION,
- invocation(CommandEmailSubmissionSet, set, "0"),
- invocation(CommandEmailSubmissionGet, get, "1"),
+ invocation(set, "0"),
+ invocation(get, "1"),
)
if err != nil {
return EmailSubmission{}, "", "", "", err
@@ -901,7 +901,7 @@ type emailSubmissionResult struct {
func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]EmailSubmission, []string, SessionState, State, Language, Error) {
logger = j.logger("GetEmailSubmissionStatus", session, logger)
- cmd, err := j.request(session, logger, NS_MAIL_SUBMISSION, invocation(CommandEmailSubmissionGet, EmailSubmissionGetCommand{
+ cmd, err := j.request(session, logger, NS_MAIL_SUBMISSION, invocation(EmailSubmissionGetCommand{
AccountId: accountId,
Ids: submissionIds,
}, "0"))
@@ -931,11 +931,11 @@ func (j *Client) EmailsInThread(accountId string, threadId string, session *Sess
})
cmd, err := j.request(session, logger, NS_MAIL,
- invocation(CommandThreadGet, ThreadGetCommand{
+ invocation(ThreadGetCommand{
AccountId: accountId,
Ids: []string{threadId},
}, "0"),
- invocation(CommandEmailGet, EmailGetRefCommand{
+ invocation(EmailGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
ResultOf: "0",
@@ -1007,9 +1007,9 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
if limit > 0 {
get.Limit = &limit
}
- invocations[i*factor+0] = invocation(CommandEmailQuery, get, mcid(accountId, "0"))
+ invocations[i*factor+0] = invocation(get, mcid(accountId, "0"))
- invocations[i*factor+1] = invocation(CommandEmailGet, EmailGetRefCommand{
+ invocations[i*factor+1] = invocation(EmailGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
Name: CommandEmailQuery,
@@ -1019,7 +1019,7 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
Properties: EmailSummaryProperties,
}, mcid(accountId, "1"))
if withThreads {
- invocations[i*factor+2] = invocation(CommandThreadGet, ThreadGetRefCommand{
+ invocations[i*factor+2] = invocation(ThreadGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
Name: CommandEmailGet,
@@ -1085,11 +1085,11 @@ type EmailSubmissionChanges struct {
// @api:tags email,changes
func (j *Client) GetEmailSubmissionChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (EmailSubmissionChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetEmailSubmissionChanges", NS_MAIL_SUBMISSION,
- CommandEmailSubmissionChanges, CommandEmailSubmissionGet,
+ return changes(j, "GetEmailSubmissionChanges", NS_MAIL_SUBMISSION,
func() EmailSubmissionChangesCommand {
return EmailSubmissionChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ EmailSubmissionChangesResponse{},
func(path string, rof string) EmailSubmissionGetRefCommand {
return EmailSubmissionGetRefCommand{
AccountId: accountId,
@@ -1100,9 +1100,6 @@ func (j *Client) GetEmailSubmissionChanges(accountId string, session *Session, c
},
}
},
- func(resp EmailSubmissionChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp EmailSubmissionGetResponse) []EmailSubmission { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []EmailSubmission, destroyed []string) EmailSubmissionChanges {
return EmailSubmissionChanges{
@@ -1114,7 +1111,6 @@ func (j *Client) GetEmailSubmissionChanges(accountId string, session *Session, c
Destroyed: destroyed,
}
},
- func(resp EmailSubmissionGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
diff --git a/pkg/jmap/api_identity.go b/pkg/jmap/api_identity.go
index eb183d37c3..82d9bacc43 100644
--- a/pkg/jmap/api_identity.go
+++ b/pkg/jmap/api_identity.go
@@ -11,35 +11,35 @@ import (
var NS_IDENTITY = ns(JmapMail)
func (j *Client) GetAllIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) ([]Identity, SessionState, State, Language, Error) {
- return getTemplate(j, "GetAllIdentities", NS_IDENTITY, CommandIdentityGet,
+ return get(j, "GetAllIdentities", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId}
},
+ IdentityGetResponse{},
func(resp IdentityGetResponse) []Identity { return resp.List },
- func(resp IdentityGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, []string{},
)
}
func (j *Client) GetIdentities(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, identityIds []string) ([]Identity, SessionState, State, Language, Error) {
- return getTemplate(j, "GetIdentities", NS_IDENTITY, CommandIdentityGet,
+ return get(j, "GetIdentities", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId, Ids: ids}
},
+ IdentityGetResponse{},
func(resp IdentityGetResponse) []Identity { return resp.List },
- func(resp IdentityGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, identityIds,
)
}
func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]Identity, SessionState, State, Language, Error) {
- return getTemplateN(j, "GetIdentitiesForAllAccounts", NS_IDENTITY, CommandIdentityGet,
+ return getN(j, "GetIdentitiesForAllAccounts", NS_IDENTITY,
func(accountId string, ids []string) IdentityGetCommand {
return IdentityGetCommand{AccountId: accountId}
},
+ IdentityGetResponse{},
func(resp IdentityGetResponse) []Identity { return resp.List },
identity1,
- func(resp IdentityGetResponse) State { return resp.State },
accountIds, session, ctx, logger, acceptLanguage, []string{},
)
}
@@ -56,9 +56,9 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
logger = j.logger("GetIdentitiesAndMailboxes", session, logger)
calls := make([]Invocation, len(uniqueAccountIds)+1)
- calls[0] = invocation(CommandMailboxGet, MailboxGetCommand{AccountId: mailboxAccountId}, "0")
+ calls[0] = invocation(MailboxGetCommand{AccountId: mailboxAccountId}, "0")
for i, accountId := range uniqueAccountIds {
- calls[i+1] = invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId}, strconv.Itoa(i+1))
+ calls[i+1] = invocation(IdentityGetCommand{AccountId: accountId}, strconv.Itoa(i+1))
}
cmd, err := j.request(session, logger, NS_IDENTITY, calls...)
@@ -97,7 +97,7 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [
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, NS_IDENTITY, invocation(CommandIdentitySet, IdentitySetCommand{
+ cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
AccountId: accountId,
Create: map[string]Identity{
"c": identity,
@@ -123,7 +123,7 @@ func (j *Client) CreateIdentity(accountId string, session *Session, ctx context.
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, NS_IDENTITY, invocation(CommandIdentitySet, IdentitySetCommand{
+ cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
AccountId: accountId,
Update: map[string]PatchObject{
"c": identity.AsPatch(),
@@ -149,7 +149,7 @@ func (j *Client) UpdateIdentity(accountId string, session *Session, ctx context.
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, NS_IDENTITY, invocation(CommandIdentitySet, IdentitySetCommand{
+ cmd, err := j.request(session, logger, NS_IDENTITY, invocation(IdentitySetCommand{
AccountId: accountId,
Destroy: ids,
}, "0"))
@@ -184,11 +184,11 @@ type IdentityChanges struct {
// @api:tags email,changes
func (j *Client) GetIdentityChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (IdentityChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetIdentityChanges", NS_IDENTITY,
- CommandIdentityChanges, CommandIdentityGet,
+ return changes(j, "GetIdentityChanges", NS_IDENTITY,
func() IdentityChangesCommand {
return IdentityChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ IdentityChangesResponse{},
func(path string, rof string) IdentityGetRefCommand {
return IdentityGetRefCommand{
AccountId: accountId,
@@ -199,9 +199,6 @@ func (j *Client) GetIdentityChanges(accountId string, session *Session, ctx cont
},
}
},
- func(resp IdentityChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp IdentityGetResponse) []Identity { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []Identity, destroyed []string) IdentityChanges {
return IdentityChanges{
@@ -213,7 +210,6 @@ func (j *Client) GetIdentityChanges(accountId string, session *Session, ctx cont
Destroyed: destroyed,
}
},
- func(resp IdentityGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
diff --git a/pkg/jmap/api_mailbox.go b/pkg/jmap/api_mailbox.go
index 2a2ef23a13..01cbc5ff7c 100644
--- a/pkg/jmap/api_mailbox.go
+++ b/pkg/jmap/api_mailbox.go
@@ -17,29 +17,29 @@ type MailboxesResponse struct {
}
func (j *Client) GetMailbox(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (MailboxesResponse, SessionState, State, Language, Error) {
- return getTemplate(j, "GetMailbox", NS_MAILBOX, CommandCalendarGet,
+ return get(j, "GetMailbox", NS_MAILBOX,
func(accountId string, ids []string) MailboxGetCommand {
return MailboxGetCommand{AccountId: accountId, Ids: ids}
},
+ MailboxGetResponse{},
func(resp MailboxGetResponse) MailboxesResponse {
return MailboxesResponse{
Mailboxes: resp.List,
NotFound: resp.NotFound,
}
},
- func(resp MailboxGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, ids,
)
}
func (j *Client) GetAllMailboxes(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string][]Mailbox, SessionState, State, Language, Error) {
- return getTemplateN(j, "GetAllMailboxes", NS_MAILBOX, CommandCalendarGet,
+ return getN(j, "GetAllMailboxes", NS_MAILBOX,
func(accountId string, ids []string) MailboxGetCommand {
return MailboxGetCommand{AccountId: accountId}
},
+ MailboxGetResponse{},
func(resp MailboxGetResponse) []Mailbox { return resp.List },
identity1,
- func(resp MailboxGetResponse) State { return resp.State },
accountIds, session, ctx, logger, acceptLanguage, []string{},
)
}
@@ -51,8 +51,8 @@ func (j *Client) SearchMailboxes(accountIds []string, session *Session, ctx cont
invocations := make([]Invocation, len(uniqueAccountIds)*2)
for i, accountId := range uniqueAccountIds {
- invocations[i*2+0] = invocation(CommandMailboxQuery, MailboxQueryCommand{AccountId: accountId, Filter: filter}, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CommandMailboxGet, MailboxGetRefCommand{
+ invocations[i*2+0] = invocation(MailboxQueryCommand{AccountId: accountId, Filter: filter}, mcid(accountId, "0"))
+ invocations[i*2+1] = invocation(MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
Name: CommandMailboxQuery,
@@ -91,7 +91,7 @@ func (j *Client) SearchMailboxIdsPerRole(accountIds []string, session *Session,
invocations := make([]Invocation, len(uniqueAccountIds)*len(roles))
for i, accountId := range uniqueAccountIds {
for j, role := range roles {
- invocations[i*len(roles)+j] = invocation(CommandMailboxQuery, MailboxQueryCommand{AccountId: accountId, Filter: MailboxFilterCondition{Role: role}}, mcid(accountId, role))
+ invocations[i*len(roles)+j] = invocation(MailboxQueryCommand{AccountId: accountId, Filter: MailboxFilterCondition{Role: role}}, mcid(accountId, role))
}
}
cmd, err := j.request(session, logger, NS_MAILBOX, invocations...)
@@ -146,11 +146,11 @@ func newMailboxChanges(oldState, newState State, hasMoreChanges bool, created, u
// Retrieve Mailbox changes since a given state.
// @apidoc mailboxes,changes
func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetMailboxChanges", NS_MAILBOX,
- CommandMailboxChanges, CommandMailboxGet,
+ return changes(j, "GetMailboxChanges", NS_MAILBOX,
func() MailboxChangesCommand {
return MailboxChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ MailboxChangesResponse{},
func(path string, rof string) MailboxGetRefCommand {
return MailboxGetRefCommand{
AccountId: accountId,
@@ -161,12 +161,8 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
},
}
},
- func(resp MailboxChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp MailboxGetResponse) []Mailbox { return resp.List },
newMailboxChanges,
- func(resp MailboxGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
@@ -174,17 +170,15 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
// Retrieve Mailbox changes of multiple Accounts.
// @api:tags email,changes
func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]State, maxChanges uint) (map[string]MailboxChanges, SessionState, State, Language, Error) { //NOSONAR
- return changesTemplateN(j, "GetMailboxChangesForMultipleAccounts", NS_MAILBOX,
- accountIds, sinceStateMap, CommandMailboxChanges, CommandMailboxGet,
+ return changesN(j, "GetMailboxChangesForMultipleAccounts", NS_MAILBOX,
+ accountIds, sinceStateMap,
func(accountId string, state State) MailboxChangesCommand {
return MailboxChangesCommand{AccountId: accountId, SinceState: state, MaxChanges: posUIntPtr(maxChanges)}
},
+ MailboxChangesResponse{},
func(accountId string, path string, ref string) MailboxGetRefCommand {
return MailboxGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: path, ResultOf: ref}}
},
- func(resp MailboxChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp MailboxGetResponse) []Mailbox { return resp.List },
newMailboxChanges,
identity1,
@@ -206,13 +200,13 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, session
invocations := make([]Invocation, n*2)
for i, accountId := range uniqueAccountIds {
- invocations[i*2+0] = invocation(CommandMailboxQuery, MailboxQueryCommand{
+ invocations[i*2+0] = invocation(MailboxQueryCommand{
AccountId: accountId,
Filter: MailboxFilterCondition{
HasAnyRole: &t,
},
}, mcid(accountId, "0"))
- invocations[i*2+1] = invocation(CommandMailboxGet, MailboxGetRefCommand{
+ invocations[i*2+1] = invocation(MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
ResultOf: mcid(accountId, "0"),
@@ -259,7 +253,7 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *S
invocations := make([]Invocation, n*2)
for i, accountId := range uniqueAccountIds {
- invocations[i*2+0] = invocation(CommandMailboxQuery, MailboxQueryCommand{
+ invocations[i*2+0] = invocation(MailboxQueryCommand{
AccountId: accountId,
Filter: MailboxFilterCondition{
Role: JmapMailboxRoleInbox,
@@ -299,7 +293,7 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, session *S
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) { //NOSONAR
logger = j.logger("UpdateMailbox", session, logger)
- cmd, err := j.request(session, logger, NS_MAILBOX, invocation(CommandMailboxSet, MailboxSetCommand{
+ cmd, err := j.request(session, logger, NS_MAILBOX, invocation(MailboxSetCommand{
AccountId: accountId,
IfInState: ifInState,
Update: map[string]PatchObject{
@@ -327,7 +321,7 @@ func (j *Client) UpdateMailbox(accountId string, session *Session, ctx context.C
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, NS_MAILBOX, invocation(CommandMailboxSet, MailboxSetCommand{
+ cmd, err := j.request(session, logger, NS_MAILBOX, invocation(MailboxSetCommand{
AccountId: accountId,
IfInState: ifInState,
Create: map[string]MailboxChange{
@@ -359,7 +353,7 @@ func (j *Client) CreateMailbox(accountId string, session *Session, ctx context.C
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, NS_MAILBOX, invocation(CommandMailboxSet, MailboxSetCommand{
+ cmd, err := j.request(session, logger, NS_MAILBOX, invocation(MailboxSetCommand{
AccountId: accountId,
IfInState: ifInState,
Destroy: mailboxIds,
diff --git a/pkg/jmap/api_objects.go b/pkg/jmap/api_objects.go
index 04ef44ec7f..6e07bb1b6a 100644
--- a/pkg/jmap/api_objects.go
+++ b/pkg/jmap/api_objects.go
@@ -61,31 +61,31 @@ func (j *Client) GetObjects(accountId string, session *Session, ctx context.Cont
methodCalls := []Invocation{}
if len(mailboxIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandMailboxGet, MailboxGetCommand{AccountId: accountId, Ids: mailboxIds}, "mailboxes"))
+ methodCalls = append(methodCalls, invocation(MailboxGetCommand{AccountId: accountId, Ids: mailboxIds}, "mailboxes"))
}
if len(emailIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandEmailGet, EmailGetCommand{AccountId: accountId, Ids: emailIds}, "emails"))
+ methodCalls = append(methodCalls, invocation(EmailGetCommand{AccountId: accountId, Ids: emailIds}, "emails"))
}
if len(addressbookIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandAddressBookGet, AddressBookGetCommand{AccountId: accountId, Ids: addressbookIds}, "addressbooks"))
+ methodCalls = append(methodCalls, invocation(AddressBookGetCommand{AccountId: accountId, Ids: addressbookIds}, "addressbooks"))
}
if len(contactIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandContactCardGet, ContactCardGetCommand{AccountId: accountId, Ids: contactIds}, "contacts"))
+ methodCalls = append(methodCalls, invocation(ContactCardGetCommand{AccountId: accountId, Ids: contactIds}, "contacts"))
}
if len(calendarIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandCalendarGet, CalendarGetCommand{AccountId: accountId, Ids: calendarIds}, "calendars"))
+ methodCalls = append(methodCalls, invocation(CalendarGetCommand{AccountId: accountId, Ids: calendarIds}, "calendars"))
}
if len(eventIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandCalendarEventGet, CalendarEventGetCommand{AccountId: accountId, Ids: eventIds}, "events"))
+ methodCalls = append(methodCalls, invocation(CalendarEventGetCommand{AccountId: accountId, Ids: eventIds}, "events"))
}
if len(quotaIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandQuotaGet, QuotaGetCommand{AccountId: accountId, Ids: quotaIds}, "quotas"))
+ methodCalls = append(methodCalls, invocation(QuotaGetCommand{AccountId: accountId, Ids: quotaIds}, "quotas"))
}
if len(identityIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandIdentityGet, IdentityGetCommand{AccountId: accountId, Ids: identityIds}, "identities"))
+ methodCalls = append(methodCalls, invocation(IdentityGetCommand{AccountId: accountId, Ids: identityIds}, "identities"))
}
if len(emailSubmissionIds) > 0 {
- methodCalls = append(methodCalls, invocation(CommandEmailSubmissionGet, EmailSubmissionGetCommand{AccountId: accountId, Ids: emailSubmissionIds}, "emailSubmissionIds"))
+ methodCalls = append(methodCalls, invocation(EmailSubmissionGetCommand{AccountId: accountId, Ids: emailSubmissionIds}, "emailSubmissionIds"))
}
cmd, err := j.request(session, logger, NS_OBJECTS, methodCalls...)
diff --git a/pkg/jmap/api_principal.go b/pkg/jmap/api_principal.go
index 2a741720e5..9eb42df7e0 100644
--- a/pkg/jmap/api_principal.go
+++ b/pkg/jmap/api_principal.go
@@ -17,7 +17,7 @@ func (j *Client) GetPrincipals(accountId string, session *Session, ctx context.C
logger = j.logger("GetPrincipals", session, logger)
cmd, err := j.request(session, logger, NS_PRINCIPALS,
- invocation(CommandPrincipalGet, PrincipalGetCommand{AccountId: accountId, Ids: ids}, "0"),
+ invocation(PrincipalGetCommand{AccountId: accountId, Ids: ids}, "0"),
)
if err != nil {
return PrincipalsResponse{}, "", "", "", err
diff --git a/pkg/jmap/api_quota.go b/pkg/jmap/api_quota.go
index 1c9946af20..c61a67bdc8 100644
--- a/pkg/jmap/api_quota.go
+++ b/pkg/jmap/api_quota.go
@@ -9,13 +9,13 @@ import (
var NS_QUOTA = ns(JmapQuota)
func (j *Client) GetQuotas(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]QuotaGetResponse, SessionState, State, Language, Error) {
- return getTemplateN(j, "GetQuotas", NS_QUOTA, CommandQuotaGet,
+ return getN(j, "GetQuotas", NS_QUOTA,
func(accountId string, ids []string) QuotaGetCommand {
return QuotaGetCommand{AccountId: accountId}
},
+ QuotaGetResponse{},
identity1,
identity1,
- func(resp QuotaGetResponse) State { return resp.State },
accountIds, session, ctx, logger, acceptLanguage, []string{},
)
}
@@ -33,11 +33,11 @@ type QuotaChanges struct {
// @api:tags quota,changes
func (j *Client) GetQuotaChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (QuotaChanges, SessionState, State, Language, Error) {
- return changesTemplate(j, "GetQuotaChanges", NS_QUOTA,
- CommandQuotaChanges, CommandQuotaGet,
+ return changes(j, "GetQuotaChanges", NS_QUOTA,
func() QuotaChangesCommand {
return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ QuotaChangesResponse{},
func(path string, rof string) QuotaGetRefCommand {
return QuotaGetRefCommand{
AccountId: accountId,
@@ -48,9 +48,6 @@ func (j *Client) GetQuotaChanges(accountId string, session *Session, ctx context
},
}
},
- func(resp QuotaChangesResponse) (State, State, bool, []string) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges, resp.Destroyed
- },
func(resp QuotaGetResponse) []Quota { return resp.List },
func(oldState, newState State, hasMoreChanges bool, created, updated []Quota, destroyed []string) QuotaChanges {
return QuotaChanges{
@@ -62,18 +59,17 @@ func (j *Client) GetQuotaChanges(accountId string, session *Session, ctx context
Destroyed: destroyed,
}
},
- func(resp QuotaGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
func (j *Client) GetQuotaUsageChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState State, maxChanges uint) (QuotaChanges, SessionState, State, Language, Error) {
- return updatedTemplate(j, "GetQuotaUsageChanges", NS_QUOTA,
- CommandQuotaChanges, CommandQuotaGet,
+ return updates(j, "GetQuotaUsageChanges", NS_QUOTA,
func() QuotaChangesCommand {
return QuotaChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: posUIntPtr(maxChanges)}
},
+ QuotaChangesResponse{},
func(path string, rof string) QuotaGetRefCommand {
return QuotaGetRefCommand{
AccountId: accountId,
@@ -89,9 +85,6 @@ func (j *Client) GetQuotaUsageChanges(accountId string, session *Session, ctx co
},
}
},
- func(resp QuotaChangesResponse) (State, State, bool) {
- return resp.OldState, resp.NewState, resp.HasMoreChanges
- },
func(resp QuotaGetResponse) []Quota { return resp.List },
func(oldState, newState State, hasMoreChanges bool, updated []Quota) QuotaChanges {
return QuotaChanges{
@@ -101,7 +94,6 @@ func (j *Client) GetQuotaUsageChanges(accountId string, session *Session, ctx co
Updated: updated,
}
},
- func(resp QuotaGetResponse) State { return resp.State },
session, ctx, logger, acceptLanguage,
)
}
diff --git a/pkg/jmap/api_vacation.go b/pkg/jmap/api_vacation.go
index 8eeeae75f4..f513ec0ff6 100644
--- a/pkg/jmap/api_vacation.go
+++ b/pkg/jmap/api_vacation.go
@@ -15,12 +15,12 @@ const (
)
func (j *Client) GetVacationResponse(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (VacationResponseGetResponse, SessionState, State, Language, Error) {
- return getTemplate(j, "GetVacationResponse", NS_VACATION, CommandVacationResponseGet,
+ return get(j, "GetVacationResponse", NS_VACATION,
func(accountId string, ids []string) VacationResponseGetCommand {
return VacationResponseGetCommand{AccountId: accountId}
},
+ VacationResponseGetResponse{},
identity1,
- func(resp VacationResponseGetResponse) State { return resp.State },
accountId, session, ctx, logger, acceptLanguage, []string{},
)
}
@@ -53,7 +53,7 @@ func (j *Client) SetVacationResponse(accountId string, vacation VacationResponse
logger = j.logger("SetVacationResponse", session, logger)
cmd, err := j.request(session, logger, NS_VACATION,
- invocation(CommandVacationResponseSet, VacationResponseSetCommand{
+ invocation(VacationResponseSetCommand{
AccountId: accountId,
Create: map[string]VacationResponse{
vacationResponseId: {
@@ -68,7 +68,7 @@ func (j *Client) SetVacationResponse(accountId string, vacation VacationResponse
}, "0"),
// chain a second request to get the current complete VacationResponse object
// after performing the changes, as that makes for a better API
- invocation(CommandVacationResponseGet, VacationResponseGetCommand{AccountId: accountId}, "1"),
+ invocation(VacationResponseGetCommand{AccountId: accountId}, "1"),
)
if err != nil {
return VacationResponse{}, "", "", "", err
@@ -94,7 +94,7 @@ func (j *Client) SetVacationResponse(accountId string, vacation VacationResponse
}
if len(getResponse.List) != 1 {
- berr := fmt.Errorf("failed to find %s in %s response", string(VacationResponseType), string(CommandVacationResponseGet))
+ berr := fmt.Errorf("failed to find %s in %s response", VacationResponseType, string(CommandVacationResponseGet))
logger.Error().Msg(berr.Error())
return VacationResponse{}, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
diff --git a/pkg/jmap/export_test.go b/pkg/jmap/export_test.go
new file mode 100644
index 0000000000..094a9ca3bf
--- /dev/null
+++ b/pkg/jmap/export_test.go
@@ -0,0 +1,68 @@
+package jmap
+
+// This is for functions that are only supposed to be visible in tests.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "log"
+ "strings"
+
+ "golang.org/x/tools/go/packages"
+)
+
+func parseConsts(pkgID string, suffix string, typeName string) (map[string]string, error) { //NOSONAR
+ result := map[string]string{}
+ {
+ cfg := &packages.Config{
+ Mode: packages.LoadSyntax,
+ Dir: ".",
+ Tests: false,
+ }
+ pkgs, err := packages.Load(cfg, ".")
+ if err != nil {
+ log.Fatal(err)
+ }
+ if packages.PrintErrors(pkgs) > 0 {
+ return nil, fmt.Errorf("failed to parse the package '%s'", pkgID)
+ }
+ for _, p := range pkgs {
+ if p.ID != pkgID {
+ continue
+ }
+ for _, syn := range p.Syntax {
+ for _, decl := range syn.Decls {
+ switch g := decl.(type) {
+ case *ast.GenDecl:
+ for _, s := range g.Specs {
+ switch e := s.(type) {
+ case *ast.ValueSpec:
+ for i, ident := range e.Names {
+ if ident != nil && strings.HasSuffix(ident.Name, suffix) {
+ value := e.Values[i]
+ switch c := value.(type) {
+ case *ast.CallExpr:
+ switch f := c.Fun.(type) {
+ case *ast.Ident:
+ if f.Name == typeName {
+ switch a := c.Args[0].(type) {
+ case *ast.BasicLit:
+ if a.Kind == token.STRING {
+ result[ident.Name] = strings.Trim(a.Value, `"`)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return result, nil
+}
diff --git a/pkg/jmap/integration_contact_test.go b/pkg/jmap/integration_contact_test.go
index 9c062058d3..aa8eca699c 100644
--- a/pkg/jmap/integration_contact_test.go
+++ b/pkg/jmap/integration_contact_test.go
@@ -3,6 +3,7 @@ package jmap
import (
"math/rand"
"regexp"
+ "slices"
"testing"
"github.com/stretchr/testify/require"
@@ -47,6 +48,7 @@ func TestAddressBooks(t *testing.T) {
user := pickUser()
session := s.Session(user.name)
+ // we first need to retrieve the list of all the Principals in order to be able to use and test AddressBook sharing
principalIds := []string{}
{
principals, _, _, _, err := s.client.GetPrincipals(session.PrimaryAccounts.Mail, session, s.ctx, s.logger, "", []string{})
@@ -55,20 +57,87 @@ func TestAddressBooks(t *testing.T) {
principalIds = structs.Map(principals.Principals, func(p Principal) string { return p.Id })
}
+ accountId := session.PrimaryAccounts.Contacts
+
+ ss := SessionState("")
+ as := EmptyState
+
+ // we need to fetch the ID of the default AddressBook that automatically exists for each user, in order to exclude it
+ // from the tests below
+ defaultAddressBookId := ""
+ {
+ resp, sessionState, state, _, err := s.client.GetAddressbooks(accountId, session, s.ctx, s.logger, "", []string{})
+ require.NoError(err)
+ require.Empty(resp.NotFound)
+ require.Len(resp.AddressBooks, 1) // the personal addressbook that exists by default
+ defaultAddressBookId = resp.AddressBooks[0].Id
+ ss = sessionState
+ as = state
+ }
+
+ // we are going to create a random amount of AddressBook objects
num := uint(5 + rand.Intn(30))
{
- accountId := ""
- a, boxes, abooks, err := s.fillAddressBook(t, num, session, user, principalIds)
+ boxes, abooks, sessionState, state, err := s.fillAddressBook(t, accountId, num, session, user, principalIds)
require.NoError(err)
- require.NotEmpty(a)
require.Len(abooks, int(num))
- accountId = a
+ ss = sessionState
+ as = state
+ {
+ // lets retrieve all the existing AddressBook objects by passing an empty ID slice
+ resp, sessionState, state, _, err := s.client.GetAddressbooks(accountId, session, s.ctx, s.logger, "", []string{})
+ require.NoError(err)
+ require.Empty(resp.NotFound)
+ // lets skip the default AddressBook since we did not create that one
+ found := structs.Filter(resp.AddressBooks, func(a AddressBook) bool { return a.Id != defaultAddressBookId })
+ require.Len(found, int(num))
+ m := structs.Index(found, func(a AddressBook) string { return a.Id })
+ require.Len(m, int(num))
+ require.Equal(sessionState, ss)
+ require.Equal(state, as)
+
+ for _, a := range abooks {
+ require.Contains(m, a.Id)
+ found, ok := m[a.Id]
+ require.True(ok)
+ require.Equal(a, found)
+ }
+
+ ss = sessionState
+ as = state
+ }
+
+ // lets retrieve every AddressBook object we created by its ID
+ for _, a := range abooks {
+ resp, sessionState, state, _, err := s.client.GetAddressbooks(accountId, session, s.ctx, s.logger, "", []string{a.Id})
+ require.NoError(err)
+ require.Empty(resp.NotFound)
+ require.Len(resp.AddressBooks, 1)
+ require.Equal(sessionState, ss)
+ require.Equal(state, as)
+ require.Equal(resp.AddressBooks[0], a)
+ }
+
+ // lets modify each AddressBook
+ for _, a := range abooks {
+ changed, sessionState, state, _, err := s.client.UpdateAddressBook(accountId, session, s.ctx, s.logger, "", a.Id, AddressBookChange{Description: strPtr(a.Description + " (changed)"), IsSubscribed: boolPtr(!a.IsSubscribed)})
+ require.NoError(err)
+ require.NotEqual(a, changed)
+ require.Equal(sessionState, ss)
+ require.NotEqual(state, as)
+ require.Equal(a.Description+" (changed)", changed.Description)
+ require.Equal(!a.IsSubscribed, changed.IsSubscribed)
+ }
+
+ // now lets delete each AddressBook that we created, all at once
ids := structs.Map(abooks, func(a AddressBook) string { return a.Id })
{
- errMap, _, _, _, err := s.client.DeleteAddressBook(accountId, ids, session, s.ctx, s.logger, "")
+ errMap, sessionState, state, _, err := s.client.DeleteAddressBook(accountId, ids, session, s.ctx, s.logger, "")
require.NoError(err)
require.Empty(errMap)
+ require.Equal(sessionState, ss)
+ require.NotEqual(state, as)
}
allBoxesAreTicked(t, boxes)
@@ -146,20 +215,20 @@ type ContactsBoxes struct {
var streetNumberRegex = regexp.MustCompile(`^(\d+)\s+(.+)$`)
-func (s *StalwartTest) fillAddressBook(
+func (s *StalwartTest) fillAddressBook( //NOSONAR
t *testing.T,
+ accountId string,
count uint,
session *Session,
_ User,
principalIds []string,
-) (string, AddressBooksBoxes, []AddressBook, error) {
+) (AddressBooksBoxes, []AddressBook, SessionState, State, error) {
require := require.New(t)
- accountId := session.PrimaryAccounts.Contacts
- require.NotEmpty(accountId, "no primary account for contacts in session")
-
boxes := AddressBooksBoxes{}
created := []AddressBook{}
+ ss := SessionState("")
+ as := EmptyState
printer := func(s string) { log.Println(s) }
@@ -201,16 +270,24 @@ func (s *StalwartTest) fillAddressBook(
a, sessionState, state, _, err := s.client.CreateAddressBook(accountId, session, s.ctx, s.logger, "", abook)
if err != nil {
- return accountId, boxes, created, err
+ return boxes, created, ss, as, err
}
require.NotEmpty(sessionState)
require.NotEmpty(state)
+ if ss != SessionState("") {
+ require.Equal(ss, sessionState)
+ }
+ if as != EmptyState {
+ require.NotEqual(as, state)
+ }
require.NotNil(a)
created = append(created, *a)
+ ss = sessionState
+ as = state
printer(fmt.Sprintf("📔 created %*s/%v id=%v", int(math.Log10(float64(count))+1), strconv.Itoa(int(i+1)), count, a.Id))
}
- return accountId, boxes, created, nil
+ return boxes, created, ss, as, nil
}
func (s *StalwartTest) fillContacts( //NOSONAR
@@ -233,7 +310,7 @@ func (s *StalwartTest) fillContacts( //NOSONAR
addressbookId := ""
{
- addressBooksById, err := c.objectsById(accountId, AddressBookType, JmapContacts)
+ addressBooksById, err := c.objectsById(accountId, AddressBookType)
require.NoError(err)
for id, addressbook := range addressBooksById {
@@ -242,8 +319,15 @@ func (s *StalwartTest) fillContacts( //NOSONAR
addressbookId = id
break
}
+ } else {
+ printer(fmt.Sprintf("abook without isDefault: %v", addressbook))
}
}
+ if addressbookId == "" {
+ ids := structs.Keys(addressBooksById)
+ slices.Sort(ids)
+ addressbookId = ids[0]
+ }
}
require.NotEmpty(addressbookId)
@@ -606,5 +690,5 @@ func (s *StalwartTest) fillContacts( //NOSONAR
}
func (s *StalwartTest) CreateContact(j *TestJmapClient, accountId string, contact map[string]any) (string, error) {
- return j.create1(accountId, ContactCardType, JmapContacts, contact)
+ return j.create1(accountId, ContactCardType, contact)
}
diff --git a/pkg/jmap/integration_event_test.go b/pkg/jmap/integration_event_test.go
index c3c5585f03..fa555b54b6 100644
--- a/pkg/jmap/integration_event_test.go
+++ b/pkg/jmap/integration_event_test.go
@@ -105,7 +105,7 @@ func (s *StalwartTest) fillEvents( //NOSONAR
calendarId := ""
{
- calendarsById, err := c.objectsById(accountId, CalendarType, JmapCalendars)
+ calendarsById, err := c.objectsById(accountId, CalendarType)
require.NoError(err)
for id, calendar := range calendarsById {
@@ -359,7 +359,7 @@ func (s *StalwartTest) fillEvents( //NOSONAR
}
func (s *StalwartTest) CreateEvent(j *TestJmapClient, accountId string, event map[string]any) (string, error) {
- return j.create1(accountId, CalendarEventType, JmapCalendars, event)
+ return j.create1(accountId, CalendarEventType, event)
}
var rooms = []jscalendar.Location{
diff --git a/pkg/jmap/integration_test.go b/pkg/jmap/integration_test.go
index fb3c7d6577..1953d2f2d6 100644
--- a/pkg/jmap/integration_test.go
+++ b/pkg/jmap/integration_test.go
@@ -721,12 +721,12 @@ func (j *TestJmapClient) create(id string, objectType ObjectType, body map[strin
}).command(body)
}
-func (j *TestJmapClient) create1(accountId string, objectType ObjectType, ns JmapNamespace, obj map[string]any) (string, error) {
+func (j *TestJmapClient) create1(accountId string, objectType ObjectType, obj map[string]any) (string, error) {
body := map[string]any{
- "using": []string{string(JmapCore), string(ns)},
+ "using": structs.Map(objectType.Namespaces, func(n JmapNamespace) string { return string(n) }),
"methodCalls": []any{
[]any{
- objectType + "/set",
+ objectType.Name + "/set",
map[string]any{
"accountId": accountId,
"create": map[string]any{
@@ -740,14 +740,14 @@ func (j *TestJmapClient) create1(accountId string, objectType ObjectType, ns Jma
return j.create("c", objectType, body)
}
-func (j *TestJmapClient) objectsById(accountId string, objectType ObjectType, scope JmapNamespace) (map[string]map[string]any, error) {
+func (j *TestJmapClient) objectsById(accountId string, objectType ObjectType) (map[string]map[string]any, error) {
m := map[string]map[string]any{}
{
body := map[string]any{
- "using": []string{string(JmapCore), string(scope)},
+ "using": structs.Map(objectType.Namespaces, func(n JmapNamespace) string { return string(n) }),
"methodCalls": []any{
[]any{
- objectType + "/get",
+ objectType.Name + "/get",
map[string]any{
"accountId": accountId,
},
diff --git a/pkg/jmap/integration_ws_test.go b/pkg/jmap/integration_ws_test.go
index f421413966..9177d2152c 100644
--- a/pkg/jmap/integration_ws_test.go
+++ b/pkg/jmap/integration_ws_test.go
@@ -32,18 +32,18 @@ func (l *testWsPushListener) OnNotification(username string, pushState StateChan
l.logger.Debug().Msgf("received %T: %v", pushState, pushState)
if changed, ok := pushState.Changed[l.mailAccountId]; ok {
l.m.Lock()
- if st, ok := changed[EmailType]; ok {
+ if st, ok := changed[EmailName]; ok {
l.emailStates = append(l.emailStates, st)
}
- if st, ok := changed[ThreadType]; ok {
+ if st, ok := changed[ThreadName]; ok {
l.threadStates = append(l.threadStates, st)
}
- if st, ok := changed[MailboxType]; ok {
+ if st, ok := changed[MailboxName]; ok {
l.mailboxStates = append(l.mailboxStates, st)
}
l.m.Unlock()
- unsupportedKeys := structs.Filter(structs.Keys(changed), func(o ObjectType) bool { return o != EmailType && o != ThreadType && o != MailboxType })
+ unsupportedKeys := structs.Filter(structs.Keys(changed), func(o ObjectTypeName) bool { return o != EmailName && o != ThreadName && o != MailboxName })
assert.Empty(l.t, unsupportedKeys)
}
unsupportedAccounts := structs.Filter(structs.Keys(pushState.Changed), func(s string) bool { return s != l.mailAccountId })
diff --git a/pkg/jmap/model.go b/pkg/jmap/model.go
index 1e1de8503a..c56757da45 100644
--- a/pkg/jmap/model.go
+++ b/pkg/jmap/model.go
@@ -9,7 +9,16 @@ import (
)
// https://www.iana.org/assignments/jmap/jmap.xml#jmap-data-types
-type ObjectType string
+type ObjectTypeName string
+
+type ObjectType struct {
+ Name ObjectTypeName
+ Namespaces []JmapNamespace
+}
+
+func (o ObjectType) String() string {
+ return string(o.Name)
+}
// Where `UTCDate` is given as a type, it means a `Date` where the "time-offset"
// component MUST be `"Z"` (i.e., it must be in UTC time).
@@ -111,29 +120,31 @@ const (
JmapTasksAlerts = JmapNamespace("urn:ietf:params:jmap:tasks:alerts")
JmapTasksMultilingual = JmapNamespace("urn:ietf:params:jmap:tasks:multilingual")
JmapTasksCustomTimezones = JmapNamespace("urn:ietf:params:jmap:tasks:customtimezones")
+ JmapFileNode = JmapNamespace("urn:ietf:params:jmap:filenode")
- CoreType = ObjectType("Core")
- PushSubscriptionType = ObjectType("PushSubscription")
- MailboxType = ObjectType("Mailbox")
- ThreadType = ObjectType("Thread")
- EmailType = ObjectType("Email")
- EmailDeliveryType = ObjectType("EmailDelivery")
- SearchSnippetType = ObjectType("SearchSnippet")
- IdentityType = ObjectType("Identity")
- EmailSubmissionType = ObjectType("EmailSubmission")
- VacationResponseType = ObjectType("VacationResponse")
- MDNType = ObjectType("MDN")
- QuotaType = ObjectType("Quota")
- SieveScriptType = ObjectType("SieveScript")
- PrincipalType = ObjectType("PrincipalType")
- ShareNotificationType = ObjectType("ShareNotification")
- AddressBookType = ObjectType("AddressBook")
- ContactCardType = ObjectType("ContactCard")
- CalendarType = ObjectType("Calendar")
- CalendarEventType = ObjectType("CalendarEvent")
- CalendarEventNotificationType = ObjectType("CalendarEventNotification")
- ParticipantIdentityType = ObjectType("ParticipantIdentity")
- FileNodeType = ObjectType("FileNode")
+ CoreName = ObjectTypeName("Core")
+ BlobName = ObjectTypeName("Blob")
+ PushSubscriptionName = ObjectTypeName("PushSubscription")
+ MailboxName = ObjectTypeName("Mailbox")
+ ThreadName = ObjectTypeName("Thread")
+ EmailName = ObjectTypeName("Email")
+ EmailDeliveryName = ObjectTypeName("EmailDelivery")
+ SearchSnippetName = ObjectTypeName("SearchSnippet")
+ IdentityName = ObjectTypeName("Identity")
+ EmailSubmissionName = ObjectTypeName("EmailSubmission")
+ VacationResponseName = ObjectTypeName("VacationResponse")
+ MDNName = ObjectTypeName("MDN")
+ QuotaName = ObjectTypeName("Quota")
+ SieveScriptName = ObjectTypeName("SieveScript")
+ PrincipalName = ObjectTypeName("Principal")
+ ShareNotificationName = ObjectTypeName("ShareNotification")
+ AddressBookName = ObjectTypeName("AddressBook")
+ ContactCardName = ObjectTypeName("ContactCard")
+ CalendarName = ObjectTypeName("Calendar")
+ CalendarEventName = ObjectTypeName("CalendarEvent")
+ CalendarEventNotificationName = ObjectTypeName("CalendarEventNotification")
+ ParticipantIdentityName = ObjectTypeName("ParticipantIdentity")
+ FileNodeName = ObjectTypeName("FileNode")
JmapKeywordPrefix = "$"
JmapKeywordSeen = "$seen"
@@ -261,6 +272,10 @@ const (
IncludeInAvailabilityNone = IncludeInAvailability("none")
)
+func newObjectType(name ObjectTypeName, namespaces ...JmapNamespace) ObjectType {
+ return ObjectType{Name: name, Namespaces: ns(namespaces...)}
+}
+
var (
JmapNamespaces = []JmapNamespace{
JmapCore,
@@ -282,8 +297,59 @@ var (
JmapTasksAlerts,
JmapTasksMultilingual,
JmapTasksCustomTimezones,
+ JmapFileNode,
}
+ ObjectTypeNames = []ObjectTypeName{
+ CoreName,
+ BlobName,
+ PushSubscriptionName,
+ MailboxName,
+ ThreadName,
+ EmailName,
+ EmailDeliveryName,
+ SearchSnippetName,
+ IdentityName,
+ EmailSubmissionName,
+ VacationResponseName,
+ MDNName,
+ QuotaName,
+ SieveScriptName,
+ PrincipalName,
+ ShareNotificationName,
+ AddressBookName,
+ ContactCardName,
+ CalendarName,
+ CalendarEventName,
+ CalendarEventNotificationName,
+ ParticipantIdentityName,
+ FileNodeName,
+ }
+
+ CoreType = newObjectType(CoreName)
+ BlobType = newObjectType(BlobName, JmapBlob)
+ PushSubscriptionType = newObjectType(PushSubscriptionName)
+ MailboxType = newObjectType(MailboxName, JmapMail)
+ ThreadType = newObjectType(ThreadName, JmapMail)
+ EmailType = newObjectType(EmailName, JmapMail)
+ EmailDeliveryType = newObjectType(EmailDeliveryName, JmapMail)
+ SearchSnippetType = newObjectType(SearchSnippetName, JmapMail)
+ IdentityType = newObjectType(IdentityName, JmapMail)
+ EmailSubmissionType = newObjectType(EmailSubmissionName, JmapMail)
+ VacationResponseType = newObjectType(VacationResponseName, JmapVacationResponse)
+ MDNType = newObjectType(MDNName, JmapMDN)
+ QuotaType = newObjectType(QuotaName, JmapQuota)
+ SieveScriptType = newObjectType(SieveScriptName, JmapSieve)
+ PrincipalType = newObjectType(PrincipalName, JmapPrincipals)
+ ShareNotificationType = newObjectType(ShareNotificationName, JmapPrincipals)
+ AddressBookType = newObjectType(AddressBookName, JmapContacts)
+ ContactCardType = newObjectType(ContactCardName, JmapContacts)
+ CalendarType = newObjectType(CalendarName, JmapCalendars)
+ CalendarEventType = newObjectType(CalendarEventName, JmapCalendars)
+ CalendarEventNotificationType = newObjectType(CalendarEventNotificationName, JmapCalendars)
+ ParticipantIdentityType = newObjectType(ParticipantIdentityName, JmapCalendars)
+ FileNodeType = newObjectType(FileNodeName, JmapFileNode)
+
ObjectTypes = []ObjectType{
CoreType,
PushSubscriptionType,
@@ -1091,6 +1157,200 @@ type SetError struct {
InvalidRecipients []string `json:"invalidRecipients,omitempty"`
}
+type Command string
+
+type Invocation struct {
+ Command Command
+ Parameters any
+ Tag string
+}
+
+func invocation(parameters JmapCommand, tag string) Invocation {
+ return Invocation{
+ Command: parameters.GetCommand(),
+ Parameters: parameters,
+ Tag: tag,
+ }
+}
+
+type TypeOfRequest string
+
+const RequestType = TypeOfRequest("Request")
+
+type Request struct {
+ // The set of capabilities the client wishes to use.
+ //
+ // The client MAY include capability identifiers even if the method calls it makes do not utilise those capabilities.
+ // The server advertises the set of specifications it supports in the Session object
+ // (see [Section 2](https://jmap.io/spec-core.html#the-jmap-session-resource)), as keys on the capabilities property.
+ Using []JmapNamespace `json:"using"`
+
+ // An array of method calls to process on the server.
+ //
+ // The method calls MUST be processed sequentially, in order.
+ MethodCalls []Invocation `json:"methodCalls"`
+
+ // A map of a (client-specified) creation id to the id the server assigned when a record was successfully created (optional).
+ CreatedIds map[string]string `json:"createdIds,omitempty"`
+
+ // This MUST be the string "Request".
+ // The specification extends the Response object with two additional arguments when used over a WebSocket.
+ Type TypeOfRequest `json:"@type,omitempty"`
+
+ // A client-specified identifier for the request to be echoed back in the response to this request (optional).
+ Id string `json:"id,omitempty"`
+}
+
+type TypeOfResponse string
+
+const ResponseType = TypeOfResponse("Response")
+
+type Response struct {
+ // An array of responses, in the same format as the methodCalls on the Request object.
+ // The output of the methods MUST be added to the methodResponses array in the same order that the methods are processed.
+ MethodResponses []Invocation `json:"methodResponses"`
+
+ // A map of a (client-specified) creation id to the id the server assigned when a record was successfully created.
+ //
+ // Optional; only returned if given in the request.
+ //
+ // This MUST include all creation ids passed in the original createdIds parameter of the Request object, as well as any
+ // additional ones added for newly created records.
+ CreatedIds map[string]string `json:"createdIds,omitempty"`
+
+ // The current value of the “state” string on the Session object, as described in [Section 2](https://jmap.io/spec-core.html#the-jmap-session-resource).
+ // Clients may use this to detect if this object has changed and needs to be refetched.
+ SessionState SessionState `json:"sessionState"`
+
+ // This MUST be the string "Response".
+ // The specification extends the Response object with two additional arguments when used over a WebSocket.
+ Type TypeOfResponse `json:"@type,omitempty"`
+
+ // MUST be returned if an identifier is included in the request (optional).
+ RequestId string `json:"requestId,omitempty"`
+}
+
+// Patch Object.
+//
+// Example:
+//
+// - moves it from the drafts folder (which has Mailbox id `"7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e"“)
+// to the sent folder (which we presume has Mailbox id `"73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6"“)
+// - removes the `$draft` flag
+//
+// ```json
+//
+// {
+// "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null,
+// "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true,
+// "keywords/$draft": null
+// }
+//
+// ```
+type PatchObject map[string]any
+
+// Reference to Previous Method Results
+//
+// To allow clients to make more efficient use of the network and avoid round trips, an argument to one method
+// can be taken from the result of a previous method call in the same request.
+//
+// To do this, the client prefixes the argument name with # (an [octothorpe](https://en.wiktionary.org/wiki/octothorpe)).
+//
+// When processing a method call, the server MUST first check the arguments object for any names beginning with #.
+//
+// If found, the result reference should be resolved and the value used as the “real” argument.
+//
+// The method is then processed as normal.
+//
+// If any result reference fails to resolve, the whole method MUST be rejected with an invalidResultReference error.
+//
+// If an arguments object contains the same argument name in normal and referenced form (e.g., foo and #foo),
+// the method MUST return an invalidArguments error.
+//
+// To resolve:
+//
+// 1. Find the first response with a method call id identical to the resultOf property of the ResultReference
+// in the methodResponses array from previously processed method calls in the same request.
+// If none, evaluation fails.
+// 2. If the response name is not identical to the name property of the ResultReference, evaluation fails.
+// 3. Apply the path to the arguments object of the response (the second item in the response array)
+// following the JSON Pointer algorithm [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901),
+// except with the following addition in “Evaluation” (see Section 4):
+// 4. If the currently referenced value is a JSON array, the reference token may be exactly the single character *,
+// making the new referenced value the result of applying the rest of the JSON Pointer tokens to every item in the
+// array and returning the results in the same order in a new array.
+// 5. If the result of applying the rest of the pointer tokens to each item was itself an array, the contents of this
+// array are added to the output rather than the array itself (i.e., the result is flattened from an array of
+// arrays to a single array).
+type ResultReference struct {
+ // The method call id of a previous method call in the current request.
+ ResultOf string `json:"resultOf"`
+
+ // The required name of a response to that method call.
+ Name Command `json:"name"`
+
+ // A pointer into the arguments of the response selected via the name and resultOf properties.
+ //
+ // This is a JSON Pointer [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901), except it also allows
+ // the use of * to map through an array.
+ Path string `json:"path,omitempty"`
+}
+
+type JmapCommand interface {
+ GetCommand() Command
+ GetObjectType() ObjectType
+}
+
+type GetCommand interface {
+ JmapCommand
+ GetResponse() GetResponse
+}
+
+type GetResponse interface {
+ GetState() State
+ GetNotFound() []string
+}
+
+type SetCommand interface {
+ JmapCommand
+ GetResponse() SetResponse
+}
+
+type SetResponse interface {
+ GetNotCreated() map[string]SetError
+ GetNotUpdated() map[string]SetError
+ GetNotDestroyed() map[string]SetError
+ GetOldState() State
+ GetNewState() State
+}
+
+type Change interface {
+ AsPatch() PatchObject
+}
+
+type ChangesCommand interface {
+ JmapCommand
+ GetResponse() ChangesResponse
+}
+
+type ChangesResponse interface {
+ GetOldState() State
+ GetNewState() State
+ GetHasMoreChanges() bool
+ GetCreated() []string
+ GetUpdated() []string
+ GetDestroyed() []string
+}
+
+type QueryCommand interface {
+ JmapCommand
+ GetResponse() QueryResponse
+}
+
+type QueryResponse interface {
+ GetQueryState() State
+}
+
type FilterOperatorTerm string
const (
@@ -1308,6 +1568,8 @@ type MailboxChange struct {
IsSubscribed *bool `json:"isSubscribed,omitempty"`
}
+var _ Change = MailboxChange{}
+
func (m MailboxChange) AsPatch() PatchObject {
p := PatchObject{}
if m.Name != "" {
@@ -1333,11 +1595,23 @@ type MailboxGetCommand struct {
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &MailboxGetCommand{}
+
+func (c MailboxGetCommand) GetCommand() Command { return CommandMailboxGet }
+func (c MailboxGetCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxGetCommand) GetResponse() GetResponse { return MailboxGetResponse{} }
+
type MailboxGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
}
+var _ GetCommand = &MailboxGetRefCommand{}
+
+func (c MailboxGetRefCommand) GetCommand() Command { return CommandMailboxGet }
+func (c MailboxGetRefCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxGetRefCommand) GetResponse() GetResponse { return MailboxGetResponse{} }
+
type MailboxSetCommand struct {
AccountId string `json:"accountId"`
IfInState string `json:"ifInState,omitempty"`
@@ -1346,6 +1620,12 @@ type MailboxSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &MailboxSetCommand{}
+
+func (c MailboxSetCommand) GetCommand() Command { return CommandMailboxSet }
+func (c MailboxSetCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxSetCommand) GetResponse() SetResponse { return MailboxSetResponse{} }
+
type MailboxSetResponse struct {
AccountId string `json:"accountId"`
OldState State `json:"oldState,omitempty"`
@@ -1358,6 +1638,14 @@ type MailboxSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &MailboxSetResponse{}
+
+func (r MailboxSetResponse) GetOldState() State { return r.OldState }
+func (r MailboxSetResponse) GetNewState() State { return r.NewState }
+func (r MailboxSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r MailboxSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r MailboxSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
type MailboxFilterElement interface {
_isAMailboxFilterElement() // marker method
}
@@ -1402,6 +1690,12 @@ type MailboxQueryCommand struct {
FilterAsTree bool `json:"filterAsTree,omitempty"`
}
+var _ QueryCommand = &MailboxQueryCommand{}
+
+func (c MailboxQueryCommand) GetCommand() Command { return CommandMailboxQuery }
+func (c MailboxQueryCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxQueryCommand) GetResponse() QueryResponse { return MailboxQueryResponse{} }
+
type EmailFilterElement interface {
_isAnEmailFilterElement() // marker method
IsNotEmpty() bool
@@ -1692,6 +1986,12 @@ type EmailQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitempty"`
}
+var _ QueryCommand = &EmailQueryCommand{}
+
+func (c EmailQueryCommand) GetCommand() Command { return CommandEmailQuery }
+func (c EmailQueryCommand) GetObjectType() ObjectType { return MailboxType }
+func (c EmailQueryCommand) GetResponse() QueryResponse { return EmailQueryResponse{} }
+
type EmailGetCommand struct {
// The ids of the Email objects to return.
//
@@ -1752,52 +2052,11 @@ type EmailGetCommand struct {
MaxBodyValueBytes uint `json:"maxBodyValueBytes,omitempty"`
}
-// Reference to Previous Method Results
-//
-// To allow clients to make more efficient use of the network and avoid round trips, an argument to one method
-// can be taken from the result of a previous method call in the same request.
-//
-// To do this, the client prefixes the argument name with # (an [octothorpe](https://en.wiktionary.org/wiki/octothorpe)).
-//
-// When processing a method call, the server MUST first check the arguments object for any names beginning with #.
-//
-// If found, the result reference should be resolved and the value used as the “real” argument.
-//
-// The method is then processed as normal.
-//
-// If any result reference fails to resolve, the whole method MUST be rejected with an invalidResultReference error.
-//
-// If an arguments object contains the same argument name in normal and referenced form (e.g., foo and #foo),
-// the method MUST return an invalidArguments error.
-//
-// To resolve:
-//
-// 1. Find the first response with a method call id identical to the resultOf property of the ResultReference
-// in the methodResponses array from previously processed method calls in the same request.
-// If none, evaluation fails.
-// 2. If the response name is not identical to the name property of the ResultReference, evaluation fails.
-// 3. Apply the path to the arguments object of the response (the second item in the response array)
-// following the JSON Pointer algorithm [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901),
-// except with the following addition in “Evaluation” (see Section 4):
-// 4. If the currently referenced value is a JSON array, the reference token may be exactly the single character *,
-// making the new referenced value the result of applying the rest of the JSON Pointer tokens to every item in the
-// array and returning the results in the same order in a new array.
-// 5. If the result of applying the rest of the pointer tokens to each item was itself an array, the contents of this
-// array are added to the output rather than the array itself (i.e., the result is flattened from an array of
-// arrays to a single array).
-type ResultReference struct {
- // The method call id of a previous method call in the current request.
- ResultOf string `json:"resultOf"`
+var _ GetCommand = &EmailGetCommand{}
- // The required name of a response to that method call.
- Name Command `json:"name"`
-
- // A pointer into the arguments of the response selected via the name and resultOf properties.
- //
- // This is a JSON Pointer [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901), except it also allows
- // the use of * to map through an array.
- Path string `json:"path,omitempty"`
-}
+func (c EmailGetCommand) GetCommand() Command { return CommandEmailGet }
+func (c EmailGetCommand) GetObjectType() ObjectType { return EmailType }
+func (c EmailGetCommand) GetResponse() GetResponse { return EmailGetResponse{} }
type EmailGetRefCommand struct {
// The ids of the Email objects to return.
@@ -1855,6 +2114,12 @@ type EmailGetRefCommand struct {
MaxBodyValueBytes uint `json:"maxBodyValueBytes,omitempty"`
}
+var _ GetCommand = &EmailGetRefCommand{}
+
+func (c EmailGetRefCommand) GetCommand() Command { return CommandEmailGet }
+func (c EmailGetRefCommand) GetObjectType() ObjectType { return EmailType }
+func (c EmailGetRefCommand) GetResponse() GetResponse { return EmailGetResponse{} }
+
type EmailChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -1873,6 +2138,12 @@ type EmailChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitempty"`
}
+var _ ChangesCommand = &EmailChangesCommand{}
+
+func (c EmailChangesCommand) GetCommand() Command { return CommandEmailChanges }
+func (c EmailChangesCommand) GetObjectType() ObjectType { return EmailType }
+func (c EmailChangesCommand) GetResponse() ChangesResponse { return EmailChangesResponse{} }
+
type EmailAddress struct {
// The display-name of the mailbox [RFC5322](https://www.rfc-editor.org/rfc/rfc5322.html).
//
@@ -2483,6 +2754,12 @@ type EmailSubmissionGetCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &EmailSubmissionGetCommand{}
+
+func (c EmailSubmissionGetCommand) GetCommand() Command { return CommandEmailSubmissionGet }
+func (c EmailSubmissionGetCommand) GetObjectType() ObjectType { return EmailSubmissionType }
+func (c EmailSubmissionGetCommand) GetResponse() GetResponse { return EmailSubmissionGetResponse{} }
+
type EmailSubmissionGetRefCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -2501,6 +2778,12 @@ type EmailSubmissionGetRefCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &EmailSubmissionGetRefCommand{}
+
+func (c EmailSubmissionGetRefCommand) GetCommand() Command { return CommandEmailSubmissionGet }
+func (c EmailSubmissionGetRefCommand) GetObjectType() ObjectType { return EmailSubmissionType }
+func (c EmailSubmissionGetRefCommand) GetResponse() GetResponse { return EmailSubmissionGetResponse{} }
+
type EmailSubmissionGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2533,6 +2816,11 @@ type EmailSubmissionGetResponse struct {
NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &EmailSubmissionGetResponse{}
+
+func (r EmailSubmissionGetResponse) GetState() State { return r.State }
+func (r EmailSubmissionGetResponse) GetNotFound() []string { return r.NotFound }
+
type EmailSubmissionChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -2556,6 +2844,14 @@ type EmailSubmissionChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &EmailSubmissionChangesCommand{}
+
+func (c EmailSubmissionChangesCommand) GetCommand() Command { return CommandEmailSubmissionChanges }
+func (c EmailSubmissionChangesCommand) GetObjectType() ObjectType { return EmailSubmissionType }
+func (c EmailSubmissionChangesCommand) GetResponse() ChangesResponse {
+ return EmailSubmissionChangesResponse{}
+}
+
type EmailSubmissionChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2581,24 +2877,14 @@ type EmailSubmissionChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
-// Patch Object.
-//
-// Example:
-//
-// - moves it from the drafts folder (which has Mailbox id `"7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e"“)
-// to the sent folder (which we presume has Mailbox id `"73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6"“)
-// - removes the `$draft` flag
-//
-// ```json
-//
-// {
-// "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null,
-// "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true,
-// "keywords/$draft": null
-// }
-//
-// ```
-type PatchObject map[string]any
+var _ ChangesResponse = &EmailSubmissionChangesResponse{}
+
+func (r EmailSubmissionChangesResponse) GetOldState() State { return r.OldState }
+func (r EmailSubmissionChangesResponse) GetNewState() State { return r.NewState }
+func (r EmailSubmissionChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r EmailSubmissionChangesResponse) GetCreated() []string { return r.Created }
+func (r EmailSubmissionChangesResponse) GetUpdated() []string { return r.Updated }
+func (r EmailSubmissionChangesResponse) GetDestroyed() []string { return r.Destroyed }
// same as EmailSubmission but without the server-set attributes
type EmailSubmissionCreate struct {
@@ -2636,6 +2922,12 @@ type EmailSubmissionSetCommand struct {
OnSuccessDestroyEmail []string `json:"onSuccessDestroyEmail,omitempty"`
}
+var _ SetCommand = &EmailSubmissionSetCommand{}
+
+func (c EmailSubmissionSetCommand) GetCommand() Command { return CommandEmailSubmissionSet }
+func (c EmailSubmissionSetCommand) GetObjectType() ObjectType { return EmailSubmissionType }
+func (c EmailSubmissionSetCommand) GetResponse() SetResponse { return EmailSubmissionSetResponse{} }
+
type CreatedEmailSubmission struct {
Id string `json:"id"`
}
@@ -2663,81 +2955,17 @@ type EmailSubmissionSetResponse struct {
// null if all successful.
NotCreated map[string]SetError `json:"notCreated,omitempty"`
- // TODO(pbleser-oc) add updated and destroyed when they are needed
+ NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
+ NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
-type Command string
+var _ SetResponse = &EmailSubmissionSetResponse{}
-type Invocation struct {
- Command Command
- Parameters any
- Tag string
-}
-
-func invocation(command Command, parameters any, tag string) Invocation {
- return Invocation{
- Command: command,
- Parameters: parameters,
- Tag: tag,
- }
-}
-
-type TypeOfRequest string
-
-const RequestType = TypeOfRequest("Request")
-
-type Request struct {
- // The set of capabilities the client wishes to use.
- //
- // The client MAY include capability identifiers even if the method calls it makes do not utilise those capabilities.
- // The server advertises the set of specifications it supports in the Session object
- // (see [Section 2](https://jmap.io/spec-core.html#the-jmap-session-resource)), as keys on the capabilities property.
- Using []JmapNamespace `json:"using"`
-
- // An array of method calls to process on the server.
- //
- // The method calls MUST be processed sequentially, in order.
- MethodCalls []Invocation `json:"methodCalls"`
-
- // A map of a (client-specified) creation id to the id the server assigned when a record was successfully created (optional).
- CreatedIds map[string]string `json:"createdIds,omitempty"`
-
- // This MUST be the string "Request".
- // The specification extends the Response object with two additional arguments when used over a WebSocket.
- Type TypeOfRequest `json:"@type,omitempty"`
-
- // A client-specified identifier for the request to be echoed back in the response to this request (optional).
- Id string `json:"id,omitempty"`
-}
-
-type TypeOfResponse string
-
-const ResponseType = TypeOfResponse("Response")
-
-type Response struct {
- // An array of responses, in the same format as the methodCalls on the Request object.
- // The output of the methods MUST be added to the methodResponses array in the same order that the methods are processed.
- MethodResponses []Invocation `json:"methodResponses"`
-
- // A map of a (client-specified) creation id to the id the server assigned when a record was successfully created.
- //
- // Optional; only returned if given in the request.
- //
- // This MUST include all creation ids passed in the original createdIds parameter of the Request object, as well as any
- // additional ones added for newly created records.
- CreatedIds map[string]string `json:"createdIds,omitempty"`
-
- // The current value of the “state” string on the Session object, as described in [Section 2](https://jmap.io/spec-core.html#the-jmap-session-resource).
- // Clients may use this to detect if this object has changed and needs to be refetched.
- SessionState SessionState `json:"sessionState"`
-
- // This MUST be the string "Response".
- // The specification extends the Response object with two additional arguments when used over a WebSocket.
- Type TypeOfResponse `json:"@type,omitempty"`
-
- // MUST be returned if an identifier is included in the request (optional).
- RequestId string `json:"requestId,omitempty"`
-}
+func (r EmailSubmissionSetResponse) GetOldState() State { return r.OldState }
+func (r EmailSubmissionSetResponse) GetNewState() State { return r.NewState }
+func (r EmailSubmissionSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r EmailSubmissionSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r EmailSubmissionSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
type EmailQueryResponse struct {
// The id of the account used for the call.
@@ -2789,6 +3017,10 @@ type EmailQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
+var _ QueryResponse = &EmailQueryResponse{}
+
+func (r EmailQueryResponse) GetQueryState() State { return r.QueryState }
+
type EmailGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2816,6 +3048,11 @@ type EmailGetResponse struct {
NotFound []string `json:"notFound"`
}
+var _ GetResponse = &EmailGetResponse{}
+
+func (r EmailGetResponse) GetState() State { return r.State }
+func (r EmailGetResponse) GetNotFound() []string { return r.NotFound }
+
type EmailChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2840,6 +3077,15 @@ type EmailChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &EmailChangesResponse{}
+
+func (r EmailChangesResponse) GetOldState() State { return r.OldState }
+func (r EmailChangesResponse) GetNewState() State { return r.NewState }
+func (r EmailChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r EmailChangesResponse) GetCreated() []string { return r.Created }
+func (r EmailChangesResponse) GetUpdated() []string { return r.Updated }
+func (r EmailChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type MailboxGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2864,6 +3110,11 @@ type MailboxGetResponse struct {
NotFound []string `json:"notFound"`
}
+var _ GetResponse = &MailboxGetResponse{}
+
+func (r MailboxGetResponse) GetState() State { return r.State }
+func (r MailboxGetResponse) GetNotFound() []string { return r.NotFound }
+
type MailboxChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -2887,6 +3138,12 @@ type MailboxChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &MailboxChangesCommand{}
+
+func (c MailboxChangesCommand) GetCommand() Command { return CommandMailboxChanges }
+func (c MailboxChangesCommand) GetObjectType() ObjectType { return MailboxType }
+func (c MailboxChangesCommand) GetResponse() ChangesResponse { return MailboxChangesResponse{} }
+
type MailboxChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2918,6 +3175,15 @@ type MailboxChangesResponse struct {
UpdatedProperties []string `json:"updatedProperties,omitempty"`
}
+var _ ChangesResponse = &MailboxChangesResponse{}
+
+func (r MailboxChangesResponse) GetOldState() State { return r.OldState }
+func (r MailboxChangesResponse) GetNewState() State { return r.NewState }
+func (r MailboxChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r MailboxChangesResponse) GetCreated() []string { return r.Created }
+func (r MailboxChangesResponse) GetUpdated() []string { return r.Updated }
+func (r MailboxChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type MailboxQueryResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -2965,6 +3231,10 @@ type MailboxQueryResponse struct {
Limit int `json:"limit,omitzero"`
}
+var _ QueryResponse = &MailboxQueryResponse{}
+
+func (r MailboxQueryResponse) GetQueryState() State { return r.QueryState }
+
type EmailCreate struct {
// The set of Mailbox ids this Email belongs to.
//
@@ -3122,6 +3392,12 @@ type EmailSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &EmailSetCommand{}
+
+func (c EmailSetCommand) GetCommand() Command { return CommandEmailSet }
+func (c EmailSetCommand) GetObjectType() ObjectType { return EmailType }
+func (c EmailSetCommand) GetResponse() SetResponse { return EmailSubmissionSetResponse{} }
+
type EmailSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -3169,6 +3445,14 @@ type EmailSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &EmailSetResponse{}
+
+func (r EmailSetResponse) GetOldState() State { return r.OldState }
+func (r EmailSetResponse) GetNewState() State { return r.NewState }
+func (r EmailSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r EmailSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r EmailSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
const (
EmailMimeType = "message/rfc822"
)
@@ -3264,29 +3548,58 @@ type ThreadGetCommand struct {
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &ThreadGetCommand{}
+
+func (c ThreadGetCommand) GetCommand() Command { return CommandThreadGet }
+func (c ThreadGetCommand) GetObjectType() ObjectType { return ThreadType }
+func (c ThreadGetCommand) GetResponse() GetResponse { return ThreadGetResponse{} }
+
type ThreadGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
}
+var _ GetCommand = &ThreadGetRefCommand{}
+
+func (c ThreadGetRefCommand) GetCommand() Command { return CommandThreadGet }
+func (c ThreadGetRefCommand) GetObjectType() ObjectType { return ThreadType }
+func (c ThreadGetRefCommand) GetResponse() GetResponse { return ThreadGetResponse{} }
+
type ThreadGetResponse struct {
AccountId string
State State
List []Thread
- NotFound []any
+ NotFound []string
}
+var _ GetResponse = &ThreadGetResponse{}
+
+func (r ThreadGetResponse) GetState() State { return r.State }
+func (r ThreadGetResponse) GetNotFound() []string { return r.NotFound }
+
type IdentityGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &IdentityGetCommand{}
+
+func (c IdentityGetCommand) GetCommand() Command { return CommandIdentityGet }
+func (c IdentityGetCommand) GetObjectType() ObjectType { return IdentityType }
+func (c IdentityGetCommand) GetResponse() GetResponse { return IdentityGetResponse{} }
+
type IdentityGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
PropertiesRef *ResultReference `json:"#properties,omitempty"`
}
+var _ GetCommand = &IdentityGetRefCommand{}
+
+func (c IdentityGetRefCommand) GetCommand() Command { return CommandIdentityGet }
+func (c IdentityGetRefCommand) GetObjectType() ObjectType { return IdentityType }
+func (c IdentityGetRefCommand) GetResponse() GetResponse { return IdentityGetResponse{} }
+
type IdentityChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -3310,6 +3623,12 @@ type IdentityChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &IdentityChangesCommand{}
+
+func (c IdentityChangesCommand) GetCommand() Command { return CommandIdentityChanges }
+func (c IdentityChangesCommand) GetObjectType() ObjectType { return IdentityType }
+func (c IdentityChangesCommand) GetResponse() ChangesResponse { return IdentityChangesResponse{} }
+
type IdentityChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -3335,6 +3654,15 @@ type IdentityChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &IdentityChangesResponse{}
+
+func (r IdentityChangesResponse) GetOldState() State { return r.OldState }
+func (r IdentityChangesResponse) GetNewState() State { return r.NewState }
+func (r IdentityChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r IdentityChangesResponse) GetCreated() []string { return r.Created }
+func (r IdentityChangesResponse) GetUpdated() []string { return r.Updated }
+func (r IdentityChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type IdentitySetCommand struct {
AccountId string `json:"accountId"`
IfInState string `json:"ifInState,omitempty"`
@@ -3343,6 +3671,12 @@ type IdentitySetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &IdentitySetCommand{}
+
+func (c IdentitySetCommand) GetCommand() Command { return CommandIdentitySet }
+func (c IdentitySetCommand) GetObjectType() ObjectType { return IdentityType }
+func (c IdentitySetCommand) GetResponse() SetResponse { return IdentitySetResponse{} }
+
type IdentitySetResponse struct {
AccountId string `json:"accountId"`
OldState State `json:"oldState,omitempty"`
@@ -3355,6 +3689,14 @@ type IdentitySetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &IdentitySetResponse{}
+
+func (r IdentitySetResponse) GetOldState() State { return r.OldState }
+func (r IdentitySetResponse) GetNewState() State { return r.NewState }
+func (r IdentitySetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r IdentitySetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r IdentitySetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
// An Identity object stores information about an email address or domain the user may send from.
type Identity struct {
// The id of the Identity.
@@ -3399,6 +3741,8 @@ type Identity struct {
MayDelete bool `json:"mayDelete,omitzero"`
}
+var _ Change = Identity{}
+
func (i Identity) AsPatch() PatchObject {
p := PatchObject{}
if i.Name != "" {
@@ -3429,10 +3773,21 @@ type IdentityGetResponse struct {
NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &IdentityGetResponse{}
+
+func (r IdentityGetResponse) GetState() State { return r.State }
+func (r IdentityGetResponse) GetNotFound() []string { return r.NotFound }
+
type VacationResponseGetCommand struct {
AccountId string `json:"accountId"`
}
+var _ GetCommand = &VacationResponseGetCommand{}
+
+func (c VacationResponseGetCommand) GetCommand() Command { return CommandVacationResponseGet }
+func (c VacationResponseGetCommand) GetObjectType() ObjectType { return VacationResponseType }
+func (c VacationResponseGetCommand) GetResponse() GetResponse { return VacationResponseGetResponse{} }
+
// Vacation Response
//
// A vacation response sends an automatic reply when a message is delivered to the mail store,
@@ -3491,9 +3846,14 @@ type VacationResponseGetResponse struct {
List []VacationResponse `json:"list,omitempty"`
// Contains identifiers of requested objects that were not found.
- NotFound []any `json:"notFound,omitempty"`
+ NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &VacationResponseGetResponse{}
+
+func (r VacationResponseGetResponse) GetState() State { return r.State }
+func (r VacationResponseGetResponse) GetNotFound() []string { return r.NotFound }
+
type VacationResponseSetCommand struct {
AccountId string `json:"accountId"`
IfInState string `json:"ifInState,omitempty"`
@@ -3502,6 +3862,12 @@ type VacationResponseSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &VacationResponseSetCommand{}
+
+func (c VacationResponseSetCommand) GetCommand() Command { return CommandVacationResponseSet }
+func (c VacationResponseSetCommand) GetObjectType() ObjectType { return VacationResponseType }
+func (c VacationResponseSetCommand) GetResponse() SetResponse { return VacationResponseSetResponse{} }
+
type VacationResponseSetResponse struct {
AccountId string `json:"accountId"`
OldState State `json:"oldState,omitempty"`
@@ -3514,6 +3880,14 @@ type VacationResponseSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &VacationResponseSetResponse{}
+
+func (r VacationResponseSetResponse) GetOldState() State { return r.OldState }
+func (r VacationResponseSetResponse) GetNewState() State { return r.NewState }
+func (r VacationResponseSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r VacationResponseSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r VacationResponseSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
// One of these attributes must be set, but not both.
type DataSourceObject struct {
DataAsText string `json:"data:asText,omitempty"`
@@ -3530,6 +3904,11 @@ type BlobUploadCommand struct {
Create map[string]UploadObject `json:"create"`
}
+var _ JmapCommand = &BlobUploadCommand{}
+
+func (c BlobUploadCommand) GetCommand() Command { return CommandBlobUpload }
+func (c BlobUploadCommand) GetObjectType() ObjectType { return BlobType }
+
type BlobUploadCreateResult struct {
Id string `json:"id"`
Type string `json:"type,omitempty"`
@@ -3561,6 +3940,12 @@ type BlobGetCommand struct {
Length int `json:"length,omitzero"`
}
+var _ GetCommand = &BlobGetCommand{}
+
+func (c BlobGetCommand) GetCommand() Command { return CommandBlobGet }
+func (c BlobGetCommand) GetObjectType() ObjectType { return BlobType }
+func (c BlobGetCommand) GetResponse() GetResponse { return BlobGetResponse{} }
+
type BlobGetRefCommand struct {
AccountId string `json:"accountId"`
IdRef *ResultReference `json:"#ids,omitempty"`
@@ -3569,6 +3954,12 @@ type BlobGetRefCommand struct {
Length int `json:"length,omitzero"`
}
+var _ GetCommand = &BlobGetRefCommand{}
+
+func (c BlobGetRefCommand) GetCommand() Command { return CommandBlobGet }
+func (c BlobGetRefCommand) GetObjectType() ObjectType { return BlobType }
+func (c BlobGetRefCommand) GetResponse() GetResponse { return BlobGetResponse{} }
+
type Blob struct {
// The unique identifier of the blob.
Id string `json:"id"`
@@ -3646,9 +4037,14 @@ type BlobGetResponse struct {
//
// The array is empty if all requested ids were found or if the ids argument passed
// in was either null or an empty array.
- NotFound []any `json:"notFound,omitempty"`
+ NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &BlobGetResponse{}
+
+func (r BlobGetResponse) GetState() State { return r.State }
+func (r BlobGetResponse) GetNotFound() []string { return r.NotFound }
+
type BlobDownload struct {
Body io.ReadCloser
Size int
@@ -3710,12 +4106,20 @@ type SearchSnippetGetRefCommand struct {
EmailIdRef *ResultReference `json:"#emailIds,omitempty"`
}
+var _ JmapCommand = &SearchSnippetGetRefCommand{}
+
+func (c SearchSnippetGetRefCommand) GetCommand() Command { return CommandSearchSnippetGet }
+func (c SearchSnippetGetRefCommand) GetObjectType() ObjectType { return SearchSnippetType }
+
type SearchSnippetGetResponse struct {
AccountId string `json:"accountId"`
List []SearchSnippet `json:"list,omitempty"`
NotFound []string `json:"notFound,omitempty"`
}
+// this is not true, as the response to SearchSnippet/get does not have a state
+// var _ GetResponse = &SearchSnippetGetResponse{}
+
type StateChangeType string
const TypeOfStateChange = StateChangeType("StateChange")
@@ -3735,7 +4139,7 @@ type StateChange struct {
// The client can compare the new state strings with its current values to see whether
// it has the current data for these types. If not, the changes can then be efficiently
// fetched in a single standard API request (using the /changes type methods).
- Changed map[string]map[ObjectType]string `json:"changed"`
+ Changed map[string]map[ObjectTypeName]string `json:"changed"`
// A (preferably short) string that encodes the entire server state visible to the user
// (not just the objects returned in this call).
@@ -5209,12 +5613,24 @@ type QuotaGetCommand struct {
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &QuotaGetCommand{}
+
+func (c QuotaGetCommand) GetCommand() Command { return CommandQuotaGet }
+func (c QuotaGetCommand) GetObjectType() ObjectType { return QuotaType }
+func (c QuotaGetCommand) GetResponse() GetResponse { return QuotaGetResponse{} }
+
type QuotaGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
PropertiesRef *ResultReference `json:"#properties,omitempty"`
}
+var _ GetCommand = &QuotaGetRefCommand{}
+
+func (c QuotaGetRefCommand) GetCommand() Command { return CommandQuotaGet }
+func (c QuotaGetRefCommand) GetObjectType() ObjectType { return QuotaType }
+func (c QuotaGetRefCommand) GetResponse() GetResponse { return QuotaGetResponse{} }
+
type QuotaGetResponse struct {
AccountId string `json:"accountId"`
State State `json:"state,omitempty"`
@@ -5222,6 +5638,11 @@ type QuotaGetResponse struct {
NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &QuotaGetResponse{}
+
+func (r QuotaGetResponse) GetState() State { return r.State }
+func (r QuotaGetResponse) GetNotFound() []string { return r.NotFound }
+
type QuotaChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -5244,6 +5665,12 @@ type QuotaChangesCommand struct {
UpdatedProperties []string `json:"updatedProperties,omitempty"`
}
+var _ ChangesCommand = &QuotaChangesCommand{}
+
+func (c QuotaChangesCommand) GetCommand() Command { return CommandQuotaChanges }
+func (c QuotaChangesCommand) GetObjectType() ObjectType { return QuotaType }
+func (c QuotaChangesCommand) GetResponse() ChangesResponse { return QuotaChangesResponse{} }
+
type QuotaChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5268,16 +5695,37 @@ type QuotaChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &QuotaChangesResponse{}
+
+func (r QuotaChangesResponse) GetOldState() State { return r.OldState }
+func (r QuotaChangesResponse) GetNewState() State { return r.NewState }
+func (r QuotaChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r QuotaChangesResponse) GetCreated() []string { return r.Created }
+func (r QuotaChangesResponse) GetUpdated() []string { return r.Updated }
+func (r QuotaChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type AddressBookGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &AddressBookGetCommand{}
+
+func (c AddressBookGetCommand) GetCommand() Command { return CommandAddressBookGet }
+func (c AddressBookGetCommand) GetObjectType() ObjectType { return AddressBookType }
+func (c AddressBookGetCommand) GetResponse() GetResponse { return AddressBookGetResponse{} }
+
type AddressBookGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
}
+var _ GetCommand = &AddressBookGetRefCommand{}
+
+func (c AddressBookGetRefCommand) GetCommand() Command { return CommandAddressBookGet }
+func (c AddressBookGetRefCommand) GetObjectType() ObjectType { return AddressBookType }
+func (c AddressBookGetRefCommand) GetResponse() GetResponse { return AddressBookGetResponse{} }
+
type AddressBookGetResponse struct {
AccountId string `json:"accountId"`
State State `json:"state,omitempty"`
@@ -5285,6 +5733,11 @@ type AddressBookGetResponse struct {
NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &AddressBookGetResponse{}
+
+func (r AddressBookGetResponse) GetState() State { return r.State }
+func (r AddressBookGetResponse) GetNotFound() []string { return r.NotFound }
+
type AddressBookChange struct {
// The user-visible name of the AddressBook.
//
@@ -5329,6 +5782,8 @@ type AddressBookChange struct {
ShareWith map[string]AddressBookRights `json:"shareWith,omitempty"`
}
+var _ Change = AddressBookChange{}
+
func (a AddressBookChange) AsPatch() PatchObject {
p := PatchObject{}
if a.Name != nil {
@@ -5354,6 +5809,12 @@ type AddressBookSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &AddressBookSetCommand{}
+
+func (c AddressBookSetCommand) GetCommand() Command { return CommandAddressBookSet }
+func (c AddressBookSetCommand) GetObjectType() ObjectType { return AddressBookType }
+func (c AddressBookSetCommand) GetResponse() SetResponse { return AddressBookSetResponse{} }
+
type AddressBookSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5401,6 +5862,14 @@ type AddressBookSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &AddressBookSetResponse{}
+
+func (r AddressBookSetResponse) GetOldState() State { return r.OldState }
+func (r AddressBookSetResponse) GetNewState() State { return r.NewState }
+func (r AddressBookSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r AddressBookSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r AddressBookSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
type AddressBookChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -5424,6 +5893,12 @@ type AddressBookChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &AddressBookChangesCommand{}
+
+func (c AddressBookChangesCommand) GetCommand() Command { return CommandAddressBookChanges }
+func (c AddressBookChangesCommand) GetObjectType() ObjectType { return AddressBookType }
+func (c AddressBookChangesCommand) GetResponse() ChangesResponse { return AddressBookChangesResponse{} }
+
type AddressBookChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5449,6 +5924,15 @@ type AddressBookChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &AddressBookChangesResponse{}
+
+func (r AddressBookChangesResponse) GetOldState() State { return r.OldState }
+func (r AddressBookChangesResponse) GetNewState() State { return r.NewState }
+func (r AddressBookChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r AddressBookChangesResponse) GetCreated() []string { return r.Created }
+func (r AddressBookChangesResponse) GetUpdated() []string { return r.Updated }
+func (r AddressBookChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type ContactCardComparator struct {
// The name of the property on the objects to compare.
Property string `json:"property,omitempty"`
@@ -5688,6 +6172,12 @@ type ContactCardQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitzero"`
}
+var _ QueryCommand = &ContactCardQueryCommand{}
+
+func (c ContactCardQueryCommand) GetCommand() Command { return CommandContactCardQuery }
+func (c ContactCardQueryCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardQueryCommand) GetResponse() QueryResponse { return ContactCardQueryResponse{} }
+
type ContactCardQueryResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5738,6 +6228,10 @@ type ContactCardQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
+var _ QueryResponse = &ContactCardQueryResponse{}
+
+func (r ContactCardQueryResponse) GetQueryState() State { return r.QueryState }
+
type ContactCardGetCommand struct {
// The ids of the ContactCard objects to return.
//
@@ -5756,6 +6250,12 @@ type ContactCardGetCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &ContactCardGetCommand{}
+
+func (c ContactCardGetCommand) GetCommand() Command { return CommandContactCardGet }
+func (c ContactCardGetCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardGetCommand) GetResponse() GetResponse { return ContactCardGetResponse{} }
+
type ContactCardGetRefCommand struct {
// The ids of the ContactCard objects to return.
//
@@ -5774,6 +6274,12 @@ type ContactCardGetRefCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &ContactCardGetRefCommand{}
+
+func (c ContactCardGetRefCommand) GetCommand() Command { return CommandContactCardGet }
+func (c ContactCardGetRefCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardGetRefCommand) GetResponse() GetResponse { return ContactCardGetResponse{} }
+
type ContactCardGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5801,6 +6307,11 @@ type ContactCardGetResponse struct {
NotFound []string `json:"notFound"`
}
+var _ GetResponse = &ContactCardGetResponse{}
+
+func (r ContactCardGetResponse) GetState() State { return r.State }
+func (r ContactCardGetResponse) GetNotFound() []string { return r.NotFound }
+
type ContactCardChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -5818,6 +6329,12 @@ type ContactCardChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitempty"`
}
+var _ ChangesCommand = &ContactCardChangesCommand{}
+
+func (c ContactCardChangesCommand) GetCommand() Command { return CommandContactCardChanges }
+func (c ContactCardChangesCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardChangesCommand) GetResponse() ChangesResponse { return ContactCardChangesResponse{} }
+
type ContactCardChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5842,6 +6359,15 @@ type ContactCardChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &ContactCardChangesResponse{}
+
+func (r ContactCardChangesResponse) GetOldState() State { return r.OldState }
+func (r ContactCardChangesResponse) GetNewState() State { return r.NewState }
+func (r ContactCardChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r ContactCardChangesResponse) GetCreated() []string { return r.Created }
+func (r ContactCardChangesResponse) GetUpdated() []string { return r.Updated }
+func (r ContactCardChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type ContactCardUpdate map[string]any
type ContactCardSetCommand struct {
@@ -5897,6 +6423,12 @@ type ContactCardSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &ContactCardSetCommand{}
+
+func (c ContactCardSetCommand) GetCommand() Command { return CommandContactCardSet }
+func (c ContactCardSetCommand) GetObjectType() ObjectType { return ContactCardType }
+func (c ContactCardSetCommand) GetResponse() SetResponse { return ContactCardSetResponse{} }
+
type ContactCardSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5944,6 +6476,14 @@ type ContactCardSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &ContactCardSetResponse{}
+
+func (r ContactCardSetResponse) GetOldState() State { return r.OldState }
+func (r ContactCardSetResponse) GetNewState() State { return r.NewState }
+func (r ContactCardSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r ContactCardSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r ContactCardSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
type CalendarEventParseCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -5957,6 +6497,11 @@ type CalendarEventParseCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ JmapCommand = &CalendarEventParseCommand{}
+
+func (c CalendarEventParseCommand) GetCommand() Command { return CommandCalendarEventParse }
+func (c CalendarEventParseCommand) GetObjectType() ObjectType { return CalendarEventType }
+
type CalendarEventParseResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -5978,11 +6523,23 @@ type CalendarGetCommand struct {
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &CalendarGetCommand{}
+
+func (c CalendarGetCommand) GetCommand() Command { return CommandCalendarGet }
+func (c CalendarGetCommand) GetObjectType() ObjectType { return CalendarType }
+func (c CalendarGetCommand) GetResponse() GetResponse { return CalendarGetResponse{} }
+
type CalendarGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
}
+var _ GetCommand = &CalendarGetRefCommand{}
+
+func (c CalendarGetRefCommand) GetCommand() Command { return CommandCalendarGet }
+func (c CalendarGetRefCommand) GetObjectType() ObjectType { return CalendarType }
+func (c CalendarGetRefCommand) GetResponse() GetResponse { return CalendarGetResponse{} }
+
type CalendarGetResponse struct {
AccountId string `json:"accountId"`
State State `json:"state,omitempty"`
@@ -5990,6 +6547,11 @@ type CalendarGetResponse struct {
NotFound []string `json:"notFound,omitempty"`
}
+var _ GetResponse = &CalendarGetResponse{}
+
+func (r CalendarGetResponse) GetState() State { return r.State }
+func (r CalendarGetResponse) GetNotFound() []string { return r.NotFound }
+
type CalendarSetCommand struct {
AccountId string `json:"accountId"`
IfInState string `json:"ifInState,omitempty"`
@@ -5998,6 +6560,12 @@ type CalendarSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &CalendarSetCommand{}
+
+func (c CalendarSetCommand) GetCommand() Command { return CommandCalendarSet }
+func (c CalendarSetCommand) GetObjectType() ObjectType { return CalendarType }
+func (c CalendarSetCommand) GetResponse() SetResponse { return CalendarSetResponse{} }
+
type CalendarSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6045,6 +6613,14 @@ type CalendarSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &CalendarSetResponse{}
+
+func (r CalendarSetResponse) GetOldState() State { return r.OldState }
+func (r CalendarSetResponse) GetNewState() State { return r.NewState }
+func (r CalendarSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r CalendarSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r CalendarSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
type CalendarChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -6068,6 +6644,12 @@ type CalendarChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &CalendarChangesCommand{}
+
+func (c CalendarChangesCommand) GetCommand() Command { return CommandCalendarChanges }
+func (c CalendarChangesCommand) GetObjectType() ObjectType { return CalendarType }
+func (c CalendarChangesCommand) GetResponse() ChangesResponse { return CalendarChangesResponse{} }
+
type CalendarChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6092,6 +6674,15 @@ type CalendarChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &CalendarChangesResponse{}
+
+func (r CalendarChangesResponse) GetOldState() State { return r.OldState }
+func (r CalendarChangesResponse) GetNewState() State { return r.NewState }
+func (r CalendarChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r CalendarChangesResponse) GetCreated() []string { return r.Created }
+func (r CalendarChangesResponse) GetUpdated() []string { return r.Updated }
+func (r CalendarChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type CalendarEventComparator struct {
// The name of the property on the objects to compare.
Property string `json:"property,omitempty"`
@@ -6283,6 +6874,12 @@ type CalendarEventQueryCommand struct {
CalculateTotal bool `json:"calculateTotal,omitempty" doc:"opt" default:"false"`
}
+var _ QueryCommand = &CalendarEventQueryCommand{}
+
+func (c CalendarEventQueryCommand) GetCommand() Command { return CommandCalendarEventQuery }
+func (c CalendarEventQueryCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventQueryCommand) GetResponse() QueryResponse { return CalendarEventQueryResponse{} }
+
type CalendarEventQueryResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6333,6 +6930,10 @@ type CalendarEventQueryResponse struct {
Limit uint `json:"limit,omitempty,omitzero"`
}
+var _ QueryResponse = &CalendarEventQueryResponse{}
+
+func (r CalendarEventQueryResponse) GetQueryState() State { return r.QueryState }
+
type CalendarEventGetCommand struct {
// The ids of the CalendarEvent objects to return.
//
@@ -6351,6 +6952,12 @@ type CalendarEventGetCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &CalendarEventGetCommand{}
+
+func (c CalendarEventGetCommand) GetCommand() Command { return CommandCalendarEventGet }
+func (c CalendarEventGetCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventGetCommand) GetResponse() GetResponse { return CalendarEventGetResponse{} }
+
type CalendarEventGetRefCommand struct {
// The ids of the CalendarEvent objects to return.
//
@@ -6369,6 +6976,12 @@ type CalendarEventGetRefCommand struct {
Properties []string `json:"properties,omitempty"`
}
+var _ GetCommand = &CalendarEventGetRefCommand{}
+
+func (c CalendarEventGetRefCommand) GetCommand() Command { return CommandCalendarEventGet }
+func (c CalendarEventGetRefCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventGetRefCommand) GetResponse() GetResponse { return CalendarEventGetResponse{} }
+
type CalendarEventGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6396,6 +7009,11 @@ type CalendarEventGetResponse struct {
NotFound []string `json:"notFound"`
}
+var _ GetResponse = &CalendarEventGetResponse{}
+
+func (r CalendarEventGetResponse) GetState() State { return r.State }
+func (r CalendarEventGetResponse) GetNotFound() []string { return r.NotFound }
+
type CalendarEventChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
@@ -6419,6 +7037,12 @@ type CalendarEventChangesCommand struct {
MaxChanges *uint `json:"maxChanges,omitzero"`
}
+var _ ChangesCommand = &CalendarEventChangesCommand{}
+
+func (c CalendarEventChangesCommand) GetCommand() Command { return CommandCalendarEventChanges }
+func (c CalendarEventChangesCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventChangesCommand) GetResponse() ChangesResponse { return CalendarChangesResponse{} }
+
type CalendarEventChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6443,6 +7067,15 @@ type CalendarEventChangesResponse struct {
Destroyed []string `json:"destroyed,omitempty"`
}
+var _ ChangesResponse = &CalendarEventChangesResponse{}
+
+func (r CalendarEventChangesResponse) GetOldState() State { return r.OldState }
+func (r CalendarEventChangesResponse) GetNewState() State { return r.NewState }
+func (r CalendarEventChangesResponse) GetHasMoreChanges() bool { return r.HasMoreChanges }
+func (r CalendarEventChangesResponse) GetCreated() []string { return r.Created }
+func (r CalendarEventChangesResponse) GetUpdated() []string { return r.Updated }
+func (r CalendarEventChangesResponse) GetDestroyed() []string { return r.Destroyed }
+
type CalendarEventUpdate map[string]any
type CalendarEventSetCommand struct {
@@ -6498,6 +7131,12 @@ type CalendarEventSetCommand struct {
Destroy []string `json:"destroy,omitempty"`
}
+var _ SetCommand = &CalendarEventSetCommand{}
+
+func (c CalendarEventSetCommand) GetCommand() Command { return CommandCalendarEventSet }
+func (c CalendarEventSetCommand) GetObjectType() ObjectType { return CalendarEventType }
+func (c CalendarEventSetCommand) GetResponse() SetResponse { return CalendarSetResponse{} }
+
type CalendarEventSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6545,16 +7184,36 @@ type CalendarEventSetResponse struct {
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
+var _ SetResponse = &CalendarEventSetResponse{}
+
+func (r CalendarEventSetResponse) GetOldState() State { return r.OldState }
+func (r CalendarEventSetResponse) GetNewState() State { return r.NewState }
+func (r CalendarEventSetResponse) GetNotCreated() map[string]SetError { return r.NotCreated }
+func (r CalendarEventSetResponse) GetNotUpdated() map[string]SetError { return r.NotUpdated }
+func (r CalendarEventSetResponse) GetNotDestroyed() map[string]SetError { return r.NotDestroyed }
+
type PrincipalGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
+var _ GetCommand = &PrincipalGetCommand{}
+
+func (c PrincipalGetCommand) GetCommand() Command { return CommandPrincipalGet }
+func (c PrincipalGetCommand) GetObjectType() ObjectType { return PrincipalType }
+func (c PrincipalGetCommand) GetResponse() GetResponse { return PrincipalGetResponse{} }
+
type PrincipalGetRefCommand struct {
AccountId string `json:"accountId"`
IdsRef *ResultReference `json:"#ids,omitempty"`
}
+var _ GetCommand = &PrincipalGetRefCommand{}
+
+func (c PrincipalGetRefCommand) GetCommand() Command { return CommandPrincipalGet }
+func (c PrincipalGetRefCommand) GetObjectType() ObjectType { return PrincipalType }
+func (c PrincipalGetRefCommand) GetResponse() GetResponse { return PrincipalGetResponse{} }
+
type PrincipalGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
@@ -6579,6 +7238,11 @@ type PrincipalGetResponse struct {
NotFound []string `json:"notFound"`
}
+var _ GetResponse = &PrincipalGetResponse{}
+
+func (r PrincipalGetResponse) GetState() State { return r.State }
+func (r PrincipalGetResponse) GetNotFound() []string { return r.NotFound }
+
type PrincipalFilterElement interface {
_isAPrincipalFilterElement() // marker method
}
@@ -6678,6 +7342,10 @@ type PrincipalQueryResponse struct {
Limit int `json:"limit,omitzero"`
}
+var _ QueryResponse = &PrincipalQueryResponse{}
+
+func (r PrincipalQueryResponse) GetQueryState() State { return r.QueryState }
+
type ErrorResponse struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
diff --git a/pkg/jmap/model_test.go b/pkg/jmap/model_test.go
new file mode 100644
index 0000000000..e44b1f9234
--- /dev/null
+++ b/pkg/jmap/model_test.go
@@ -0,0 +1,19 @@
+package jmap
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestObjectNames(t *testing.T) { //NOSONAR
+ require := require.New(t)
+ objectTypeNames, err := parseConsts("github.com/opencloud-eu/opencloud/pkg/jmap", "Name", "ObjectTypeName")
+ require.NoError(err)
+ for n, v := range objectTypeNames {
+ require.True(strings.HasSuffix(n, "Name"))
+ prefix := n[0 : len(n)-len("Name")]
+ require.Equal(prefix, v)
+ }
+}
diff --git a/pkg/jmap/templates.go b/pkg/jmap/templates.go
index f2fb8021b6..117fee7ac9 100644
--- a/pkg/jmap/templates.go
+++ b/pkg/jmap/templates.go
@@ -9,18 +9,19 @@ import (
"github.com/rs/zerolog"
)
-func getTemplate[GETREQ any, GETRESP any, RESP any]( //NOSONAR
+func get[GETREQ GetCommand, GETRESP GetResponse, RESP any]( //NOSONAR
client *Client, name string, using []JmapNamespace,
- getCommand Command, getCommandFactory func(string, []string) GETREQ,
+ getCommandFactory func(string, []string) GETREQ,
+ _ GETRESP,
mapper func(GETRESP) RESP,
- stateMapper func(GETRESP) State,
accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error) {
logger = client.logger(name, session, logger)
var zero RESP
+ get := getCommandFactory(accountId, ids)
cmd, err := client.request(session, logger, using,
- invocation(getCommand, getCommandFactory(accountId, ids), "0"),
+ invocation(get, "0"),
)
if err != nil {
return zero, "", "", "", err
@@ -28,21 +29,21 @@ func getTemplate[GETREQ any, GETRESP any, RESP any]( //NOSONAR
return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
var response GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, "0", &response)
+ err = retrieveGet(logger, body, get, "0", &response)
if err != nil {
return zero, "", err
}
- return mapper(response), stateMapper(response), nil
+ return mapper(response), response.GetState(), nil
})
}
-func getTemplateN[GETREQ any, GETRESP any, ITEM any, RESP any]( //NOSONAR
+func getN[GETREQ GetCommand, GETRESP GetResponse, ITEM any, RESP any]( //NOSONAR
client *Client, name string, using []JmapNamespace,
- getCommand Command, getCommandFactory func(string, []string) GETREQ,
+ getCommandFactory func(string, []string) GETREQ,
+ _ GETRESP,
itemMapper func(GETRESP) ITEM,
respMapper func(map[string]ITEM) RESP,
- stateMapper func(GETRESP) State,
accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, ids []string) (RESP, SessionState, State, Language, Error) {
logger = client.logger(name, session, logger)
@@ -51,8 +52,11 @@ func getTemplateN[GETREQ any, GETRESP any, ITEM any, RESP any]( //NOSONAR
uniqueAccountIds := structs.Uniq(accountIds)
invocations := make([]Invocation, len(uniqueAccountIds))
+ var c Command
for i, accountId := range uniqueAccountIds {
- invocations[i] = invocation(getCommand, getCommandFactory(accountId, ids), mcid(accountId, "0"))
+ get := getCommandFactory(accountId, ids)
+ c = get.GetCommand()
+ invocations[i] = invocation(get, mcid(accountId, "0"))
}
cmd, err := client.request(session, logger, using, invocations...)
@@ -65,33 +69,32 @@ func getTemplateN[GETREQ any, GETRESP any, ITEM any, RESP any]( //NOSONAR
responses := map[string]GETRESP{}
for _, accountId := range uniqueAccountIds {
var resp GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, mcid(accountId, "0"), &resp)
+ err = retrieveResponseMatchParameters(logger, body, c, mcid(accountId, "0"), &resp)
if err != nil {
return zero, "", err
}
responses[accountId] = resp
result[accountId] = itemMapper(resp)
}
- return respMapper(result), squashStateFunc(responses, stateMapper), nil
+ return respMapper(result), squashStateFunc(responses, func(r GETRESP) State { return r.GetState() }), nil
})
}
-func createTemplate[T any, C any, SETREQ any, GETREQ any, SETRESP any, GETRESP any]( //NOSONAR
- client *Client, name string, using []JmapNamespace, t ObjectType,
- setCommand Command, getCommand Command,
+func create[T any, C any, SETREQ SetCommand, GETREQ GetCommand, SETRESP SetResponse, GETRESP GetResponse]( //NOSONAR
+ client *Client, name string, using []JmapNamespace,
setCommandFactory func(string, map[string]C) SETREQ,
getCommandFactory func(string, string) GETREQ,
createdMapper func(SETRESP) map[string]*T,
- notCreatedMapper func(SETRESP) map[string]SetError,
listMapper func(GETRESP) []T,
- stateMapper func(SETRESP) State,
accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, create C) (*T, SessionState, State, Language, Error) {
logger = client.logger(name, session, logger)
createMap := map[string]C{"c": create}
+ get := getCommandFactory(accountId, "#c")
+ set := setCommandFactory(accountId, createMap)
cmd, err := client.request(session, logger, using,
- invocation(setCommand, setCommandFactory(accountId, createMap), "0"),
- invocation(getCommand, getCommandFactory(accountId, "#c"), "1"),
+ invocation(set, "0"),
+ invocation(get, "1"),
)
if err != nil {
return nil, "", "", "", err
@@ -99,27 +102,27 @@ func createTemplate[T any, C any, SETREQ any, GETREQ any, SETRESP any, GETRESP a
return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (*T, State, Error) {
var setResponse SETRESP
- err = retrieveResponseMatchParameters(logger, body, setCommand, "0", &setResponse)
+ err = retrieveSet(logger, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
- notCreatedMap := notCreatedMapper(setResponse)
+ notCreatedMap := setResponse.GetNotCreated()
setErr, notok := notCreatedMap["c"]
if notok {
logger.Error().Msgf("%T.NotCreated returned an error %v", setResponse, setErr)
- return nil, "", setErrorError(setErr, t)
+ return nil, "", setErrorError(setErr, set.GetObjectType())
}
createdMap := createdMapper(setResponse)
if created, ok := createdMap["c"]; !ok || created == nil {
- berr := fmt.Errorf("failed to find %s in %s response", string(t), string(setCommand))
+ berr := fmt.Errorf("failed to find %s in %s response", set.GetObjectType(), set.GetCommand())
logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
var getResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, "1", &getResponse)
+ err = retrieveGet(logger, body, get, "1", &getResponse)
if err != nil {
return nil, "", err
}
@@ -127,24 +130,23 @@ func createTemplate[T any, C any, SETREQ any, GETREQ any, SETRESP any, GETRESP a
list := listMapper(getResponse)
if len(list) < 1 {
- berr := fmt.Errorf("failed to find %s in %s response", string(t), string(getCommand))
+ berr := fmt.Errorf("failed to find %s in %s response", get.GetObjectType(), get.GetCommand())
logger.Error().Err(berr)
return nil, "", jmapError(berr, JmapErrorInvalidJmapResponsePayload)
}
- return &list[0], stateMapper(setResponse), nil
+ return &list[0], setResponse.GetNewState(), nil
})
}
-func deleteTemplate[REQ any, RESP any](client *Client, name string, using []JmapNamespace, //NOSONAR
- c Command, commandFactory func(string, []string) REQ,
- notDestroyedMapper func(RESP) map[string]SetError,
- stateMapper func(RESP) State,
+func destroy[REQ SetCommand, RESP SetResponse](client *Client, name string, using []JmapNamespace, //NOSONAR
+ setCommandFactory func(string, []string) REQ, _ RESP,
accountId string, destroy []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (map[string]SetError, SessionState, State, Language, Error) {
logger = client.logger(name, session, logger)
+ set := setCommandFactory(accountId, destroy)
cmd, err := client.request(session, logger, using,
- invocation(c, commandFactory(accountId, destroy), "0"),
+ invocation(set, "0"),
)
if err != nil {
return nil, "", "", "", err
@@ -152,23 +154,21 @@ func deleteTemplate[REQ any, RESP any](client *Client, name string, using []Jmap
return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (map[string]SetError, State, Error) {
var setResponse RESP
- err = retrieveResponseMatchParameters(logger, body, c, "0", &setResponse)
+ err = retrieveSet(logger, body, set, "0", &setResponse)
if err != nil {
return nil, "", err
}
- return notDestroyedMapper(setResponse), stateMapper(setResponse), nil
+ return setResponse.GetNotDestroyed(), setResponse.GetNewState(), nil
})
}
-func changesTemplate[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any, ITEM any, RESP any]( //NOSONAR
+func changes[CHANGESREQ ChangesCommand, GETREQ GetCommand, CHANGESRESP ChangesResponse, GETRESP GetResponse, ITEM any, RESP any]( //NOSONAR
client *Client, name string, using []JmapNamespace,
- changesCommand Command, getCommand Command,
changesCommandFactory func() CHANGESREQ,
+ _ CHANGESRESP,
getCommandFactory func(string, string) GETREQ,
- changesMapper func(CHANGESRESP) (State, State, bool, []string),
getMapper func(GETRESP) []ITEM,
respMapper func(State, State, bool, []ITEM, []ITEM, []string) RESP,
- stateMapper func(GETRESP) State,
session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
logger = client.logger(name, session, logger)
var zero RESP
@@ -176,10 +176,11 @@ func changesTemplate[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any, I
changes := changesCommandFactory()
getCreated := getCommandFactory("/created", "0") //NOSONAR
getUpdated := getCommandFactory("/updated", "0") //NOSONAR
+
cmd, err := client.request(session, logger, using,
- invocation(changesCommand, changes, "0"),
- invocation(getCommand, getCreated, "1"),
- invocation(getCommand, getUpdated, "2"),
+ invocation(changes, "0"),
+ invocation(getCreated, "1"),
+ invocation(getUpdated, "2"),
)
if err != nil {
return zero, "", "", "", err
@@ -187,88 +188,40 @@ func changesTemplate[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any, I
return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
var changesResponse CHANGESRESP
- err = retrieveResponseMatchParameters(logger, body, changesCommand, "0", &changesResponse)
+ err = retrieveChanges(logger, body, changes, "0", &changesResponse)
if err != nil {
return zero, "", err
}
var createdResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, "1", &createdResponse)
+ err = retrieveGet(logger, body, getCreated, "1", &createdResponse)
if err != nil {
logger.Error().Err(err).Send()
return zero, "", err
}
var updatedResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, "2", &updatedResponse)
+ err = retrieveGet(logger, body, getUpdated, "2", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return zero, "", err
}
- oldState, newState, hasMoreChanges, destroyed := changesMapper(changesResponse)
created := getMapper(createdResponse)
updated := getMapper(updatedResponse)
- result := respMapper(oldState, newState, hasMoreChanges, created, updated, destroyed)
+ result := respMapper(changesResponse.GetOldState(), changesResponse.GetNewState(), changesResponse.GetHasMoreChanges(), created, updated, changesResponse.GetDestroyed())
- return result, stateMapper(createdResponse), nil
+ return result, changesResponse.GetNewState(), nil
})
}
-func updatedTemplate[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any, ITEM any, RESP any]( //NOSONAR
- client *Client, name string, using []JmapNamespace,
- changesCommand Command, getCommand Command,
- changesCommandFactory func() CHANGESREQ,
- getCommandFactory func(string, string) GETREQ,
- changesMapper func(CHANGESRESP) (State, State, bool),
- getMapper func(GETRESP) []ITEM,
- respMapper func(State, State, bool, []ITEM) RESP,
- stateMapper func(GETRESP) State,
- session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
- logger = client.logger(name, session, logger)
- var zero RESP
-
- changes := changesCommandFactory()
- getUpdated := getCommandFactory("/updated", "0") //NOSONAR
- cmd, err := client.request(session, logger, using,
- invocation(changesCommand, changes, "0"),
- invocation(getCommand, getUpdated, "1"),
- )
- if err != nil {
- return zero, "", "", "", err
- }
-
- return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
- var changesResponse CHANGESRESP
- err = retrieveResponseMatchParameters(logger, body, changesCommand, "0", &changesResponse)
- if err != nil {
- return zero, "", err
- }
-
- var updatedResponse GETRESP
- err = retrieveResponseMatchParameters(logger, body, getCommand, "1", &updatedResponse)
- if err != nil {
- logger.Error().Err(err).Send()
- return zero, "", err
- }
-
- oldState, newState, hasMoreChanges := changesMapper(changesResponse)
- updated := getMapper(updatedResponse)
-
- result := respMapper(oldState, newState, hasMoreChanges, updated)
-
- return result, stateMapper(updatedResponse), nil
- })
-}
-
-func changesTemplateN[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any, ITEM any, CHANGESITEM any, RESP any]( //NOSONAR
+func changesN[CHANGESREQ ChangesCommand, GETREQ GetCommand, CHANGESRESP ChangesResponse, GETRESP GetResponse, ITEM any, CHANGESITEM any, RESP any]( //NOSONAR
client *Client, name string, using []JmapNamespace,
accountIds []string, sinceStateMap map[string]State,
- changesCommand Command, getCommand Command,
changesCommandFactory func(string, State) CHANGESREQ,
+ _ CHANGESRESP,
getCommandFactory func(string, string, string) GETREQ,
- changesMapper func(CHANGESRESP) (State, State, bool, []string),
getMapper func(GETRESP) []ITEM,
changesItemMapper func(State, State, bool, []ITEM, []ITEM, []string) CHANGESITEM,
respMapper func(map[string]CHANGESITEM) RESP,
@@ -291,6 +244,8 @@ func changesTemplateN[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any,
}
invocations := make([]Invocation, n*3)
+ getCommand := Command("")
+ changesCommand := Command("")
for i, accountId := range uniqueAccountIds {
sinceState, ok := sinceStateMap[accountId]
if !ok {
@@ -302,9 +257,12 @@ func changesTemplateN[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any,
getCreated := getCommandFactory(accountId, "/created", ref)
getUpdated := getCommandFactory(accountId, "/updated", ref)
- invocations[i*3+0] = invocation(changesCommand, changes, ref)
- invocations[i*3+1] = invocation(getCommand, getCreated, mcid(accountId, "1"))
- invocations[i*3+2] = invocation(getCommand, getUpdated, mcid(accountId, "2"))
+ invocations[i*3+0] = invocation(changes, ref)
+ invocations[i*3+1] = invocation(getCreated, mcid(accountId, "1"))
+ invocations[i*3+2] = invocation(getUpdated, mcid(accountId, "2"))
+
+ changesCommand = changes.GetCommand()
+ getCommand = getCreated.GetCommand()
}
cmd, err := client.request(session, logger, using, invocations...)
@@ -334,12 +292,90 @@ func changesTemplateN[CHANGESREQ any, GETREQ any, CHANGESRESP any, GETRESP any,
return zero, "", err
}
- oldState, newState, hasMoreChanges, destroyed := changesMapper(changesResponse)
created := getMapper(createdResponse)
updated := getMapper(updatedResponse)
- changesItemByAccount[accountId] = changesItemMapper(oldState, newState, hasMoreChanges, created, updated, destroyed)
+ changesItemByAccount[accountId] = changesItemMapper(changesResponse.GetOldState(), changesResponse.GetNewState(), changesResponse.GetHasMoreChanges(), created, updated, changesResponse.GetDestroyed())
stateByAccountId[accountId] = stateMapper(createdResponse)
}
return respMapper(changesItemByAccount), squashState(stateByAccountId), nil
})
}
+
+func updates[CHANGESREQ ChangesCommand, GETREQ GetCommand, CHANGESRESP ChangesResponse, GETRESP GetResponse, ITEM any, RESP any]( //NOSONAR
+ client *Client, name string, using []JmapNamespace,
+ changesCommandFactory func() CHANGESREQ,
+ _ CHANGESRESP,
+ getCommandFactory func(string, string) GETREQ,
+ getMapper func(GETRESP) []ITEM,
+ respMapper func(State, State, bool, []ITEM) RESP,
+ session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
+ logger = client.logger(name, session, logger)
+ var zero RESP
+
+ changes := changesCommandFactory()
+ getUpdated := getCommandFactory("/updated", "0") //NOSONAR
+ cmd, err := client.request(session, logger, using,
+ invocation(changes, "0"),
+ invocation(getUpdated, "1"),
+ )
+ if err != nil {
+ return zero, "", "", "", err
+ }
+
+ return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ var changesResponse CHANGESRESP
+ err = retrieveChanges(logger, body, changes, "0", &changesResponse)
+ if err != nil {
+ return zero, "", err
+ }
+
+ var updatedResponse GETRESP
+ err = retrieveGet(logger, body, getUpdated, "1", &updatedResponse)
+ if err != nil {
+ logger.Error().Err(err).Send()
+ return zero, "", err
+ }
+
+ updated := getMapper(updatedResponse)
+ result := respMapper(changesResponse.GetOldState(), changesResponse.GetNewState(), changesResponse.GetHasMoreChanges(), updated)
+
+ return result, changesResponse.GetNewState(), nil
+ })
+}
+
+func update[CHANGES Change, SET SetCommand, GET GetCommand, RESP any, SETRESP SetResponse, GETRESP GetResponse](client *Client, name string, using []JmapNamespace, //NOSONAR
+ setCommandFactory func(map[string]PatchObject) SET,
+ getCommandFactory func(string) GET,
+ notUpdatedExtractor func(SETRESP) map[string]SetError,
+ objExtractor func(GETRESP) RESP,
+ id string, changes CHANGES,
+ session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (RESP, SessionState, State, Language, Error) {
+ logger = client.logger(name, session, logger)
+ update := setCommandFactory(map[string]PatchObject{id: changes.AsPatch()})
+ get := getCommandFactory(id)
+ cmd, err := client.request(session, logger, using, invocation(update, "0"), invocation(get, "1"))
+ var zero RESP
+ if err != nil {
+ return zero, "", "", "", err
+ }
+
+ return command(client.api, logger, ctx, session, client.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (RESP, State, Error) {
+ var setResponse SETRESP
+ err = retrieveSet(logger, body, update, "0", &setResponse)
+ if err != nil {
+ return zero, setResponse.GetNewState(), err
+ }
+ nc := notUpdatedExtractor(setResponse)
+ setErr, notok := nc[id]
+ if notok {
+ logger.Error().Msgf("%T.NotUpdated returned an error %v", setResponse, setErr)
+ return zero, "", setErrorError(setErr, update.GetObjectType())
+ }
+ var getResponse GETRESP
+ err = retrieveGet(logger, body, get, "1", &getResponse)
+ if err != nil {
+ return zero, setResponse.GetNewState(), err
+ }
+ return objExtractor(getResponse), setResponse.GetNewState(), nil
+ })
+}
diff --git a/pkg/jmap/tools.go b/pkg/jmap/tools.go
index 284f789221..678ff9ab96 100644
--- a/pkg/jmap/tools.go
+++ b/pkg/jmap/tools.go
@@ -202,14 +202,14 @@ func retrieveResponseMatch(data *Response, command Command, tag string) (Invocat
func retrieveResponseMatchParameters[T any](logger *log.Logger, data *Response, command Command, tag string, target *T) Error {
match, ok := retrieveResponseMatch(data, command, tag)
if !ok {
- err := fmt.Errorf("failed to find JMAP response invocation match for command '%v' and tag '%v'", command, tag)
+ err := fmt.Errorf("failed to find JMAP response invocation match for command '%v' and tag '%v'", command, tag) // NOSONAR
logger.Error().Msg(err.Error())
return jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
params := match.Parameters
typedParams, ok := params.(T)
if !ok {
- err := fmt.Errorf("JMAP response invocation matches command '%v' and tag '%v' but the type %T does not match the expected %T", command, tag, params, *target)
+ err := fmt.Errorf("JMAP response invocation matches command '%v' and tag '%v' but the type %T does not match the expected %T", command, tag, params, *target) // NOSONAR
logger.Error().Msg(err.Error())
return jmapError(err, JmapErrorInvalidJmapResponsePayload)
}
@@ -233,6 +233,22 @@ func tryRetrieveResponseMatchParameters[T any](logger *log.Logger, data *Respons
return true, nil
}
+func retrieveGet[C GetCommand, T GetResponse](logger *log.Logger, data *Response, command C, tag string, target *T) Error {
+ return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+}
+
+func retrieveSet[C SetCommand, T SetResponse](logger *log.Logger, data *Response, command C, tag string, target *T) Error {
+ return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+}
+
+func retrieveQuery[C QueryCommand, T QueryResponse](logger *log.Logger, data *Response, command C, tag string, target *T) Error {
+ return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+}
+
+func retrieveChanges[C ChangesCommand, T ChangesResponse](logger *log.Logger, data *Response, command C, tag string, target *T) Error {
+ return retrieveResponseMatchParameters(logger, data, command.GetCommand(), tag, target)
+}
+
func (i *Invocation) MarshalJSON() ([]byte, error) {
// JMAP requests have a slightly unusual structure since they are not a JSON object
// but, instead, a three-element array composed of
@@ -363,6 +379,10 @@ func mapPairs[K comparable, L, R any](left map[K]L, right map[K]R) map[K]pair[L,
return result
}
+func strPtr(s string) *string {
+ return &s
+}
+
func intPtr(i int) *int {
return &i
}
diff --git a/pkg/structs/structs.go b/pkg/structs/structs.go
index 80aaf457fe..6131817532 100644
--- a/pkg/structs/structs.go
+++ b/pkg/structs/structs.go
@@ -99,6 +99,15 @@ func MapKeys2[S comparable, T comparable, V any](m map[S]V, mapper func(S, V) T)
return r
}
+func ToMap[E any, K comparable, V any](source []E, mapper func(E) (K, V)) map[K]V {
+ m := map[K]V{}
+ for _, e := range source {
+ k, v := mapper(e)
+ m[k] = v
+ }
+ return m
+}
+
func ToBoolMap[E comparable](source []E) map[E]bool {
m := make(map[E]bool, len(source))
for _, v := range source {