From 7e8caab9795746383ca412df4985cc70c9428548 Mon Sep 17 00:00:00 2001 From: Pascal Bleser Date: Wed, 3 Jun 2026 15:51:00 +0200 Subject: [PATCH] groupware: add strongly typed aliases for AccountId, PrincipalId and SupplierId Purpose is to make APIs and parameters easier to understand, since plain strings are used all over the place for all sorts of identifiers. --- pkg/jmap/api_addressbook.go | 18 +- pkg/jmap/api_blob.go | 13 +- pkg/jmap/api_bootstrap.go | 16 +- pkg/jmap/api_calendar.go | 20 +- pkg/jmap/api_changes.go | 2 +- pkg/jmap/api_contact.go | 26 +- pkg/jmap/api_email.go | 72 ++--- pkg/jmap/api_event.go | 26 +- pkg/jmap/api_identity.go | 34 +- pkg/jmap/api_mailbox.go | 70 ++-- pkg/jmap/api_objects.go | 2 +- pkg/jmap/api_principal.go | 12 +- pkg/jmap/api_quota.go | 8 +- pkg/jmap/api_vacation.go | 6 +- pkg/jmap/export_integration_test.go | 22 +- pkg/jmap/integration_addressbook_test.go | 26 +- pkg/jmap/integration_calendar_test.go | 26 +- pkg/jmap/integration_email_test.go | 20 +- pkg/jmap/integration_ws_test.go | 4 +- pkg/jmap/model.go | 303 +++++++++--------- pkg/jmap/model_examples.go | 16 +- pkg/jmap/templates.go | 66 ++-- pkg/jmap/tools.go | 18 +- pkg/log/log_safely.go | 7 +- pkg/structs/structs.go | 14 + pkg/structs/structs_test.go | 18 ++ services/groupware/QueryPagination.md | 1 - .../groupware/address_book_supplier_jmap.go | 12 +- .../groupware/address_book_supplier_mock.go | 14 +- .../groupware/pkg/groupware/api_account.go | 14 +- .../pkg/groupware/api_addressbooks.go | 4 +- services/groupware/pkg/groupware/api_blob.go | 6 +- .../groupware/pkg/groupware/api_changes.go | 2 +- .../groupware/pkg/groupware/api_contacts.go | 2 +- .../groupware/pkg/groupware/api_emails.go | 38 +-- services/groupware/pkg/groupware/api_index.go | 16 +- .../groupware/pkg/groupware/api_mailbox.go | 16 +- .../groupware/pkg/groupware/api_objects.go | 2 +- services/groupware/pkg/groupware/api_quota.go | 7 +- .../groupware/pkg/groupware/api_tasklists.go | 6 +- .../groupware/pkg/groupware/api_vacation.go | 4 +- services/groupware/pkg/groupware/error.go | 10 +- .../groupware/pkg/groupware/examples_test.go | 16 +- services/groupware/pkg/groupware/framework.go | 38 +-- .../groupware/pkg/groupware/mock_tasks.go | 4 +- services/groupware/pkg/groupware/next.go | 32 +- services/groupware/pkg/groupware/objtypes.go | 2 +- services/groupware/pkg/groupware/request.go | 80 ++--- services/groupware/pkg/groupware/response.go | 40 +-- services/groupware/pkg/groupware/suppliers.go | 66 ++-- .../groupware/pkg/groupware/suppliers_test.go | 40 +-- services/groupware/pkg/groupware/templates.go | 70 ++-- 52 files changed, 715 insertions(+), 692 deletions(-) diff --git a/pkg/jmap/api_addressbook.go b/pkg/jmap/api_addressbook.go index 4294ebce55..71fd606263 100644 --- a/pkg/jmap/api_addressbook.go +++ b/pkg/jmap/api_addressbook.go @@ -2,9 +2,9 @@ package jmap var NS_ADDRESSBOOKS = ns(JmapContacts) -func (j *Client) GetAddressbooks(accountId string, ids []string, ctx Context) (Result[AddressBookGetResponse], error) { +func (j *Client) GetAddressbooks(accountId AccountId, ids []string, ctx Context) (Result[AddressBookGetResponse], error) { return get(j, "GetAddressbooks", MailboxType, - func(accountId string, ids []string) AddressBookGetCommand { + func(accountId AccountId, ids []string) AddressBookGetCommand { return AddressBookGetCommand{AccountId: accountId, Ids: ids} }, AddressBookGetResponse{}, @@ -27,7 +27,7 @@ func (c AddressBookChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve Address Book changes since a given state. // @apidoc addressbook,changes -func (j *Client) GetAddressbookChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (Result[AddressBookChanges], error) { +func (j *Client) GetAddressbookChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[AddressBookChanges], error) { return changesA(j, "GetAddressbookChanges", MailboxType, func() AddressBookChangesCommand { return AddressBookChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)} @@ -58,12 +58,12 @@ func (j *Client) GetAddressbookChanges(accountId string, sinceState State, maxCh ) } -func (j *Client) CreateAddressBook(accountId string, addressbook AddressBookChange, ctx Context) (Result[*AddressBook], error) { +func (j *Client) CreateAddressBook(accountId AccountId, addressbook AddressBookChange, ctx Context) (Result[*AddressBook], error) { return create(j, "CreateAddressBook", MailboxType, - func(accountId string, create map[string]AddressBookChange) AddressBookSetCommand { + func(accountId AccountId, create map[string]AddressBookChange) AddressBookSetCommand { return AddressBookSetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ids string) AddressBookGetCommand { + func(accountId AccountId, ids string) AddressBookGetCommand { return AddressBookGetCommand{AccountId: accountId, Ids: []string{ids}} }, func(resp AddressBookSetResponse) map[string]*AddressBook { @@ -77,9 +77,9 @@ func (j *Client) CreateAddressBook(accountId string, addressbook AddressBookChan ) } -func (j *Client) DeleteAddressBook(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteAddressBook(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteAddressBook", MailboxType, - func(accountId string, destroy []string) AddressBookSetCommand { + func(accountId AccountId, destroy []string) AddressBookSetCommand { return AddressBookSetCommand{AccountId: accountId, Destroy: destroy} }, AddressBookSetResponse{}, @@ -88,7 +88,7 @@ func (j *Client) DeleteAddressBook(accountId string, destroyIds []string, ctx Co ) } -func (j *Client) UpdateAddressBook(accountId string, id string, changes AddressBookChange, ctx Context) (Result[AddressBook], error) { +func (j *Client) UpdateAddressBook(accountId AccountId, id string, changes AddressBookChange, ctx Context) (Result[AddressBook], error) { return update(j, "UpdateAddressBook", MailboxType, func(update map[string]PatchObject) AddressBookSetCommand { return AddressBookSetCommand{AccountId: accountId, Update: update} diff --git a/pkg/jmap/api_blob.go b/pkg/jmap/api_blob.go index c094e254e2..7cf1037da1 100644 --- a/pkg/jmap/api_blob.go +++ b/pkg/jmap/api_blob.go @@ -10,7 +10,7 @@ import ( var NS_BLOB = ns(JmapBlob) -func (j *Client) GetBlobMetadata(accountId string, ids []string, ctx Context) (Result[BlobGetResponse], error) { +func (j *Client) GetBlobMetadata(accountId AccountId, ids []string, ctx Context) (Result[BlobGetResponse], error) { get := BlobGetCommand{ AccountId: accountId, Ids: ids, @@ -41,20 +41,20 @@ type UploadedBlobWithHash struct { Sha512 string `json:"sha:512,omitempty"` } -func (j *Client) UploadBlobStream(accountId string, contentType string, body io.Reader, ctx Context) (UploadedBlob, Language, error) { +func (j *Client) UploadBlobStream(accountId AccountId, contentType string, body io.Reader, ctx Context) (UploadedBlob, Language, error) { logger := log.From(ctx.Logger.With().Str(logEndpoint, ctx.Session.UploadEndpoint)) ctx = ctx.WithLogger(logger) // TODO(pbleser-oc) use a library for proper URL template parsing - uploadUrl := strings.ReplaceAll(ctx.Session.UploadUrlTemplate, "{accountId}", accountId) + uploadUrl := strings.ReplaceAll(ctx.Session.UploadUrlTemplate, "{accountId}", string(accountId)) return j.blob.UploadBinary(uploadUrl, ctx.Session.UploadEndpoint, contentType, body, ctx) } -func (j *Client) DownloadBlobStream(accountId string, blobId string, name string, typ string, ctx Context) (*BlobDownload, Language, error) { //NOSONAR +func (j *Client) DownloadBlobStream(accountId AccountId, blobId string, name string, typ string, ctx Context) (*BlobDownload, Language, error) { //NOSONAR logger := log.From(ctx.Logger.With().Str(logEndpoint, ctx.Session.DownloadEndpoint)) ctx = ctx.WithLogger(logger) // TODO(pbleser-oc) use a library for proper URL template parsing downloadUrl := ctx.Session.DownloadUrlTemplate - downloadUrl = strings.ReplaceAll(downloadUrl, "{accountId}", accountId) + downloadUrl = strings.ReplaceAll(downloadUrl, "{accountId}", string(accountId)) downloadUrl = strings.ReplaceAll(downloadUrl, "{blobId}", blobId) downloadUrl = strings.ReplaceAll(downloadUrl, "{name}", name) downloadUrl = strings.ReplaceAll(downloadUrl, "{type}", typ) @@ -62,7 +62,7 @@ func (j *Client) DownloadBlobStream(accountId string, blobId string, name string return j.blob.DownloadBinary(downloadUrl, ctx.Session.DownloadEndpoint, ctx) } -func (j *Client) UploadBlob(accountId string, data []byte, contentType string, ctx Context) (Result[UploadedBlobWithHash], error) { +func (j *Client) UploadBlob(accountId AccountId, data []byte, contentType string, ctx Context) (Result[UploadedBlobWithHash], error) { encoded := base64.StdEncoding.EncodeToString(data) upload := BlobUploadCommand{ @@ -131,5 +131,4 @@ func (j *Client) UploadBlob(accountId string, data []byte, contentType string, c Sha512: get.DigestSha512, }, getResponse.State, nil }) - } diff --git a/pkg/jmap/api_bootstrap.go b/pkg/jmap/api_bootstrap.go index ae373703aa..cf2c69af64 100644 --- a/pkg/jmap/api_bootstrap.go +++ b/pkg/jmap/api_bootstrap.go @@ -11,7 +11,7 @@ type AccountBootstrapResult struct { var NS_MAIL_QUOTA = ns(JmapMail, JmapQuota) -func (j *Client) GetBootstrap(accountIds []string, ctx Context) (Result[map[string]AccountBootstrapResult], error) { //NOSONAR +func (j *Client) GetBootstrap(accountIds []AccountId, ctx Context) (Result[map[AccountId]AccountBootstrapResult], error) { //NOSONAR uniqueAccountIds := structs.Uniq(accountIds) logger := j.logger("GetBootstrap", ctx) @@ -25,13 +25,13 @@ func (j *Client) GetBootstrap(accountIds []string, ctx Context) (Result[map[stri cmd, err := j.request(ctx, NS_MAIL_QUOTA, calls...) if err != nil { - return ZeroResult[map[string]AccountBootstrapResult](), err + return ZeroResult[map[AccountId]AccountBootstrapResult](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]AccountBootstrapResult, State, Error) { - identityPerAccount := map[string][]Identity{} - quotaPerAccount := map[string][]Quota{} - identityStatesPerAccount := map[string]State{} - quotaStatesPerAccount := map[string]State{} + return command(j, ctx, cmd, func(body *Response) (map[AccountId]AccountBootstrapResult, State, Error) { + identityPerAccount := map[AccountId][]Identity{} + quotaPerAccount := map[AccountId][]Quota{} + identityStatesPerAccount := map[AccountId]State{} + quotaStatesPerAccount := map[AccountId]State{} for _, accountId := range uniqueAccountIds { var identityResponse IdentityGetResponse err = retrieveResponseMatchParameters(ctx, body, CommandIdentityGet, mcid(accountId, "I"), &identityResponse) @@ -52,7 +52,7 @@ func (j *Client) GetBootstrap(accountIds []string, ctx Context) (Result[map[stri } } - result := map[string]AccountBootstrapResult{} + result := map[AccountId]AccountBootstrapResult{} for accountId, value := range identityPerAccount { r, ok := result[accountId] if !ok { diff --git a/pkg/jmap/api_calendar.go b/pkg/jmap/api_calendar.go index 1727501ca0..774541dd46 100644 --- a/pkg/jmap/api_calendar.go +++ b/pkg/jmap/api_calendar.go @@ -2,7 +2,7 @@ package jmap var NS_CALENDARS = ns(JmapCalendars) -func (j *Client) ParseICalendarBlob(accountId string, blobIds []string, ctx Context) (Result[CalendarEventParseResponse], error) { +func (j *Client) ParseICalendarBlob(accountId AccountId, blobIds []string, ctx Context) (Result[CalendarEventParseResponse], error) { logger := j.logger("ParseICalendarBlob", ctx) parse := CalendarEventParseCommand{AccountId: accountId, BlobIds: blobIds} @@ -23,9 +23,9 @@ func (j *Client) ParseICalendarBlob(accountId string, blobIds []string, ctx Cont }) } -func (j *Client) GetCalendars(accountId string, ids []string, ctx Context) (Result[CalendarGetResponse], error) { +func (j *Client) GetCalendars(accountId AccountId, ids []string, ctx Context) (Result[CalendarGetResponse], error) { return get(j, "GetCalendars", CalendarType, - func(accountId string, ids []string) CalendarGetCommand { + func(accountId AccountId, ids []string) CalendarGetCommand { return CalendarGetCommand{AccountId: accountId, Ids: ids} }, CalendarGetResponse{}, @@ -48,7 +48,7 @@ func (c CalendarChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve Calendar changes since a given state. // @apidoc calendar,changes -func (j *Client) GetCalendarChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (Result[CalendarChanges], error) { +func (j *Client) GetCalendarChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[CalendarChanges], error) { return changes(j, "GetCalendarChanges", CalendarType, func() CalendarChangesCommand { return CalendarChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)} @@ -79,12 +79,12 @@ func (j *Client) GetCalendarChanges(accountId string, sinceState State, maxChang ) } -func (j *Client) CreateCalendar(accountId string, calendar CalendarChange, ctx Context) (Result[*Calendar], error) { +func (j *Client) CreateCalendar(accountId AccountId, calendar CalendarChange, ctx Context) (Result[*Calendar], error) { return create(j, "CreateCalendar", CalendarEventType, - func(accountId string, create map[string]CalendarChange) CalendarSetCommand { + func(accountId AccountId, create map[string]CalendarChange) CalendarSetCommand { return CalendarSetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ref string) CalendarGetCommand { + func(accountId AccountId, ref string) CalendarGetCommand { return CalendarGetCommand{AccountId: accountId, Ids: []string{ref}} }, func(resp CalendarSetResponse) map[string]*Calendar { @@ -98,9 +98,9 @@ func (j *Client) CreateCalendar(accountId string, calendar CalendarChange, ctx C ) } -func (j *Client) DeleteCalendar(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteCalendar(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteCalendar", CalendarEventType, - func(accountId string, destroy []string) CalendarSetCommand { + func(accountId AccountId, destroy []string) CalendarSetCommand { return CalendarSetCommand{AccountId: accountId, Destroy: destroy} }, CalendarSetResponse{}, @@ -109,7 +109,7 @@ func (j *Client) DeleteCalendar(accountId string, destroyIds []string, ctx Conte ) } -func (j *Client) UpdateCalendar(accountId string, id string, changes CalendarChange, ctx Context) (Result[Calendar], error) { +func (j *Client) UpdateCalendar(accountId AccountId, id string, changes CalendarChange, ctx Context) (Result[Calendar], error) { return update(j, "UpdateCalendar", CalendarEventType, func(update map[string]PatchObject) CalendarSetCommand { return CalendarSetCommand{AccountId: accountId, Update: update} diff --git a/pkg/jmap/api_changes.go b/pkg/jmap/api_changes.go index 7cc455b687..df54d549e3 100644 --- a/pkg/jmap/api_changes.go +++ b/pkg/jmap/api_changes.go @@ -74,7 +74,7 @@ func (s StateMap) MarshalZerologObject(e *zerolog.Event) { // Retrieve the changes in any type of objects at once since a given State. // @api:tags changes -func (j *Client) GetChanges(accountId string, stateMap StateMap, maxChanges uint, ctx Context) (Result[ObjectChanges], error) { //NOSONAR +func (j *Client) GetChanges(accountId AccountId, stateMap StateMap, maxChanges uint, ctx Context) (Result[ObjectChanges], error) { //NOSONAR logger := log.From(j.logger("GetChanges", ctx).With().Object("state", stateMap).Uint("maxChanges", maxChanges)) ctx = ctx.WithLogger(logger) diff --git a/pkg/jmap/api_contact.go b/pkg/jmap/api_contact.go index d7172eb5c0..4a5ee283b4 100644 --- a/pkg/jmap/api_contact.go +++ b/pkg/jmap/api_contact.go @@ -6,9 +6,9 @@ var NS_CONTACTS = ns(JmapContacts) var DEFAULT_CONTACT_CARD_VERSION = jscontact.JSContactVersion_1_0 -func (j *Client) GetContactCards(accountId string, contactIds []string, ctx Context) (Result[ContactCardGetResponse], error) { +func (j *Client) GetContactCards(accountId AccountId, contactIds []string, ctx Context) (Result[ContactCardGetResponse], error) { return get(j, "GetContactCards", ContactCardType, - func(accountId string, ids []string) ContactCardGetCommand { + func(accountId AccountId, ids []string) ContactCardGetCommand { return ContactCardGetCommand{AccountId: accountId, Ids: contactIds} }, ContactCardGetResponse{}, @@ -31,7 +31,7 @@ func (c ContactCardChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve the changes in Contact Cards since a given State. // @api:tags contact,changes -func (j *Client) GetContactCardChanges(accountId string, sinceState State, maxChanges uint, ctx Context) (Result[ContactCardChanges], error) { +func (j *Client) GetContactCardChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[ContactCardChanges], error) { return changes(j, "GetContactCardChanges", ContactCardType, func() ContactCardChangesCommand { return ContactCardChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)} @@ -77,19 +77,19 @@ func (r *ContactCardSearchResults) RemoveResults() { r.Results = nil func (r *ContactCardSearchResults) SetLimit(limit *uint) { r.Limit = limit } func (r *ContactCardSearchResults) SetPosition(position *uint) { r.Position = position } -func (j *Client) QueryContactCards(accountIds map[string]QueryParams, //NOSONAR +func (j *Client) QueryContactCards(accountIds map[AccountId]QueryParams, //NOSONAR limit *uint, filter ContactCardFilterElement, sortBy []ContactCardComparator, calculateTotal bool, - ctx Context) (Result[map[string]*ContactCardSearchResults], error) { + ctx Context) (Result[map[AccountId]*ContactCardSearchResults], error) { return queryN(j, "QueryContactCards", ContactCardType, []ContactCardComparator{{Property: ContactCardPropertyUpdated, IsAscending: false}}, - func(accountId string, qp QueryParams, limit *uint, filter ContactCardFilterElement, sortBy []ContactCardComparator) ContactCardQueryCommand { + func(accountId AccountId, qp QueryParams, limit *uint, filter ContactCardFilterElement, sortBy []ContactCardComparator) ContactCardQueryCommand { if qp.Anchor != "" { return ContactCardQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Anchor: qp.Anchor, AnchorOffset: qp.AnchorOffset, Limit: limit, CalculateTotal: calculateTotal} } else { return ContactCardQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: qp.Position, Limit: limit, CalculateTotal: calculateTotal} } }, - func(accountId string, cmd Command, path string, rof string) ContactCardGetRefCommand { + func(accountId AccountId, cmd Command, path string, rof string) ContactCardGetRefCommand { return ContactCardGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}} }, func(query ContactCardQueryResponse, queryParams QueryParams, limit *uint) *ContactCardSearchResults { @@ -116,15 +116,15 @@ func (j *Client) QueryContactCards(accountIds map[string]QueryParams, //NOSONAR } // @api:example create -func (j *Client) CreateContactCard(accountId string, contact ContactCardChange, ctx Context) (Result[*ContactCard], error) { +func (j *Client) CreateContactCard(accountId AccountId, contact ContactCardChange, ctx Context) (Result[*ContactCard], error) { if contact.Version == nil { contact.Version = &DEFAULT_CONTACT_CARD_VERSION } return create(j, "CreateContactCard", ContactCardType, - func(accountId string, create map[string]ContactCardChange) ContactCardSetCommand { + func(accountId AccountId, create map[string]ContactCardChange) ContactCardSetCommand { return ContactCardSetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ids string) ContactCardGetCommand { + func(accountId AccountId, ids string) ContactCardGetCommand { return ContactCardGetCommand{AccountId: accountId, Ids: []string{ids}} }, func(resp ContactCardSetResponse) map[string]*ContactCard { @@ -138,9 +138,9 @@ func (j *Client) CreateContactCard(accountId string, contact ContactCardChange, ) } -func (j *Client) DeleteContactCard(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteContactCard(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteContactCard", ContactCardType, - func(accountId string, destroy []string) ContactCardSetCommand { + func(accountId AccountId, destroy []string) ContactCardSetCommand { return ContactCardSetCommand{AccountId: accountId, Destroy: destroy} }, ContactCardSetResponse{}, @@ -150,7 +150,7 @@ func (j *Client) DeleteContactCard(accountId string, destroyIds []string, ctx Co } // @api:example update -func (j *Client) UpdateContactCard(accountId string, id string, changes ContactCardChange, ctx Context) (Result[ContactCard], error) { +func (j *Client) UpdateContactCard(accountId AccountId, id string, changes ContactCardChange, ctx Context) (Result[ContactCard], error) { return update(j, "UpdateContactCard", ContactCardType, func(update map[string]PatchObject) ContactCardSetCommand { return ContactCardSetCommand{AccountId: accountId, Update: update} diff --git a/pkg/jmap/api_email.go b/pkg/jmap/api_email.go index 41ef7c4f24..7eca1a0cab 100644 --- a/pkg/jmap/api_email.go +++ b/pkg/jmap/api_email.go @@ -14,7 +14,7 @@ var NS_MAIL = ns(JmapMail) var NS_MAIL_SUBMISSION = ns(JmapMail, JmapSubmission) // Retrieve specific Emails by their id. -func (j *Client) GetEmails(accountId string, ids []string, //NOSONAR +func (j *Client) GetEmails(accountId AccountId, ids []string, //NOSONAR fetchBodies bool, maxBodyValueBytes uint, markAsSeen bool, withThreads bool, ctx Context) (Result[EmailGetResponse], error) { logger := j.logger("GetEmails", ctx) @@ -82,7 +82,7 @@ func (j *Client) GetEmails(accountId string, ids []string, //NOSONAR }) } -func (j *Client) GetEmailBlobId(accountId string, id string, ctx Context) (Result[string], error) { +func (j *Client) GetEmailBlobId(accountId AccountId, id string, ctx Context) (Result[string], error) { logger := j.logger("GetEmailBlobId", ctx) ctx = ctx.WithLogger(logger) @@ -119,7 +119,7 @@ func (r *EmailSearchResults) SetLimit(limit *uint) { r.Limi func (r *EmailSearchResults) SetPosition(position *uint) { r.Position = position } // Retrieve all the Emails in a given Mailbox by its id. -func (j *Client) GetAllEmailsInMailbox(accountId string, mailboxId string, //NOSONAR +func (j *Client) GetAllEmailsInMailbox(accountId AccountId, mailboxId string, //NOSONAR qp QueryParams, limit *uint, collapseThreads bool, fetchBodies bool, maxBodyValueBytes uint, withThreads bool, ctx Context) (Result[*EmailSearchResults], error) { logger := j.loggerParams("GetAllEmailsInMailbox", ctx, func(z zerolog.Context) zerolog.Context { @@ -220,7 +220,7 @@ func (c EmailChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve the changes in Emails since a given State. // @api:tags email,changes -func (j *Client) GetEmailChanges(accountId string, +func (j *Client) GetEmailChanges(accountId AccountId, sinceState State, fetchBodies bool, maxBodyValueBytes uint, maxChanges uint, ctx Context) (Result[EmailChanges], error) { //NOSONAR logger := j.loggerParams("GetEmailChanges", ctx, func(z zerolog.Context) zerolog.Context { @@ -302,9 +302,9 @@ type SearchSnippetWithMeta struct { type EmailSnippetSearchResults SearchResultsTemplate[SearchSnippetWithMeta] -func (j *Client) QueryEmailSnippets(accountIds []string, //NOSONAR +func (j *Client) QueryEmailSnippets(accountIds []AccountId, //NOSONAR filter EmailFilterElement, position int, anchor string, anchorOffset *int, limit *uint, - ctx Context) (Result[map[string]EmailSnippetSearchResults], error) { + ctx Context) (Result[map[AccountId]EmailSnippetSearchResults], error) { logger := j.loggerParams("QueryEmailSnippets", ctx, func(z zerolog.Context) zerolog.Context { l := z.Int(logPosition, position) if limit != nil { @@ -358,12 +358,12 @@ func (j *Client) QueryEmailSnippets(accountIds []string, //NOSONAR cmd, err := j.request(ctx, NS_MAIL, invocations...) if err != nil { - return ZeroResult[map[string]EmailSnippetSearchResults](), err + return ZeroResult[map[AccountId]EmailSnippetSearchResults](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]EmailSnippetSearchResults, State, Error) { - results := make(map[string]EmailSnippetSearchResults, len(uniqueAccountIds)) - states := make(map[string]State, len(uniqueAccountIds)) + return command(j, ctx, cmd, func(body *Response) (map[AccountId]EmailSnippetSearchResults, State, Error) { + results := make(map[AccountId]EmailSnippetSearchResults, len(uniqueAccountIds)) + states := make(map[AccountId]State, len(uniqueAccountIds)) for _, accountId := range uniqueAccountIds { var queryResponse EmailQueryResponse err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse) @@ -428,9 +428,9 @@ type EmailQueryResult struct { } */ -func (j *Client) QueryEmails(accountIds []string, +func (j *Client) QueryEmails(accountIds []AccountId, filter EmailFilterElement, position int, limit uint, fetchBodies bool, maxBodyValueBytes uint, - ctx Context) (Result[map[string]EmailSearchResults], error) { //NOSONAR + ctx Context) (Result[map[AccountId]EmailSearchResults], error) { //NOSONAR logger := j.loggerParams("QueryEmails", ctx, func(z zerolog.Context) zerolog.Context { return z.Bool(logFetchBodies, fetchBodies) }) @@ -470,12 +470,12 @@ func (j *Client) QueryEmails(accountIds []string, cmd, err := j.request(ctx, NS_MAIL, invocations...) if err != nil { - return ZeroResult[map[string]EmailSearchResults](), err + return ZeroResult[map[AccountId]EmailSearchResults](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]EmailSearchResults, State, Error) { - results := make(map[string]EmailSearchResults, len(uniqueAccountIds)) - queryStates := map[string]State{} + return command(j, ctx, cmd, func(body *Response) (map[AccountId]EmailSearchResults, State, Error) { + results := make(map[AccountId]EmailSearchResults, len(uniqueAccountIds)) + queryStates := map[AccountId]State{} for _, accountId := range uniqueAccountIds { var queryResponse EmailQueryResponse err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse) @@ -515,9 +515,9 @@ type EmailQueryWithSnippetsResult struct { QueryState State `json:"queryState"` } -func (j *Client) QueryEmailsWithSnippets(accountIds []string, //NOSONAR +func (j *Client) QueryEmailsWithSnippets(accountIds []AccountId, //NOSONAR filter EmailFilterElement, position int, anchor string, anchorOffset *int, limit *uint, collapseThreads bool, calculateTotal bool, fetchBodies bool, maxBodyValueBytes uint, - ctx Context) (Result[map[string]EmailQueryWithSnippetsResult], error) { + ctx Context) (Result[map[AccountId]EmailQueryWithSnippetsResult], error) { logger := j.loggerParams("QueryEmailsWithSnippets", ctx, func(z zerolog.Context) zerolog.Context { return z.Bool(logFetchBodies, fetchBodies) }) @@ -565,11 +565,11 @@ func (j *Client) QueryEmailsWithSnippets(accountIds []string, //NOSONAR cmd, err := j.request(ctx, NS_MAIL, invocations...) if err != nil { - return ZeroResult[map[string]EmailQueryWithSnippetsResult](), err + return ZeroResult[map[AccountId]EmailQueryWithSnippetsResult](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]EmailQueryWithSnippetsResult, State, Error) { - result := make(map[string]EmailQueryWithSnippetsResult, len(uniqueAccountIds)) + return command(j, ctx, cmd, func(body *Response) (map[AccountId]EmailQueryWithSnippetsResult, State, Error) { + result := make(map[AccountId]EmailQueryWithSnippetsResult, len(uniqueAccountIds)) for _, accountId := range uniqueAccountIds { var queryResponse EmailQueryResponse err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse) @@ -629,7 +629,7 @@ type UploadedEmail struct { Sha512 string `json:"sha:512"` } -func (j *Client) ImportEmail(accountId string, data []byte, ctx Context) (Result[UploadedEmail], error) { +func (j *Client) ImportEmail(accountId AccountId, data []byte, ctx Context) (Result[UploadedEmail], error) { encoded := base64.StdEncoding.EncodeToString(data) upload := BlobUploadCommand{ @@ -702,7 +702,7 @@ func (j *Client) ImportEmail(accountId string, data []byte, ctx Context) (Result } -func (j *Client) CreateEmail(accountId string, email EmailChange, replaceId string, ctx Context) (Result[*Email], error) { +func (j *Client) CreateEmail(accountId AccountId, email EmailChange, replaceId string, ctx Context) (Result[*Email], error) { set := EmailSetCommand{ AccountId: accountId, Create: map[string]EmailChange{ @@ -757,7 +757,7 @@ func (j *Client) CreateEmail(accountId string, email EmailChange, replaceId stri // To create drafts, use the CreateEmail function instead. // // To delete mails, use the DeleteEmails function instead. -func (j *Client) UpdateEmails(accountId string, updates map[string]PatchObject, ctx Context) (Result[map[string]*Email], error) { +func (j *Client) UpdateEmails(accountId AccountId, updates map[string]PatchObject, ctx Context) (Result[map[string]*Email], error) { set := EmailSetCommand{ AccountId: accountId, Update: updates, @@ -783,7 +783,7 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]PatchObject, }) } -func (j *Client) UpdateEmail(accountId string, id string, changes EmailChange, ctx Context) (Result[Email], error) { +func (j *Client) UpdateEmail(accountId AccountId, id string, changes EmailChange, ctx Context) (Result[Email], error) { return update(j, "UpdateEmail", EmailType, func(update map[string]PatchObject) EmailSetCommand { return EmailSetCommand{AccountId: accountId, Update: update} @@ -798,9 +798,9 @@ func (j *Client) UpdateEmail(accountId string, id string, changes EmailChange, c ) } -func (j *Client) DeleteEmails(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteEmails(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteEmails", EmailType, - func(accountId string, destroy []string) EmailSetCommand { + func(accountId AccountId, destroy []string) EmailSetCommand { return EmailSetCommand{AccountId: accountId, Destroy: destroy} }, EmailSetResponse{}, @@ -838,7 +838,7 @@ type MoveMail struct { ToMailboxId string } -func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, move *MoveMail, //NOSONAR +func (j *Client) SubmitEmail(accountId AccountId, identityId string, emailId string, move *MoveMail, //NOSONAR ctx Context) (Result[EmailSubmission], error) { logger := j.logger("SubmitEmail", ctx) ctx = ctx.WithLogger(logger) @@ -926,7 +926,7 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string }) } -func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []string, ctx Context) (Result[EmailSubmissionGetResponse], error) { +func (j *Client) GetEmailSubmissionStatus(accountId AccountId, submissionIds []string, ctx Context) (Result[EmailSubmissionGetResponse], error) { logger := j.logger("GetEmailSubmissionStatus", ctx) ctx = ctx.WithLogger(logger) @@ -949,7 +949,7 @@ func (j *Client) GetEmailSubmissionStatus(accountId string, submissionIds []stri }) } -func (j *Client) EmailsInThread(accountId string, threadId string, +func (j *Client) EmailsInThread(accountId AccountId, threadId string, fetchBodies bool, maxBodyValueBytes uint, ctx Context) (Result[[]Email], Error) { //NOSONAR logger := j.loggerParams("EmailsInThread", ctx, func(z zerolog.Context) zerolog.Context { @@ -1018,9 +1018,9 @@ var EmailSummaryProperties = []string{ EmailPropertyPreview, } -func (j *Client) QueryEmailSummaries(accountIds []string, //NOSONAR +func (j *Client) QueryEmailSummaries(accountIds []AccountId, //NOSONAR filter EmailFilterElement, position int, anchor string, anchorOffset *int, limit *uint, withThreads bool, calculateTotal bool, - ctx Context) (Result[map[string]EmailsSummary], error) { + ctx Context) (Result[map[AccountId]EmailsSummary], error) { logger := j.logger("QueryEmailSummaries", ctx) ctx = ctx.WithLogger(logger) @@ -1067,11 +1067,11 @@ func (j *Client) QueryEmailSummaries(accountIds []string, //NOSONAR } cmd, err := j.request(ctx, NS_MAIL, invocations...) if err != nil { - return ZeroResult[map[string]EmailsSummary](), err + return ZeroResult[map[AccountId]EmailsSummary](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]EmailsSummary, State, Error) { - resp := map[string]EmailsSummary{} + return command(j, ctx, cmd, func(body *Response) (map[AccountId]EmailsSummary, State, Error) { + resp := map[AccountId]EmailsSummary{} for _, accountId := range uniqueAccountIds { var queryResponse EmailQueryResponse err = retrieveResponseMatchParameters(ctx, body, CommandEmailQuery, mcid(accountId, "0"), &queryResponse) @@ -1112,7 +1112,7 @@ type EmailSubmissionChanges = ChangesTemplate[EmailSubmission] // Retrieve the changes in Email Submissions since a given State. // @api:tags email,changes -func (j *Client) GetEmailSubmissionChanges(accountId string, sinceState State, maxChanges uint, +func (j *Client) GetEmailSubmissionChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[EmailSubmissionChanges], error) { return changes(j, "GetEmailSubmissionChanges", EmailSubmissionType, func() EmailSubmissionChangesCommand { diff --git a/pkg/jmap/api_event.go b/pkg/jmap/api_event.go index 8e7d8469d1..374cf5d295 100644 --- a/pkg/jmap/api_event.go +++ b/pkg/jmap/api_event.go @@ -17,9 +17,9 @@ func (r *CalendarEventSearchResults) RemoveResults() { r.Results = n func (r *CalendarEventSearchResults) SetLimit(limit *uint) { r.Limit = limit } func (r *CalendarEventSearchResults) SetPosition(position *uint) { r.Position = position } -func (j *Client) GetCalendarEvents(accountId string, eventIds []string, ctx Context) (Result[CalendarEventGetResponse], error) { +func (j *Client) GetCalendarEvents(accountId AccountId, eventIds []string, ctx Context) (Result[CalendarEventGetResponse], error) { return get(j, "GetCalendarEvents", CalendarEventType, - func(accountId string, ids []string) CalendarEventGetCommand { + func(accountId AccountId, ids []string) CalendarEventGetCommand { return CalendarEventGetCommand{AccountId: accountId, Ids: eventIds} }, CalendarEventGetResponse{}, @@ -29,15 +29,15 @@ func (j *Client) GetCalendarEvents(accountId string, eventIds []string, ctx Cont ) } -func (j *Client) QueryCalendarEvents(accountIds map[string]QueryParams, limit *uint, //NOSONAR +func (j *Client) QueryCalendarEvents(accountIds map[AccountId]QueryParams, limit *uint, //NOSONAR filter CalendarEventFilterElement, sortBy []CalendarEventComparator, calculateTotal bool, - ctx Context) (Result[map[string]*CalendarEventSearchResults], error) { + ctx Context) (Result[map[AccountId]*CalendarEventSearchResults], error) { return queryN(j, "QueryCalendarEvents", CalendarEventType, []CalendarEventComparator{{Property: CalendarEventPropertyStart, IsAscending: false}}, - func(accountId string, queryParams QueryParams, limit *uint, filter CalendarEventFilterElement, sortBy []CalendarEventComparator) CalendarEventQueryCommand { + func(accountId AccountId, queryParams QueryParams, limit *uint, filter CalendarEventFilterElement, sortBy []CalendarEventComparator) CalendarEventQueryCommand { return CalendarEventQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: queryParams.Position, Anchor: queryParams.Anchor, AnchorOffset: queryParams.AnchorOffset, Limit: limit, CalculateTotal: calculateTotal} }, - func(accountId string, cmd Command, path string, rof string) CalendarEventGetRefCommand { + func(accountId AccountId, cmd Command, path string, rof string) CalendarEventGetRefCommand { return CalendarEventGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}} }, func(query CalendarEventQueryResponse, queryParams QueryParams, limit *uint) *CalendarEventSearchResults { @@ -76,7 +76,7 @@ func (c CalendarEventChanges) GetDestroyed() []string { return c.Destroyed // Retrieve the changes in Calendar Events since a given State. // @api:tags event,changes -func (j *Client) GetCalendarEventChanges(accountId string, sinceState State, maxChanges uint, +func (j *Client) GetCalendarEventChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[CalendarEventChanges], error) { return changes(j, "GetCalendarEventChanges", CalendarEventType, func() CalendarEventChangesCommand { @@ -108,12 +108,12 @@ func (j *Client) GetCalendarEventChanges(accountId string, sinceState State, max ) } -func (j *Client) CreateCalendarEvent(accountId string, event CalendarEventChange, ctx Context) (Result[*CalendarEvent], error) { +func (j *Client) CreateCalendarEvent(accountId AccountId, event CalendarEventChange, ctx Context) (Result[*CalendarEvent], error) { return create(j, "CreateCalendarEvent", CalendarEventType, - func(accountId string, create map[string]CalendarEventChange) CalendarEventSetCommand { + func(accountId AccountId, create map[string]CalendarEventChange) CalendarEventSetCommand { return CalendarEventSetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ref string) CalendarEventGetCommand { + func(accountId AccountId, ref string) CalendarEventGetCommand { return CalendarEventGetCommand{AccountId: accountId, Ids: []string{ref}} }, func(resp CalendarEventSetResponse) map[string]*CalendarEvent { @@ -127,9 +127,9 @@ func (j *Client) CreateCalendarEvent(accountId string, event CalendarEventChange ) } -func (j *Client) DeleteCalendarEvent(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteCalendarEvent(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteCalendarEvent", CalendarEventType, - func(accountId string, destroy []string) CalendarEventSetCommand { + func(accountId AccountId, destroy []string) CalendarEventSetCommand { return CalendarEventSetCommand{AccountId: accountId, Destroy: destroy} }, CalendarEventSetResponse{}, @@ -138,7 +138,7 @@ func (j *Client) DeleteCalendarEvent(accountId string, destroyIds []string, ctx ) } -func (j *Client) UpdateCalendarEvent(accountId string, id string, changes CalendarEventChange, ctx Context) (Result[CalendarEvent], error) { +func (j *Client) UpdateCalendarEvent(accountId AccountId, id string, changes CalendarEventChange, ctx Context) (Result[CalendarEvent], error) { return update(j, "UpdateCalendarEvent", CalendarEventType, func(update map[string]PatchObject) CalendarEventSetCommand { return CalendarEventSetCommand{AccountId: accountId, Update: update} diff --git a/pkg/jmap/api_identity.go b/pkg/jmap/api_identity.go index da13c549a3..d3bf5bda14 100644 --- a/pkg/jmap/api_identity.go +++ b/pkg/jmap/api_identity.go @@ -8,9 +8,9 @@ import ( var NS_IDENTITY = ns(JmapMail) -func (j *Client) GetIdentities(accountId string, identityIds []string, ctx Context) (Result[IdentityGetResponse], error) { +func (j *Client) GetIdentities(accountId AccountId, identityIds []string, ctx Context) (Result[IdentityGetResponse], error) { return get(j, "GetIdentities", IdentityType, - func(accountId string, ids []string) IdentityGetCommand { + func(accountId AccountId, ids []string) IdentityGetCommand { return IdentityGetCommand{AccountId: accountId, Ids: ids} }, IdentityGetResponse{}, @@ -20,9 +20,9 @@ func (j *Client) GetIdentities(accountId string, identityIds []string, ctx Conte ) } -func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, ctx Context) (Result[map[string][]Identity], error) { +func (j *Client) GetIdentitiesForAllAccounts(accountIds []AccountId, ctx Context) (Result[map[AccountId][]Identity], error) { return getN(j, "GetIdentitiesForAllAccounts", IdentityType, - func(accountId string, ids []string) IdentityGetCommand { + func(accountId AccountId, ids []string) IdentityGetCommand { return IdentityGetCommand{AccountId: accountId} }, IdentityGetResponse{}, @@ -34,12 +34,12 @@ func (j *Client) GetIdentitiesForAllAccounts(accountIds []string, ctx Context) ( } type IdentitiesAndMailboxesGetResponse struct { - Identities map[string][]Identity `json:"identities,omitempty"` - NotFound []string `json:"notFound,omitempty"` - Mailboxes []Mailbox `json:"mailboxes"` + Identities map[AccountId][]Identity `json:"identities,omitempty"` + NotFound []string `json:"notFound,omitempty"` + Mailboxes []Mailbox `json:"mailboxes"` } -func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds []string, ctx Context) (Result[IdentitiesAndMailboxesGetResponse], error) { +func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId AccountId, accountIds []AccountId, ctx Context) (Result[IdentitiesAndMailboxesGetResponse], error) { uniqueAccountIds := structs.Uniq(accountIds) logger := j.logger("GetIdentitiesAndMailboxes", ctx) @@ -56,8 +56,8 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [ return ZeroResult[IdentitiesAndMailboxesGetResponse](), err } return command(j, ctx, cmd, func(body *Response) (IdentitiesAndMailboxesGetResponse, State, Error) { - identities := make(map[string][]Identity, len(uniqueAccountIds)) - stateByAccountId := make(map[string]State, len(uniqueAccountIds)) + identities := make(map[AccountId][]Identity, len(uniqueAccountIds)) + stateByAccountId := make(map[AccountId]State, len(uniqueAccountIds)) notFound := []string{} for i, accountId := range uniqueAccountIds { var response IdentityGetResponse @@ -85,12 +85,12 @@ func (j *Client) GetIdentitiesAndMailboxes(mailboxAccountId string, accountIds [ }) } -func (j *Client) CreateIdentity(accountId string, identity IdentityChange, ctx Context) (Result[*Identity], error) { +func (j *Client) CreateIdentity(accountId AccountId, identity IdentityChange, ctx Context) (Result[*Identity], error) { return create(j, "CreateIdentity", IdentityType, - func(accountId string, create map[string]IdentityChange) IdentitySetCommand { + func(accountId AccountId, create map[string]IdentityChange) IdentitySetCommand { return IdentitySetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ids string) IdentityGetCommand { + func(accountId AccountId, ids string) IdentityGetCommand { return IdentityGetCommand{AccountId: accountId, Ids: []string{ids}} }, func(resp IdentitySetResponse) map[string]*Identity { @@ -104,7 +104,7 @@ func (j *Client) CreateIdentity(accountId string, identity IdentityChange, ctx C ) } -func (j *Client) UpdateIdentity(accountId string, id string, changes IdentityChange, ctx Context) (Result[Identity], error) { +func (j *Client) UpdateIdentity(accountId AccountId, id string, changes IdentityChange, ctx Context) (Result[Identity], error) { return update(j, "UpdateIdentity", IdentityType, func(update map[string]PatchObject) IdentitySetCommand { return IdentitySetCommand{AccountId: accountId, Update: update} @@ -119,9 +119,9 @@ func (j *Client) UpdateIdentity(accountId string, id string, changes IdentityCha ) } -func (j *Client) DeleteIdentity(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteIdentity(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteIdentity", IdentityType, - func(accountId string, destroy []string) IdentitySetCommand { + func(accountId AccountId, destroy []string) IdentitySetCommand { return IdentitySetCommand{AccountId: accountId, Destroy: destroyIds} }, IdentitySetResponse{}, @@ -143,7 +143,7 @@ func (c IdentityChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve the changes in Email Identities since a given State. // @api:tags email,changes -func (j *Client) GetIdentityChanges(accountId string, sinceState State, maxChanges uint, +func (j *Client) GetIdentityChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[IdentityChanges], error) { return changes(j, "GetIdentityChanges", IdentityType, func() IdentityChangesCommand { diff --git a/pkg/jmap/api_mailbox.go b/pkg/jmap/api_mailbox.go index 7e5fb72393..48447b74bb 100644 --- a/pkg/jmap/api_mailbox.go +++ b/pkg/jmap/api_mailbox.go @@ -8,9 +8,9 @@ import ( var NS_MAILBOX = ns(JmapMail) -func (j *Client) GetMailbox(accountId string, ids []string, ctx Context) (Result[MailboxGetResponse], error) { +func (j *Client) GetMailbox(accountId AccountId, ids []string, ctx Context) (Result[MailboxGetResponse], error) { return get(j, "GetMailbox", MailboxType, - func(accountId string, ids []string) MailboxGetCommand { + func(accountId AccountId, ids []string) MailboxGetCommand { return MailboxGetCommand{AccountId: accountId, Ids: ids} }, MailboxGetResponse{}, @@ -20,9 +20,9 @@ func (j *Client) GetMailbox(accountId string, ids []string, ctx Context) (Result ) } -func (j *Client) GetAllMailboxes(accountIds []string, ctx Context) (Result[map[string][]Mailbox], error) { +func (j *Client) GetAllMailboxes(accountIds []AccountId, ctx Context) (Result[map[AccountId][]Mailbox], error) { return getAN(j, "GetAllMailboxes", MailboxType, - func(accountId string, ids []string) MailboxGetCommand { + func(accountId AccountId, ids []string) MailboxGetCommand { return MailboxGetCommand{AccountId: accountId} }, MailboxGetResponse{}, @@ -32,7 +32,7 @@ func (j *Client) GetAllMailboxes(accountIds []string, ctx Context) (Result[map[s ) } -func (j *Client) SearchMailboxes(accountIds []string, filter MailboxFilterElement, ctx Context) (Result[map[string][]Mailbox], error) { +func (j *Client) SearchMailboxes(accountIds []AccountId, filter MailboxFilterElement, ctx Context) (Result[map[AccountId][]Mailbox], error) { logger := j.logger("SearchMailboxes", ctx) ctx = ctx.WithLogger(logger) @@ -52,12 +52,12 @@ func (j *Client) SearchMailboxes(accountIds []string, filter MailboxFilterElemen } cmd, err := j.request(ctx, NS_MAILBOX, invocations...) if err != nil { - return ZeroResult[map[string][]Mailbox](), err + return ZeroResult[map[AccountId][]Mailbox](), err } - return command(j, ctx, cmd, func(body *Response) (map[string][]Mailbox, State, Error) { - resp := map[string][]Mailbox{} - stateByAccountid := map[string]State{} + return command(j, ctx, cmd, func(body *Response) (map[AccountId][]Mailbox, State, Error) { + resp := map[AccountId][]Mailbox{} + stateByAccountid := map[AccountId]State{} for _, accountId := range uniqueAccountIds { var response MailboxGetResponse err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, mcid(accountId, "1"), &response) @@ -72,7 +72,7 @@ func (j *Client) SearchMailboxes(accountIds []string, filter MailboxFilterElemen }) } -func (j *Client) SearchMailboxIdsPerRole(accountIds []string, roles []string, ctx Context) (Result[map[string]map[string]string], error) { //NOSONAR +func (j *Client) SearchMailboxIdsPerRole(accountIds []AccountId, roles []string, ctx Context) (Result[map[AccountId]map[string]string], error) { //NOSONAR logger := j.logger("SearchMailboxIdsPerRole", ctx) ctx = ctx.WithLogger(logger) @@ -86,12 +86,12 @@ func (j *Client) SearchMailboxIdsPerRole(accountIds []string, roles []string, ct } cmd, err := j.request(ctx, NS_MAILBOX, invocations...) if err != nil { - return ZeroResult[map[string]map[string]string](), err + return ZeroResult[map[AccountId]map[string]string](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]map[string]string, State, Error) { - resp := map[string]map[string]string{} - stateByAccountid := map[string]State{} + return command(j, ctx, cmd, func(body *Response) (map[AccountId]map[string]string, State, Error) { + resp := map[AccountId]map[string]string{} + stateByAccountid := map[AccountId]State{} for _, accountId := range uniqueAccountIds { mailboxIdsByRole := map[string]string{} for _, role := range roles { @@ -137,7 +137,7 @@ 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, sinceState State, maxChanges uint, ctx Context) (Result[MailboxChanges], error) { +func (j *Client) GetMailboxChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[MailboxChanges], error) { return changesA(j, "GetMailboxChanges", MailboxType, func() MailboxChangesCommand { return MailboxChangesCommand{AccountId: accountId, SinceState: sinceState, MaxChanges: uintPtr(maxChanges)} @@ -161,16 +161,16 @@ func (j *Client) GetMailboxChanges(accountId string, sinceState State, maxChange // Retrieve Mailbox changes of multiple Accounts. // @api:tags email,changes -func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, //NOSONAR - sinceStateMap map[string]State, maxChanges uint, - ctx Context) (Result[map[string]MailboxChanges], error) { +func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []AccountId, //NOSONAR + sinceStateMap map[AccountId]State, maxChanges uint, + ctx Context) (Result[map[AccountId]MailboxChanges], error) { return changesN(j, "GetMailboxChangesForMultipleAccounts", MailboxType, accountIds, sinceStateMap, - func(accountId string, state State) MailboxChangesCommand { + func(accountId AccountId, state State) MailboxChangesCommand { return MailboxChangesCommand{AccountId: accountId, SinceState: state, MaxChanges: uintPtr(maxChanges)} }, MailboxChangesResponse{}, - func(accountId string, path string, ref string) MailboxGetRefCommand { + func(accountId AccountId, path string, ref string) MailboxGetRefCommand { return MailboxGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: CommandMailboxChanges, Path: path, ResultOf: ref}} }, func(resp MailboxGetResponse) []Mailbox { return resp.List }, @@ -181,13 +181,13 @@ func (j *Client) GetMailboxChangesForMultipleAccounts(accountIds []string, //NOS ) } -func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, ctx Context) (Result[map[string]*[]string], error) { +func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []AccountId, ctx Context) (Result[map[AccountId]*[]string], error) { return queryN(j, "GetMailboxRolesForMultipleAccounts", MailboxType, []MailboxComparator{{Property: MailboxPropertySortOrder, IsAscending: true}}, - func(accountId string, _ QueryParams, _ *uint, filter MailboxFilterCondition, sortBy []MailboxComparator) MailboxQueryCommand { + func(accountId AccountId, _ QueryParams, _ *uint, filter MailboxFilterCondition, sortBy []MailboxComparator) MailboxQueryCommand { return MailboxQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, SortAsTree: false, FilterAsTree: false, Position: 0, Anchor: "", AnchorOffset: nil, Limit: nil, CalculateTotal: false} }, - func(accountId string, cmd Command, path, rof string) MailboxGetRefCommand { + func(accountId AccountId, cmd Command, path, rof string) MailboxGetRefCommand { return MailboxGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}} }, func(_ MailboxQueryResponse, _ QueryParams, _ *uint) *[]string { @@ -203,14 +203,14 @@ func (j *Client) GetMailboxRolesForMultipleAccounts(accountIds []string, ctx Con ) } -func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, ctx Context) (Result[map[string]string], error) { +func (j *Client) GetInboxNameForMultipleAccounts(accountIds []AccountId, ctx Context) (Result[map[AccountId]string], error) { logger := j.logger("GetInboxNameForMultipleAccounts", ctx) ctx = ctx.WithLogger(logger) uniqueAccountIds := structs.Uniq(accountIds) n := len(uniqueAccountIds) if n < 1 { - return ZeroResult[map[string]string](), nil + return ZeroResult[map[AccountId]string](), nil } invocations := make([]Invocation, n*2) @@ -225,12 +225,12 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, ctx Contex cmd, err := j.request(ctx, NS_MAILBOX, invocations...) if err != nil { - return ZeroResult[map[string]string](), err + return ZeroResult[map[AccountId]string](), err } - return command(j, ctx, cmd, func(body *Response) (map[string]string, State, Error) { - resp := make(map[string]string, n) - stateByAccountId := make(map[string]State, n) + return command(j, ctx, cmd, func(body *Response) (map[AccountId]string, State, Error) { + resp := make(map[AccountId]string, n) + stateByAccountId := make(map[AccountId]State, n) for _, accountId := range uniqueAccountIds { var r MailboxQueryResponse err = retrieveResponseMatchParameters(ctx, body, CommandMailboxGet, mcid(accountId, "0"), &r) @@ -253,7 +253,7 @@ func (j *Client) GetInboxNameForMultipleAccounts(accountIds []string, ctx Contex }) } -func (j *Client) UpdateMailbox(accountId string, mailboxId string, change MailboxChange, //NOSONAR +func (j *Client) UpdateMailbox(accountId AccountId, mailboxId string, change MailboxChange, //NOSONAR ctx Context) (Result[Mailbox], error) { return update(j, "UpdateMailbox", MailboxType, func(update map[string]PatchObject) MailboxSetCommand { @@ -269,12 +269,12 @@ func (j *Client) UpdateMailbox(accountId string, mailboxId string, change Mailbo ) } -func (j *Client) CreateMailbox(accountId string, mailbox MailboxChange, ctx Context) (Result[*Mailbox], error) { +func (j *Client) CreateMailbox(accountId AccountId, mailbox MailboxChange, ctx Context) (Result[*Mailbox], error) { return create(j, "CreateMailbox", MailboxType, - func(accountId string, create map[string]MailboxChange) MailboxSetCommand { + func(accountId AccountId, create map[string]MailboxChange) MailboxSetCommand { return MailboxSetCommand{AccountId: accountId, Create: create} }, - func(accountId string, ids string) MailboxGetCommand { + func(accountId AccountId, ids string) MailboxGetCommand { return MailboxGetCommand{AccountId: accountId, Ids: []string{ids}} }, func(resp MailboxSetResponse) map[string]*Mailbox { @@ -288,9 +288,9 @@ func (j *Client) CreateMailbox(accountId string, mailbox MailboxChange, ctx Cont ) } -func (j *Client) DeleteMailboxes(accountId string, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { +func (j *Client) DeleteMailboxes(accountId AccountId, destroyIds []string, ctx Context) (Result[map[string]SetError], error) { return destroy(j, "DeleteMailboxes", MailboxType, - func(accountId string, destroy []string) MailboxSetCommand { + func(accountId AccountId, destroy []string) MailboxSetCommand { return MailboxSetCommand{AccountId: accountId, Destroy: destroyIds} }, MailboxSetResponse{}, diff --git a/pkg/jmap/api_objects.go b/pkg/jmap/api_objects.go index 401523e110..14820a84a2 100644 --- a/pkg/jmap/api_objects.go +++ b/pkg/jmap/api_objects.go @@ -20,7 +20,7 @@ type Objects struct { // Retrieve objects of all types by their identifiers in a single batch. // @api:tags changes -func (j *Client) GetObjects(accountId string, //NOSONAR +func (j *Client) GetObjects(accountId AccountId, //NOSONAR mailboxIds []string, emailIds []string, addressbookIds []string, contactIds []string, calendarIds []string, eventIds []string, diff --git a/pkg/jmap/api_principal.go b/pkg/jmap/api_principal.go index 9f3470bcdb..6cf66fc4a6 100644 --- a/pkg/jmap/api_principal.go +++ b/pkg/jmap/api_principal.go @@ -2,9 +2,9 @@ package jmap var NS_PRINCIPALS = ns(JmapPrincipals) -func (j *Client) GetPrincipals(accountId string, ids []string, ctx Context) (Result[PrincipalGetResponse], error) { +func (j *Client) GetPrincipals(accountId AccountId, ids []PrincipalId, ctx Context) (Result[PrincipalGetResponse], error) { return get(j, "GetPrincipals", PrincipalType, - func(accountId string, ids []string) PrincipalGetCommand { + func(accountId AccountId, ids []PrincipalId) PrincipalGetCommand { return PrincipalGetCommand{AccountId: accountId, Ids: ids} }, PrincipalGetResponse{}, @@ -29,15 +29,15 @@ func (r *PrincipalSearchResults) RemoveResults() { r.Results = nil } func (r *PrincipalSearchResults) SetLimit(limit *uint) { r.Limit = limit } func (r *PrincipalSearchResults) SetPosition(position *uint) { r.Position = position } -func (j *Client) QueryPrincipals(accountIds map[string]QueryParams, limit *uint, //NOSONAR +func (j *Client) QueryPrincipals(accountIds map[AccountId]QueryParams, limit *uint, //NOSONAR filter PrincipalFilterElement, sortBy []PrincipalComparator, calculateTotal bool, - ctx Context) (Result[map[string]*PrincipalSearchResults], error) { + ctx Context) (Result[map[AccountId]*PrincipalSearchResults], error) { return queryN(j, "QueryPrincipals", PrincipalType, []PrincipalComparator{{Property: PrincipalPropertyName, IsAscending: true}}, - func(accountId string, p QueryParams, limit *uint, filter PrincipalFilterElement, sortBy []PrincipalComparator) PrincipalQueryCommand { + func(accountId AccountId, p QueryParams, limit *uint, filter PrincipalFilterElement, sortBy []PrincipalComparator) PrincipalQueryCommand { return PrincipalQueryCommand{AccountId: accountId, Filter: filter, Sort: sortBy, Position: p.Position, Anchor: p.Anchor, AnchorOffset: p.AnchorOffset, Limit: limit, CalculateTotal: calculateTotal} }, - func(accountId string, cmd Command, path, rof string) PrincipalGetRefCommand { + func(accountId AccountId, cmd Command, path, rof string) PrincipalGetRefCommand { return PrincipalGetRefCommand{AccountId: accountId, IdsRef: &ResultReference{Name: cmd, Path: path, ResultOf: rof}} }, func(query PrincipalQueryResponse, queryParams QueryParams, limit *uint) *PrincipalSearchResults { diff --git a/pkg/jmap/api_quota.go b/pkg/jmap/api_quota.go index 3950402c7d..4fbb6e34f8 100644 --- a/pkg/jmap/api_quota.go +++ b/pkg/jmap/api_quota.go @@ -2,9 +2,9 @@ package jmap var NS_QUOTA = ns(JmapQuota) -func (j *Client) GetQuotas(accountIds []string, ctx Context) (Result[map[string]QuotaGetResponse], error) { +func (j *Client) GetQuotas(accountIds []AccountId, ctx Context) (Result[map[AccountId]QuotaGetResponse], error) { return getN(j, "GetQuotas", QuotaType, - func(accountId string, ids []string) QuotaGetCommand { + func(accountId AccountId, ids []string) QuotaGetCommand { return QuotaGetCommand{AccountId: accountId} }, QuotaGetResponse{}, @@ -28,7 +28,7 @@ func (c QuotaChanges) GetDestroyed() []string { return c.Destroyed } // Retrieve the changes in Quotas since a given State. // @api:tags quota,changes -func (j *Client) GetQuotaChanges(accountId string, sinceState State, maxChanges uint, +func (j *Client) GetQuotaChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[QuotaChanges], error) { return changesA(j, "GetQuotaChanges", QuotaType, func() QuotaChangesCommand { @@ -60,7 +60,7 @@ func (j *Client) GetQuotaChanges(accountId string, sinceState State, maxChanges ) } -func (j *Client) GetQuotaUsageChanges(accountId string, sinceState State, maxChanges uint, +func (j *Client) GetQuotaUsageChanges(accountId AccountId, sinceState State, maxChanges uint, ctx Context) (Result[QuotaChanges], error) { return updates(j, "GetQuotaUsageChanges", QuotaType, func() QuotaChangesCommand { diff --git a/pkg/jmap/api_vacation.go b/pkg/jmap/api_vacation.go index 21eb6ad1ce..eab5d50b78 100644 --- a/pkg/jmap/api_vacation.go +++ b/pkg/jmap/api_vacation.go @@ -11,9 +11,9 @@ const ( vacationResponseId = "singleton" ) -func (j *Client) GetVacationResponse(accountId string, ctx Context) (Result[VacationResponseGetResponse], error) { +func (j *Client) GetVacationResponse(accountId AccountId, ctx Context) (Result[VacationResponseGetResponse], error) { return get(j, "GetVacationResponse", VacationResponseType, - func(accountId string, ids []string) VacationResponseGetCommand { + func(accountId AccountId, ids []string) VacationResponseGetCommand { return VacationResponseGetCommand{AccountId: accountId} }, VacationResponseGetResponse{}, @@ -64,7 +64,7 @@ func (c VacationResponseChanges) GetCreated() []VacationResponse { return c.Crea func (c VacationResponseChanges) GetUpdated() []VacationResponse { return c.Updated } func (c VacationResponseChanges) GetDestroyed() []string { return c.Destroyed } -func (j *Client) SetVacationResponse(accountId string, vacation VacationResponseChange, +func (j *Client) SetVacationResponse(accountId AccountId, vacation VacationResponseChange, ctx Context) (Result[VacationResponse], error) { logger := j.logger("SetVacationResponse", ctx) ctx = ctx.WithLogger(logger) diff --git a/pkg/jmap/export_integration_test.go b/pkg/jmap/export_integration_test.go index 3d8dc02721..ae596bbfd1 100644 --- a/pkg/jmap/export_integration_test.go +++ b/pkg/jmap/export_integration_test.go @@ -584,8 +584,8 @@ type uploadedBlob struct { Sha512 string `json:"sha:512"` } -func (j *TestJmapClient) uploadBlob(accountId string, data []byte, mimetype string) (uploadedBlob, error) { //NOSONAR - uploadUrl := strings.ReplaceAll(j.session.UploadUrl, "{accountId}", accountId) +func (j *TestJmapClient) uploadBlob(accountId AccountId, data []byte, mimetype string) (uploadedBlob, error) { //NOSONAR + uploadUrl := strings.ReplaceAll(j.session.UploadUrl, "{accountId}", string(accountId)) req, err := http.NewRequest(http.MethodPost, uploadUrl, bytes.NewReader(data)) if err != nil { return uploadedBlob{}, err @@ -707,7 +707,7 @@ func (c Commander[T]) command(body map[string]any) (T, error) { return c.closure(methodResponses) } -func (j *TestJmapClient) objectsById(accountId string, objectType ObjectType) (map[string]map[string]any, error) { +func (j *TestJmapClient) objectsById(accountId AccountId, objectType ObjectType) (map[string]map[string]any, error) { m := map[string]map[string]any{} { body := map[string]any{ @@ -1154,13 +1154,13 @@ func deepEqual[T any](t *testing.T, expected, actual T) { } func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change](t *testing.T, //NOSONAR - acc func(session *Session) string, + acc func(session *Session) AccountId, obj func(RESP) []OBJ, id func(OBJ) string, - get func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[RESP], error), - update func(s *StalwartTest, accountId string, id string, change CHANGE, ctx Context) (Result[OBJ], error), - destroy func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[map[string]SetError], error), - fill func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, _ User, principalIds []string) (BOXES, []OBJ, SessionState, State, error), + get func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[RESP], error), + update func(s *StalwartTest, accountId AccountId, id string, change CHANGE, ctx Context) (Result[OBJ], error), + destroy func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[map[string]SetError], error), + fill func(s *StalwartTest, t *testing.T, accountId AccountId, count uint, ctx Context, _ User, principalIds []PrincipalId) (BOXES, []OBJ, SessionState, State, error), change func(OBJ) CHANGE, checkChanged func(t *testing.T, orig OBJ, change CHANGE, changed OBJ), ) { @@ -1177,12 +1177,12 @@ func containerTest[OBJ Idable, RESP GetResponse[OBJ], BOXES any, CHANGE Change]( accountId := acc(session) // we first need to retrieve the list of all the Principals in order to be able to use and test sharing - principalIds := []string{} + principalIds := []PrincipalId{} { - result, err := s.client.GetPrincipals(accountId, []string{}, ctx) + result, err := s.client.GetPrincipals(accountId, []PrincipalId{}, ctx) require.NoError(err) require.NotEmpty(result.Payload.List) - principalIds = structs.Map(result.Payload.List, func(p Principal) string { return p.Id }) + principalIds = structs.Map(result.Payload.List, func(p Principal) PrincipalId { return PrincipalId(p.Id) }) } ss := EmptySessionState diff --git a/pkg/jmap/integration_addressbook_test.go b/pkg/jmap/integration_addressbook_test.go index 50261d59cd..97428cc78e 100644 --- a/pkg/jmap/integration_addressbook_test.go +++ b/pkg/jmap/integration_addressbook_test.go @@ -42,19 +42,19 @@ func TestAddressBooks(t *testing.T) { } containerTest(t, - func(session *Session) string { return session.PrimaryAccounts.Contacts }, + func(session *Session) AccountId { return session.PrimaryAccounts.Contacts }, list, getid, - func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[AddressBookGetResponse], error) { + func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[AddressBookGetResponse], error) { return s.client.GetAddressbooks(accountId, ids, ctx) }, - func(s *StalwartTest, accountId string, id string, change AddressBookChange, ctx Context) (Result[AddressBook], error) { //NOSONAR + func(s *StalwartTest, accountId AccountId, id string, change AddressBookChange, ctx Context) (Result[AddressBook], error) { //NOSONAR return s.client.UpdateAddressBook(accountId, id, change, ctx) }, - func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[map[string]SetError], error) { //NOSONAR + func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[map[string]SetError], error) { //NOSONAR return s.client.DeleteAddressBook(accountId, ids, ctx) }, - func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, user User, principalIds []string) (AddressBookBoxes, []AddressBook, SessionState, State, error) { + func(s *StalwartTest, t *testing.T, accountId AccountId, count uint, ctx Context, user User, principalIds []PrincipalId) (AddressBookBoxes, []AddressBook, SessionState, State, error) { return s.fillAddressBook(t, accountId, count, ctx, user, principalIds) }, func(orig AddressBook) AddressBookChange { @@ -104,7 +104,7 @@ func TestContacts(t *testing.T) { ss := EmptySessionState os := EmptyState { - result, err := s.client.QueryContactCards(toNullQueryParams([]string{accountId}), nil, filter, sortBy, true, ctx) + result, err := s.client.QueryContactCards(toNullQueryParams([]AccountId{accountId}), nil, filter, sortBy, true, ctx) require.NoError(err) require.Len(result.Payload, 1) @@ -161,7 +161,7 @@ func TestContacts(t *testing.T) { for i := range slices { position := int(i * limit) page := min(remainder, limit) - result, err := s.client.QueryContactCards(map[string]QueryParams{accountId: {Position: position}}, &limit, filter, sortBy, true, ctx) + result, err := s.client.QueryContactCards(map[AccountId]QueryParams{accountId: {Position: position}}, &limit, filter, sortBy, true, ctx) require.NoError(err) require.Len(result.Payload, 1) require.Contains(result.Payload, accountId) @@ -186,7 +186,7 @@ func TestContacts(t *testing.T) { offset := 0 i := 0 for chunk := range slices.Chunk(results.Results, chunkSize) { - result, err := s.client.QueryContactCards(map[string]QueryParams{accountId: {Anchor: anchor, AnchorOffset: &offset}}, uintPtr(chunkSize), filter, sortBy, true, ctx) + result, err := s.client.QueryContactCards(map[AccountId]QueryParams{accountId: {Anchor: anchor, AnchorOffset: &offset}}, uintPtr(chunkSize), filter, sortBy, true, ctx) require.Equal(ss, result.GetSessionState()) require.NoError(err) require.Len(result.Payload, 1) @@ -236,7 +236,7 @@ func TestContacts(t *testing.T) { os = result.GetState() } { - result, err := s.client.QueryContactCards(toNullQueryParams([]string{accountId}), nil, filter, sortBy, true, ctx) + result, err := s.client.QueryContactCards(toNullQueryParams([]AccountId{accountId}), nil, filter, sortBy, true, ctx) require.NoError(err) require.Contains(result.Payload, accountId) resp := result.Payload[accountId] @@ -278,11 +278,11 @@ var streetNumberRegex = regexp.MustCompile(`^(\d+)\s+(.+)$`) func (s *StalwartTest) fillAddressBook( //NOSONAR t *testing.T, - accountId string, + accountId AccountId, count uint, ctx Context, _ User, - principalIds []string, + principalIds []PrincipalId, ) (AddressBookBoxes, []AddressBook, SessionState, State, error) { require := require.New(t) @@ -322,7 +322,7 @@ func (s *StalwartTest) fillAddressBook( //NOSONAR } if sharing != nil { numPrincipals := 1 + rand.Intn(len(principalIds)-1) - m := make(map[string]AddressBookRights, numPrincipals) + m := make(map[PrincipalId]AddressBookRights, numPrincipals) for _, p := range pickRandomN(numPrincipals, principalIds...) { m[p] = *sharing } @@ -357,7 +357,7 @@ func (s *StalwartTest) fillContacts( //NOSONAR session *Session, ctx Context, user User, -) (string, string, map[string]ContactCard, ContactsBoxes, error) { +) (AccountId, string, map[string]ContactCard, ContactsBoxes, error) { require := require.New(t) c, err := NewTestJmapClient(session, user.name, user.password, true, true) require.NoError(err) diff --git a/pkg/jmap/integration_calendar_test.go b/pkg/jmap/integration_calendar_test.go index 4978097851..53aaf24976 100644 --- a/pkg/jmap/integration_calendar_test.go +++ b/pkg/jmap/integration_calendar_test.go @@ -32,19 +32,19 @@ func TestCalendars(t *testing.T) { //NOSONAR } containerTest(t, - func(session *Session) string { return session.PrimaryAccounts.Calendars }, + func(session *Session) AccountId { return session.PrimaryAccounts.Calendars }, func(resp CalendarGetResponse) []Calendar { return resp.List }, func(obj Calendar) string { return obj.Id }, - func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[CalendarGetResponse], error) { + func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[CalendarGetResponse], error) { return s.client.GetCalendars(accountId, ids, ctx) }, - func(s *StalwartTest, accountId string, id string, change CalendarChange, ctx Context) (Result[Calendar], error) { //NOSONAR + func(s *StalwartTest, accountId AccountId, id string, change CalendarChange, ctx Context) (Result[Calendar], error) { //NOSONAR return s.client.UpdateCalendar(accountId, id, change, ctx) }, - func(s *StalwartTest, accountId string, ids []string, ctx Context) (Result[map[string]SetError], error) { //NOSONAR + func(s *StalwartTest, accountId AccountId, ids []string, ctx Context) (Result[map[string]SetError], error) { //NOSONAR return s.client.DeleteCalendar(accountId, ids, ctx) }, - func(s *StalwartTest, t *testing.T, accountId string, count uint, ctx Context, user User, principalIds []string) (CalendarBoxes, []Calendar, SessionState, State, error) { + func(s *StalwartTest, t *testing.T, accountId AccountId, count uint, ctx Context, user User, principalIds []PrincipalId) (CalendarBoxes, []Calendar, SessionState, State, error) { return s.fillCalendar(t, accountId, count, ctx, user, principalIds) }, func(orig Calendar) CalendarChange { @@ -94,7 +94,7 @@ func TestEvents(t *testing.T) { os := EmptyState var results *CalendarEventSearchResults { - result, err := s.client.QueryCalendarEvents(toNullQueryParams([]string{accountId}), nil, filter, sortBy, true, ctx) + result, err := s.client.QueryCalendarEvents(toNullQueryParams([]AccountId{accountId}), nil, filter, sortBy, true, ctx) require.NoError(err) require.Len(result.Payload, 1) @@ -127,7 +127,7 @@ func TestEvents(t *testing.T) { for i := range slices { position := int(i * limit) page := min(remainder, limit) - result, err := s.client.QueryCalendarEvents(map[string]QueryParams{accountId: {Position: position}}, &limit, filter, sortBy, true, ctx) + result, err := s.client.QueryCalendarEvents(map[AccountId]QueryParams{accountId: {Position: position}}, &limit, filter, sortBy, true, ctx) require.NoError(err) require.Len(result.Payload, 1) require.Contains(result.Payload, accountId) @@ -152,7 +152,7 @@ func TestEvents(t *testing.T) { offset := 0 i := 0 for chunk := range slices.Chunk(results.Results, chunkSize) { - result, err := s.client.QueryCalendarEvents(map[string]QueryParams{accountId: {Anchor: anchor, AnchorOffset: &offset}}, uintPtr(chunkSize), filter, sortBy, true, ctx) + result, err := s.client.QueryCalendarEvents(map[AccountId]QueryParams{accountId: {Anchor: anchor, AnchorOffset: &offset}}, uintPtr(chunkSize), filter, sortBy, true, ctx) require.Equal(ss, result.GetSessionState()) require.NoError(err) require.Len(result.Payload, 1) @@ -207,7 +207,7 @@ func TestEvents(t *testing.T) { } { - result, err := s.client.QueryCalendarEvents(toNullQueryParams([]string{accountId}), nil, filter, sortBy, true, ctx) + result, err := s.client.QueryCalendarEvents(toNullQueryParams([]AccountId{accountId}), nil, filter, sortBy, true, ctx) require.NoError(err) require.Contains(result.Payload, accountId) resp := result.Payload[accountId] @@ -239,11 +239,11 @@ type CalendarBoxes struct { func (s *StalwartTest) fillCalendar( //NOSONAR t *testing.T, - accountId string, + accountId AccountId, count uint, ctx Context, _ User, - principalIds []string, + principalIds []PrincipalId, ) (CalendarBoxes, []Calendar, SessionState, State, error) { require := require.New(t) @@ -340,7 +340,7 @@ func (s *StalwartTest) fillCalendar( //NOSONAR } if sharing != nil { numPrincipals := 1 + rand.Intn(len(principalIds)-1) - m := make(map[string]CalendarRights, numPrincipals) + m := make(map[PrincipalId]CalendarRights, numPrincipals) for _, p := range pickRandomN(numPrincipals, principalIds...) { m[p] = *sharing } @@ -380,7 +380,7 @@ func (s *StalwartTest) fillEvents( //NOSONAR count uint, ctx Context, user User, -) (string, string, map[string]CalendarEvent, EventsBoxes, error) { +) (AccountId, string, map[string]CalendarEvent, EventsBoxes, error) { require := require.New(t) c, err := NewTestJmapClient(ctx.Session, user.name, user.password, true, true) require.NoError(err) diff --git a/pkg/jmap/integration_email_test.go b/pkg/jmap/integration_email_test.go index b85dfa3b0a..d7b1e1389f 100644 --- a/pkg/jmap/integration_email_test.go +++ b/pkg/jmap/integration_email_test.go @@ -65,7 +65,7 @@ func TestEmails(t *testing.T) { } { - result, err := s.client.GetAllMailboxes([]string{accountId}, ctx) + result, err := s.client.GetAllMailboxes([]AccountId{accountId}, ctx) require.NoError(err) require.Equal(session.State, result.GetSessionState()) require.Len(result.Payload, 1) @@ -144,7 +144,7 @@ func TestSendingEmails(t *testing.T) { var mailboxPerRole map[string]Mailbox { - result, err := s.client.GetAllMailboxes([]string{accountId}, ctx) + result, err := s.client.GetAllMailboxes([]AccountId{accountId}, ctx) require.NoError(err) mailboxPerRole = structs.Index(result.Payload[accountId], func(m Mailbox) string { return m.Role }) require.Contains(mailboxPerRole, JmapMailboxRoleInbox) @@ -154,7 +154,7 @@ func TestSendingEmails(t *testing.T) { } { roles := []string{JmapMailboxRoleDrafts, JmapMailboxRoleSent, JmapMailboxRoleInbox} - result, err := s.client.SearchMailboxIdsPerRole([]string{accountId}, roles, ctx) + result, err := s.client.SearchMailboxIdsPerRole([]AccountId{accountId}, roles, ctx) require.NoError(err) require.Contains(result.Payload, accountId) a := result.Payload[accountId] @@ -165,7 +165,7 @@ func TestSendingEmails(t *testing.T) { // let's ensure that the recipients have zero emails in their mailboxes before we send them any for _, u := range []struct { - accountId string + accountId AccountId session *Session }{{toAccountId, toSession}, {ccAccountId, ccSession}} { uctx := Context{ @@ -174,7 +174,7 @@ func TestSendingEmails(t *testing.T) { Logger: ctx.Logger, AcceptLanguage: ctx.AcceptLanguage, } - result, err := s.client.GetAllMailboxes([]string{u.accountId}, uctx) + result, err := s.client.GetAllMailboxes([]AccountId{u.accountId}, uctx) require.NoError(err) for _, mailbox := range result.Payload[u.accountId] { require.Equal(0, mailbox.TotalEmails) @@ -306,7 +306,7 @@ func TestSendingEmails(t *testing.T) { for _, r := range []struct { user User - accountId string + accountId AccountId session *Session }{{to, toAccountId, toSession}, {cc, ccAccountId, ccSession}} { rctx := Context{ @@ -317,7 +317,7 @@ func TestSendingEmails(t *testing.T) { } inboxId := "" { - result, err := s.client.GetAllMailboxes([]string{r.accountId}, rctx) + result, err := s.client.GetAllMailboxes([]AccountId{r.accountId}, rctx) require.NoError(err) for _, mailbox := range result.Payload[r.accountId] { if mailbox.Role == JmapMailboxRoleInbox { @@ -328,7 +328,7 @@ func TestSendingEmails(t *testing.T) { require.NotEmpty(inboxId, "failed to find the Mailbox with the 'inbox' role for %v", r.user.name) } - result, err := s.client.QueryEmails([]string{r.accountId}, EmailFilterCondition{InMailbox: inboxId}, 0, 0, true, 0, rctx) + result, err := s.client.QueryEmails([]AccountId{r.accountId}, EmailFilterCondition{InMailbox: inboxId}, 0, 0, true, 0, rctx) require.NoError(err) require.Contains(result.Payload, r.accountId) require.Len(result.Payload[r.accountId].Results, 1) @@ -385,9 +385,9 @@ func matchEmail(t *testing.T, actual Email, expected filledMail, hasBodies bool) } } -func (s *StalwartTest) findInbox(t *testing.T, accountId string, ctx Context) (string, string) { +func (s *StalwartTest) findInbox(t *testing.T, accountId AccountId, ctx Context) (string, string) { require := require.New(t) - result, err := s.client.GetAllMailboxes([]string{accountId}, ctx) + result, err := s.client.GetAllMailboxes([]AccountId{accountId}, ctx) require.NoError(err) require.Equal(ctx.Session.State, result.GetSessionState()) require.Len(result.Payload, 1) diff --git a/pkg/jmap/integration_ws_test.go b/pkg/jmap/integration_ws_test.go index 499141038b..8bcd0b35e3 100644 --- a/pkg/jmap/integration_ws_test.go +++ b/pkg/jmap/integration_ws_test.go @@ -17,7 +17,7 @@ type testWsPushListener struct { t *testing.T logger *log.Logger username string - mailAccountId string + mailAccountId AccountId calls atomic.Uint32 m sync.Mutex emailStates []string @@ -46,7 +46,7 @@ func (l *testWsPushListener) OnNotification(username string, pushState StateChan 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 }) + unsupportedAccounts := structs.Filter(structs.Keys(pushState.Changed), func(s AccountId) bool { return s != l.mailAccountId }) assert.Empty(l.t, unsupportedAccounts) } diff --git a/pkg/jmap/model.go b/pkg/jmap/model.go index 9dfe499a6f..1ec63cc164 100644 --- a/pkg/jmap/model.go +++ b/pkg/jmap/model.go @@ -445,6 +445,10 @@ var ( } ) +type AccountId string + +type PrincipalId string + type SessionMailAccountCapabilities struct { // The maximum number of Mailboxes that can be can assigned to a single Email object. // @@ -683,16 +687,16 @@ type SessionTasksCustomTimezonesAccountCapabilities struct { type SessionPrincipalsAccountCapabilities struct { // The id of the principal in this account that corresponds to the user fetching this object, if any. - CurrentUserPrincipalId string `json:"currentUserPrincipalId,omitempty"` + CurrentUserPrincipalId PrincipalId `json:"currentUserPrincipalId,omitempty"` } type SessionPrincipalsOwnerAccountCapabilities struct { // The id of an account with the `urn:ietf:params:jmap:principals` capability that contains the // corresponding `Principal` object. - AccountIdForPrincipal string `json:"accountIdForPrincipal,omitempty"` + AccountIdForPrincipal AccountId `json:"accountIdForPrincipal,omitempty"` // The id of the `Principal` that owns this account. - PrincipalId string `json:"principalId,omitempty"` + PrincipalId PrincipalId `json:"principalId,omitempty"` } type SessionPrincipalAvailabilityAccountCapabilities struct { @@ -821,7 +825,7 @@ type SessionPrincipalCapabilities struct { // // The corresponding Account object can be found in the Principal's "accounts" property, as // per Section 2 of [RFC9670](https://www.rfc-editor.org/rfc/rfc9670.html). - AccountId string `json:"accountId,omitempty"` + AccountId AccountId `json:"accountId,omitempty"` // If true, the user may call the "Principal/getAvailability" method with this Principal. MayGetAvailability *bool `json:"mayGetAvailability,omitzero"` @@ -916,19 +920,19 @@ type SessionCapabilities struct { } type SessionPrimaryAccounts struct { - Core string `json:"urn:ietf:params:jmap:core,omitempty"` - Mail string `json:"urn:ietf:params:jmap:mail,omitempty"` - Submission string `json:"urn:ietf:params:jmap:submission,omitempty"` - VacationResponse string `json:"urn:ietf:params:jmap:vacationresponse,omitempty"` - Sieve string `json:"urn:ietf:params:jmap:sieve,omitempty"` - Blob string `json:"urn:ietf:params:jmap:blob,omitempty"` - Quota string `json:"urn:ietf:params:jmap:quota,omitempty"` - Websocket string `json:"urn:ietf:params:jmap:websocket,omitempty"` - Task string `json:"urn:ietf:params:jmap:task,omitempty"` - Calendars string `json:"urn:ietf:params:jmap:calendars,omitempty"` - CalendarsParse string `json:"urn:ietf:params:jmap:calendars:parse,omitempty"` - Contacts string `json:"urn:ietf:params:jmap:contacts,omitempty"` - ContactsParse string `json:"urn:ietf:params:jmap:contacts:parse,omitempty"` + Core AccountId `json:"urn:ietf:params:jmap:core,omitempty"` + Mail AccountId `json:"urn:ietf:params:jmap:mail,omitempty"` + Submission AccountId `json:"urn:ietf:params:jmap:submission,omitempty"` + VacationResponse AccountId `json:"urn:ietf:params:jmap:vacationresponse,omitempty"` + Sieve AccountId `json:"urn:ietf:params:jmap:sieve,omitempty"` + Blob AccountId `json:"urn:ietf:params:jmap:blob,omitempty"` + Quota AccountId `json:"urn:ietf:params:jmap:quota,omitempty"` + Websocket AccountId `json:"urn:ietf:params:jmap:websocket,omitempty"` + Task AccountId `json:"urn:ietf:params:jmap:task,omitempty"` + Calendars AccountId `json:"urn:ietf:params:jmap:calendars,omitempty"` + CalendarsParse AccountId `json:"urn:ietf:params:jmap:calendars:parse,omitempty"` + Contacts AccountId `json:"urn:ietf:params:jmap:contacts,omitempty"` + ContactsParse AccountId `json:"urn:ietf:params:jmap:contacts:parse,omitempty"` } type SessionState string @@ -946,7 +950,7 @@ const NoLanguage = Language("") type SessionResponse struct { Capabilities SessionCapabilities `json:"capabilities"` - Accounts map[string]Account `json:"accounts,omitempty"` + Accounts map[AccountId]Account `json:"accounts,omitempty"` // A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the user’s main or default // account for data pertaining to that capability. @@ -1164,6 +1168,8 @@ type SetError struct { type Command string +const NoCommand = Command("") + type Invocation struct { Command Command Parameters any @@ -1179,7 +1185,7 @@ func invocation(parameters JmapCommand, tag string) Invocation { } func skipInvocation() Invocation { - return Invocation{Command: ""} + return Invocation{Command: NoCommand} } type TypeOfRequest string @@ -1239,10 +1245,12 @@ type Response struct { RequestId string `json:"requestId,omitempty"` } +// A JMAP object. type Foo interface { GetObjectType() ObjectType } +// A JMAP object that also has an identifier. type Idable interface { GetId() string Foo @@ -1384,6 +1392,7 @@ type ChangesResponse[T Foo] interface { type QueryCommand[T Foo, SELF QueryCommand[T, SELF]] interface { JmapCommand GetResponse() QueryResponse[T] + // Wither that creates a new object of the same type, keeping all the same values except for the limit that is specified as parameter. WithLimit(limit *uint) SELF } @@ -1436,8 +1445,8 @@ type QueryParams struct { var NullQueryParams = QueryParams{Position: 0, Anchor: "", AnchorOffset: nil} -func toNullQueryParams(accountIds []string) map[string]QueryParams { - return structs.ToMap(accountIds, func(k string) (string, QueryParams) { return k, NullQueryParams }) +func toNullQueryParams(accountIds []AccountId) map[AccountId]QueryParams { + return structs.ToMap(accountIds, func(k AccountId) (AccountId, QueryParams) { return k, NullQueryParams }) } type FilterElement[T Foo] interface { @@ -1751,8 +1760,8 @@ func (m MailboxChange) AsPatch() (PatchObject, error) { } type MailboxGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[Mailbox] = &MailboxGetCommand{} @@ -1762,7 +1771,7 @@ func (c MailboxGetCommand) GetObjectType() ObjectType { return MailboxTy func (c MailboxGetCommand) GetResponse() GetResponse[Mailbox] { return MailboxGetResponse{} } type MailboxGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` } @@ -1773,8 +1782,8 @@ func (c MailboxGetRefCommand) GetObjectType() ObjectType { return Mailbo func (c MailboxGetRefCommand) GetResponse() GetResponse[Mailbox] { return MailboxGetResponse{} } type MailboxSetCommand struct { - AccountId string `json:"accountId"` - IfInState string `json:"ifInState,omitempty"` + AccountId AccountId `json:"accountId"` + IfInState State `json:"ifInState,omitempty"` Create map[string]MailboxChange `json:"create,omitempty"` Update map[string]PatchObject `json:"update,omitempty"` Destroy []string `json:"destroy,omitempty"` @@ -1787,7 +1796,7 @@ func (c MailboxSetCommand) GetObjectType() ObjectType { return MailboxTy func (c MailboxSetCommand) GetResponse() SetResponse[Mailbox] { return MailboxSetResponse{} } type MailboxSetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` OldState State `json:"oldState,omitempty"` NewState State `json:"newState,omitempty"` Created map[string]*Mailbox `json:"created,omitempty"` @@ -1853,7 +1862,7 @@ var _ Comparator[Mailbox] = &MailboxComparator{} func (c MailboxComparator) GetMarker() Mailbox { return Mailbox{} } type MailboxQueryCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Filter MailboxFilterElement `json:"filter,omitempty"` Sort []MailboxComparator `json:"sort,omitempty"` SortAsTree bool `json:"sortAsTree,omitempty"` @@ -2157,7 +2166,7 @@ func (c EmailComparator) GetMarker() Email { return Email{} } // A client can use anchor instead of position to find the index of an id within a large set of results. type EmailQueryCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // Determines the set of Emails returned in the results. // @@ -2250,7 +2259,7 @@ type EmailGetCommand struct { Ids []string `json:"ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each Email object. // @@ -2316,7 +2325,7 @@ type EmailGetRefCommand struct { IdsRef *ResultReference `json:"#ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each Email object. // @@ -2372,7 +2381,7 @@ func (c EmailGetRefCommand) GetResponse() GetResponse[Email] { return EmailGetRe type EmailChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -2634,7 +2643,7 @@ type Email struct { // The account ID this email belongs to. // Note that this is not part of the JMAP specification, and is only contained in all-account operations. - AccountId string `json:"accountId,omitempty"` + AccountId AccountId `json:"accountId,omitempty"` // The set of Mailbox ids this Email belongs to. // @@ -2998,7 +3007,7 @@ func (f EmailSubmission) GetId() string { return f.Id } type EmailSubmissionGetCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The ids of the EmailSubmission objects to return. // @@ -3024,7 +3033,7 @@ func (c EmailSubmissionGetCommand) GetResponse() GetResponse[EmailSubmission] { type EmailSubmissionGetRefCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The ids of the EmailSubmission objects to return. // @@ -3050,7 +3059,7 @@ func (c EmailSubmissionGetRefCommand) GetResponse() GetResponse[EmailSubmission] type EmailSubmissionGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data // of this type in the account (not just the objects returned in this call). @@ -3089,7 +3098,7 @@ func (r EmailSubmissionGetResponse) GetList() []EmailSubmission { return r.List type EmailSubmissionChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -3120,7 +3129,7 @@ func (c EmailSubmissionChangesCommand) GetResponse() ChangesResponse[EmailSubmis type EmailSubmissionChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -3169,7 +3178,7 @@ type EmailSubmissionCreate struct { } type EmailSubmissionSetCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Create map[string]EmailSubmissionCreate `json:"create,omitempty"` OldState State `json:"oldState,omitempty"` NewState State `json:"newState,omitempty"` @@ -3203,7 +3212,7 @@ type CreatedEmailSubmission struct { type EmailSubmissionSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -3239,7 +3248,7 @@ func (r EmailSubmissionSetResponse) GetMarker() EmailSubmission { retu type EmailQueryResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string encoding the current state of the query on the server. // @@ -3294,7 +3303,7 @@ func (r EmailQueryResponse) GetMarker() Email { return Email{} } type EmailGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data of this type // in the account (not just the objects returned in this call). @@ -3328,7 +3337,7 @@ func (r EmailGetResponse) GetMarker() Email { return Email{} } type EmailChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -3362,7 +3371,7 @@ func (r EmailChangesResponse) GetMarker() Email { return Email{} } type MailboxGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data of this type in the account // (not just the objects returned in this call). @@ -3393,7 +3402,7 @@ func (r MailboxGetResponse) GetList() []Mailbox { return r.List } type MailboxChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -3424,7 +3433,7 @@ func (c MailboxChangesCommand) GetResponse() ChangesResponse[Mailbox] { type MailboxChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -3465,7 +3474,7 @@ func (r MailboxChangesResponse) GetMarker() Mailbox { return Mailbox{} } type MailboxQueryResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string encoding the current state of the query on the server. // @@ -3625,7 +3634,7 @@ func (e EmailChange) AsPatch() (PatchObject, error) { type EmailSetCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is a state string as returned by the `Email/get` method. // @@ -3633,7 +3642,7 @@ type EmailSetCommand struct { // `stateMismatch` error returned. // // If null, any changes will be applied to the current state. - IfInState string `json:"ifInState,omitempty"` + IfInState State `json:"ifInState,omitempty"` // A map of a creation id (a temporary id set by the client) to Email objects, // or null if no objects are to be created. @@ -3684,7 +3693,7 @@ func (c EmailSetCommand) GetResponse() SetResponse[Email] { return EmailSetRespo type EmailSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by Email/get before making the // requested changes, or null if the server doesn’t know what the previous state @@ -3760,7 +3769,7 @@ type EmailImport struct { } type EmailImportCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is a state string as returned by the Email/get method. // @@ -3769,7 +3778,7 @@ type EmailImportCommand struct { // error returned. // // If null, any changes will be applied to the current state. - IfInState string `json:"ifInState,omitempty"` + IfInState State `json:"ifInState,omitempty"` // A map of creation id (client specified) to EmailImport objects. Emails map[string]EmailImport `json:"emails"` @@ -3792,7 +3801,7 @@ type ImportedEmail struct { type EmailImportResponse struct { // The id of the account used for this call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by Email/get on this account // before making the requested changes, or null if the server doesn’t know @@ -3834,8 +3843,8 @@ func (f Thread) GetObjectType() ObjectType { return ThreadType } func (f Thread) GetId() string { return f.Id } type ThreadGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[Thread] = &ThreadGetCommand{} @@ -3845,7 +3854,7 @@ func (c ThreadGetCommand) GetObjectType() ObjectType { return ThreadType func (c ThreadGetCommand) GetResponse() GetResponse[Thread] { return ThreadGetResponse{} } type ThreadGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` } @@ -3856,7 +3865,7 @@ func (c ThreadGetRefCommand) GetObjectType() ObjectType { return ThreadTy func (c ThreadGetRefCommand) GetResponse() GetResponse[Thread] { return ThreadGetResponse{} } type ThreadGetResponse struct { - AccountId string + AccountId AccountId State State List []Thread NotFound []string @@ -3870,8 +3879,8 @@ func (r ThreadGetResponse) GetList() []Thread { return r.List } func (r ThreadGetResponse) GetMarker() Thread { return Thread{} } type IdentityGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[Identity] = &IdentityGetCommand{} @@ -3881,7 +3890,7 @@ func (c IdentityGetCommand) GetObjectType() ObjectType { return Identit func (c IdentityGetCommand) GetResponse() GetResponse[Identity] { return IdentityGetResponse{} } type IdentityGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` PropertiesRef *ResultReference `json:"#properties,omitempty"` } @@ -3894,7 +3903,7 @@ func (c IdentityGetRefCommand) GetResponse() GetResponse[Identity] { return Iden type IdentityChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -3925,7 +3934,7 @@ func (c IdentityChangesCommand) GetResponse() ChangesResponse[Identity] { type IdentityChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -3959,8 +3968,8 @@ func (r IdentityChangesResponse) GetDestroyed() []string { return r.Destroyed } func (r IdentityChangesResponse) GetMarker() Identity { return Identity{} } type IdentitySetCommand struct { - AccountId string `json:"accountId"` - IfInState string `json:"ifInState,omitempty"` + AccountId AccountId `json:"accountId"` + IfInState State `json:"ifInState,omitempty"` Create map[string]IdentityChange `json:"create,omitempty"` Update map[string]PatchObject `json:"update,omitempty"` Destroy []string `json:"destroy,omitempty"` @@ -3973,7 +3982,7 @@ func (c IdentitySetCommand) GetObjectType() ObjectType { return Identit func (c IdentitySetCommand) GetResponse() SetResponse[Identity] { return IdentitySetResponse{} } type IdentitySetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` OldState State `json:"oldState,omitempty"` NewState State `json:"newState,omitempty"` Created map[string]*Identity `json:"created,omitempty"` @@ -4081,7 +4090,7 @@ func (i IdentityChange) AsPatch() (PatchObject, error) { } type IdentityGetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` State State `json:"state"` List []Identity `json:"list,omitempty"` NotFound []string `json:"notFound,omitempty"` @@ -4095,7 +4104,7 @@ func (r IdentityGetResponse) GetNotFound() []string { return r.NotFound } func (r IdentityGetResponse) GetList() []Identity { return r.List } type VacationResponseGetCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` } var _ GetCommand[VacationResponse] = &VacationResponseGetCommand{} @@ -4156,7 +4165,7 @@ func (f VacationResponse) GetId() string { return f.Id } type VacationResponseGetResponse struct { // The identifier of the account this response pertains to. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string representing the state on the server for all the data of this type in the account // (not just the objects returned in this call). @@ -4180,7 +4189,7 @@ func (r VacationResponseGetResponse) GetNotFound() []string { return r.Not func (r VacationResponseGetResponse) GetList() []VacationResponse { return r.List } type VacationResponseSetCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IfInState string `json:"ifInState,omitempty"` Create map[string]VacationResponse `json:"create,omitempty"` Update map[string]PatchObject `json:"update,omitempty"` @@ -4196,7 +4205,7 @@ func (c VacationResponseSetCommand) GetResponse() SetResponse[VacationResponse] } type VacationResponseSetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` OldState State `json:"oldState,omitempty"` NewState State `json:"newState,omitempty"` Created map[string]VacationResponse `json:"created,omitempty"` @@ -4228,7 +4237,7 @@ type UploadObject struct { } type BlobUploadCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Create map[string]UploadObject `json:"create"` } @@ -4245,7 +4254,7 @@ type BlobUploadCreateResult struct { } type BlobUploadResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Created map[string]BlobUploadCreateResult `json:"created"` } @@ -4341,11 +4350,11 @@ func (c BlobChanges) GetUpdated() []Blob { return c.Updated } func (c BlobChanges) GetDestroyed() []string { return c.Destroyed } type BlobGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` - Properties []string `json:"properties,omitempty"` - Position int `json:"position,omitzero"` - Length int `json:"length,omitzero"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` + Properties []string `json:"properties,omitempty"` + Position int `json:"position,omitzero"` + Length int `json:"length,omitzero"` } var _ GetCommand[Blob] = &BlobGetCommand{} @@ -4355,7 +4364,7 @@ func (c BlobGetCommand) GetObjectType() ObjectType { return BlobType } func (c BlobGetCommand) GetResponse() GetResponse[Blob] { return BlobGetResponse{} } type BlobGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdRef *ResultReference `json:"#ids,omitempty"` Properties []string `json:"properties,omitempty"` Position int `json:"position,omitzero"` @@ -4370,7 +4379,7 @@ func (c BlobGetRefCommand) GetResponse() GetResponse[Blob] { return BlobGetRespo type BlobGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string representing the state on the server for all the data of this type in the // account (not just the objects returned in this call). @@ -4461,7 +4470,7 @@ func (f SearchSnippet) GetObjectType() ObjectType { return SearchSnippetType } type SearchSnippetGetRefCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The same filter as passed to Email/query. Filter EmailFilterElement `json:"filter,omitempty"` @@ -4476,7 +4485,7 @@ func (c SearchSnippetGetRefCommand) GetCommand() Command { return CommandS func (c SearchSnippetGetRefCommand) GetObjectType() ObjectType { return SearchSnippetType } type SearchSnippetGetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` List []SearchSnippet `json:"list,omitempty"` NotFound []string `json:"notFound,omitempty"` } @@ -4503,7 +4512,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[ObjectTypeName]string `json:"changed"` + Changed map[AccountId]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). @@ -4586,7 +4595,7 @@ type AddressBook struct { // // The account id for the principals may be found in the urn:ietf:params:jmap:principals:owner capability // of the Account to which the AddressBook belongs. - ShareWith map[string]AddressBookRights `json:"shareWith,omitempty"` + ShareWith map[PrincipalId]AddressBookRights `json:"shareWith,omitempty"` // The set of access rights the user has in relation to this AddressBook (server-set). MyRights AddressBookRights `json:"myRights"` @@ -5337,7 +5346,7 @@ type Calendar struct { // // The account id for the principals may be found in the `urn:ietf:params:jmap:principals:owner` // capability of the `Account` to which the calendar belongs. - ShareWith map[string]CalendarRights `json:"shareWith,omitempty"` + ShareWith map[PrincipalId]CalendarRights `json:"shareWith,omitempty"` // The set of access rights the user has in relation to this Calendar. // @@ -5468,7 +5477,7 @@ type CalendarChange struct { // // The account id for the principals may be found in the `urn:ietf:params:jmap:principals:owner` // capability of the `Account` to which the calendar belongs. - ShareWith map[string]CalendarRights `json:"shareWith,omitempty"` + ShareWith map[PrincipalId]CalendarRights `json:"shareWith,omitempty"` // The set of access rights the user has in relation to this Calendar. // @@ -5790,7 +5799,7 @@ type CalendarAlert struct { Type TypeOfCalendarAlert `json:"@type,omitempty"` // The account id for the calendar in which the alert triggered. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The CalendarEvent id for the alert that triggered. // @@ -5818,7 +5827,7 @@ type Person struct { // The id of the `Principal` corresponding to the person who made the change, if any. // // This will be null if the change was due to receving an iTIP message. - PrincipalId string `json:"principalId,omitempty"` + PrincipalId PrincipalId `json:"principalId,omitempty"` // The `scheduleId` URI of the person who made the change, if any. // @@ -6052,7 +6061,7 @@ type TaskList struct { // // The account id for the principals may be found in the `urn:ietf:params:jmap:principals:owner` capability // of the `Account` to which the task list belongs. - ShareWith map[string]TaskRights `json:"shareWith,omitempty"` + ShareWith map[PrincipalId]TaskRights `json:"shareWith,omitempty"` // The set of access rights the user has in relation to this `TaskList`. // @@ -6366,7 +6375,7 @@ type Principal struct { // A map of account id to `Account` object for each JMAP Account containing data for // this principal that the user has access to, or null if none. - Accounts map[string]Account `json:"accounts,omitempty"` + Accounts map[AccountId]Account `json:"accounts,omitempty"` } var _ Idable = &Principal{} @@ -6598,7 +6607,7 @@ type MDN struct { type SendMDN struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The id of the `Identity` to associate with these MDNs. // @@ -6616,8 +6625,8 @@ type SendMDN struct { } type QuotaGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[Quota] = &QuotaGetCommand{} @@ -6627,7 +6636,7 @@ func (c QuotaGetCommand) GetObjectType() ObjectType { return QuotaType } func (c QuotaGetCommand) GetResponse() GetResponse[Quota] { return QuotaGetResponse{} } type QuotaGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` PropertiesRef *ResultReference `json:"#properties,omitempty"` } @@ -6639,10 +6648,10 @@ func (c QuotaGetRefCommand) GetObjectType() ObjectType { return QuotaType func (c QuotaGetRefCommand) GetResponse() GetResponse[Quota] { return QuotaGetResponse{} } type QuotaGetResponse struct { - AccountId string `json:"accountId"` - State State `json:"state,omitempty"` - List []Quota `json:"list,omitempty"` - NotFound []string `json:"notFound,omitempty"` + AccountId AccountId `json:"accountId"` + State State `json:"state,omitempty"` + List []Quota `json:"list,omitempty"` + NotFound []string `json:"notFound,omitempty"` } var _ GetResponse[Quota] = &QuotaGetResponse{} @@ -6654,7 +6663,7 @@ func (r QuotaGetResponse) GetList() []Quota { return r.List } type QuotaChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // This is the string that was returned as the "state" argument in the "Quota/get" response. @@ -6682,7 +6691,7 @@ func (c QuotaChangesCommand) GetResponse() ChangesResponse[Quota] { return Quota type QuotaChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the "sinceState" argument echoed back; it's the state from which the server is returning changes. OldState State `json:"oldState"` @@ -6715,8 +6724,8 @@ func (r QuotaChangesResponse) GetDestroyed() []string { return r.Destroyed } func (r QuotaChangesResponse) GetMarker() Quota { return Quota{} } type AddressBookGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[AddressBook] = &AddressBookGetCommand{} @@ -6728,7 +6737,7 @@ func (c AddressBookGetCommand) GetResponse() GetResponse[AddressBook] { } type AddressBookGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` } @@ -6741,7 +6750,7 @@ func (c AddressBookGetRefCommand) GetResponse() GetResponse[AddressBook] { } type AddressBookGetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` State State `json:"state,omitempty"` List []AddressBook `json:"list,omitempty"` NotFound []string `json:"notFound,omitempty"` @@ -6795,7 +6804,7 @@ type AddressBookChange struct { // // The account id for the principals may be found in the urn:ietf:params:jmap:principals:owner capability // of the Account to which the AddressBook belongs. - ShareWith map[string]AddressBookRights `json:"shareWith,omitempty"` + ShareWith map[PrincipalId]AddressBookRights `json:"shareWith,omitempty"` } var _ Change = AddressBookChange{} @@ -6805,8 +6814,8 @@ func (a AddressBookChange) AsPatch() (PatchObject, error) { } type AddressBookSetCommand struct { - AccountId string `json:"accountId"` - IfInState string `json:"ifInState,omitempty"` + AccountId AccountId `json:"accountId"` + IfInState State `json:"ifInState,omitempty"` Create map[string]AddressBookChange `json:"create,omitempty"` Update map[string]PatchObject `json:"update,omitempty"` Destroy []string `json:"destroy,omitempty"` @@ -6822,7 +6831,7 @@ func (c AddressBookSetCommand) GetResponse() SetResponse[AddressBook] { type AddressBookSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by AddressBook/get before making the // requested changes, or null if the server doesn’t know what the previous state @@ -6878,7 +6887,7 @@ func (r AddressBookSetResponse) GetMarker() AddressBook { return A type AddressBookChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -6909,7 +6918,7 @@ func (c AddressBookChangesCommand) GetResponse() ChangesResponse[AddressBook] { type AddressBookChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the sinceState argument echoed back; it’s the state from which the server is returning changes. OldState State `json:"oldState"` @@ -7144,7 +7153,7 @@ func (o ContactCardFilterOperator) IsNotEmpty() bool { } type ContactCardQueryCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Filter ContactCardFilterElement `json:"filter,omitempty"` @@ -7216,7 +7225,7 @@ func (c ContactCardQueryCommand) WithLimit(limit *uint) ContactCardQueryCommand type ContactCardQueryResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string encoding the current state of the query on the server. // @@ -7277,7 +7286,7 @@ type ContactCardGetCommand struct { Ids []string `json:"ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each ContactCard object. // @@ -7303,7 +7312,7 @@ type ContactCardGetRefCommand struct { IdsRef *ResultReference `json:"#ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each ContactCard object. // @@ -7323,7 +7332,7 @@ func (c ContactCardGetRefCommand) GetResponse() GetResponse[ContactCard] { type ContactCardGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data of this type // in the account (not just the objects returned in this call). @@ -7357,7 +7366,7 @@ func (r ContactCardGetResponse) GetList() []ContactCard { return r.List } type ContactCardChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // This is the string that was returned as the "state" argument in the "ContactCard/get" response. @@ -7382,7 +7391,7 @@ func (c ContactCardChangesCommand) GetResponse() ChangesResponse[ContactCard] { type ContactCardChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the "sinceState" argument echoed back; it's the state from which the server is returning changes. OldState State `json:"oldState"` @@ -7416,7 +7425,7 @@ func (r ContactCardChangesResponse) GetMarker() ContactCard { return ContactCar type ContactCardSetCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is a state string as returned by the `ContactCard/get` method. // @@ -7424,7 +7433,7 @@ type ContactCardSetCommand struct { // `stateMismatch` error returned. // // If null, any changes will be applied to the current state. - IfInState string `json:"ifInState,omitempty"` + IfInState State `json:"ifInState,omitempty"` // A map of a creation id (a temporary id set by the client) to ContactCard objects, // or null if no objects are to be created. @@ -7477,7 +7486,7 @@ func (c ContactCardSetCommand) GetResponse() SetResponse[ContactCard] { type ContactCardSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by ContactCard/get before making the // requested changes, or null if the server doesn’t know what the previous state @@ -7533,7 +7542,7 @@ func (r ContactCardSetResponse) GetMarker() ContactCard { return C type CalendarEventParseCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The ids of the blobs to parse BlobIds []string `json:"blobIds,omitempty"` @@ -7554,7 +7563,7 @@ func (c CalendarEventParseCommand) GetResponse() ParseResponse[CalendarEvent] { type CalendarEventParseResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A map of blob ids to parsed CalendarEvent objects representations for each successfully // parsed blob, or null if none. @@ -7573,8 +7582,8 @@ var _ ParseResponse[CalendarEvent] = &CalendarEventParseResponse{} func (r CalendarEventParseResponse) GetMarker() CalendarEvent { return CalendarEvent{} } type CalendarGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []string `json:"ids,omitempty"` } var _ GetCommand[Calendar] = &CalendarGetCommand{} @@ -7584,7 +7593,7 @@ func (c CalendarGetCommand) GetObjectType() ObjectType { return Calenda func (c CalendarGetCommand) GetResponse() GetResponse[Calendar] { return CalendarGetResponse{} } type CalendarGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` } @@ -7595,7 +7604,7 @@ func (c CalendarGetRefCommand) GetObjectType() ObjectType { return Cale func (c CalendarGetRefCommand) GetResponse() GetResponse[Calendar] { return CalendarGetResponse{} } type CalendarGetResponse struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` State State `json:"state,omitempty"` List []Calendar `json:"list,omitempty"` NotFound []string `json:"notFound,omitempty"` @@ -7609,8 +7618,8 @@ func (r CalendarGetResponse) GetNotFound() []string { return r.NotFound } func (r CalendarGetResponse) GetList() []Calendar { return r.List } type CalendarSetCommand struct { - AccountId string `json:"accountId"` - IfInState string `json:"ifInState,omitempty"` + AccountId AccountId `json:"accountId"` + IfInState State `json:"ifInState,omitempty"` Create map[string]CalendarChange `json:"create,omitempty"` Update map[string]PatchObject `json:"update,omitempty"` Destroy []string `json:"destroy,omitempty"` @@ -7626,7 +7635,7 @@ func (c CalendarSetCommand) GetResponse() SetResponse[Calendar] { type CalendarSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by Calendar/get before making the // requested changes, or null if the server doesn’t know what the previous state @@ -7682,7 +7691,7 @@ func (r CalendarSetResponse) GetMarker() Calendar { return Cale type CalendarChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -7713,7 +7722,7 @@ func (c CalendarChangesCommand) GetResponse() ChangesResponse[Calendar] { type CalendarChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the "sinceState" argument echoed back; it's the state from which the server is returning changes. OldState State `json:"oldState"` @@ -7893,7 +7902,7 @@ func (o CalendarEventFilterOperator) IsNotEmpty() bool { var _ CalendarEventFilterElement = &CalendarEventFilterOperator{} type CalendarEventQueryCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Filter CalendarEventFilterElement `json:"filter,omitempty"` @@ -7965,7 +7974,7 @@ func (c CalendarEventQueryCommand) WithLimit(limit *uint) CalendarEventQueryComm type CalendarEventQueryResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string encoding the current state of the query on the server. // @@ -8026,7 +8035,7 @@ type CalendarEventGetCommand struct { Ids []string `json:"ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each CalendarEvent object. // @@ -8052,7 +8061,7 @@ type CalendarEventGetRefCommand struct { IdsRef *ResultReference `json:"#ids,omitempty"` // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // If supplied, only the properties listed in the array are returned for each CalendarEvent object. // @@ -8072,7 +8081,7 @@ func (c CalendarEventGetRefCommand) GetResponse() GetResponse[CalendarEvent] { type CalendarEventGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data of this type // in the account (not just the objects returned in this call). @@ -8106,7 +8115,7 @@ func (r CalendarEventGetResponse) GetList() []CalendarEvent { return r.List } type CalendarEventChangesCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The current state of the client. // @@ -8137,7 +8146,7 @@ func (c CalendarEventChangesCommand) GetResponse() ChangesResponse[CalendarEvent type CalendarEventChangesResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is the "sinceState" argument echoed back; it's the state from which the server is returning changes. OldState State `json:"oldState"` @@ -8171,7 +8180,7 @@ func (r CalendarEventChangesResponse) GetMarker() CalendarEvent { return Calenda type CalendarEventSetCommand struct { // The id of the account to use. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // This is a state string as returned by the `CalendarEvent/get` method. // @@ -8179,7 +8188,7 @@ type CalendarEventSetCommand struct { // `stateMismatch` error returned. // // If null, any changes will be applied to the current state. - IfInState string `json:"ifInState,omitempty"` + IfInState State `json:"ifInState,omitempty"` // A map of a creation id (a temporary id set by the client) to CalendarEvent objects, // or null if no objects are to be created. @@ -8232,7 +8241,7 @@ func (c CalendarEventSetCommand) GetResponse() SetResponse[CalendarEvent] { type CalendarEventSetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // The state string that would have been returned by CalendarEvent/get before making the // requested changes, or null if the server doesn’t know what the previous state @@ -8287,8 +8296,8 @@ func (r CalendarEventSetResponse) GetNotDestroyed() map[string]SetError { return func (r CalendarEventSetResponse) GetMarker() CalendarEvent { return CalendarEvent{} } type PrincipalGetCommand struct { - AccountId string `json:"accountId"` - Ids []string `json:"ids,omitempty"` + AccountId AccountId `json:"accountId"` + Ids []PrincipalId `json:"ids,omitempty"` } var _ GetCommand[Principal] = &PrincipalGetCommand{} @@ -8298,7 +8307,7 @@ func (c PrincipalGetCommand) GetObjectType() ObjectType { return Princ func (c PrincipalGetCommand) GetResponse() GetResponse[Principal] { return PrincipalGetResponse{} } type PrincipalGetRefCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` IdsRef *ResultReference `json:"#ids,omitempty"` } @@ -8310,7 +8319,7 @@ func (c PrincipalGetRefCommand) GetResponse() GetResponse[Principal] { return Pr type PrincipalGetResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A (preferably short) string representing the state on the server for all the data of this type in the account // (not just the objects returned in this call). @@ -8346,7 +8355,7 @@ type PrincipalFilterElement interface { type PrincipalFilterCondition struct { // A list of Account ids. // The Principal matches if any of the ids in this list are keys in the Principal's "accounts" property (i.e., if any of the Account ids belong to the Principal). - AccountIds []string `json:"accountIds,omitempty"` + AccountIds []AccountId `json:"accountIds,omitempty"` // The email property of the Principal contains the given string. Email string `json:"email,omitempty"` // The name property of the Principal contains the given string. @@ -8394,7 +8403,7 @@ var _ Comparator[Principal] = &PrincipalComparator{} func (c PrincipalComparator) GetMarker() Principal { return Principal{} } type PrincipalQueryCommand struct { - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` Filter PrincipalFilterElement `json:"filter,omitempty"` Sort []PrincipalComparator `json:"sort,omitempty"` @@ -8464,7 +8473,7 @@ func (c PrincipalQueryCommand) WithLimit(limit *uint) PrincipalQueryCommand { type PrincipalQueryResponse struct { // The id of the account used for the call. - AccountId string `json:"accountId"` + AccountId AccountId `json:"accountId"` // A string encoding the current state of the query on the server. // diff --git a/pkg/jmap/model_examples.go b/pkg/jmap/model_examples.go index 1c763bcabd..2585ca15bc 100644 --- a/pkg/jmap/model_examples.go +++ b/pkg/jmap/model_examples.go @@ -124,8 +124,8 @@ func SerializeExamples(e any) { //NOSONAR } type Exemplar struct { - AccountId string - SharedAccountId string + AccountId AccountId + SharedAccountId AccountId IdentityId string IdentityName string EmailAddress string @@ -331,9 +331,9 @@ func (e Exemplar) SessionCalendarsParseAccountCapabilities() SessionCalendarsPar return SessionCalendarsParseAccountCapabilities{} } -func (e Exemplar) sessionPrincipalsAccountCapabilities(accountId string) SessionPrincipalsAccountCapabilities { +func (e Exemplar) sessionPrincipalsAccountCapabilities(accountId AccountId) SessionPrincipalsAccountCapabilities { return SessionPrincipalsAccountCapabilities{ - CurrentUserPrincipalId: accountId, + CurrentUserPrincipalId: PrincipalId(accountId), } } @@ -380,7 +380,7 @@ func (e Exemplar) SessionTasksCustomTimezonesAccountCapabilities() SessionTasksC func (e Exemplar) SessionPrincipalsOwnerAccountCapabilities() SessionPrincipalsOwnerAccountCapabilities { return SessionPrincipalsOwnerAccountCapabilities{ AccountIdForPrincipal: e.AccountId, - PrincipalId: e.AccountId, + PrincipalId: PrincipalId(e.AccountId), } } @@ -392,7 +392,7 @@ func (e Exemplar) SessionAccountCapabilities() SessionAccountCapabilities { return e.sessionAccountCapabilities(e.AccountId) } -func (e Exemplar) sessionAccountCapabilities(accountId string) SessionAccountCapabilities { +func (e Exemplar) sessionAccountCapabilities(accountId AccountId) SessionAccountCapabilities { mail := e.SessionMailAccountCapabilities() submission := e.SessionSubmissionAccountCapabilities() vacationResponse := e.SessionVacationResponseAccountCapabilities() @@ -446,7 +446,7 @@ func (e Exemplar) Account() (Account, string) { func (e Exemplar) SharedAccount() (Account, string, string) { return Account{ - Name: e.SharedAccountId, + Name: string(e.SharedAccountId), IsPersonal: false, IsReadOnly: true, AccountCapabilities: e.sessionAccountCapabilities(e.SharedAccountId), @@ -489,7 +489,7 @@ func (e Exemplar) Quotas() []Quota { Scope: "account", Used: 29102918, HardLimit: 50000000000, - Name: e.SharedAccountId, + Name: string(e.SharedAccountId), Types: []ObjectTypeName{ EmailName, SieveScriptName, diff --git a/pkg/jmap/templates.go b/pkg/jmap/templates.go index fc8ba115be..fffeda7ca1 100644 --- a/pkg/jmap/templates.go +++ b/pkg/jmap/templates.go @@ -8,12 +8,12 @@ import ( "github.com/rs/zerolog" ) -func get[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOSONAR +func get[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], ID any, RESP any]( //NOSONAR client *Client, name string, objType ObjectType, - getCommandFactory func(string, []string) GETREQ, + getCommandFactory func(AccountId, []ID) GETREQ, _ GETRESP, mapper func(GETRESP) RESP, - accountId string, ids []string, ctx Context) (Result[RESP], Error) { + accountId AccountId, ids []ID, ctx Context) (Result[RESP], Error) { ctx = ctx.WithLogger(client.logger(name, ctx)) get := getCommandFactory(accountId, ids) @@ -34,12 +34,12 @@ func get[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOSON }) } -func getAN[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOSONAR +func getAN[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], ID any, RESP any]( //NOSONAR client *Client, name string, objType ObjectType, - getCommandFactory func(string, []string) GETREQ, + getCommandFactory func(AccountId, []ID) GETREQ, resp GETRESP, - respMapper func(map[string][]T) RESP, - accountIds []string, ids []string, ctx Context) (Result[RESP], Error) { + respMapper func(map[AccountId][]T) RESP, + accountIds []AccountId, ids []ID, ctx Context) (Result[RESP], Error) { return getN(client, name, objType, getCommandFactory, resp, func(r GETRESP) []T { return r.GetList() }, respMapper, @@ -48,13 +48,13 @@ func getAN[T Foo, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOS ) } -func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP any]( //NOSONAR +func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], ID any, RESP any]( //NOSONAR client *Client, name string, objType ObjectType, - getCommandFactory func(string, []string) GETREQ, + getCommandFactory func(AccountId, []ID) GETREQ, _ GETRESP, itemMapper func(GETRESP) ITEM, - respMapper func(map[string]ITEM) RESP, - accountIds []string, ids []string, ctx Context) (Result[RESP], Error) { + respMapper func(map[AccountId]ITEM) RESP, + accountIds []AccountId, ids []ID, ctx Context) (Result[RESP], Error) { logger := client.logger(name, ctx) ctx = ctx.WithLogger(logger) @@ -74,8 +74,8 @@ func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP an } return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) { - result := map[string]ITEM{} - responses := map[string]GETRESP{} + result := map[AccountId]ITEM{} + responses := map[AccountId]GETRESP{} for _, accountId := range uniqueAccountIds { var resp GETRESP err = retrieveResponseMatchParameters(ctx, body, c, mcid(accountId, "0"), &resp) @@ -92,11 +92,11 @@ func getN[T Foo, ITEM any, GETREQ GetCommand[T], GETRESP GetResponse[T], RESP an func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP SetResponse[T], GETRESP GetResponse[T]]( //NOSONAR client *Client, name string, objType ObjectType, - setCommandFactory func(string, map[string]C) SETREQ, - getCommandFactory func(string, string) GETREQ, + setCommandFactory func(AccountId, map[string]C) SETREQ, + getCommandFactory func(AccountId, string) GETREQ, createdMapper func(SETRESP) map[string]*T, listMapper func(GETRESP) []T, - accountId string, create C, + accountId AccountId, create C, ctx Context) (Result[*T], Error) { logger := client.logger(name, ctx) ctx = ctx.WithLogger(logger) @@ -152,8 +152,8 @@ func create[T Foo, C any, SETREQ SetCommand[T], GETREQ GetCommand[T], SETRESP Se } func destroy[T Foo, REQ SetCommand[T], RESP SetResponse[T]](client *Client, name string, objType ObjectType, //NOSONAR - setCommandFactory func(string, []string) REQ, _ RESP, - accountId string, destroy []string, ctx Context) (Result[map[string]SetError], Error) { + setCommandFactory func(AccountId, []string) REQ, _ RESP, + accountId AccountId, destroy []string, ctx Context) (Result[map[string]SetError], Error) { logger := client.logger(name, ctx) ctx = ctx.WithLogger(logger) @@ -248,19 +248,19 @@ func changes[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESR func changesN[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGESRESP ChangesResponse[T], GETRESP GetResponse[T], ITEM any, CHANGESITEM any, RESP any]( //NOSONAR client *Client, name string, objType ObjectType, - accountIds []string, sinceStateMap map[string]State, - changesCommandFactory func(string, State) CHANGESREQ, + accountIds []AccountId, sinceStateMap map[AccountId]State, + changesCommandFactory func(AccountId, State) CHANGESREQ, _ CHANGESRESP, - getCommandFactory func(string, string, string) GETREQ, + getCommandFactory func(AccountId, string, string) GETREQ, getMapper func(GETRESP) []ITEM, changesItemMapper func(State, State, bool, []ITEM, []ITEM, []string) CHANGESITEM, - respMapper func(map[string]CHANGESITEM) RESP, + respMapper func(map[AccountId]CHANGESITEM) RESP, stateMapper func(GETRESP) State, ctx Context) (Result[RESP], Error) { logger := client.loggerParams(name, ctx, func(z zerolog.Context) zerolog.Context { sinceStateLogDict := zerolog.Dict() for k, v := range sinceStateMap { - sinceStateLogDict.Str(log.SafeString(k), log.SafeString(string(v))) + sinceStateLogDict.Str(log.SafeString(string(k)), log.SafeString(string(v))) } return z.Dict(logSinceState, sinceStateLogDict) }) @@ -303,8 +303,8 @@ func changesN[T Foo, CHANGESREQ ChangesCommand[T], GETREQ GetCommand[T], CHANGES } return command(client, ctx, cmd, func(body *Response) (RESP, State, Error) { - changesItemByAccount := make(map[string]CHANGESITEM, n) - stateByAccountId := make(map[string]State, n) + changesItemByAccount := make(map[AccountId]CHANGESITEM, n) + stateByAccountId := make(map[AccountId]State, n) for _, accountId := range uniqueAccountIds { var changesResponse CHANGESRESP err = retrieveChanges(ctx, body, ch, mcid(accountId, "0"), &changesResponse) @@ -488,13 +488,13 @@ func query[T Foo, FILTER any, SORT any, QUERY QueryCommand[T], GET GetCommand[T] func queryN[T Foo, FILTER any, SORT any, QUERY QueryCommand[T, QUERY], GET GetCommand[T], QUERYRESP QueryResponse[T], GETRESP GetResponse[T], RESP any]( //NOSONAR client *Client, name string, objType ObjectType, defaultSortBy []SORT, - queryCommandFactory func(accountId string, queryParams QueryParams, limit *uint, filter FILTER, sortBy []SORT) QUERY, - getCommandFactory func(accountId string, cmd Command, path string, rof string) GET, + queryCommandFactory func(accountId AccountId, queryParams QueryParams, limit *uint, filter FILTER, sortBy []SORT) QUERY, + getCommandFactory func(accountId AccountId, cmd Command, path string, rof string) GET, respMapper0 func(query QUERYRESP, queryParams QueryParams, limit *uint) *RESP, respMapper func(query QUERYRESP, get GETRESP, queryParams QueryParams, limit *uint) *RESP, - accountIds map[string]QueryParams, + accountIds map[AccountId]QueryParams, limit *uint, filter FILTER, sortBy []SORT, - ctx Context) (Result[map[string]*RESP], Error) { + ctx Context) (Result[map[AccountId]*RESP], Error) { logger := client.logger(name, ctx) ctx = ctx.WithLogger(logger) @@ -525,12 +525,12 @@ func queryN[T Foo, FILTER any, SORT any, QUERY QueryCommand[T, QUERY], GET GetCo cmd, err := client.request(ctx, objType.Namespaces, invocations...) if err != nil { - return ZeroResult[map[string]*RESP](), err + return ZeroResult[map[AccountId]*RESP](), err } - return command(client, ctx, cmd, func(body *Response) (map[string]*RESP, State, Error) { - resp := map[string]*RESP{} - stateByAccountId := map[string]State{} + return command(client, ctx, cmd, func(body *Response) (map[AccountId]*RESP, State, Error) { + resp := map[AccountId]*RESP{} + stateByAccountId := map[AccountId]State{} for accountId, queryParams := range accountIds { var queryResponse QUERYRESP err = retrieveQuery(ctx, body, q, mcid(accountId, "0"), &queryResponse) diff --git a/pkg/jmap/tools.go b/pkg/jmap/tools.go index 3956aa3673..e00bef9bcc 100644 --- a/pkg/jmap/tools.go +++ b/pkg/jmap/tools.go @@ -44,13 +44,13 @@ func newEventListeners[T any]() *eventListeners[T] { // Create an identifier to use as a method call ID, from the specified accountId and additional // tag, to make something unique within that API request. -func mcid(accountId string, tag string) string { +func mcid(accountId AccountId, tag string) string { // https://jmap.io/spec-core.html#the-invocation-data-type // May be any string of data: // An arbitrary string from the client to be echoed back with the responses emitted by that method // call (a method may return 1 or more responses, as it may make implicit calls to other methods; // all responses initiated by this method call get the same method call id in the response). - return accountId + ":" + tag + return string(accountId) + ":" + tag } type Cmdr interface { @@ -308,7 +308,7 @@ func (i *Invocation) UnmarshalJSON(bs []byte) error { return nil } -func squashState(all map[string]State) State { +func squashState[K ~string](all map[K]State) State { return squashStateFunc(all, func(s State) State { return s }) } @@ -318,11 +318,11 @@ func squashStates(states ...State) State { } */ -func squashKeyedStates(m map[string]State) State { +func squashKeyedStates[K ~string](m map[K]State) State { return squashStateFunc(m, identity1) } -func squashStateFunc[V any](all map[string]V, mapper func(V) State) State { +func squashStateFunc[K ~string, V any](all map[K]V, mapper func(V) State) State { n := len(all) if n == 0 { return State("") @@ -334,7 +334,7 @@ func squashStateFunc[V any](all map[string]V, mapper func(V) State) State { } parts := make([]string, n) - sortedKeys := make([]string, n) + sortedKeys := make([]K, n) i := 0 for k := range all { sortedKeys[i] = k @@ -343,15 +343,15 @@ func squashStateFunc[V any](all map[string]V, mapper func(V) State) State { slices.Sort(sortedKeys) for i, k := range sortedKeys { if v, ok := all[k]; ok { - parts[i] = k + ":" + string(mapper(v)) + parts[i] = string(k) + ":" + string(mapper(v)) } else { - parts[i] = k + ":" + parts[i] = string(k) + ":" } } return State(strings.Join(parts, ",")) } -func squashStateMaps(first map[string]State, second map[string]State) State { +func squashStateMaps(first map[AccountId]State, second map[AccountId]State) State { return squashStateFunc(mapPairs(first, second), func(p pair[State, State]) State { if p.left != nil { if p.right != nil { diff --git a/pkg/log/log_safely.go b/pkg/log/log_safely.go index 67c4df9dd6..686a2d67e2 100644 --- a/pkg/log/log_safely.go +++ b/pkg/log/log_safely.go @@ -9,11 +9,12 @@ const ( // Safely caps a string to a given size to avoid log bombing. // Use this function to wrap strings that are user input (HTTP headers, path parameters, URI parameters, HTTP body, ...). -func SafeString(text string) string { - runes := []rune(text) +func SafeString[S ~string](text S) string { + t := string(text) + runes := []rune(t) if len(runes) <= logMaxStrLength { - return text + return t } else { return string(runes[0:logMaxStrLength-1]) + `\u2026` // hellip } diff --git a/pkg/structs/structs.go b/pkg/structs/structs.go index ff7cc3cdaf..ce997ea7ce 100644 --- a/pkg/structs/structs.go +++ b/pkg/structs/structs.go @@ -95,6 +95,10 @@ func Set[V comparable](source []V) map[V]struct{} { return result } +func ToStrings[A ~string](s []A) []string { + return Map(s, func(a A) string { return string(a) }) +} + // Creates a slice from a slice, putting each value from the source slice through the // mapper function to determine the value to store into the resulting slice. func Map[E any, R any](source []E, mapper func(E) R) []R { @@ -156,6 +160,16 @@ func MapO[E any, R any](source []E, indexer func(E) (R, bool)) []R { return result } +// Created a map from a map, mapping both the key and the value using the mapper function. +func MapMap[A comparable, B any, X comparable, Y any](m map[A]B, mapper func(A, B) (X, Y)) map[X]Y { + r := make(map[X]Y, len(m)) + for a, b := range m { + x, y := mapper(a, b) + r[x] = y + } + return r +} + // Creates a map from a map, keeping each key as-is, and using the mapper // function to determine the value to store into the resulting map. func MapValues[K comparable, S any, T any](m map[K]S, mapper func(S) T) map[K]T { diff --git a/pkg/structs/structs_test.go b/pkg/structs/structs_test.go index 82ca9034a4..c4eac27285 100644 --- a/pkg/structs/structs_test.go +++ b/pkg/structs/structs_test.go @@ -122,6 +122,24 @@ func TestIndex(t *testing.T) { } } +func TestMapMap(t *testing.T) { + { + m := map[string]int{ + "un": 1, + "deux": 2, + "trois": 3, + } + n := MapMap(m, func(a string, b int) (string, int) { return strings.ToUpper(a), b + 100 }) + assert.Len(t, n, 3) + assert.Contains(t, n, "UN") + assert.Equal(t, 1001, n["UN"]) + assert.Contains(t, n, "DEUX") + assert.Equal(t, 1002, n["DEUX"]) + assert.Contains(t, n, "TROIS") + assert.Equal(t, 1003, n["TROIS"]) + } +} + func TestMap(t *testing.T) { tests := []struct { input []string diff --git a/services/groupware/QueryPagination.md b/services/groupware/QueryPagination.md index d65a203046..0f64bad7d9 100644 --- a/services/groupware/QueryPagination.md +++ b/services/groupware/QueryPagination.md @@ -244,4 +244,3 @@ The alternative would be to have different APIs depending on what each endpoint * endpoint with multiple backends or multiple accounts:
same as above with `?first=...` and `?next=...` * endpoint with a single backend and a single account:
`?position=...&anchor=...&offset=...&limit=...` - diff --git a/services/groupware/pkg/groupware/address_book_supplier_jmap.go b/services/groupware/pkg/groupware/address_book_supplier_jmap.go index 25e1d962f4..7dd86b3183 100644 --- a/services/groupware/pkg/groupware/address_book_supplier_jmap.go +++ b/services/groupware/pkg/groupware/address_book_supplier_jmap.go @@ -17,18 +17,20 @@ func newJmapContactCardSupplier(client *jmap.Client) *JmapContactCardSupplier { return &JmapContactCardSupplier{client: client} } -func (c *JmapContactCardSupplier) GetId() string { - return "jmap" +const jmapContactCardSupplierid = SupplierId("jmap") + +func (c *JmapContactCardSupplier) GetId() SupplierId { + return jmapContactCardSupplierid } func (c *JmapContactCardSupplier) IsMine(id string) bool { return id != "" && !strings.Contains(id, ":") } -func (c *JmapContactCardSupplier) GetAll(accountId string, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { +func (c *JmapContactCardSupplier) GetAll(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { return c.client.GetAddressbooks(accountId, ids, ctx) } -func (c *JmapContactCardSupplier) Query(accountIds []string, qps QueryParamsSupplier, limit *uint, filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[string]*jmap.ContactCardSearchResults], error) { //NOSONAR +func (c *JmapContactCardSupplier) Query(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]*jmap.ContactCardSearchResults], error) { //NOSONAR if m, err := mapQueryParams(c.GetId(), accountIds, qps); err != nil { - return jmap.ZeroResult[map[string]*jmap.ContactCardSearchResults](), err + return jmap.ZeroResult[map[jmap.AccountId]*jmap.ContactCardSearchResults](), err } else { return c.client.QueryContactCards(m, limit, filter, sortBy, calculateTotal, ctx) } diff --git a/services/groupware/pkg/groupware/address_book_supplier_mock.go b/services/groupware/pkg/groupware/address_book_supplier_mock.go index ca8f9ea441..ab10314123 100644 --- a/services/groupware/pkg/groupware/address_book_supplier_mock.go +++ b/services/groupware/pkg/groupware/address_book_supplier_mock.go @@ -64,13 +64,13 @@ func newMockContactCardSupplier() *MockContactCardSupplier { return MockContactCardSupplierInstance } -func (c *MockContactCardSupplier) GetId() string { - return "mock" +func (c *MockContactCardSupplier) GetId() SupplierId { + return SupplierId("mock") } func (c *MockContactCardSupplier) IsMine(id string) bool { return strings.HasPrefix(id, "mock:") } -func (c *MockContactCardSupplier) GetAll(accountId string, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { +func (c *MockContactCardSupplier) GetAll(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { abooks := []jmap.AddressBook{c.addressBook} if len(ids) > 0 { abooks = structs.Filter(abooks, func(a jmap.AddressBook) bool { return slices.Contains(ids, a.Id) }) @@ -86,14 +86,14 @@ func (c *MockContactCardSupplier) GetAll(accountId string, ids []string, ctx jma }, }, nil } -func (c *MockContactCardSupplier) Query(accountIds []string, qps QueryParamsSupplier, limit *uint, filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[string]*jmap.ContactCardSearchResults], error) { //NOSONAR - payload := make(map[string]*jmap.ContactCardSearchResults, len(accountIds)) +func (c *MockContactCardSupplier) Query(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]*jmap.ContactCardSearchResults], error) { //NOSONAR + payload := make(map[jmap.AccountId]*jmap.ContactCardSearchResults, len(accountIds)) total := len(c.contacts) for _, accountId := range accountIds { all := []jmap.ContactCard{} var qp jmap.QueryParams if q, ok, err := qps.ForSupplier(c.GetId(), accountId); err != nil { - return jmap.ZeroResult[map[string]*jmap.ContactCardSearchResults](), err + return jmap.ZeroResult[map[jmap.AccountId]*jmap.ContactCardSearchResults](), err } else if ok { qp = q } else { @@ -141,7 +141,7 @@ func (c *MockContactCardSupplier) Query(accountIds []string, qps QueryParamsSupp payload[accountId] = res } - return jmap.Result[map[string]*jmap.ContactCardSearchResults]{ + return jmap.Result[map[jmap.AccountId]*jmap.ContactCardSearchResults]{ Payload: payload, SessionState: jmap.EmptySessionState, State: jmap.EmptyState, diff --git a/services/groupware/pkg/groupware/api_account.go b/services/groupware/pkg/groupware/api_account.go index 92b6b7ef1d..dbcbeafe2a 100644 --- a/services/groupware/pkg/groupware/api_account.go +++ b/services/groupware/pkg/groupware/api_account.go @@ -34,9 +34,9 @@ func (g *Groupware) GetAccounts(w http.ResponseWriter, r *http.Request) { i++ } // sort on accountId to have a stable order that remains the same with every query - slices.SortFunc(list, func(a, b AccountWithId) int { return strings.Compare(a.AccountId, b.AccountId) }) + slices.SortFunc(list, func(a, b AccountWithId) int { return strings.Compare(string(a.AccountId), string(b.AccountId)) }) var RBODY []AccountWithId = list - return req.respondN(structs.Map(list, func(a AccountWithId) string { return a.AccountId }), RBODY, AccountResponseObjectType, req.session) + return req.respondN(structs.Map(list, func(a AccountWithId) jmap.AccountId { return a.AccountId }), RBODY, AccountResponseObjectType, req.session) }) } @@ -64,19 +64,21 @@ func (g *Groupware) GetAccountsWithTheirIdentities(w http.ResponseWriter, r *htt i++ } // sort on accountId to have a stable order that remains the same with every query - slices.SortFunc(list, func(a, b AccountWithIdAndIdentities) int { return strings.Compare(a.AccountId, b.AccountId) }) + slices.SortFunc(list, func(a, b AccountWithIdAndIdentities) int { + return strings.Compare(string(a.AccountId), string(b.AccountId)) + }) var RBODY []AccountWithIdAndIdentities = list - return req.respondN(structs.Map(list, func(a AccountWithIdAndIdentities) string { return a.AccountId }), RBODY, AccountResponseObjectType, resp) + return req.respondN(structs.Map(list, func(a AccountWithIdAndIdentities) jmap.AccountId { return a.AccountId }), RBODY, AccountResponseObjectType, resp) }) } type AccountWithId struct { - AccountId string `json:"accountId,omitempty"` + AccountId jmap.AccountId `json:"accountId,omitempty"` jmap.Account } type AccountWithIdAndIdentities struct { - AccountId string `json:"accountId,omitempty"` + AccountId jmap.AccountId `json:"accountId,omitempty"` Identities []jmap.Identity `json:"identities,omitempty"` jmap.Account } diff --git a/services/groupware/pkg/groupware/api_addressbooks.go b/services/groupware/pkg/groupware/api_addressbooks.go index 38f9a880d3..45621b2859 100644 --- a/services/groupware/pkg/groupware/api_addressbooks.go +++ b/services/groupware/pkg/groupware/api_addressbooks.go @@ -34,8 +34,8 @@ func (g *Groupware) ModifyAddressBook(w http.ResponseWriter, r *http.Request) { modify(AddressBook, w, r, g, g.jmap.UpdateAddressBook) } -func (g *Groupware) addressbooks(accountId string, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { - return slist(g.addressBookListSuppliers, accountId, ids, ctx, func(accountId string, state jmap.State, notFound []string, list []jmap.AddressBook) jmap.AddressBookGetResponse { +func (g *Groupware) addressbooks(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[jmap.AddressBookGetResponse], error) { + return slist(g.addressBookListSuppliers, accountId, ids, ctx, func(accountId jmap.AccountId, state jmap.State, notFound []string, list []jmap.AddressBook) jmap.AddressBookGetResponse { return jmap.AddressBookGetResponse{AccountId: accountId, State: state, NotFound: notFound, List: list} }) } diff --git a/services/groupware/pkg/groupware/api_blob.go b/services/groupware/pkg/groupware/api_blob.go index 8d33ab78ee..e612247a1c 100644 --- a/services/groupware/pkg/groupware/api_blob.go +++ b/services/groupware/pkg/groupware/api_blob.go @@ -34,7 +34,7 @@ func (g *Groupware) UploadBlob(w http.ResponseWriter, r *http.Request) { if err != nil { return req.error(accountId, err) } - logger := log.From(req.logger.With().Str(logAccountId, accountId)) + logger := log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId))) ctx := req.ctx.WithLogger(logger) resp, _, jerr := g.jmap.UploadBlobStream(accountId, contentType, body, ctx) if jerr != nil { @@ -63,7 +63,7 @@ func (g *Groupware) DownloadBlob(w http.ResponseWriter, r *http.Request) { } typ, _ := req.getStringParam(QueryParamBlobType, "") // optionally, the Content-Type of the blob, which is then used in the response - logger := log.From(req.logger.With().Str(logAccountId, accountId).Str(UriParamBlobId, blobId)) + logger := log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId)).Str(UriParamBlobId, blobId)) ctx := req.ctx.WithLogger(logger) if err := req.serveBlob(blobId, name, typ, ctx, accountId, w); err != nil { @@ -74,7 +74,7 @@ func (g *Groupware) DownloadBlob(w http.ResponseWriter, r *http.Request) { }) } -func (r *Request) serveBlob(blobId string, name string, typ string, ctx jmap.Context, accountId string, w http.ResponseWriter) *Error { //NOSONAR +func (r *Request) serveBlob(blobId string, name string, typ string, ctx jmap.Context, accountId jmap.AccountId, w http.ResponseWriter) *Error { //NOSONAR if typ == "" { typ = DefaultBlobDownloadType } diff --git a/services/groupware/pkg/groupware/api_changes.go b/services/groupware/pkg/groupware/api_changes.go index fa7ec29068..4650eeb91a 100644 --- a/services/groupware/pkg/groupware/api_changes.go +++ b/services/groupware/pkg/groupware/api_changes.go @@ -34,7 +34,7 @@ func (g *Groupware) GetChanges(w http.ResponseWriter, r *http.Request) { //NOSON if err != nil { return req.error(accountId, err) } - l = l.Str(logAccountId, accountId) + l = l.Str(logAccountId, log.SafeString(accountId)) var maxChanges uint = 0 if v, ok, err := req.parseUIntParam(QueryParamMaxChanges, 0); err != nil { // The maximum amount of changes to emit for each type of object. diff --git a/services/groupware/pkg/groupware/api_contacts.go b/services/groupware/pkg/groupware/api_contacts.go index e5ee8f2906..cfe96db940 100644 --- a/services/groupware/pkg/groupware/api_contacts.go +++ b/services/groupware/pkg/groupware/api_contacts.go @@ -82,7 +82,7 @@ func (g *Groupware) ModifyContact(w http.ResponseWriter, r *http.Request) { modify(Contact, w, r, g, g.jmap.UpdateContactCard) } -func (g *Groupware) contacts(accountIds []string, qps QueryParamsSupplier, limit *uint, //NOSONAR +func (g *Groupware) contacts(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, //NOSONAR filter jmap.ContactCardFilterElement, sortBy []jmap.ContactCardComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[*jmap.ContactCardSearchResults], NextToken, error) { return squery(g.contactCardQuerySuppliers, accountIds, qps, limit, filter, sortBy, calculateTotal, ctx, diff --git a/services/groupware/pkg/groupware/api_emails.go b/services/groupware/pkg/groupware/api_emails.go index 9e6f6d5eac..5582cac8c1 100644 --- a/services/groupware/pkg/groupware/api_emails.go +++ b/services/groupware/pkg/groupware/api_emails.go @@ -24,7 +24,7 @@ import ( // Get the changes tp Emails since a certain State. // @api:tags email,changes func (g *Groupware) GetEmailChanges(w http.ResponseWriter, r *http.Request) { - changes(Email, w, r, g, func(accountId string, sinceState jmap.State, maxChanges uint, ctx jmap.Context) (jmap.Result[jmap.EmailChanges], error) { + changes(Email, w, r, g, func(accountId jmap.AccountId, sinceState jmap.State, maxChanges uint, ctx jmap.Context) (jmap.Result[jmap.EmailChanges], error) { return g.jmap.GetEmailChanges(accountId, sinceState, true, g.config.maxBodyValueBytes, maxChanges, ctx) }) } @@ -42,7 +42,7 @@ func (g *Groupware) GetAllEmailsInMailbox(w http.ResponseWriter, r *http.Request fetchBodies := false withThreads := true query(Email, w, r, g, g.defaults.emailLimit, - func(req Request, accountId string, containerId string, qp jmap.QueryParams, limit *uint, ctx jmap.Context) (jmap.Result[*jmap.EmailSearchResults], error) { //NOSONAR + func(req Request, accountId jmap.AccountId, containerId string, qp jmap.QueryParams, limit *uint, ctx jmap.Context) (jmap.Result[*jmap.EmailSearchResults], error) { //NOSONAR result, jerr := g.jmap.GetAllEmailsInMailbox(accountId, containerId, qp, limit, collapseThreads, fetchBodies, g.config.maxBodyValueBytes, withThreads, ctx) if jerr != nil { return jmap.ZeroResult[*jmap.EmailSearchResults](), jerr @@ -348,13 +348,13 @@ func (g *Groupware) getEmailsSince(w http.ResponseWriter, r *http.Request, since type EmailSearchSnippetsResults jmap.SearchResultsTemplate[Snippet] type EmailWithSnippets struct { - AccountId string `json:"accountId,omitempty"` + AccountId jmap.AccountId `json:"accountId,omitempty"` Snippets []SnippetWithoutEmailId `json:"snippets,omitempty"` jmap.Email } type Snippet struct { - AccountId string `json:"accountId,omitempty"` + AccountId jmap.AccountId `json:"accountId,omitempty"` jmap.SearchSnippetWithMeta } @@ -649,7 +649,7 @@ func (g *Groupware) GetEmailsForAllAccounts(w http.ResponseWriter, r *http.Reque if !ok { return req.errorN(allAccountIds, err) } - logger = log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(allAccountIds))) + logger = log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(structs.ToStrings(allAccountIds)))) ctx := req.ctx.WithLogger(logger) if !filter.IsNotEmpty() { @@ -758,7 +758,7 @@ var draftEmailAutoMailboxRolePrecedence = []string{ jmap.JmapMailboxRoleInbox, // but if there is none, we will use the Mailbox with the inbox role instead } -func findDraftsMailboxId(j *jmap.Client, accountId string, req Request, ctx jmap.Context) (string, Response) { +func findDraftsMailboxId(j *jmap.Client, accountId jmap.AccountId, req Request, ctx jmap.Context) (string, Response) { result, jerr := j.SearchMailboxIdsPerRole(single(accountId), draftEmailAutoMailboxRolePrecedence, ctx) if jerr != nil { return "", req.jmapError(accountId, jerr, result) @@ -781,7 +781,7 @@ var sentEmailAutoMailboxRolePrecedence = []string{ var draftAndSentMailboxRoles = structs.Uniq(structs.Concat(draftEmailAutoMailboxRolePrecedence, sentEmailAutoMailboxRolePrecedence)) -func findSentMailboxId(j *jmap.Client, accountId string, req Request, ctx jmap.Context) (string, string, Response) { //NOSONAR +func findSentMailboxId(j *jmap.Client, accountId jmap.AccountId, req Request, ctx jmap.Context) (string, string, Response) { //NOSONAR result, jerr := j.SearchMailboxIdsPerRole(single(accountId), draftAndSentMailboxRoles, ctx) if jerr != nil { return "", "", req.jmapError(accountId, jerr, result) @@ -812,7 +812,7 @@ func findSentMailboxId(j *jmap.Client, accountId string, req Request, ctx jmap.C func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) { create(Email, w, r, g, - func(r Request, accountId string, body *jmap.EmailChange, ctx jmap.Context) (bool, Response) { + func(r Request, accountId jmap.AccountId, body *jmap.EmailChange, ctx jmap.Context) (bool, Response) { if len(body.MailboxIds) < 1 { mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, r, ctx) if mailboxId != "" && body != nil { @@ -826,7 +826,7 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) { } return true, Response{} }, - func(accountId string, body jmap.EmailChange, ctx jmap.Context) (jmap.Result[*jmap.Email], error) { + func(accountId jmap.AccountId, body jmap.EmailChange, ctx jmap.Context) (jmap.Result[*jmap.Email], error) { return g.jmap.CreateEmail(accountId, body, "", ctx) }, ) @@ -835,7 +835,7 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) { func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) { replaceId := "" create(Email, w, r, g, - func(r Request, accountId string, body *jmap.EmailChange, ctx jmap.Context) (bool, Response) { + func(r Request, accountId jmap.AccountId, body *jmap.EmailChange, ctx jmap.Context) (bool, Response) { if len(body.MailboxIds) < 1 { mailboxId, resp := findDraftsMailboxId(g.jmap, accountId, r, ctx) if mailboxId != "" { @@ -852,7 +852,7 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) { return true, Response{} }, - func(accountId string, body jmap.EmailChange, ctx jmap.Context) (jmap.Result[*jmap.Email], error) { + func(accountId jmap.AccountId, body jmap.EmailChange, ctx jmap.Context) (jmap.Result[*jmap.Email], error) { ctx = ctx.WithLogger(log.From(ctx.Logger.With().Str("replaceId", replaceId))) return g.jmap.CreateEmail(accountId, body, replaceId, ctx) }, @@ -883,7 +883,7 @@ func (g *Groupware) UpdateEmailKeywords(w http.ResponseWriter, r *http.Request) if gwerr != nil { return req.error(accountId, gwerr) } - l.Str(logAccountId, accountId) + l.Str(logAccountId, log.SafeString(accountId)) emailId, err := req.PathParam(UriParamEmailId) if err != nil { @@ -945,7 +945,7 @@ func (g *Groupware) AddEmailKeywords(w http.ResponseWriter, r *http.Request) { / if gwerr != nil { return req.error(accountId, gwerr) } - l.Str(logAccountId, accountId) + l.Str(logAccountId, log.SafeString(accountId)) emailId, err := req.PathParam(UriParamEmailId) if err != nil { @@ -1008,7 +1008,7 @@ func (g *Groupware) RemoveEmailKeywords(w http.ResponseWriter, r *http.Request) if err != nil { return req.error(accountId, err) } - l.Str(logAccountId, accountId) + l.Str(logAccountId, log.SafeString(accountId)) emailId, err := req.PathParam(UriParamEmailId) if err != nil { @@ -1082,7 +1082,7 @@ func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) { //NOSONA if gwerr != nil { return req.error(accountId, gwerr) } - l.Str(logAccountId, accountId) + l.Str(logAccountId, log.SafeString(accountId)) emailId, err := req.PathParam(UriParamEmailId) if err != nil { @@ -1309,7 +1309,7 @@ func (g *Groupware) RelatedToEmail(w http.ResponseWriter, r *http.Request) { //N type EmailSummary struct { // The id of the account this Email summary pertains to. - AccountId string `json:"accountId,omitempty"` + AccountId jmap.AccountId `json:"accountId,omitempty"` // The id of the Email object. // @@ -1450,7 +1450,7 @@ var _ jmap.Foo = EmailSummary{} func (e EmailSummary) GetObjectType() jmap.ObjectType { return jmap.EmailType } -func summarizeEmail(accountId string, email jmap.Email) EmailSummary { +func summarizeEmail(accountId jmap.AccountId, email jmap.Email) EmailSummary { return EmailSummary{ AccountId: accountId, Id: email.Id, @@ -1474,7 +1474,7 @@ func summarizeEmail(accountId string, email jmap.Email) EmailSummary { } type emailWithAccountId struct { - accountId string + accountId jmap.AccountId email jmap.Email } @@ -1495,7 +1495,7 @@ func (g *Groupware) GetLatestEmailsSummaryForAllAccounts(w http.ResponseWriter, l := req.logger.With() allAccountIds := req.AllAccountIds() - l.Array(logAccountId, log.SafeStringArray(allAccountIds)) + l.Array(logAccountId, log.SafeStringArray(structs.ToStrings(allAccountIds))) limit, ok, err := req.parseUIntParam(QueryParamLimit, 10) // TODO from configuration if err != nil { diff --git a/services/groupware/pkg/groupware/api_index.go b/services/groupware/pkg/groupware/api_index.go index db6efe5c17..7671d1ff13 100644 --- a/services/groupware/pkg/groupware/api_index.go +++ b/services/groupware/pkg/groupware/api_index.go @@ -90,7 +90,7 @@ type IndexAccountCapabilities struct { } type IndexAccount struct { - AccountId string `json:"accountId"` + AccountId jmap.AccountId `json:"accountId"` // A user-friendly string to show when presenting content from this Account, // e.g., the email address representing the owner of the account. @@ -116,11 +116,11 @@ type IndexAccount struct { // Primary account identifiers per API usage type. type IndexPrimaryAccounts struct { - Mail string `json:"mail"` - Submission string `json:"submission"` - Blob string `json:"blob"` - VacationResponse string `json:"vacationResponse"` - Sieve string `json:"sieve"` + Mail jmap.AccountId `json:"mail"` + Submission jmap.AccountId `json:"submission"` + Blob jmap.AccountId `json:"blob"` + VacationResponse jmap.AccountId `json:"vacationResponse"` + Sieve jmap.AccountId `json:"sieve"` } type IndexResponse struct { @@ -185,7 +185,7 @@ func buildIndexPrimaryAccounts(session *jmap.Session) IndexPrimaryAccounts { } } -func buildIndexAccounts(session *jmap.Session, boot map[string]jmap.AccountBootstrapResult) []IndexAccount { +func buildIndexAccounts(session *jmap.Session, boot map[jmap.AccountId]jmap.AccountBootstrapResult) []IndexAccount { accounts := make([]IndexAccount, len(session.Accounts)) i := 0 for accountId, account := range session.Accounts { @@ -206,7 +206,7 @@ func buildIndexAccounts(session *jmap.Session, boot map[string]jmap.AccountBoots accounts[i] = indexAccount i++ } - slices.SortFunc(accounts, func(a, b IndexAccount) int { return strings.Compare(a.AccountId, b.AccountId) }) + slices.SortFunc(accounts, func(a, b IndexAccount) int { return strings.Compare(string(a.AccountId), string(b.AccountId)) }) return accounts } diff --git a/services/groupware/pkg/groupware/api_mailbox.go b/services/groupware/pkg/groupware/api_mailbox.go index f7b26cb0ac..8bcbf85978 100644 --- a/services/groupware/pkg/groupware/api_mailbox.go +++ b/services/groupware/pkg/groupware/api_mailbox.go @@ -71,7 +71,7 @@ func (g *Groupware) GetMailboxes(w http.ResponseWriter, r *http.Request) { //NOS return resp } - logger := log.From(req.logger.With().Str(logAccountId, accountId)) + logger := log.From(req.logger.With().Str(logAccountId, log.SafeString(accountId))) ctx := req.ctx.WithLogger(logger) if hasCriteria { @@ -106,7 +106,7 @@ func (g *Groupware) GetMailboxesForAllAccounts(w http.ResponseWriter, r *http.Re if len(accountIds) < 1 { return req.noopN(nil) // when the user has no accounts } - logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds))) + logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(structs.ToStrings(accountIds)))) ctx := req.ctx.WithLogger(logger) var filter jmap.MailboxFilterCondition @@ -153,7 +153,7 @@ func (g *Groupware) GetMailboxByRoleForAllAccounts(w http.ResponseWriter, r *htt return req.errorN(accountIds, err) } - logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds)).Str("role", role)) + logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(structs.ToStrings(accountIds))).Str("role", role)) ctx := req.ctx.WithLogger(logger) filter := jmap.MailboxFilterCondition{ @@ -180,7 +180,7 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht l := req.logger.With() allAccountIds := req.AllAccountIds() - l.Array(logAccountId, log.SafeStringArray(allAccountIds)) + l.Array(logAccountId, log.SafeStringArray(structs.ToStrings(allAccountIds))) sinceStateStrMap, ok, err := req.parseMapParam(QueryParamSince) if err != nil { @@ -205,7 +205,7 @@ func (g *Groupware) GetMailboxChangesForAllAccounts(w http.ResponseWriter, r *ht logger := log.From(l) ctx := req.ctx.WithLogger(logger) - sinceStateMap := structs.MapValues(sinceStateStrMap, toState) + sinceStateMap := structs.MapMap(sinceStateStrMap, toAccountIdState) result, jerr := g.jmap.GetMailboxChangesForMultipleAccounts(allAccountIds, sinceStateMap, maxChanges, ctx) if jerr != nil { return req.jmapErrorN(allAccountIds, jerr, result) @@ -221,7 +221,7 @@ func (g *Groupware) GetMailboxRoles(w http.ResponseWriter, r *http.Request) { g.respond(w, r, func(req Request) Response { l := req.logger.With() allAccountIds := req.AllAccountIds() - l.Array(logAccountId, log.SafeStringArray(allAccountIds)) + l.Array(logAccountId, log.SafeStringArray(structs.ToStrings(allAccountIds))) logger := log.From(l) ctx := req.ctx.WithLogger(logger) @@ -257,8 +257,8 @@ func scoreMailbox(m jmap.Mailbox) int { return 1000 } -func sortMailboxesMap(mailboxesByAccountId map[string][]jmap.Mailbox) map[string][]jmap.Mailbox { - sortedByAccountId := make(map[string][]jmap.Mailbox, len(mailboxesByAccountId)) +func sortMailboxesMap(mailboxesByAccountId map[jmap.AccountId][]jmap.Mailbox) map[jmap.AccountId][]jmap.Mailbox { + sortedByAccountId := make(map[jmap.AccountId][]jmap.Mailbox, len(mailboxesByAccountId)) for accountId, unsorted := range mailboxesByAccountId { mailboxes := make([]jmap.Mailbox, len(unsorted)) copy(mailboxes, unsorted) diff --git a/services/groupware/pkg/groupware/api_objects.go b/services/groupware/pkg/groupware/api_objects.go index be75e2b177..e377e8cf72 100644 --- a/services/groupware/pkg/groupware/api_objects.go +++ b/services/groupware/pkg/groupware/api_objects.go @@ -45,7 +45,7 @@ func (g *Groupware) GetObjects(w http.ResponseWriter, r *http.Request) { //NOSON if err != nil { return req.error(accountId, err) } - l = l.Str(logAccountId, accountId) + l = l.Str(logAccountId, log.SafeString(accountId)) mailboxIds := []string{} emailIds := []string{} diff --git a/services/groupware/pkg/groupware/api_quota.go b/services/groupware/pkg/groupware/api_quota.go index a751a71b87..bac9727b49 100644 --- a/services/groupware/pkg/groupware/api_quota.go +++ b/services/groupware/pkg/groupware/api_quota.go @@ -5,6 +5,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/jmap" "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/pkg/structs" ) // Get quota limits. @@ -13,7 +14,7 @@ import ( // // Note that there may be multiple Quota objects for different resource types. func (g *Groupware) GetQuota(w http.ResponseWriter, r *http.Request) { - getFromMap(Quota, w, r, g, func(accountIds, _ []string, ctx jmap.Context) (jmap.Result[map[string]jmap.QuotaGetResponse], error) { + getFromMap(Quota, w, r, g, func(accountIds []jmap.AccountId, _ []string, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]jmap.QuotaGetResponse], error) { return g.jmap.GetQuotas(accountIds, ctx) }) } @@ -33,7 +34,7 @@ func (g *Groupware) GetQuotaForAllAccounts(w http.ResponseWriter, r *http.Reques if len(accountIds) < 1 { return req.noopN(accountIds) // user has no accounts } - logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(accountIds))) + logger := log.From(req.logger.With().Array(logAccountId, log.SafeStringArray(structs.ToStrings(accountIds)))) ctx := req.ctx.WithLogger(logger) result, jerr := g.jmap.GetQuotas(accountIds, ctx) @@ -41,7 +42,7 @@ func (g *Groupware) GetQuotaForAllAccounts(w http.ResponseWriter, r *http.Reques return req.jmapErrorN(accountIds, jerr, result) } - body := make(map[string]AccountQuota, len(result.Payload)) + body := make(map[jmap.AccountId]AccountQuota, len(result.Payload)) for accountId, accountQuotas := range result.Payload { body[accountId] = AccountQuota{ State: accountQuotas.State, diff --git a/services/groupware/pkg/groupware/api_tasklists.go b/services/groupware/pkg/groupware/api_tasklists.go index fee45a857e..096318e069 100644 --- a/services/groupware/pkg/groupware/api_tasklists.go +++ b/services/groupware/pkg/groupware/api_tasklists.go @@ -13,7 +13,7 @@ func (g *Groupware) GetTaskLists(w http.ResponseWriter, r *http.Request) { if !ok { return resp } - var _ string = accountId + var _ jmap.AccountId = accountId var body []jmap.TaskList = AllTaskLists meta := TaskListsMeta{SessionState: req.session.State} @@ -28,7 +28,7 @@ func (g *Groupware) GetTaskListById(w http.ResponseWriter, r *http.Request) { if !ok { return resp } - var _ string = accountId + var _ jmap.AccountId = accountId tasklistId, err := req.PathParam(UriParamTaskListId) if err != nil { @@ -52,7 +52,7 @@ func (g *Groupware) GetTasksInTaskList(w http.ResponseWriter, r *http.Request) { if !ok { return resp } - var _ string = accountId + var _ jmap.AccountId = accountId tasklistId, err := req.PathParam(UriParamTaskListId) if err != nil { diff --git a/services/groupware/pkg/groupware/api_vacation.go b/services/groupware/pkg/groupware/api_vacation.go index 0f8f2c344e..04c58a3974 100644 --- a/services/groupware/pkg/groupware/api_vacation.go +++ b/services/groupware/pkg/groupware/api_vacation.go @@ -13,7 +13,7 @@ import ( // // The VacationResponse object represents the state of vacation-response-related settings for an account. func (g *Groupware) GetVacation(w http.ResponseWriter, r *http.Request) { - get(VacationResponse, w, r, g, func(accountId string, ids []string, ctx jmap.Context) (jmap.Result[jmap.VacationResponseGetResponse], error) { + get(VacationResponse, w, r, g, func(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[jmap.VacationResponseGetResponse], error) { return g.jmap.GetVacationResponse(accountId, ctx) }) } @@ -23,7 +23,7 @@ func (g *Groupware) GetVacation(w http.ResponseWriter, r *http.Request) { // A vacation response sends an automatic reply when a message is delivered to the mail store, informing the original // sender that their message may not be read for some time. func (g *Groupware) SetVacation(w http.ResponseWriter, r *http.Request) { - modify(VacationResponse, w, r, g, func(accountId string, id string, change jmap.VacationResponseChange, ctx jmap.Context) (jmap.Result[jmap.VacationResponse], error) { + modify(VacationResponse, w, r, g, func(accountId jmap.AccountId, id string, change jmap.VacationResponseChange, ctx jmap.Context) (jmap.Result[jmap.VacationResponse], error) { return g.jmap.SetVacationResponse(accountId, change, ctx) }) } diff --git a/services/groupware/pkg/groupware/error.go b/services/groupware/pkg/groupware/error.go index e9de913a92..037e23d738 100644 --- a/services/groupware/pkg/groupware/error.go +++ b/services/groupware/pkg/groupware/error.go @@ -750,19 +750,19 @@ func errorResponses(errors ...Error) ErrorResponse { return ErrorResponse{Errors: errors} } -func (r *Request) error(accountId string, err *Error) Response { +func (r *Request) error(accountId jmap.AccountId, err *Error) Response { return errorResponse(single(accountId), err, r.session.State, jmap.NoLanguage) } -func (r *Request) errorS(accountId string, err *Error, result jmap.ResultMetadata) Response { +func (r *Request) errorS(accountId jmap.AccountId, err *Error, result jmap.ResultMetadata) Response { return errorResponse(single(accountId), err, result.GetSessionState(), result.GetLanguage()) } -func (r *Request) errorN(accountIds []string, err *Error) Response { +func (r *Request) errorN(accountIds []jmap.AccountId, err *Error) Response { return errorResponse(accountIds, err, r.session.State, jmap.NoLanguage) } -func (r *Request) jmapError(accountId string, err error, result jmap.ResultMetadata) Response { +func (r *Request) jmapError(accountId jmap.AccountId, err error, result jmap.ResultMetadata) Response { switch e := err.(type) { case jmap.Error: if result != nil { @@ -779,7 +779,7 @@ func (r *Request) jmapError(accountId string, err error, result jmap.ResultMetad } } -func (r *Request) jmapErrorN(accountIds []string, err error, result jmap.ResultMetadata) Response { +func (r *Request) jmapErrorN(accountIds []jmap.AccountId, err error, result jmap.ResultMetadata) Response { switch e := err.(type) { case jmap.Error: if result != nil { diff --git a/services/groupware/pkg/groupware/examples_test.go b/services/groupware/pkg/groupware/examples_test.go index 0867b6ee2c..d470faf054 100644 --- a/services/groupware/pkg/groupware/examples_test.go +++ b/services/groupware/pkg/groupware/examples_test.go @@ -27,8 +27,8 @@ func (e Exemplar) AccountQuota() AccountQuota { } } -func (e Exemplar) AccountQuotaMap() map[string]AccountQuota { - return map[string]AccountQuota{ +func (e Exemplar) AccountQuotaMap() map[jmap.AccountId]AccountQuota { + return map[jmap.AccountId]AccountQuota{ j.AccountId: e.AccountQuota(), } } @@ -148,23 +148,23 @@ func (e Exemplar) ErrorResponse() ErrorResponse { } } -func (e Exemplar) MailboxesByAccountId() (map[string][]jmap.Mailbox, string) { +func (e Exemplar) MailboxesByAccountId() (map[jmap.AccountId][]jmap.Mailbox, string) { j := jmap.ExemplarInstance - return map[string][]jmap.Mailbox{ + return map[jmap.AccountId][]jmap.Mailbox{ j.AccountId: j.Mailboxes(), }, "All mailboxes for all accounts, without a role filter" } -func (e Exemplar) MailboxesByAccountIdFilteredOnInboxRole() (map[string][]jmap.Mailbox, string, string) { +func (e Exemplar) MailboxesByAccountIdFilteredOnInboxRole() (map[jmap.AccountId][]jmap.Mailbox, string, string) { j := jmap.ExemplarInstance - return map[string][]jmap.Mailbox{ + return map[jmap.AccountId][]jmap.Mailbox{ j.AccountId: structs.Filter(j.Mailboxes(), func(m jmap.Mailbox) bool { return m.Role == jmap.JmapMailboxRoleInbox }), }, "All mailboxes for all accounts, filtered on the 'inbox' role", "inboxrole" } -func (e Exemplar) MailboxRolesByAccounts() (map[string][]string, string, string, string) { +func (e Exemplar) MailboxRolesByAccounts() (map[jmap.AccountId][]string, string, string, string) { j := jmap.ExemplarInstance - return map[string][]string{ + return map[jmap.AccountId][]string{ j.AccountId: jmap.JmapMailboxRoles, j.SharedAccountId: jmap.JmapMailboxRoles, }, "Roles of the Mailboxes of each Account", "", "mailboxrolesbyaccount" diff --git a/services/groupware/pkg/groupware/framework.go b/services/groupware/pkg/groupware/framework.go index 27074171fc..543268f750 100644 --- a/services/groupware/pkg/groupware/framework.go +++ b/services/groupware/pkg/groupware/framework.go @@ -25,6 +25,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/jmap" "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/pkg/structs" "github.com/opencloud-eu/opencloud/services/groupware/pkg/config" "github.com/opencloud-eu/opencloud/services/groupware/pkg/metrics" @@ -778,10 +779,9 @@ func (g *Groupware) sendResponse(w http.ResponseWriter, r *http.Request, respons case 0: break case 1: - w.Header().Add(AccountIdResponseHeader, response.accountIds[0]) + w.Header().Add(AccountIdResponseHeader, string(response.accountIds[0])) default: - c := make([]string, len(response.accountIds)) - copy(c, response.accountIds) + c := structs.Map(response.accountIds, func(a jmap.AccountId) string { return string(a) }) slices.Sort(c) value := strings.Join(c, ",") w.Header().Add(AccountIdsResponseHeader, value) @@ -890,12 +890,12 @@ func single[S any](s S) []S { } type QueryParamsSupplier interface { - ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) - ForAccountId(accountId string) (jmap.QueryParams, bool, error) + ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) + ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) } -func mapQueryParams(supplierId string, accountIds []string, qps QueryParamsSupplier) (map[string]jmap.QueryParams, error) { - m := map[string]jmap.QueryParams{} +func mapQueryParams(supplierId SupplierId, accountIds []jmap.AccountId, qps QueryParamsSupplier) (map[jmap.AccountId]jmap.QueryParams, error) { + m := map[jmap.AccountId]jmap.QueryParams{} if supplierId == "" { for _, accountId := range accountIds { if q, ok, err := qps.ForAccountId(accountId); err != nil { @@ -920,11 +920,11 @@ type ErrorQueryParamsSupplier struct { err error } -func (s ErrorQueryParamsSupplier) ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) { +func (s ErrorQueryParamsSupplier) ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return jmap.NullQueryParams, false, s.err } -func (s ErrorQueryParamsSupplier) ForAccountId(accountId string) (jmap.QueryParams, bool, error) { +func (s ErrorQueryParamsSupplier) ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return jmap.NullQueryParams, false, s.err } @@ -933,11 +933,11 @@ var _ QueryParamsSupplier = ErrorQueryParamsSupplier{} type FirstQueryParamsSupplier struct { } -func (s FirstQueryParamsSupplier) ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) { +func (s FirstQueryParamsSupplier) ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return jmap.NullQueryParams, true, nil } -func (s FirstQueryParamsSupplier) ForAccountId(accountId string) (jmap.QueryParams, bool, error) { +func (s FirstQueryParamsSupplier) ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return jmap.NullQueryParams, true, nil } @@ -947,21 +947,21 @@ type StaticQueryParamsSupplier struct { qp jmap.QueryParams } -func (s StaticQueryParamsSupplier) ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) { +func (s StaticQueryParamsSupplier) ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return s.qp, true, nil } -func (s StaticQueryParamsSupplier) ForAccountId(accountId string) (jmap.QueryParams, bool, error) { +func (s StaticQueryParamsSupplier) ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return s.qp, true, nil } var _ QueryParamsSupplier = StaticQueryParamsSupplier{} type MultiSupplierQueryParamsSupplier struct { - m map[string]map[string]jmap.QueryParams + m map[SupplierId]map[jmap.AccountId]jmap.QueryParams } -func (s MultiSupplierQueryParamsSupplier) ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) { +func (s MultiSupplierQueryParamsSupplier) ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) { if a, ok := s.m[supplierId]; ok { if b, ok := a[accountId]; ok { return b, true, nil @@ -970,7 +970,7 @@ func (s MultiSupplierQueryParamsSupplier) ForSupplier(supplierId string, account return jmap.NullQueryParams, false, nil } -func (s MultiSupplierQueryParamsSupplier) ForAccountId(accountId string) (jmap.QueryParams, bool, error) { +func (s MultiSupplierQueryParamsSupplier) ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) { switch len(s.m) { case 1: for _, v := range s.m { @@ -989,14 +989,14 @@ func (s MultiSupplierQueryParamsSupplier) ForAccountId(accountId string) (jmap.Q var _ QueryParamsSupplier = MultiSupplierQueryParamsSupplier{} type SingleSupplierQueryParamsSupplier struct { - m map[string]jmap.QueryParams + m map[jmap.AccountId]jmap.QueryParams } -func (s SingleSupplierQueryParamsSupplier) ForSupplier(supplierId string, accountId string) (jmap.QueryParams, bool, error) { +func (s SingleSupplierQueryParamsSupplier) ForSupplier(supplierId SupplierId, accountId jmap.AccountId) (jmap.QueryParams, bool, error) { return jmap.NullQueryParams, false, fmt.Errorf("unable to provide for supplier with single supplier token") } -func (s SingleSupplierQueryParamsSupplier) ForAccountId(accountId string) (jmap.QueryParams, bool, error) { +func (s SingleSupplierQueryParamsSupplier) ForAccountId(accountId jmap.AccountId) (jmap.QueryParams, bool, error) { if b, ok := s.m[accountId]; ok { return b, true, nil } diff --git a/services/groupware/pkg/groupware/mock_tasks.go b/services/groupware/pkg/groupware/mock_tasks.go index 5b91ce0028..d7e79f600c 100644 --- a/services/groupware/pkg/groupware/mock_tasks.go +++ b/services/groupware/pkg/groupware/mock_tasks.go @@ -26,8 +26,8 @@ var TL1 = jmap.TaskList{ WorkflowStatuses: []string{ "new", "todo", "in-progress", "done", }, - ShareWith: map[string]jmap.TaskRights{ - "eefeeb4p": { + ShareWith: map[jmap.PrincipalId]jmap.TaskRights{ + jmap.PrincipalId("eefeeb4p"): { MayReadItems: true, MayWriteAll: false, MayWriteOwn: true, diff --git a/services/groupware/pkg/groupware/next.go b/services/groupware/pkg/groupware/next.go index c03b4cfcce..5eb3635904 100644 --- a/services/groupware/pkg/groupware/next.go +++ b/services/groupware/pkg/groupware/next.go @@ -13,7 +13,7 @@ type NextToken string const NoNextToken = NextToken("") -func nextSingle(m map[string]jmap.QueryParams) (NextToken, error) { +func nextSingle(m map[jmap.AccountId]jmap.QueryParams) (NextToken, error) { if b, err := json.Marshal(m); err != nil { return NoNextToken, err } else { @@ -21,7 +21,7 @@ func nextSingle(m map[string]jmap.QueryParams) (NextToken, error) { } } -func nextMulti(m map[string]map[string]jmap.QueryParams) (NextToken, error) { +func nextMulti(m map[SupplierId]map[jmap.AccountId]jmap.QueryParams) (NextToken, error) { if b, err := json.Marshal(m); err != nil { return NoNextToken, err } else { @@ -42,7 +42,7 @@ func unnext(n NextToken) (QueryParamsSupplier, error) { if b, err := DecodeBytesFromBase62(payload); err != nil { return ErrorQueryParamsSupplier{err: err}, err } else { - var m map[string]jmap.QueryParams + var m map[jmap.AccountId]jmap.QueryParams if err := json.Unmarshal(b, &m); err != nil { return ErrorQueryParamsSupplier{err: err}, err } else { @@ -54,7 +54,7 @@ func unnext(n NextToken) (QueryParamsSupplier, error) { if b, err := DecodeBytesFromBase62(payload); err != nil { return ErrorQueryParamsSupplier{err: err}, err } else { - var m map[string]map[string]jmap.QueryParams + var m map[SupplierId]map[jmap.AccountId]jmap.QueryParams if err := json.Unmarshal(b, &m); err != nil { return ErrorQueryParamsSupplier{err: err}, err } else { @@ -68,11 +68,11 @@ func unnext(n NextToken) (QueryParamsSupplier, error) { } func curryNoNextMapQuery[SRES jmap.SearchResults[T], T jmap.Idable, FILTER any, COMP any]( - f func(accountIds map[string]jmap.QueryParams, limit *uint, filter FILTER, sortBy []COMP, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[string]SRES], error), + f func(accountIds map[jmap.AccountId]jmap.QueryParams, limit *uint, filter FILTER, sortBy []COMP, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]SRES], error), sorter func(a, b T) int, searchResultCtor func(canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint, results []T) SRES, -) func(req Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { - return func(_ Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { //NOSONAR +) func(req Request, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { + return func(_ Request, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { //NOSONAR if m, err := mapQueryParams("", accountIds, qps); err != nil { return jmap.ZeroResult[SRES](), NoNextToken, err } else { @@ -80,7 +80,7 @@ func curryNoNextMapQuery[SRES jmap.SearchResults[T], T jmap.Idable, FILTER any, if err != nil { return jmap.ZeroResult[SRES](), NoNextToken, err } else { - singleAccountId := "" + var singleAccountId jmap.AccountId = "" // TODO what about requests with zero accountIds, can these even happen at all? if len(accountIds) == 1 { // optimization: no need to combine the results of several accounts if the query was @@ -91,14 +91,14 @@ func curryNoNextMapQuery[SRES jmap.SearchResults[T], T jmap.Idable, FILTER any, // all accounts: let's first calculate the number of results we have for each account totalByAccount := structs.MapValues(result.Payload, func(a SRES) int { return len(a.GetResults()) }) // and let's now pick out the accounts that do have results - accountsWithResults := structs.FilterKeys(totalByAccount, func(_ string, total int) bool { return total > 0 }) + accountsWithResults := structs.FilterKeys(totalByAccount, func(_ jmap.AccountId, total int) bool { return total > 0 }) if len(accountsWithResults) == 1 { singleAccountId = accountsWithResults[0] } // TODO what if we don't have any results at all? (accountsWithResults == 0) } if singleAccountId != "" { - r, err := jmap.RefineResultPayload(result, func(a map[string]SRES) (SRES, bool, error) { + r, err := jmap.RefineResultPayload(result, func(a map[jmap.AccountId]SRES) (SRES, bool, error) { r, ok := a[accountIds[0]] return r, ok, nil }) @@ -113,10 +113,10 @@ func curryNoNextMapQuery[SRES jmap.SearchResults[T], T jmap.Idable, FILTER any, } func flattenMultipleAccounts[SRES jmap.SearchResults[T], T jmap.Idable]( - accountIds []string, + accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, - result jmap.Result[map[string]SRES], + result jmap.Result[map[jmap.AccountId]SRES], sorter func(a, b T) int, searchResultCtor func(canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint, results []T) SRES, ) (jmap.Result[SRES], NextToken, error) { @@ -133,7 +133,7 @@ func flattenMultipleAccounts[SRES jmap.SearchResults[T], T jmap.Idable]( cc := true total := uint(0) - lastByAccountId := map[string]string{} + lastByAccountId := map[jmap.AccountId]string{} for accountId, searchResult := range result.Payload { if !searchResult.GetCanCalculateChanges() { cc = false @@ -150,7 +150,7 @@ func flattenMultipleAccounts[SRES jmap.SearchResults[T], T jmap.Idable]( // 4. we need to build the NextToken by taking the ID of the last item // we kept after shrinking, but separately for each accountId - n := map[string]jmap.QueryParams{} + n := map[jmap.AccountId]jmap.QueryParams{} for accountId, lastId := range lastByAccountId { n[accountId] = jmap.QueryParams{Anchor: lastId, AnchorOffset: ptr(1)} } @@ -160,7 +160,7 @@ func flattenMultipleAccounts[SRES jmap.SearchResults[T], T jmap.Idable]( if t, err := nextSingle(n); err != nil { return jmap.ZeroResult[SRES](), NoNextToken, err } else { - if r, err := jmap.RefineResultPayload(result, func(a map[string]SRES) (SRES, bool, error) { + if r, err := jmap.RefineResultPayload(result, func(a map[jmap.AccountId]SRES) (SRES, bool, error) { return searchResultCtor(jmap.ChangeCalculation(cc), nil, limit, &total, all), true, nil }); err != nil { return jmap.ZeroResult[SRES](), NoNextToken, err @@ -184,7 +184,7 @@ func flattenMultipleAccounts[SRES jmap.SearchResults[T], T jmap.Idable]( // no need to compute a NextToken since there was no limit, // which means that this Result contains all the elements, // and thus there is no "next" to query for - if r, err := jmap.RefineResultPayload(result, func(a map[string]SRES) (SRES, bool, error) { + if r, err := jmap.RefineResultPayload(result, func(a map[jmap.AccountId]SRES) (SRES, bool, error) { return searchResultCtor(jmap.ChangeCalculation(cc), nil, limit, &total, all), true, nil }); err != nil { return jmap.ZeroResult[SRES](), NoNextToken, err diff --git a/services/groupware/pkg/groupware/objtypes.go b/services/groupware/pkg/groupware/objtypes.go index c52a61032a..8a51ad64ed 100644 --- a/services/groupware/pkg/groupware/objtypes.go +++ b/services/groupware/pkg/groupware/objtypes.go @@ -10,7 +10,7 @@ type ObjectType[T jmap.Foo, CH jmap.Change, CHS jmap.Changes[T]] struct { responseType ResponseObjectType uriParamName string containerUriParamName string - accountFunc func(r *Request) (bool, string, Response) + accountFunc func(r *Request) (bool, jmap.AccountId, Response) failedToDeleteError GroupwareError } diff --git a/services/groupware/pkg/groupware/request.go b/services/groupware/pkg/groupware/request.go index 3881c12c75..950c561c0b 100644 --- a/services/groupware/pkg/groupware/request.go +++ b/services/groupware/pkg/groupware/request.go @@ -45,8 +45,8 @@ type Request struct { ctx jmap.Context } -func isDefaultAccountId(accountId string) bool { - return slices.Contains(defaultAccountIds, accountId) +func isDefaultAccountId(accountId jmap.AccountId) bool { + return slices.Contains(defaultAccountIds, string(accountId)) } func (r *Request) push(typ string, event any) { @@ -129,13 +129,13 @@ func (r *Request) PathListParamDoc(name string, _ string) ([]string, *Error) { return strings.Split(value, ","), nil } -func (r *Request) AllAccountIds() []string { +func (r *Request) AllAccountIds() []jmap.AccountId { // TODO potentially filter on "subscribed" accounts? return structs.Uniq(structs.Keys(r.session.Accounts)) } -func (r *Request) GetAccountIdWithoutFallback() (string, *Error) { - accountId := chi.URLParam(r.r, UriParamAccountId) +func (r *Request) GetAccountIdWithoutFallback() (jmap.AccountId, *Error) { + accountId := jmap.AccountId(chi.URLParam(r.r, UriParamAccountId)) if accountId == "" || isDefaultAccountId(accountId) { r.logger.Error().Err(errNoPrimaryAccountFallback).Msg("failed to determine the accountId") return "", apiError(r.errorId(), ErrorNonExistingAccount, @@ -146,8 +146,8 @@ func (r *Request) GetAccountIdWithoutFallback() (string, *Error) { return accountId, nil } -func (r *Request) getAccountId(fallback string, err error) (string, *Error) { - accountId := chi.URLParam(r.r, UriParamAccountId) +func (r *Request) getAccountId(fallback jmap.AccountId, err error) (jmap.AccountId, *Error) { + accountId := jmap.AccountId(chi.URLParam(r.r, UriParamAccountId)) if accountId == "" || isDefaultAccountId(accountId) { accountId = fallback } @@ -161,45 +161,45 @@ func (r *Request) getAccountId(fallback string, err error) (string, *Error) { return accountId, nil } -func (r *Request) GetAccountIdForMail() (string, *Error) { +func (r *Request) GetAccountIdForMail() (jmap.AccountId, *Error) { return r.getAccountId(r.session.PrimaryAccounts.Mail, errNoPrimaryAccountForMail) } -func (r *Request) GetAccountIdForBlob() (string, *Error) { +func (r *Request) GetAccountIdForBlob() (jmap.AccountId, *Error) { return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForBlob) } -func (r *Request) GetAccountIdForVacationResponse() (string, *Error) { +func (r *Request) GetAccountIdForVacationResponse() (jmap.AccountId, *Error) { return r.getAccountId(r.session.PrimaryAccounts.VacationResponse, errNoPrimaryAccountForVacationResponse) } -func (r *Request) GetAccountIdForQuota() (string, *Error) { +func (r *Request) GetAccountIdForQuota() (jmap.AccountId, *Error) { return r.getAccountId(r.session.PrimaryAccounts.Quota, errNoPrimaryAccountForQuota) } -func (r *Request) GetAccountIdForSubmission() (string, *Error) { +func (r *Request) GetAccountIdForSubmission() (jmap.AccountId, *Error) { return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForSubmission) } -func (r *Request) GetAccountIdForTask() (string, *Error) { +func (r *Request) GetAccountIdForTask() (jmap.AccountId, *Error) { // TODO we don't have these yet, not implemented in Stalwart // return r.getAccountId(r.session.PrimaryAccounts.Task, errNoPrimaryAccountForTask) return r.GetAccountIdForMail() } -func (r *Request) GetAccountIdForCalendar() (string, *Error) { +func (r *Request) GetAccountIdForCalendar() (jmap.AccountId, *Error) { // TODO we don't have these yet, not implemented in Stalwart // return r.getAccountId(r.session.PrimaryAccounts.Calendar, errNoPrimaryAccountForCalendar) return r.GetAccountIdForMail() } -func (r *Request) GetAccountIdForContact() (string, *Error) { +func (r *Request) GetAccountIdForContact() (jmap.AccountId, *Error) { // TODO we don't have these yet, not implemented in Stalwart // return r.getAccountId(r.session.PrimaryAccounts.Contact, errNoPrimaryAccountForContact) return r.GetAccountIdForMail() } -func (r *Request) GetAccountForMail() (string, jmap.Account, *Error) { +func (r *Request) GetAccountForMail() (jmap.AccountId, jmap.Account, *Error) { accountId, err := r.GetAccountIdForMail() if err != nil { return "", jmap.Account{}, err @@ -210,7 +210,7 @@ func (r *Request) GetAccountForMail() (string, jmap.Account, *Error) { r.logger.Debug().Msgf("failed to find account '%v'", accountId) // TODO metric for inexistent accounts return accountId, jmap.Account{}, apiError(r.errorId(), ErrorNonExistingAccount, - withDetail(fmt.Sprintf("The account '%v' does not exist", log.SafeString(accountId))), + withDetail(fmt.Sprintf("The account '%v' does not exist", log.SafeString(string(accountId)))), withSource(&ErrorSource{Parameter: UriParamAccountId}), ) } @@ -223,11 +223,11 @@ func (r *Request) parameterError(param string, detail string) *Error { withSource(&ErrorSource{Parameter: param})) } -func (r *Request) parameterErrorResponse(accountId string, param string, detail string) Response { +func (r *Request) parameterErrorResponse(accountId jmap.AccountId, param string, detail string) Response { return r.error(accountId, r.parameterError(param, detail)) } -func (r *Request) parameterErrorResponseN(accountIds []string, param string, detail string) Response { +func (r *Request) parameterErrorResponseN(accountIds []jmap.AccountId, param string, detail string) Response { return r.errorN(accountIds, r.parameterError(param, detail)) } @@ -239,7 +239,7 @@ func toSupportedQueryParams(params ...string) supportedQueryParams { var noSupportedQueryParams supportedQueryParams = toSupportedQueryParams() -func (r *Request) unsupportedQueryParams(accountIds []string, allowed supportedQueryParams) (bool, Response) { +func (r *Request) unsupportedQueryParams(accountIds []jmap.AccountId, allowed supportedQueryParams) (bool, Response) { q := r.r.URL.Query() for n := range q { if _, ok := allowed[n]; !ok { @@ -470,14 +470,14 @@ func (r *Request) observeJmapError(jerr jmap.Error) jmap.Error { return jerr } -func (r *Request) needBlob(accountId string) (bool, Response) { +func (r *Request) needBlob(accountId jmap.AccountId) (bool, Response) { if r.session.Capabilities.Blob == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingBlobSessionCapability), r.session.State, jmap.NoLanguage) } return true, Response{} } -func (r *Request) needBlobForAccount(accountId string) (bool, Response) { +func (r *Request) needBlobForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needBlob(accountId); !ok { return ok, resp } @@ -491,7 +491,7 @@ func (r *Request) needBlobForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needBloblWithAccount() (bool, string, Response) { +func (r *Request) needBloblWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForBlob() if err != nil { return false, "", r.error(accountId, err) @@ -502,14 +502,14 @@ func (r *Request) needBloblWithAccount() (bool, string, Response) { return true, accountId, Response{} } -func (r *Request) needMail(accountId string) (bool, Response) { +func (r *Request) needMail(accountId jmap.AccountId) (bool, Response) { if r.session.Capabilities.Mail == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingMailsSessionCapability), r.session.State, jmap.NoLanguage) } return true, Response{} } -func (r *Request) needMailForAccount(accountId string) (bool, Response) { +func (r *Request) needMailForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needMail(accountId); !ok { return ok, resp } @@ -523,7 +523,7 @@ func (r *Request) needMailForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needMailWithAccount() (bool, string, Response) { +func (r *Request) needMailWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForMail() if err != nil { return false, "", r.error(accountId, err) @@ -534,7 +534,7 @@ func (r *Request) needMailWithAccount() (bool, string, Response) { return true, accountId, Response{} } -func (r *Request) needTask(accountId string) (bool, Response) { +func (r *Request) needTask(accountId jmap.AccountId) (bool, Response) { if !IgnoreSessionCapabilityChecksForTasks { if r.session.Capabilities.Tasks == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingTasksSessionCapability), r.session.State, jmap.Language(r.language())) @@ -543,7 +543,7 @@ func (r *Request) needTask(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needTaskForAccount(accountId string) (bool, Response) { +func (r *Request) needTaskForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needTask(accountId); !ok { return ok, resp } @@ -557,7 +557,7 @@ func (r *Request) needTaskForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needTaskWithAccount() (bool, string, Response) { +func (r *Request) needTaskWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForTask() if err != nil { return false, "", r.error(accountId, err) @@ -568,14 +568,14 @@ func (r *Request) needTaskWithAccount() (bool, string, Response) { return true, accountId, Response{} } -func (r *Request) needCalendar(accountId string) (bool, Response) { +func (r *Request) needCalendar(accountId jmap.AccountId) (bool, Response) { if r.session.Capabilities.Calendars == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingCalendarsSessionCapability), r.session.State, jmap.NoLanguage) } return true, Response{} } -func (r *Request) needCalendarForAccount(accountId string) (bool, Response) { +func (r *Request) needCalendarForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needCalendar(accountId); !ok { return ok, resp } @@ -589,7 +589,7 @@ func (r *Request) needCalendarForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needCalendarWithAccount() (bool, string, Response) { +func (r *Request) needCalendarWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForCalendar() if err != nil { return false, "", r.error(accountId, err) @@ -600,14 +600,14 @@ func (r *Request) needCalendarWithAccount() (bool, string, Response) { return true, accountId, Response{} } -func (r *Request) needContact(accountId string) (bool, Response) { +func (r *Request) needContact(accountId jmap.AccountId) (bool, Response) { if r.session.Capabilities.Contacts == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingContactsSessionCapability), r.session.State, jmap.NoLanguage) } return true, Response{} } -func (r *Request) needContactForAccount(accountId string) (bool, Response) { +func (r *Request) needContactForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needContact(accountId); !ok { return ok, resp } @@ -621,7 +621,7 @@ func (r *Request) needContactForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needContactWithAccount() (bool, string, Response) { +func (r *Request) needContactWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForContact() if err != nil { return false, "", r.error(accountId, err) @@ -632,14 +632,14 @@ func (r *Request) needContactWithAccount() (bool, string, Response) { return true, accountId, Response{} } -func (r *Request) needQuota(accountId string) (bool, Response) { +func (r *Request) needQuota(accountId jmap.AccountId) (bool, Response) { if r.session.Capabilities.Quota == nil { return false, errorResponse(single(accountId), r.apiError(&ErrorMissingQuotaSessionCapability), r.session.State, jmap.NoLanguage) } return true, Response{} } -func (r *Request) needQuotaForAccount(accountId string) (bool, Response) { +func (r *Request) needQuotaForAccount(accountId jmap.AccountId) (bool, Response) { if ok, resp := r.needQuota(accountId); !ok { return ok, resp } @@ -653,7 +653,7 @@ func (r *Request) needQuotaForAccount(accountId string) (bool, Response) { return true, Response{} } -func (r *Request) needQuotaWithAccount() (bool, string, Response) { +func (r *Request) needQuotaWithAccount() (bool, jmap.AccountId, Response) { accountId, err := r.GetAccountIdForQuota() if err != nil { return false, "", r.error(accountId, err) @@ -707,3 +707,7 @@ func (r *Request) parseSort(s string, props []string) ([]SortCrit, *Error) { func toState(s string) jmap.State { return jmap.State(s) } + +func toAccountIdState(accountId string, state string) (jmap.AccountId, jmap.State) { + return jmap.AccountId(accountId), jmap.State(state) +} diff --git a/services/groupware/pkg/groupware/response.go b/services/groupware/pkg/groupware/response.go index 196c2d19c3..973650998a 100644 --- a/services/groupware/pkg/groupware/response.go +++ b/services/groupware/pkg/groupware/response.go @@ -32,13 +32,13 @@ type Response struct { err *Error etag jmap.State objectType ResponseObjectType - accountIds []string + accountIds []jmap.AccountId sessionState jmap.SessionState contentLanguage jmap.Language next NextToken } -func errorResponse(accountIds []string, err *Error, sessionState jmap.SessionState, contentLanguage jmap.Language) Response { +func errorResponse(accountIds []jmap.AccountId, err *Error, sessionState jmap.SessionState, contentLanguage jmap.Language) Response { return Response{ accountIds: accountIds, body: nil, @@ -50,7 +50,7 @@ func errorResponse(accountIds []string, err *Error, sessionState jmap.SessionSta } } -func response(accountIds []string, body any, sessionState jmap.SessionState, contentLanguage jmap.Language) Response { +func response(accountIds []jmap.AccountId, body any, sessionState jmap.SessionState, contentLanguage jmap.Language) Response { return Response{ accountIds: accountIds, body: body, @@ -62,11 +62,11 @@ func response(accountIds []string, body any, sessionState jmap.SessionState, con } } -func (r *Request) respondWithoutStatus(accountId string, body any) Response { +func (r *Request) respondWithoutStatus(accountId jmap.AccountId, body any) Response { return response(single(accountId), body, r.session.State, jmap.Language(r.language())) } -func etaggedResponse(accountIds []string, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response { +func etaggedResponse(accountIds []jmap.AccountId, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response { return Response{ accountIds: accountIds, body: body, @@ -79,15 +79,15 @@ func etaggedResponse(accountIds []string, body any, sessionState jmap.SessionSta } } -func (r *Request) respond(accountId string, body any, objectType ResponseObjectType, result jmap.ResultMetadata) Response { +func (r *Request) respond(accountId jmap.AccountId, body any, objectType ResponseObjectType, result jmap.ResultMetadata) Response { return etaggedResponse(single(accountId), body, result.GetSessionState(), objectType, result.GetState(), result.GetLanguage()) } -func (r *Request) respondN(accountIds []string, body any, objectType ResponseObjectType, result jmap.ResultMetadata) Response { +func (r *Request) respondN(accountIds []jmap.AccountId, body any, objectType ResponseObjectType, result jmap.ResultMetadata) Response { return etaggedResponse(accountIds, body, result.GetSessionState(), objectType, result.GetState(), result.GetLanguage()) } -func etaggedNextResponse(accountIds []string, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language, next NextToken) Response { +func etaggedNextResponse(accountIds []jmap.AccountId, body any, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language, next NextToken) Response { return Response{ accountIds: accountIds, body: body, @@ -100,7 +100,7 @@ func etaggedNextResponse(accountIds []string, body any, sessionState jmap.Sessio } } -func (r *Request) respondNext(accountId string, body any, objectType ResponseObjectType, result jmap.ResultMetadata, next NextToken) Response { +func (r *Request) respondNext(accountId jmap.AccountId, body any, objectType ResponseObjectType, result jmap.ResultMetadata, next NextToken) Response { return etaggedNextResponse(single(accountId), body, result.GetSessionState(), objectType, result.GetState(), result.GetLanguage(), next) } @@ -117,7 +117,7 @@ func etagOnlyResponse(body any, etag jmap.State, objectType ResponseObjectType, } */ -func noContentResponse(accountIds []string, sessionState jmap.SessionState) Response { +func noContentResponse(accountIds []jmap.AccountId, sessionState jmap.SessionState) Response { return Response{ accountIds: accountIds, body: nil, @@ -129,15 +129,15 @@ func noContentResponse(accountIds []string, sessionState jmap.SessionState) Resp } } -func (r *Request) noop(accountId string) Response { +func (r *Request) noop(accountId jmap.AccountId) Response { return noContentResponse(single(accountId), r.session.State) } -func (r *Request) noopN(accountIds []string) Response { +func (r *Request) noopN(accountIds []jmap.AccountId) Response { return noContentResponse(accountIds, r.session.State) } -func noContentResponseWithEtag(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { +func noContentResponseWithEtag(accountIds []jmap.AccountId, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { return Response{ accountIds: accountIds, body: nil, @@ -150,7 +150,7 @@ func noContentResponseWithEtag(accountIds []string, sessionState jmap.SessionSta } } -func (r *Request) noContent(accountId string, objectType ResponseObjectType, result jmap.ResultMetadata) Response { +func (r *Request) noContent(accountId jmap.AccountId, objectType ResponseObjectType, result jmap.ResultMetadata) Response { return noContentResponseWithEtag(single(accountId), result.GetSessionState(), objectType, result.GetState()) } @@ -178,7 +178,7 @@ func timeoutResponse(sessionState jmap.SessionState) Response { } */ -func notFoundResponse(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { +func notFoundResponse(accountIds []jmap.AccountId, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { return Response{ accountIds: accountIds, body: nil, @@ -191,11 +191,11 @@ func notFoundResponse(accountIds []string, sessionState jmap.SessionState, objec } } -func (r *Request) notFound(accountId string, objectType ResponseObjectType, result jmap.ResultMetadata) Response { +func (r *Request) notFound(accountId jmap.AccountId, objectType ResponseObjectType, result jmap.ResultMetadata) Response { return notFoundResponse(single(accountId), result.GetSessionState(), objectType, result.GetState()) } -func etaggedNotFoundResponse(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response { +func etaggedNotFoundResponse(accountIds []jmap.AccountId, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State, contentLanguage jmap.Language) Response { return Response{ accountIds: accountIds, body: nil, @@ -209,11 +209,11 @@ func etaggedNotFoundResponse(accountIds []string, sessionState jmap.SessionState } } -func (r *Request) etaggedNotFound(accountId string, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { +func (r *Request) etaggedNotFound(accountId jmap.AccountId, sessionState jmap.SessionState, objectType ResponseObjectType, etag jmap.State) Response { return etaggedNotFoundResponse(single(accountId), sessionState, objectType, etag, jmap.Language(r.language())) } -func notImplementedResponse(accountIds []string, sessionState jmap.SessionState, objectType ResponseObjectType) Response { +func notImplementedResponse(accountIds []jmap.AccountId, sessionState jmap.SessionState, objectType ResponseObjectType) Response { return Response{ accountIds: accountIds, body: nil, @@ -225,6 +225,6 @@ func notImplementedResponse(accountIds []string, sessionState jmap.SessionState, } } -func (r *Request) notImplementedN(accountIds []string, objectType ResponseObjectType) Response { +func (r *Request) notImplementedN(accountIds []jmap.AccountId, objectType ResponseObjectType) Response { return notImplementedResponse(accountIds, r.session.State, objectType) } diff --git a/services/groupware/pkg/groupware/suppliers.go b/services/groupware/pkg/groupware/suppliers.go index 91b706a865..4bf35fa302 100644 --- a/services/groupware/pkg/groupware/suppliers.go +++ b/services/groupware/pkg/groupware/suppliers.go @@ -11,26 +11,28 @@ import ( "github.com/opencloud-eu/opencloud/pkg/structs" ) +type SupplierId string + type Supplier[T jmap.Foo] interface { - GetId() string + GetId() SupplierId IsMine(id string) bool } type ListSupplier[T jmap.Foo, G jmap.GetResponse[T]] interface { - GetAll(accountId string, ids []string, ctx jmap.Context) (jmap.Result[G], error) + GetAll(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[G], error) Supplier[T] } type QuerySupplier[T jmap.Foo, R jmap.SearchResults[T], F jmap.FilterElement[T], C jmap.Comparator[T]] interface { - Query(accountIds []string, qps QueryParamsSupplier, limit *uint, filter F, sortBy []C, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[string]R], error) + Query(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter F, sortBy []C, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]R], error) Supplier[T] } // queryFunc func(req Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SEARCHRESULTS], NextToken, error), func curryQueryFunc[SRES jmap.SearchResults[T], T jmap.Foo, FILTER any, COMP any]( - f func(accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, calculateTotal bool, ctx jmap.Context) (jmap.Result[SRES], NextToken, error), -) func(req Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { - return func(_ Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { //NOSONAR + f func(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, calculateTotal bool, ctx jmap.Context) (jmap.Result[SRES], NextToken, error), +) func(req Request, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { + return func(_ Request, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SRES], NextToken, error) { //NOSONAR result, next, err := f(accountIds, qps, limit, filter, sortBy, true, ctx) if err != nil { return jmap.ZeroResult[SRES](), next, err @@ -40,8 +42,8 @@ func curryQueryFunc[SRES jmap.SearchResults[T], T jmap.Foo, FILTER any, COMP any } } -func agg[T jmap.Idable, R jmap.GetResponse[T]](accountId string, supplierIds []string, responses []*R, //NOSONAR - ctor func(accountId string, state jmap.State, notFound []string, list []T) R) (R, error) { +func agg[T jmap.Idable, R jmap.GetResponse[T]](accountId jmap.AccountId, supplierIds []SupplierId, responses []*R, //NOSONAR + ctor func(accountId jmap.AccountId, state jmap.State, notFound []string, list []T) R) (R, error) { if len(responses) < 1 { var zero R return zero, fmt.Errorf("requires at least one response") @@ -54,7 +56,7 @@ func agg[T jmap.Idable, R jmap.GetResponse[T]](accountId string, supplierIds []s return zero } })...) - states, err := structs.MeshMap(supplierIds, responses, func(id string, e *R) (string, jmap.State, bool) { + states, err := structs.MeshMap(supplierIds, responses, func(id SupplierId, e *R) (SupplierId, jmap.State, bool) { if e != nil { state := (*e).GetState() if state != jmap.EmptyState { @@ -85,8 +87,8 @@ func agg[T jmap.Idable, R jmap.GetResponse[T]](accountId string, supplierIds []s return ctor(accountId, state, notFounds, lists), nil } -func slist[T jmap.Idable, G jmap.GetResponse[T], S ListSupplier[T, G]](suppliers []S, accountId string, ids []string, ctx jmap.Context, //NOSONAR - ctor func(accountId string, state jmap.State, notFound []string, list []T) G) (jmap.Result[G], error) { +func slist[T jmap.Idable, G jmap.GetResponse[T], S ListSupplier[T, G]](suppliers []S, accountId jmap.AccountId, ids []string, ctx jmap.Context, //NOSONAR + ctor func(accountId jmap.AccountId, state jmap.State, notFound []string, list []T) G) (jmap.Result[G], error) { switch len(suppliers) { case 0: return jmap.ZeroResult[G](), nil @@ -94,7 +96,7 @@ func slist[T jmap.Idable, G jmap.GetResponse[T], S ListSupplier[T, G]](suppliers return suppliers[0].GetAll(accountId, ids, ctx) default: results := make([]*jmap.Result[G], len(suppliers)) - supplierIds := make([]string, len(suppliers)) + supplierIds := make([]SupplierId, len(suppliers)) for i, supplier := range suppliers { supplierIds[i] = supplier.GetId() localIds := []string{} @@ -118,7 +120,7 @@ func slist[T jmap.Idable, G jmap.GetResponse[T], S ListSupplier[T, G]](suppliers if err != nil { return resp, jmap.EmptySessionState, jmap.EmptyState, jmap.NoLanguage, err } - m, err := structs.MeshMap(supplierIds, sessionStates, func(id string, state *jmap.SessionState) (string, jmap.SessionState, bool) { + m, err := structs.MeshMap(supplierIds, sessionStates, func(id SupplierId, state *jmap.SessionState) (SupplierId, jmap.SessionState, bool) { if state != nil && *state != jmap.EmptySessionState { return id, *state, true } else { @@ -141,7 +143,7 @@ func slist[T jmap.Idable, G jmap.GetResponse[T], S ListSupplier[T, G]](suppliers } } -func fillMissingAccounts(qps QueryParamsSupplier, supplierId string, accountIds []string, n map[string]jmap.QueryParams) error { +func fillMissingAccounts(qps QueryParamsSupplier, supplierId SupplierId, accountIds []jmap.AccountId, n map[jmap.AccountId]jmap.QueryParams) error { for _, accountId := range accountIds { if _, ok := n[accountId]; !ok { // no result item was kept for this accountId @@ -167,7 +169,7 @@ func fillMissingAccounts(qps QueryParamsSupplier, supplierId string, accountIds } func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], F jmap.FilterElement[T], C jmap.Comparator[T]]( //NOSONAR - suppliers []S, accountIds []string, qps QueryParamsSupplier, limit *uint, filter F, sortBy []C, + suppliers []S, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter F, sortBy []C, calculateTotal bool, ctx jmap.Context, sorter func(T, T) int, searchResultCtor func(canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint, results []T) R) ( @@ -190,7 +192,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], // use anchor and anchorOffset and limit: // the anchor for the next query is the ID of the last element in the results for this query // using an anchor offset of +1 - n := map[string]jmap.QueryParams{} + n := map[jmap.AccountId]jmap.QueryParams{} for accountId, payload := range result.Payload { items := payload.GetResults() last := items[len(items)-1] @@ -199,7 +201,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], if nextToken, err := nextSingle(n); err != nil { return jmap.ZeroResult[R](), NoNextToken, err } else { - single, err := jmap.RefineResultPayload(result, func(m map[string]R) (R, bool, error) { + single, err := jmap.RefineResultPayload(result, func(m map[jmap.AccountId]R) (R, bool, error) { if r, ok := m[accountId]; ok { return r, true, nil } else { @@ -211,7 +213,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], } else { // multiple accountIds => we need to merge/flatten the results, and we must use anchor and offset for the next page payloads := []T{} - originals := map[string][]T{} + originals := map[jmap.AccountId][]T{} cc := true total := uint(0) for accountId, searchResult := range result.Payload { @@ -249,7 +251,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], r = searchResultCtor(jmap.ChangeCalculation(cc), nil, limit, &total, payloads) // TODO can we determine the position here, instead of nil? } - lastIdByAccountId := map[string]string{} + lastIdByAccountId := map[jmap.AccountId]string{} // not amazing, but since the accountId information is not attached to every single // search result element (e.g. a ContactCard), we have to iterate over the original results that we // have by accountId to find them again, in order to determine the "last ID" @@ -263,7 +265,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], } } - n := map[string]jmap.QueryParams{} + n := map[jmap.AccountId]jmap.QueryParams{} for accountId, lastId := range lastIdByAccountId { // the anchor for the next query is the ID of the last element in the results for this query // using an anchor offset of +1 @@ -273,7 +275,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], return jmap.ZeroResult[R](), NoNextToken, err } - nextBySupplier := map[string]map[string]jmap.QueryParams{supplier.GetId(): n} + nextBySupplier := map[SupplierId]map[jmap.AccountId]jmap.QueryParams{supplier.GetId(): n} if nextToken, err := nextMulti(nextBySupplier); err != nil { return jmap.ZeroResult[R](), NoNextToken, err } else { @@ -287,11 +289,11 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], } default: payloads := []T{} - originals := map[string]map[string][]T{} + originals := map[SupplierId]map[jmap.AccountId][]T{} cc := true total := uint(0) sessionState := jmap.EmptySessionState - states := map[string]jmap.State{} + states := map[SupplierId]jmap.State{} lang := jmap.NoLanguage for _, supplier := range suppliers { // we are not injecting id prefixes here for all the objects, as each supplier is responsible for doing that if necessary @@ -306,7 +308,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], lang = result.GetLanguage() } // iterate over results by accountId and flatten everything into the 'payloads' array - o := map[string][]T{} + o := map[jmap.AccountId][]T{} for accountId, searchResult := range result.Payload { o[accountId] = searchResult.GetResults() if !searchResult.GetCanCalculateChanges() { @@ -345,7 +347,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], r = searchResultCtor(jmap.ChangeCalculation(cc), nil, limit, &total, payloads) // TODO cen we provide the position here instead of nil? } - lastIdBySupplierByAccountId := map[string]map[string]string{} + lastIdBySupplierByAccountId := map[SupplierId]map[jmap.AccountId]string{} // not amazing, but since the accountId and supplier information is not attached to every single // search result element (e.g. a ContactCard), we have to iterate over the original results that we // kept by supplier and by accountId to find them again, in order to determine the "last ID" @@ -356,7 +358,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], for accountId, items := range o { if slices.IndexFunc(items, func(t T) bool { return t.GetId() == item.GetId() }) >= 0 { if _, ok := lastIdBySupplierByAccountId[supplierId]; !ok { - lastIdBySupplierByAccountId[supplierId] = map[string]string{} + lastIdBySupplierByAccountId[supplierId] = map[jmap.AccountId]string{} } lastIdBySupplierByAccountId[supplierId][accountId] = item.GetId() } @@ -364,9 +366,9 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], } } - nextBySupplier := map[string]map[string]jmap.QueryParams{} + nextBySupplier := map[SupplierId]map[jmap.AccountId]jmap.QueryParams{} for supplierId, m := range lastIdBySupplierByAccountId { - n := map[string]jmap.QueryParams{} + n := map[jmap.AccountId]jmap.QueryParams{} for accountId, lastId := range m { // the anchor for the next query is the ID of the last element in the results for this query // using an anchor offset of +1 @@ -397,7 +399,7 @@ func squery[T jmap.Idable, R jmap.SearchResults[T], S QuerySupplier[T, R, F, C], const combinedStateEncodingPrefix = "=" -func combineState[S jmap.State | jmap.SessionState](m map[string]S) (S, error) { +func combineState[K ~string, S jmap.State | jmap.SessionState](m map[K]S) (S, error) { if b, err := json.Marshal(m); err != nil { return "", err } else { @@ -406,13 +408,13 @@ func combineState[S jmap.State | jmap.SessionState](m map[string]S) (S, error) { } } -func splitState[S jmap.State | jmap.SessionState](state S) (map[string]S, error) { +func splitState[K ~string, S jmap.State | jmap.SessionState](state S) (map[K]S, error) { s := string(state) if strings.HasPrefix(s, combinedStateEncodingPrefix) { if b, err := base64.RawURLEncoding.DecodeString(s[len(combinedStateEncodingPrefix):]); err != nil { return nil, err } else { - m := map[string]S{} + m := map[K]S{} if err := json.Unmarshal(b, &m); err != nil { return nil, err } else { @@ -420,6 +422,6 @@ func splitState[S jmap.State | jmap.SessionState](state S) (map[string]S, error) } } } else { - return map[string]S{"jmap": state}, nil + return map[K]S{K("jmap"): state}, nil } } diff --git a/services/groupware/pkg/groupware/suppliers_test.go b/services/groupware/pkg/groupware/suppliers_test.go index 5d2fa43ce5..e0ca3defe0 100644 --- a/services/groupware/pkg/groupware/suppliers_test.go +++ b/services/groupware/pkg/groupware/suppliers_test.go @@ -10,20 +10,20 @@ import ( ) type PetSupplier struct { - id string - petsByAccountId map[string][]Pet + id SupplierId + petsByAccountId map[jmap.AccountId][]Pet } // var _ ListSupplier[Pet, PetGetResponse] = &PetSupplier{} var _ QuerySupplier[Pet, *PetSearchResults, PetFilterElement, PetComparator] = &PetSupplier{} -func (s *PetSupplier) GetId() string { +func (s *PetSupplier) GetId() SupplierId { return s.id } func (s *PetSupplier) IsMine(id string) bool { - return strings.HasPrefix(id, s.id+":") + return strings.HasPrefix(id, string(s.id)+":") } -func (s *PetSupplier) Query(accountIds []string, qps QueryParamsSupplier, limit *uint, filter PetFilterElement, sortBy []PetComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[string]*PetSearchResults], error) { +func (s *PetSupplier) Query(accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter PetFilterElement, sortBy []PetComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]*PetSearchResults], error) { return inmemquery(s.id, s.petsByAccountId, accountIds, qps, limit, calculateTotal, func(results []Pet, canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint) *PetSearchResults { return &PetSearchResults{Results: results, CanCalculateChanges: canCalculateChanges, Position: position, Limit: limit, Total: total} @@ -32,18 +32,18 @@ func (s *PetSupplier) Query(accountIds []string, qps QueryParamsSupplier, limit } func inmemquery[T jmap.Idable, R jmap.SearchResults[T]]( - supplierId string, - store map[string][]T, - accountIds []string, + supplierId SupplierId, + store map[jmap.AccountId][]T, + accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, calculateTotal bool, searchResultCtor func(results []T, canCalculateChanges jmap.ChangeCalculation, position *uint, limit *uint, total *uint) R, -) (jmap.Result[map[string]R], error) { - payload := make(map[string]R, len(accountIds)) +) (jmap.Result[map[jmap.AccountId]R], error) { + payload := make(map[jmap.AccountId]R, len(accountIds)) for _, accountId := range accountIds { qp := jmap.NullQueryParams if q, ok, err := qps.ForSupplier(supplierId, accountId); err != nil { - return jmap.ZeroResult[map[string]R](), err + return jmap.ZeroResult[map[jmap.AccountId]R](), err } else if ok { qp = q } @@ -91,7 +91,7 @@ func inmemquery[T jmap.Idable, R jmap.SearchResults[T]]( payload[accountId] = res } - return jmap.Result[map[string]R]{ + return jmap.Result[map[jmap.AccountId]R]{ Payload: payload, SessionState: jmap.EmptySessionState, State: jmap.EmptyState, @@ -101,7 +101,7 @@ func inmemquery[T jmap.Idable, R jmap.SearchResults[T]]( func pets( suppliers []QuerySupplier[Pet, *PetSearchResults, PetFilterElement, PetComparator], - accountIds []string, qps QueryParamsSupplier, limit *uint, + accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter PetFilterElement, sortBy []PetComparator, calculateTotal bool, ctx jmap.Context) (jmap.Result[*PetSearchResults], NextToken, error) { @@ -125,7 +125,7 @@ func TestSquery(t *testing.T) { suppliers := []QuerySupplier[Pet, *PetSearchResults, PetFilterElement, PetComparator]{ &PetSupplier{ id: "X", - petsByAccountId: map[string][]Pet{ + petsByAccountId: map[jmap.AccountId][]Pet{ "a": { {id: "X:1", name: "ace"}, {id: "X:2", name: "bella"}, @@ -137,7 +137,7 @@ func TestSquery(t *testing.T) { }, &PetSupplier{ id: "Y", - petsByAccountId: map[string][]Pet{ + petsByAccountId: map[jmap.AccountId][]Pet{ "a": { {id: "Y:1", name: "cupcake"}, {id: "Y:2", name: "elvis"}, @@ -149,14 +149,14 @@ func TestSquery(t *testing.T) { }, }, } - f := func(accountIds []string, position int, anchor string, anchorOffset *int, limit *uint) (jmap.Result[*PetSearchResults], NextToken, error) { + f := func(accountIds []jmap.AccountId, position int, anchor string, anchorOffset *int, limit *uint) (jmap.Result[*PetSearchResults], NextToken, error) { return pets(suppliers, accountIds, StaticQueryParamsSupplier{qp: jmap.QueryParams{Position: position, Anchor: anchor, AnchorOffset: anchorOffset}}, limit, PetFilterCondition{}, []PetComparator{{Property: "id", IsAscending: true}}, true, jmap.Context{}, ) } - n := func(accountIds []string, nextToken NextToken, limit *uint) (jmap.Result[*PetSearchResults], NextToken, error) { + n := func(accountIds []jmap.AccountId, nextToken NextToken, limit *uint) (jmap.Result[*PetSearchResults], NextToken, error) { if qps, err := unnext(nextToken); err != nil { return jmap.ZeroResult[*PetSearchResults](), NoNextToken, err } else { @@ -165,7 +165,7 @@ func TestSquery(t *testing.T) { } { - res, n, err := f([]string{"a", "b", "c"}, 0, "", nil, nil) + res, n, err := f([]jmap.AccountId{"a", "b", "c"}, 0, "", nil, nil) require.NoError(err) require.Len(res.Payload.Results, 7) require.Equal(uint(len(res.Payload.Results)), *res.Payload.Total) @@ -189,7 +189,7 @@ func TestSquery(t *testing.T) { } var nextToken NextToken { - res, n, err := f([]string{"a", "b", "c"}, 0, "", nil, uintPtr(4)) + res, n, err := f([]jmap.AccountId{"a", "b", "c"}, 0, "", nil, uintPtr(4)) nextToken = n require.NoError(err) require.Len(res.Payload.Results, 4) @@ -210,7 +210,7 @@ func TestSquery(t *testing.T) { } } { - res, _, err := n([]string{"a", "b", "c"}, nextToken, uintPtr(4)) + res, _, err := n([]jmap.AccountId{"a", "b", "c"}, nextToken, uintPtr(4)) require.NoError(err) require.Equal(uint(7), *res.Payload.Total) require.Len(res.Payload.Results, 3) diff --git a/services/groupware/pkg/groupware/templates.go b/services/groupware/pkg/groupware/templates.go index 7cbeddd9f7..6d2c559c76 100644 --- a/services/groupware/pkg/groupware/templates.go +++ b/services/groupware/pkg/groupware/templates.go @@ -14,15 +14,15 @@ func create[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T]]( //NOSONAR o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - bodyFunc func(r Request, accountId string, body *CHANGE, ctx jmap.Context) (bool, Response), - createFunc func(accountId string, change CHANGE, ctx jmap.Context) (jmap.Result[*T], error), + bodyFunc func(r Request, accountId jmap.AccountId, body *CHANGE, ctx jmap.Context) (bool, Response), + createFunc func(accountId jmap.AccountId, change CHANGE, ctx jmap.Context) (jmap.Result[*T], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) if notok, resp := req.unsupportedQueryParams(single(accountId), noSupportedQueryParams); notok { return resp @@ -66,14 +66,14 @@ func getall[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jmap.G o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - getFunc func(accountId string, ids []string, ctx jmap.Context) (jmap.Result[RESP], error), + getFunc func(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[RESP], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) if notok, resp := req.unsupportedQueryParams(single(accountId), noSupportedQueryParams); notok { return resp @@ -112,14 +112,14 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER withContainerId bool, filterFunc func(containerId string) FILTER, sortBy []COMP, - queryFunc func(req Request, accountIds []string, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SEARCHRESULTS], NextToken, error), + queryFunc func(req Request, accountIds []jmap.AccountId, qps QueryParamsSupplier, limit *uint, filter FILTER, sortBy []COMP, ctx jmap.Context) (jmap.Result[SEARCHRESULTS], NextToken, error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) var limit *uint = nil { @@ -147,34 +147,6 @@ func getallpaged[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], FILTER } else { supportedQueryParams = firstQueryParams qps = FirstQueryParamsSupplier{} - - /* - position, ok, err := req.parseIntParam(QueryParamPosition, 0) - if err != nil { - return req.error(accountId, err) - } - if ok { - l = l.Int(QueryParamPosition, position) - } - - anchor, ok := req.getStringParam(QueryParamAnchor, "") - if ok { - l = l.Str(QueryParamAnchor, log.SafeString(anchor)) - } - - var anchorOffset *int = nil - { - v, ok, err := req.parseIntParam(QueryParamAnchorOffset, 0) - if err != nil { - return req.error(accountId, err) - } - if ok { - l = l.Int(QueryParamAnchorOffset, v) - anchorOffset = &v - } - } - */ - } } @@ -220,14 +192,14 @@ func query[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], SEARCHRESULT w http.ResponseWriter, r *http.Request, g *Groupware, defaultLimit *uint, - queryFunc func(req Request, accountId string, containerId string, qp jmap.QueryParams, limit *uint, ctx jmap.Context) (jmap.Result[SEARCHRESULTS], error), + queryFunc func(req Request, accountId jmap.AccountId, containerId string, qp jmap.QueryParams, limit *uint, ctx jmap.Context) (jmap.Result[SEARCHRESULTS], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) containerId := "" if o.containerUriParamName != "" { @@ -366,14 +338,14 @@ func get[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jmap.GetR o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - getFunc func(accountId string, ids []string, ctx jmap.Context) (jmap.Result[RESP], error), + getFunc func(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[RESP], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) ids := []string{} if o.uriParamName != "" { id, err := req.PathParamDoc(o.uriParamName, "The unique identifier of the object to retrieve") @@ -423,14 +395,14 @@ func getFromMap[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T], RESP jm o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - getFunc func(accountIds []string, ids []string, ctx jmap.Context) (jmap.Result[map[string]RESP], error), + getFunc func(accountIds []jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[map[jmap.AccountId]RESP], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) id, err := req.PathParamDoc(o.uriParamName, "The unique identifier of the object to retrieve") if err != nil { return req.error(accountId, err) @@ -482,14 +454,14 @@ func changes[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T]]( //NOSONAR o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - changesFunc func(accountId string, sinceState jmap.State, maxChanges uint, ctx jmap.Context) (jmap.Result[CHANGES], error), + changesFunc func(accountId jmap.AccountId, sinceState jmap.State, maxChanges uint, ctx jmap.Context) (jmap.Result[CHANGES], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) maxChanges, ok, err := req.parseUIntParam(QueryParamMaxChanges, 0) if err != nil { @@ -535,14 +507,14 @@ func delete[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T]]( //NOSONAR o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - deleteFunc func(accountId string, ids []string, ctx jmap.Context) (jmap.Result[map[string]jmap.SetError], error), + deleteFunc func(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[map[string]jmap.SetError], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) id, err := req.PathParamDoc(o.uriParamName, "The unique identifier of the object to delete") if err != nil { return req.error(accountId, err) @@ -597,14 +569,14 @@ func deleteMany[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T]]( //NOSO o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - deleteFunc func(accountId string, ids []string, ctx jmap.Context) (jmap.Result[map[string]jmap.SetError], error), + deleteFunc func(accountId jmap.AccountId, ids []string, ctx jmap.Context) (jmap.Result[map[string]jmap.SetError], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) ids := []string{} if o.uriParamName != "" { @@ -686,14 +658,14 @@ func modify[T jmap.Foo, CHANGE jmap.Change, CHANGES jmap.Changes[T]]( o ObjectType[T, CHANGE, CHANGES], w http.ResponseWriter, r *http.Request, g *Groupware, - updateFunc func(accountId string, id string, change CHANGE, ctx jmap.Context) (jmap.Result[T], error), + updateFunc func(accountId jmap.AccountId, id string, change CHANGE, ctx jmap.Context) (jmap.Result[T], error), ) { g.respond(w, r, func(req Request) Response { ok, accountId, resp := o.accountFunc(&req) if !ok { return resp } - l := req.logger.With().Str(accountId, log.SafeString(accountId)) + l := req.logger.With().Str(logAccountId, log.SafeString(accountId)) id, err := req.PathParamDoc(o.uriParamName, "The unique identifier of the object to modify") if err != nil { return req.error(accountId, err)