groupware: refactor contactcard changes, and Request framework

* implement ContactCard retrieval endpoint for syncing

 * re-implement that endpoint for Email too

 * fix the Mailbox changes endpoint to actually return changes about
   Mailboxes, and not about Emails

 * when querying the diff of Mailboxes without any prior state, return
   an error since the result is not what one would expect

 * introduce the 'changes' API tag and group

 * refactor the successful response functions to consistently return an
   object type and object state whenever possible

 * move the syncing endpoints under /accounts/*/changes/ for better
   clarity, e.g. /changes/emails instead of /emails/mailbox/*/changes
This commit is contained in:
Pascal Bleser
2026-03-24 09:57:45 +01:00
parent 7e4124ad94
commit fa244b9316
22 changed files with 732 additions and 466 deletions

View File

@@ -63,6 +63,103 @@ 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) {
logger = j.logger("GetContactCards", session, logger)
cmd, err := j.request(session, logger, invocation(CommandContactCardGet, ContactCardGetCommand{
Ids: contactIds,
AccountId: accountId,
}, "0"))
if err != nil {
return nil, "", "", "", err
}
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) ([]jscontact.ContactCard, State, Error) {
var response ContactCardGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, "0", &response)
if err != nil {
return nil, "", err
}
return response.List, response.State, nil
})
}
type ContactCardChanges struct {
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState"`
HasMoreChanges bool `json:"hasMoreChanges"`
Created []jscontact.ContactCard `json:"created,omitempty"`
Updated []jscontact.ContactCard `json:"updated,omitempty"`
Destroyed []string `json:"destroyed,omitempty"`
}
func (j *Client) GetContactCardsSince(accountId string, session *Session, ctx context.Context, logger *log.Logger,
acceptLanguage string, sinceState string, maxChanges uint) (ContactCardChanges, SessionState, State, Language, Error) {
logger = j.logger("GetContactCards", session, logger)
maxChangesPtr := &maxChanges
if maxChanges < 1 {
maxChangesPtr = nil
}
cmd, err := j.request(session, logger,
invocation(CommandContactCardChanges, ContactCardChangesCommand{
AccountId: accountId,
SinceState: sinceState,
MaxChanges: maxChangesPtr,
}, "0"),
invocation(CommandContactCardGet, ContactCardGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
ResultOf: "0",
Name: CommandContactCardChanges,
Path: "/created",
},
}, "1"),
invocation(CommandContactCardGet, ContactCardGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{
ResultOf: "0",
Name: CommandContactCardChanges,
Path: "/updated",
},
}, "2"),
)
if err != nil {
return ContactCardChanges{}, "", "", "", err
}
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (ContactCardChanges, State, Error) {
result := ContactCardChanges{}
var changes ContactCardChangesResponse
err = retrieveResponseMatchParameters(logger, body, CommandContactCardChanges, "0", &changes)
if err != nil {
return ContactCardChanges{}, "", err
}
result.NewState = changes.NewState
result.OldState = changes.OldState
result.HasMoreChanges = changes.HasMoreChanges
result.Destroyed = changes.Destroyed
var created ContactCardGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, "1", &created)
if err != nil {
return ContactCardChanges{}, "", err
}
result.Created = created.List
var updated ContactCardGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandContactCardGet, "2", &updated)
if err != nil {
return ContactCardChanges{}, "", err
}
result.Updated = updated.List
return result, changes.NewState, nil
})
}
func (j *Client) QueryContactCards(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string,
filter ContactCardFilterElement, sortBy []ContactCardComparator,
position uint, limit uint) (map[string][]jscontact.ContactCard, SessionState, State, Language, Error) {

View File

@@ -194,38 +194,18 @@ func (j *Client) GetAllEmailsInMailbox(accountId string, session *Session, ctx c
})
}
func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, maxChanges uint) (EmailChangesResponse, SessionState, State, Language, Error) {
logger = j.loggerParams("GetEmailChanges", session, logger, func(z zerolog.Context) zerolog.Context {
return z.Str(logSinceState, string(sinceState))
})
changes := EmailChangesCommand{
AccountId: accountId,
SinceState: sinceState,
}
if maxChanges > 0 {
changes.MaxChanges = maxChanges
}
cmd, err := j.request(session, logger, invocation(CommandEmailChanges, changes, "0"))
if err != nil {
return EmailChangesResponse{}, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
}
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (EmailChangesResponse, State, Error) {
var changesResponse EmailChangesResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailChanges, "0", &changesResponse)
if err != nil {
return EmailChangesResponse{}, "", err
}
return changesResponse, changesResponse.NewState, nil
})
type EmailChanges struct {
HasMoreChanges bool `json:"hasMoreChanges"`
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState"`
Created []Email `json:"created,omitempty"`
Updated []Email `json:"updated,omitempty"`
Destroyed []string `json:"destroyed,omitempty"`
}
// Get all the Emails that have been created, updated or deleted since a given state.
func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
logger = j.loggerParams("GetEmailsSince", session, logger, func(z zerolog.Context) zerolog.Context {
func (j *Client) GetEmailChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceState State, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (EmailChanges, SessionState, State, Language, Error) {
logger = j.loggerParams("GetEmailChanges", session, logger, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies).Str(logSinceState, string(sinceState))
})
@@ -234,7 +214,7 @@ func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.
SinceState: sinceState,
}
if maxChanges > 0 {
changes.MaxChanges = maxChanges
changes.MaxChanges = &maxChanges
}
getCreated := EmailGetRefCommand{
@@ -260,33 +240,34 @@ func (j *Client) GetEmailsSince(accountId string, session *Session, ctx context.
invocation(CommandEmailGet, getUpdated, "2"),
)
if err != nil {
return MailboxChanges{}, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
return EmailChanges{}, "", "", "", simpleError(err, JmapErrorInvalidJmapRequestPayload)
}
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (MailboxChanges, State, Error) {
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (EmailChanges, State, Error) {
var changesResponse EmailChangesResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailChanges, "0", &changesResponse)
if err != nil {
return MailboxChanges{}, "", err
return EmailChanges{}, "", err
}
var createdResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &createdResponse)
if err != nil {
logger.Error().Err(err).Send()
return MailboxChanges{}, "", err
return EmailChanges{}, "", err
}
var updatedResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "2", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return MailboxChanges{}, "", err
return EmailChanges{}, "", err
}
return MailboxChanges{
return EmailChanges{
Destroyed: changesResponse.Destroyed,
HasMoreChanges: changesResponse.HasMoreChanges,
OldState: changesResponse.OldState,
NewState: changesResponse.NewState,
Created: createdResponse.List,
Updated: updatedResponse.List,

View File

@@ -155,48 +155,41 @@ func (j *Client) SearchMailboxIdsPerRole(accountIds []string, session *Session,
}
type MailboxChanges struct {
Destroyed []string `json:"destroyed,omitzero"`
HasMoreChanges bool `json:"hasMoreChanges,omitzero"`
NewState State `json:"newState"`
Created []Email `json:"created,omitempty"`
Updated []Email `json:"updated,omitempty"`
HasMoreChanges bool `json:"hasMoreChanges"`
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState"`
Created []Mailbox `json:"created,omitempty"`
Updated []Mailbox `json:"updated,omitempty"`
Destroyed []string `json:"destroyed,omitempty"`
}
// Retrieve Email changes in a given Mailbox since a given state.
func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, mailboxId string, sinceState string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
logger = j.loggerParams("GetMailboxChanges", session, logger, func(z zerolog.Context) zerolog.Context {
return z.Bool(logFetchBodies, fetchBodies).Str(logSinceState, sinceState)
})
// 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 string, maxChanges uint) (MailboxChanges, SessionState, State, Language, Error) {
logger = j.logger("GetMailboxChanges", session, logger)
changes := MailboxChangesCommand{
AccountId: accountId,
SinceState: sinceState,
MaxChanges: nil,
}
if maxChanges > 0 {
changes.MaxChanges = maxChanges
changes.MaxChanges = &maxChanges
}
getCreated := EmailGetRefCommand{
AccountId: accountId,
FetchAllBodyValues: fetchBodies,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/created", ResultOf: "0"},
getCreated := MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/created", ResultOf: "0"},
}
if maxBodyValueBytes > 0 {
getCreated.MaxBodyValueBytes = maxBodyValueBytes
}
getUpdated := EmailGetRefCommand{
AccountId: accountId,
FetchAllBodyValues: fetchBodies,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/updated", ResultOf: "0"},
}
if maxBodyValueBytes > 0 {
getUpdated.MaxBodyValueBytes = maxBodyValueBytes
getUpdated := MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/updated", ResultOf: "0"},
}
cmd, err := j.request(session, logger,
invocation(CommandMailboxChanges, changes, "0"),
invocation(CommandEmailGet, getCreated, "1"),
invocation(CommandEmailGet, getUpdated, "2"),
invocation(CommandMailboxGet, getCreated, "1"),
invocation(CommandMailboxGet, getUpdated, "2"),
)
if err != nil {
return MailboxChanges{}, "", "", "", err
@@ -209,15 +202,15 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
return MailboxChanges{}, "", err
}
var createdResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "1", &createdResponse)
var createdResponse MailboxGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, "1", &createdResponse)
if err != nil {
logger.Error().Err(err).Send()
return MailboxChanges{}, "", err
}
var updatedResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, "2", &updatedResponse)
var updatedResponse MailboxGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, "2", &updatedResponse)
if err != nil {
logger.Error().Err(err).Send()
return MailboxChanges{}, "", err
@@ -226,6 +219,7 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
return MailboxChanges{
Destroyed: mailboxResponse.Destroyed,
HasMoreChanges: mailboxResponse.HasMoreChanges,
OldState: mailboxResponse.OldState,
NewState: mailboxResponse.NewState,
Created: createdResponse.List,
Updated: createdResponse.List,
@@ -234,13 +228,13 @@ func (j *Client) GetMailboxChanges(accountId string, session *Session, ctx conte
}
// Retrieve Email changes in Mailboxes of multiple Accounts.
func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]string, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint) (map[string]MailboxChanges, SessionState, State, Language, Error) {
func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, sinceStateMap map[string]string, maxChanges uint) (map[string]MailboxChanges, SessionState, State, Language, Error) {
logger = j.loggerParams("GetMailboxChangesForMultipleAccounts", session, logger, func(z zerolog.Context) zerolog.Context {
sinceStateLogDict := zerolog.Dict()
for k, v := range sinceStateMap {
sinceStateLogDict.Str(log.SafeString(k), log.SafeString(v))
}
return z.Bool(logFetchBodies, fetchBodies).Dict(logSinceState, sinceStateLogDict)
return z.Dict(logSinceState, sinceStateLogDict)
})
uniqueAccountIds := structs.Uniq(accountIds)
@@ -261,29 +255,21 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
}
if maxChanges > 0 {
changes.MaxChanges = maxChanges
changes.MaxChanges = &maxChanges
}
getCreated := EmailGetRefCommand{
AccountId: accountId,
FetchAllBodyValues: fetchBodies,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/created", ResultOf: mcid(accountId, "0")},
getCreated := MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/created", ResultOf: mcid(accountId, "0")},
}
if maxBodyValueBytes > 0 {
getCreated.MaxBodyValueBytes = maxBodyValueBytes
}
getUpdated := EmailGetRefCommand{
AccountId: accountId,
FetchAllBodyValues: fetchBodies,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/updated", ResultOf: mcid(accountId, "0")},
}
if maxBodyValueBytes > 0 {
getUpdated.MaxBodyValueBytes = maxBodyValueBytes
getUpdated := MailboxGetRefCommand{
AccountId: accountId,
IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: "/updated", ResultOf: mcid(accountId, "0")},
}
invocations[i*3+0] = invocation(CommandMailboxChanges, changes, mcid(accountId, "0"))
invocations[i*3+1] = invocation(CommandEmailGet, getCreated, mcid(accountId, "1"))
invocations[i*3+2] = invocation(CommandEmailGet, getUpdated, mcid(accountId, "2"))
invocations[i*3+1] = invocation(CommandMailboxGet, getCreated, mcid(accountId, "1"))
invocations[i*3+2] = invocation(CommandMailboxGet, getUpdated, mcid(accountId, "2"))
}
cmd, err := j.request(session, logger, invocations...)
@@ -301,14 +287,14 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, sessi
return nil, "", err
}
var createdResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "1"), &createdResponse)
var createdResponse MailboxGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "1"), &createdResponse)
if err != nil {
return nil, "", err
}
var updatedResponse EmailGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandEmailGet, mcid(accountId, "2"), &updatedResponse)
var updatedResponse MailboxGetResponse
err = retrieveResponseMatchParameters(logger, body, CommandMailboxGet, mcid(accountId, "2"), &updatedResponse)
if err != nil {
return nil, "", err
}

View File

@@ -90,7 +90,7 @@ func TestWs(t *testing.T) {
var initialState State
{
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", "", 0)
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", State(""), true, 0, 0)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEmpty(state)
@@ -104,7 +104,7 @@ func TestWs(t *testing.T) {
require.NotEmpty(initialState)
{
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, 0)
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, true, 0, 0)
require.NoError(err)
require.Equal(session.State, sessionState)
require.Equal(initialState, state)
@@ -147,7 +147,7 @@ func TestWs(t *testing.T) {
}
var lastState State
{
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, 0)
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", initialState, true, 0, 0)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(initialState, state)
@@ -158,7 +158,7 @@ func TestWs(t *testing.T) {
require.Empty(changes.Updated)
lastState = state
emailIds = append(emailIds, changes.Created...)
emailIds = append(emailIds, structs.Map(changes.Created, func(e Email) string { return e.Id })...)
}
{
@@ -181,7 +181,7 @@ func TestWs(t *testing.T) {
l.m.Unlock()
}
{
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, 0)
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, true, 0, 0)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(lastState, state)
@@ -192,7 +192,7 @@ func TestWs(t *testing.T) {
require.Empty(changes.Updated)
lastState = state
emailIds = append(emailIds, changes.Created...)
emailIds = append(emailIds, structs.Map(changes.Created, func(e Email) string { return e.Id })...)
}
{
@@ -215,7 +215,7 @@ func TestWs(t *testing.T) {
l.m.Unlock()
}
{
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, 0)
changes, sessionState, state, _, err := s.client.GetEmailChanges(mailAccountId, session, s.ctx, s.logger, "", lastState, true, 0, 0)
require.NoError(err)
require.Equal(session.State, sessionState)
require.NotEqual(lastState, state)

View File

@@ -842,8 +842,12 @@ type SessionState string
type State string
const EmptyState = State("")
type Language string
const NoLanguage = Language("")
type SessionResponse struct {
Capabilities SessionCapabilities `json:"capabilities"`
@@ -1350,7 +1354,7 @@ type MailboxChangesCommand struct {
// If supplied by the client, the value MUST be a positive integer greater than 0.
//
// If a value outside of this range is given, the server MUST reject the call with an invalidArguments error.
MaxChanges uint `json:"maxChanges,omitzero"`
MaxChanges *uint `json:"maxChanges,omitzero"`
}
type MailboxFilterElement interface {
@@ -1859,7 +1863,7 @@ type EmailChangesCommand struct {
// The server MAY choose to return fewer than this value but MUST NOT return more.
// If not given by the client, the server may choose how many to return.
// If supplied by the client, the value MUST be a positive integer greater than 0.
MaxChanges uint `json:"maxChanges,omitzero"`
MaxChanges *uint `json:"maxChanges,omitempty"`
}
type EmailAddress struct {
@@ -5307,6 +5311,47 @@ type ContactCardGetResponse struct {
NotFound []any `json:"notFound"`
}
type ContactCardChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// The current state of the client.
// This is the string that was returned as the "state" argument in the "ContactCard/get" response.
// The server will return the changes that have occurred since this state.
SinceState string `json:"sinceState,omitempty"`
// The maximum number of ids to return in the response.
// The server MAY choose to return fewer than this value but MUST NOT return more.
// If not given by the client, the server may choose how many to return.
// If supplied by the client, the value MUST be a positive integer greater than 0.
// If a value outside of this range is given, the server MUST reject the call with an `invalidArguments` error.
MaxChanges *uint `json:"maxChanges,omitempty"`
}
type ContactCardChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// This is the "sinceState" argument echoed back; it's the state from which the server is returning changes.
OldState State `json:"oldState"`
// This is the state the client will be in after applying the set of changes to the old state.
NewState State `json:"newState"`
// If true, the client may call "ContactCard/changes" again with the "newState" returned to get further updates.
// If false, "newState" is the current server state.
HasMoreChanges bool `json:"hasMoreChanges"`
// An array of ids for records that have been created since the old state.
Created []string `json:"created,omitempty"`
// An array of ids for records that have been updated since the old state.
Updated []string `json:"updated,omitempty"`
// An array of ids for records that have been destroyed since the old state.
Destroyed []string `json:"destroyed,omitempty"`
}
type ContactCardUpdate map[string]any
type ContactCardSetCommand struct {
@@ -5884,6 +5929,7 @@ const (
CommandAddressBookGet Command = "AddressBook/get"
CommandContactCardQuery Command = "ContactCard/query"
CommandContactCardGet Command = "ContactCard/get"
CommandContactCardChanges Command = "ContactCard/changes"
CommandContactCardSet Command = "ContactCard/set"
CommandCalendarEventParse Command = "CalendarEvent/parse"
CommandCalendarGet Command = "Calendar/get"
@@ -5916,6 +5962,7 @@ var CommandResponseTypeMap = map[Command]func() any{
CommandAddressBookGet: func() any { return AddressBookGetResponse{} },
CommandContactCardQuery: func() any { return ContactCardQueryResponse{} },
CommandContactCardGet: func() any { return ContactCardGetResponse{} },
CommandContactCardChanges: func() any { return ContactCardChangesResponse{} },
CommandContactCardSet: func() any { return ContactCardSetResponse{} },
CommandCalendarEventParse: func() any { return CalendarEventParseResponse{} },
CommandCalendarGet: func() any { return CalendarGetResponse{} },

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/opencloud-eu/opencloud/pkg/jscontact"
c "github.com/opencloud-eu/opencloud/pkg/jscontact"
)
@@ -697,9 +698,10 @@ func (e Exemplar) Mailboxes() []Mailbox {
}
func (e Exemplar) MailboxChanges() MailboxChanges {
a, _, _ := e.MailboxInbox()
return MailboxChanges{
NewState: "aesh2ahj",
Created: []Email{e.Email()},
Created: []Mailbox{a},
Destroyed: []string{"baingow4"},
}
}
@@ -1783,3 +1785,35 @@ func (e Exemplar) ContactCard() c.ContactCard {
},
}
}
func (e Exemplar) ContactCardChanges() (ContactCardChanges, string, string) {
c := e.ContactCard()
return ContactCardChanges{
OldState: "xai3iiraipoo",
NewState: "ni7thah7eeY4",
HasMoreChanges: true,
Created: []jscontact.ContactCard{c},
Destroyed: []string{"eaae", "bcba"},
}, "A created ContactCard and two deleted ones", "created"
}
func (e Exemplar) OtherContactCardChanges() (ContactCardChanges, string, string) {
c := e.ContactCard()
return ContactCardChanges{
OldState: "xai3iiraipoo",
NewState: "ni7thah7eeY4",
HasMoreChanges: false,
Updated: []jscontact.ContactCard{c},
}, "An updated ContactCard", "updated"
}
func (e Exemplar) EmailChanges() EmailChanges {
emails := []Email{e.Email()}
return EmailChanges{
OldState: "xai3iiraipoo",
NewState: "ni7thah7eeY4",
HasMoreChanges: true,
Created: emails,
Destroyed: []string{"mmnan", "moxzz"},
}
}