mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-25 19:47:05 -05:00
groupware: add JMAP capability checking (in part: for contacts, calendars, tasks)
This commit is contained in:
@@ -131,20 +131,28 @@ type ActionMode string
|
||||
type SendingMode string
|
||||
type DispositionTypeOption string
|
||||
|
||||
type Duration string
|
||||
|
||||
const (
|
||||
JmapCore = "urn:ietf:params:jmap:core"
|
||||
JmapMail = "urn:ietf:params:jmap:mail"
|
||||
JmapMDN = "urn:ietf:params:jmap:mdn" // https://datatracker.ietf.org/doc/rfc9007/
|
||||
JmapSubmission = "urn:ietf:params:jmap:submission"
|
||||
JmapVacationResponse = "urn:ietf:params:jmap:vacationresponse"
|
||||
JmapCalendars = "urn:ietf:params:jmap:calendars"
|
||||
JmapContacts = "urn:ietf:params:jmap:contacts"
|
||||
JmapSieve = "urn:ietf:params:jmap:sieve"
|
||||
JmapBlob = "urn:ietf:params:jmap:blob"
|
||||
JmapQuota = "urn:ietf:params:jmap:quota"
|
||||
JmapWebsocket = "urn:ietf:params:jmap:websocket"
|
||||
JmapPrincipals = "urn:ietf:params:jmap:principals"
|
||||
JmapPrincipalsOwner = "urn:ietf:params:jmap:principals:owner"
|
||||
JmapCore = "urn:ietf:params:jmap:core"
|
||||
JmapMail = "urn:ietf:params:jmap:mail"
|
||||
JmapMDN = "urn:ietf:params:jmap:mdn" // https://datatracker.ietf.org/doc/rfc9007/
|
||||
JmapSubmission = "urn:ietf:params:jmap:submission"
|
||||
JmapVacationResponse = "urn:ietf:params:jmap:vacationresponse"
|
||||
JmapCalendars = "urn:ietf:params:jmap:calendars"
|
||||
JmapContacts = "urn:ietf:params:jmap:contacts"
|
||||
JmapSieve = "urn:ietf:params:jmap:sieve"
|
||||
JmapBlob = "urn:ietf:params:jmap:blob"
|
||||
JmapQuota = "urn:ietf:params:jmap:quota"
|
||||
JmapWebsocket = "urn:ietf:params:jmap:websocket"
|
||||
JmapPrincipals = "urn:ietf:params:jmap:principals"
|
||||
JmapPrincipalsOwner = "urn:ietf:params:jmap:principals:owner"
|
||||
JmapTasks = "urn:ietf:params:jmap:tasks"
|
||||
JmapTasksRecurrences = "urn:ietf:params:jmap:tasks:recurrences"
|
||||
JmapTasksAssignees = "urn:ietf:params:jmap:tasks:assignees"
|
||||
JmapTasksAlerts = "urn:ietf:params:jmap:tasks:alerts"
|
||||
JmapTasksMultilingual = "urn:ietf:params:jmap:tasks:multilingual"
|
||||
JmapTasksCustomTimezones = "urn:ietf:params:jmap:tasks:customtimezones"
|
||||
|
||||
CoreType = ObjectType("Core")
|
||||
PushSubscriptionType = ObjectType("PushSubscription")
|
||||
@@ -584,6 +592,42 @@ type SessionContactsAccountCapabilities struct {
|
||||
MayCreateAddressBook bool `json:"mayCreateAddressBook"`
|
||||
}
|
||||
|
||||
type SessionCalendarsAccountCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionCalendarsParseAccountCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksAccountCapabilities struct {
|
||||
// The earliest date-time the server is willing to accept for any date stored in a Task.
|
||||
MinDateTime LocalDate `json:"minDateTime,omitzero"`
|
||||
|
||||
// The latest date-time the server is willing to accept for any date stored in a Task.
|
||||
MaxDateTime LocalDate `json:"maxDateTime,omitzero"`
|
||||
|
||||
// If true, the user may create a task list in this account.
|
||||
MayCreateTaskList bool `json:"mayCreateTaskList"`
|
||||
}
|
||||
|
||||
type SessionTasksRecurrencesAccountCapabilities struct {
|
||||
// The maximum duration the user may query over when asking the server to expand recurrences.
|
||||
MaxExpandedQueryDuration Duration `json:"maxExpandedQueryDuration"`
|
||||
}
|
||||
|
||||
type SessionTasksAssigneesAccountCapabilities struct {
|
||||
// The maximum number of participants a single task may have, or null for no limit.
|
||||
MaxParticipantsPerTask *uint `json:"maxParticipantsPerTask,omitzero"`
|
||||
}
|
||||
|
||||
type SessionTasksAlertsAccountCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksMultilingualAccountCapabilities struct {
|
||||
}
|
||||
|
||||
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"`
|
||||
@@ -598,20 +642,34 @@ type SessionPrincipalsOwnerAccountCapabilities struct {
|
||||
PrincipalId string `json:"principalId,omitempty"`
|
||||
}
|
||||
|
||||
type SessionPrincipalAvailabilityAccountCapabilities struct {
|
||||
// The maximum duration over which the server is prepared to calculate availability in a single call.
|
||||
MaxAvailabilityDuration Duration `json:"maxAvailabilityDuration"`
|
||||
}
|
||||
|
||||
type SessionMDNAccountCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionAccountCapabilities struct {
|
||||
Mail *SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail,omitempty"`
|
||||
Submission *SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission,omitempty"`
|
||||
VacationResponse *SessionVacationResponseAccountCapabilities `json:"urn:ietf:params:jmap:vacationresponse,omitempty"`
|
||||
Sieve *SessionSieveAccountCapabilities `json:"urn:ietf:params:jmap:sieve,omitempty"`
|
||||
Blob *SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob,omitempty"`
|
||||
Quota *SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota,omitempty"`
|
||||
Contacts *SessionContactsAccountCapabilities `json:"urn:ietf:params:jmap:contacts,omitempty"`
|
||||
Principals *SessionPrincipalsAccountCapabilities `json:"urn:ietf:params:jmap:principals,omitempty"`
|
||||
PrincipalsOwner *SessionPrincipalsOwnerAccountCapabilities `json:"urn:ietf:params:jmap:principals:owner,omitempty"`
|
||||
MDN *SessionMDNAccountCapabilities `json:"urn:ietf:params:jmap:mdn,omitempty"`
|
||||
Mail *SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail,omitempty"`
|
||||
Submission *SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission,omitempty"`
|
||||
VacationResponse *SessionVacationResponseAccountCapabilities `json:"urn:ietf:params:jmap:vacationresponse,omitempty"`
|
||||
Sieve *SessionSieveAccountCapabilities `json:"urn:ietf:params:jmap:sieve,omitempty"`
|
||||
Blob *SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob,omitempty"`
|
||||
Quota *SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota,omitempty"`
|
||||
Contacts *SessionContactsAccountCapabilities `json:"urn:ietf:params:jmap:contacts,omitempty"`
|
||||
Calendars *SessionCalendarsAccountCapabilities `json:"urn:ietf:params:jmap:calendars,omitempty"`
|
||||
CalendarsParse *SessionCalendarsParseAccountCapabilities `json:"urn:ietf:params:jmap:calendars:parse,omitempty"`
|
||||
Tasks *SessionTasksAccountCapabilities `json:"urn:ietf:params:jmap:tasks,omitempty"`
|
||||
TasksRecurrences *SessionTasksRecurrencesAccountCapabilities `json:"urn:ietf:params:jmap:tasks:recurrences,omitempty"`
|
||||
TasksAssignees *SessionTasksAssigneesAccountCapabilities `json:"urn:ietf:params:jmap:tasks:assignees,omitempty"`
|
||||
TasksAlerts *SessionTasksAlertsAccountCapabilities `json:"urn:ietf:params:jmap:tasks:alerts,omitempty"`
|
||||
TasksMultilingual *SessionTasksMultilingualAccountCapabilities `json:"urn:ietf:params:jmap:tasks:multilingual,omitempty"`
|
||||
TasksCustomTimezones *SessionTasksCustomTimezonesAccountCapabilities `json:"urn:ietf:params:jmap:tasks:customtimezones,omitempty"`
|
||||
Principals *SessionPrincipalsAccountCapabilities `json:"urn:ietf:params:jmap:principals,omitempty"`
|
||||
PrincipalsOwner *SessionPrincipalsOwnerAccountCapabilities `json:"urn:ietf:params:jmap:principals:owner,omitempty"`
|
||||
PrincipalsAvailability *SessionPrincipalAvailabilityAccountCapabilities `json:"urn:ietf:params:jmap:principals:availability,omitempty"`
|
||||
MDN *SessionMDNAccountCapabilities `json:"urn:ietf:params:jmap:mdn,omitempty"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
@@ -644,7 +702,7 @@ type SessionCoreCapabilities struct {
|
||||
MaxObjectsInSet int `json:"maxObjectsInSet"`
|
||||
// A list of identifiers for algorithms registered in the collation registry, as defined in [@!RFC4790], that the server
|
||||
// supports for sorting when querying records.
|
||||
CollationAlgorithms []string `json:"collationAlgorithms"`
|
||||
CollationAlgorithms []string `json:"collationAlgorithms,omitempty"`
|
||||
}
|
||||
|
||||
type SessionMailCapabilities struct {
|
||||
@@ -682,35 +740,125 @@ type SessionWebsocketCapabilities struct {
|
||||
type SessionContactsCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionCalendarsCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionCalendarsParseCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksRecurrencesCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksAssigneesCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksAlertsCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksMultilingualCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionTasksCustomTimezonesCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionPrincipalCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionPrincipalAvailabilityCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionMDNCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionCapabilities struct {
|
||||
Core SessionCoreCapabilities `json:"urn:ietf:params:jmap:core"`
|
||||
Mail SessionMailCapabilities `json:"urn:ietf:params:jmap:mail"`
|
||||
Submission SessionSubmissionCapabilities `json:"urn:ietf:params:jmap:submission"`
|
||||
VacationResponse SessionVacationResponseCapabilities `json:"urn:ietf:params:jmap:vacationresponse"`
|
||||
Sieve SessionSieveCapabilities `json:"urn:ietf:params:jmap:sieve"`
|
||||
Blob SessionBlobCapabilities `json:"urn:ietf:params:jmap:blob"`
|
||||
Quota SessionQuotaCapabilities `json:"urn:ietf:params:jmap:quota"`
|
||||
Websocket SessionWebsocketCapabilities `json:"urn:ietf:params:jmap:websocket"`
|
||||
Contacts *SessionContactsCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
||||
Principals *SessionPrincipalCapabilities `json:"urn:ietf:params:jmap:principals"`
|
||||
MDN *SessionMDNCapabilities `json:"urn:ietf:params:jmap:mdn,omitempty"`
|
||||
Core *SessionCoreCapabilities `json:"urn:ietf:params:jmap:core,omitempty"`
|
||||
Mail *SessionMailCapabilities `json:"urn:ietf:params:jmap:mail,omitempty"`
|
||||
Submission *SessionSubmissionCapabilities `json:"urn:ietf:params:jmap:submission,omitempty"`
|
||||
VacationResponse *SessionVacationResponseCapabilities `json:"urn:ietf:params:jmap:vacationresponse,omitempty"`
|
||||
Sieve *SessionSieveCapabilities `json:"urn:ietf:params:jmap:sieve,omitempty"`
|
||||
Blob *SessionBlobCapabilities `json:"urn:ietf:params:jmap:blob,omitempty"`
|
||||
Quota *SessionQuotaCapabilities `json:"urn:ietf:params:jmap:quota,omitempty"`
|
||||
Websocket *SessionWebsocketCapabilities `json:"urn:ietf:params:jmap:websocket,omitempty"`
|
||||
|
||||
// This represents support for the `AddressBook` and `ContactCard` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
Contacts *SessionContactsCapabilities `json:"urn:ietf:params:jmap:contacts,omitempty"`
|
||||
|
||||
// This represents support for the `Calendar`, `CalendarEvent`, `CalendarEventNotification`,
|
||||
// and `ParticipantIdentity` data types and associated API methods, except for `CalendarEvent/parse`.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
Calendars *SessionCalendarsCapabilities `json:"urn:ietf:params:jmap:calendars,omitempty"`
|
||||
|
||||
// This represents support for the `CalendarEvent/parse` method.
|
||||
//
|
||||
// The value of this property is an empty object in the JMAP session `capabilities` property.
|
||||
CalendarsParse *SessionCalendarsParseCapabilities `json:"urn:ietf:params:jmap:calendars:parse,omitempty"`
|
||||
|
||||
// This represents support for the core properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
Tasks *SessionTasksCapabilities `json:"urn:ietf:params:jmap:tasks,omitempty"`
|
||||
|
||||
// This represents support for the `recurrence` properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
TasksRecurrences *SessionTasksRecurrencesCapabilities `json:"urn:ietf:params:jmap:tasks:recurrences,omitempty"`
|
||||
|
||||
// This represents support for the `assignee` properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
TasksAssignees *SessionTasksAssigneesCapabilities `json:"urn:ietf:params:jmap:tasks:assignees,omitempty"`
|
||||
|
||||
// This represents support for the `alerts` properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
TasksAlerts *SessionTasksAlertsCapabilities `json:"urn:ietf:params:jmap:tasks:alerts,omitempty"`
|
||||
|
||||
// This represents support for the multilingual properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
TasksMultilingual *SessionTasksMultilingualCapabilities `json:"urn:ietf:params:jmap:tasks:multilingual,omitempty"`
|
||||
|
||||
// This represents support for the custom time zone properties and objects of the `TaskList`,
|
||||
// `Task` and `TaskNotification` data types and associated API methods.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
TasksCustomTimezones *SessionTasksCustomTimezonesCapabilities `json:"urn:ietf:params:jmap:tasks:customtimezones,omitempty"`
|
||||
|
||||
Principals *SessionPrincipalCapabilities `json:"urn:ietf:params:jmap:principals,omitempty"`
|
||||
|
||||
// Represents support for the `Principal/getAvailability` method.
|
||||
//
|
||||
// Any account with this capability MUST also have the `urn:ietf:params:jmap:principals` capability.
|
||||
//
|
||||
// The value of this property in the JMAP Session `capabilities` property is an empty object.
|
||||
PrincipalsAvailability *SessionPrincipalAvailabilityCapabilities `json:"urn:ietf:params:jmap:principals:availability,omitempty"`
|
||||
|
||||
MDN *SessionMDNCapabilities `json:"urn:ietf:params:jmap:mdn,omitempty"`
|
||||
}
|
||||
|
||||
type SessionPrimaryAccounts struct {
|
||||
Core string `json:"urn:ietf:params:jmap:core"`
|
||||
Mail string `json:"urn:ietf:params:jmap:mail"`
|
||||
Submission string `json:"urn:ietf:params:jmap:submission"`
|
||||
VacationResponse string `json:"urn:ietf:params:jmap:vacationresponse"`
|
||||
Sieve string `json:"urn:ietf:params:jmap:sieve"`
|
||||
Blob string `json:"urn:ietf:params:jmap:blob"`
|
||||
Quota string `json:"urn:ietf:params:jmap:quota"`
|
||||
Websocket string `json:"urn:ietf:params:jmap:websocket"`
|
||||
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"`
|
||||
Calendar string `json:"urn:ietf:params:jmap:calendar,omitempty"`
|
||||
Contact string `json:"urn:ietf:params:jmap:contact,omitempty"`
|
||||
}
|
||||
|
||||
type SessionState string
|
||||
|
||||
@@ -20,6 +20,48 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func jsoneq[X any](t *testing.T, expected string, object X) {
|
||||
data, err := json.MarshalIndent(object, "", "")
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, expected, string(data))
|
||||
|
||||
var rec X
|
||||
err = json.Unmarshal(data, &rec)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, object, rec)
|
||||
}
|
||||
|
||||
func TestEmptySessionCapabilitiesMarshalling(t *testing.T) {
|
||||
jsoneq(t, `{}`, SessionCapabilities{})
|
||||
}
|
||||
|
||||
func TestSessionCapabilitiesMarshalling(t *testing.T) {
|
||||
jsoneq(t, `{
|
||||
"urn:ietf:params:jmap:core": {
|
||||
"maxSizeUpload": 123,
|
||||
"maxConcurrentUpload": 4,
|
||||
"maxSizeRequest": 1000,
|
||||
"maxConcurrentRequests": 8,
|
||||
"maxCallsInRequest": 16,
|
||||
"maxObjectsInGet": 32,
|
||||
"maxObjectsInSet": 8
|
||||
},
|
||||
"urn:ietf:params:jmap:tasks": {
|
||||
}
|
||||
}`, SessionCapabilities{
|
||||
Core: &SessionCoreCapabilities{
|
||||
MaxSizeUpload: 123,
|
||||
MaxConcurrentUpload: 4,
|
||||
MaxSizeRequest: 1000,
|
||||
MaxConcurrentRequests: 8,
|
||||
MaxCallsInRequest: 16,
|
||||
MaxObjectsInGet: 32,
|
||||
MaxObjectsInSet: 8,
|
||||
},
|
||||
Tasks: &SessionTasksCapabilities{},
|
||||
})
|
||||
}
|
||||
|
||||
type TestJmapWellKnownClient struct {
|
||||
t *testing.T
|
||||
}
|
||||
@@ -48,7 +90,7 @@ func (t *TestJmapWellKnownClient) GetSession(sessionUrl *url.URL, username strin
|
||||
Websocket: pa,
|
||||
},
|
||||
Capabilities: SessionCapabilities{
|
||||
Core: SessionCoreCapabilities{
|
||||
Core: &SessionCoreCapabilities{
|
||||
MaxCallsInRequest: 64,
|
||||
},
|
||||
},
|
||||
@@ -182,7 +224,7 @@ func TestRequests(t *testing.T) {
|
||||
JmapUrl: *jmapUrl,
|
||||
SessionResponse: SessionResponse{
|
||||
Capabilities: SessionCapabilities{
|
||||
Core: SessionCoreCapabilities{
|
||||
Core: &SessionCoreCapabilities{
|
||||
MaxCallsInRequest: 10,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -25,6 +25,12 @@ type SwaggerGetCalendars200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetCalendars(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needCalendarWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
return response(AllCalendars, req.session.State)
|
||||
})
|
||||
}
|
||||
@@ -49,6 +55,12 @@ type SwaggerGetCalendarById200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetCalendarById(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needCalendarWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
calendarId := chi.URLParam(r, UriParamCalendarId)
|
||||
// TODO replace with proper implementation
|
||||
for _, calendar := range AllCalendars {
|
||||
@@ -78,6 +90,12 @@ type SwaggerGetEventsInCalendar200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetEventsInCalendar(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needCalendarWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
calendarId := chi.URLParam(r, UriParamCalendarId)
|
||||
// TODO replace with proper implementation
|
||||
events, ok := EventsMapByCalendarId[calendarId]
|
||||
|
||||
@@ -26,6 +26,12 @@ type SwaggerGetAddressbooks200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetAddressbooks(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needContactWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
// TODO replace with proper implementation
|
||||
return response(AllAddressBooks, req.session.State)
|
||||
})
|
||||
@@ -51,6 +57,12 @@ type SwaggerGetAddressbookById200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetAddressbook(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needContactWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
addressBookId := chi.URLParam(r, UriParamAddressBookId)
|
||||
// TODO replace with proper implementation
|
||||
for _, ab := range AllAddressBooks {
|
||||
@@ -80,6 +92,12 @@ type SwaggerGetContactsInAddressbook200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetContactsInAddressbook(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needContactWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
addressBookId := chi.URLParam(r, UriParamAddressBookId)
|
||||
// TODO replace with proper implementation
|
||||
contactCards, ok := ContactsMapByAddressBookId[addressBookId]
|
||||
|
||||
@@ -25,6 +25,12 @@ type SwaggerGetTaskLists200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTaskLists(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needTaskWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
return response(AllTaskLists, req.session.State)
|
||||
})
|
||||
}
|
||||
@@ -49,6 +55,12 @@ type SwaggerGetTaskListById200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTaskListById(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needTaskWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
tasklistId := chi.URLParam(r, UriParamTaskListId)
|
||||
// TODO replace with proper implementation
|
||||
for _, tasklist := range AllTaskLists {
|
||||
@@ -78,6 +90,12 @@ type SwaggerGetTasksInTaskList200 struct {
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTasksInTaskList(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
ok, accountId, resp := req.needTaskWithAccount()
|
||||
if !ok {
|
||||
return resp
|
||||
}
|
||||
var _ string = accountId
|
||||
|
||||
tasklistId := chi.URLParam(r, UriParamTaskListId)
|
||||
// TODO replace with proper implementation
|
||||
tasks, ok := TaskMapByTaskListId[tasklistId]
|
||||
|
||||
@@ -188,6 +188,12 @@ const (
|
||||
ErrorCodeAccountNotFound = "ACCNFD"
|
||||
ErrorCodeAccountNotSupportedByMethod = "ACCNSM"
|
||||
ErrorCodeAccountReadOnly = "ACCRDO"
|
||||
ErrorCodeMissingCalendarsSessionCapability = "MSCCAL"
|
||||
ErrorCodeMissingCalendarsAccountCapability = "MACCAL"
|
||||
ErrorCodeMissingContactsSessionCapability = "MSCCON"
|
||||
ErrorCodeMissingContactsAccountCapability = "MACCON"
|
||||
ErrorCodeMissingTasksSessionCapability = "MSCTSK"
|
||||
ErrorCodeMissingTaskAccountCapability = "MACTSK"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -371,6 +377,42 @@ var (
|
||||
Title: "The referenced Account is read-only",
|
||||
Detail: "The Account that was referenced in the request only supports read-only operations.",
|
||||
}
|
||||
ErrorMissingCalendarsSessionCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingCalendarsSessionCapability,
|
||||
Title: "Session is missing the task capability '" + jmap.JmapCalendars + "'",
|
||||
Detail: "The JMAP Session of the user does not have the required capability '" + jmap.JmapTasks + "'.",
|
||||
}
|
||||
ErrorMissingCalendarsAccountCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingCalendarsSessionCapability,
|
||||
Title: "Account is missing the task capability '" + jmap.JmapCalendars + "'",
|
||||
Detail: "The JMAP Account of the user does not have the required capability '" + jmap.JmapTasks + "'.",
|
||||
}
|
||||
ErrorMissingContactsSessionCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingContactsSessionCapability,
|
||||
Title: "Session is missing the task capability '" + jmap.JmapContacts + "'",
|
||||
Detail: "The JMAP Session of the user does not have the required capability '" + jmap.JmapContacts + "'.",
|
||||
}
|
||||
ErrorMissingContactsAccountCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingContactsSessionCapability,
|
||||
Title: "Account is missing the task capability '" + jmap.JmapContacts + "'",
|
||||
Detail: "The JMAP Account of the user does not have the required capability '" + jmap.JmapContacts + "'.",
|
||||
}
|
||||
ErrorMissingTasksSessionCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingTasksSessionCapability,
|
||||
Title: "Session is missing the task capability '" + jmap.JmapTasks + "'",
|
||||
Detail: "The JMAP Session of the user does not have the required capability '" + jmap.JmapTasks + "'.",
|
||||
}
|
||||
ErrorMissingTasksAccountCapability = GroupwareError{
|
||||
Status: http.StatusExpectationFailed,
|
||||
Code: ErrorCodeMissingTasksSessionCapability,
|
||||
Title: "Account is missing the task capability '" + jmap.JmapTasks + "'",
|
||||
Detail: "The JMAP Account of the user does not have the required capability '" + jmap.JmapTasks + "'.",
|
||||
}
|
||||
)
|
||||
|
||||
type ErrorOpt interface {
|
||||
@@ -527,6 +569,14 @@ func (r Request) observedParameterError(gwerr GroupwareError, options ...ErrorOp
|
||||
return r.observeParameterError(apiError(r.errorId(), gwerr, options...))
|
||||
}
|
||||
|
||||
func (r Request) apiError(err *GroupwareError) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
errorId := r.errorId()
|
||||
return apiError(errorId, *err)
|
||||
}
|
||||
|
||||
func (r Request) apiErrorFromJmap(err jmap.Error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
|
||||
@@ -57,6 +57,9 @@ var (
|
||||
errNoPrimaryAccountForBlob = errors.New("no primary account for blob")
|
||||
errNoPrimaryAccountForVacationResponse = errors.New("no primary account for vacation response")
|
||||
errNoPrimaryAccountForSubmission = errors.New("no primary account for submission")
|
||||
errNoPrimaryAccountForTask = errors.New("no primary account for task")
|
||||
errNoPrimaryAccountForCalendar = errors.New("no primary account for calendar")
|
||||
errNoPrimaryAccountForContact = errors.New("no primary account for contact")
|
||||
// errNoPrimaryAccountForSieve = errors.New("no primary account for sieve")
|
||||
// errNoPrimaryAccountForQuota = errors.New("no primary account for quota")
|
||||
// errNoPrimaryAccountForWebsocket = errors.New("no primary account for websocket")
|
||||
@@ -105,6 +108,18 @@ func (r Request) GetAccountIdForSubmission() (string, *Error) {
|
||||
return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForSubmission)
|
||||
}
|
||||
|
||||
func (r Request) GetAccountIdForTask() (string, *Error) {
|
||||
return r.getAccountId(r.session.PrimaryAccounts.Task, errNoPrimaryAccountForTask)
|
||||
}
|
||||
|
||||
func (r Request) GetAccountIdForCalendar() (string, *Error) {
|
||||
return r.getAccountId(r.session.PrimaryAccounts.Calendar, errNoPrimaryAccountForCalendar)
|
||||
}
|
||||
|
||||
func (r Request) GetAccountIdForContact() (string, *Error) {
|
||||
return r.getAccountId(r.session.PrimaryAccounts.Contact, errNoPrimaryAccountForContact)
|
||||
}
|
||||
|
||||
func (r Request) GetAccountForMail() (jmap.Account, *Error) {
|
||||
accountId, err := r.GetAccountIdForMail()
|
||||
if err != nil {
|
||||
@@ -277,3 +292,99 @@ func (r Request) observeJmapError(jerr jmap.Error) jmap.Error {
|
||||
}
|
||||
return jerr
|
||||
}
|
||||
|
||||
func (r Request) needTask() (bool, Response) {
|
||||
if r.session.Capabilities.Tasks == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingTasksSessionCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needTaskForAccount(accountId string) (bool, Response) {
|
||||
if ok, resp := r.needTask(); !ok {
|
||||
return ok, resp
|
||||
}
|
||||
account, ok := r.session.Accounts[accountId]
|
||||
if !ok {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorAccountNotFound), r.session.State)
|
||||
}
|
||||
if account.AccountCapabilities.Tasks == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingTasksAccountCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needTaskWithAccount() (bool, string, Response) {
|
||||
accountId, err := r.GetAccountIdForTask()
|
||||
if err != nil {
|
||||
return false, "", errorResponse(err)
|
||||
}
|
||||
if ok, resp := r.needTaskForAccount(accountId); !ok {
|
||||
return false, accountId, resp
|
||||
}
|
||||
return true, accountId, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needCalendar() (bool, Response) {
|
||||
if r.session.Capabilities.Calendars == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingCalendarsSessionCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needCalendarForAccount(accountId string) (bool, Response) {
|
||||
if ok, resp := r.needCalendar(); !ok {
|
||||
return ok, resp
|
||||
}
|
||||
account, ok := r.session.Accounts[accountId]
|
||||
if !ok {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorAccountNotFound), r.session.State)
|
||||
}
|
||||
if account.AccountCapabilities.Calendars == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingCalendarsAccountCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needCalendarWithAccount() (bool, string, Response) {
|
||||
accountId, err := r.GetAccountIdForCalendar()
|
||||
if err != nil {
|
||||
return false, "", errorResponse(err)
|
||||
}
|
||||
if ok, resp := r.needCalendarForAccount(accountId); !ok {
|
||||
return false, accountId, resp
|
||||
}
|
||||
return true, accountId, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needContact() (bool, Response) {
|
||||
if r.session.Capabilities.Contacts == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingContactsSessionCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needContactForAccount(accountId string) (bool, Response) {
|
||||
if ok, resp := r.needContact(); !ok {
|
||||
return ok, resp
|
||||
}
|
||||
account, ok := r.session.Accounts[accountId]
|
||||
if !ok {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorAccountNotFound), r.session.State)
|
||||
}
|
||||
if account.AccountCapabilities.Contacts == nil {
|
||||
return false, errorResponseWithSessionState(r.apiError(&ErrorMissingContactsAccountCapability), r.session.State)
|
||||
}
|
||||
return true, Response{}
|
||||
}
|
||||
|
||||
func (r Request) needContactWithAccount() (bool, string, Response) {
|
||||
accountId, err := r.GetAccountIdForContact()
|
||||
if err != nil {
|
||||
return false, "", errorResponse(err)
|
||||
}
|
||||
if ok, resp := r.needContactForAccount(accountId); !ok {
|
||||
return false, accountId, resp
|
||||
}
|
||||
return true, accountId, Response{}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,15 @@ func errorResponse(err *Error) Response {
|
||||
}
|
||||
}
|
||||
|
||||
func errorResponseWithSessionState(err *Error, sessionState jmap.SessionState) Response {
|
||||
return Response{
|
||||
body: nil,
|
||||
err: err,
|
||||
etag: "",
|
||||
sessionState: sessionState,
|
||||
}
|
||||
}
|
||||
|
||||
func response(body any, sessionState jmap.SessionState) Response {
|
||||
return Response{
|
||||
body: body,
|
||||
|
||||
Reference in New Issue
Block a user