mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-10 22:19:17 -06:00
groupware: implement/fix email submission
This commit is contained in:
@@ -730,9 +730,11 @@ func (j *Client) UpdateEmails(accountId string, updates map[string]EmailUpdate,
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(setResponse.NotUpdated) != len(updates) {
|
||||
// error occured
|
||||
// TODO(pbleser-oc) handle submission errors
|
||||
if len(setResponse.NotUpdated) > 0 {
|
||||
// TODO we don't have composite errors
|
||||
for _, notUpdated := range setResponse.NotUpdated {
|
||||
return nil, "", setErrorError(notUpdated, EmailType)
|
||||
}
|
||||
}
|
||||
return setResponse.Updated, setResponse.NewState, nil
|
||||
})
|
||||
@@ -783,29 +785,49 @@ type SubmittedEmail struct {
|
||||
MdnBlobIds []string `json:"mdnBlobIds,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string, data []byte) (SubmittedEmail, SessionState, State, Language, Error) {
|
||||
type MoveMail struct {
|
||||
FromMailboxId string
|
||||
ToMailboxId string
|
||||
}
|
||||
|
||||
func (j *Client) SubmitEmail(accountId string, identityId string, emailId string, move *MoveMail, session *Session, ctx context.Context, logger *log.Logger, acceptLanguage string) (EmailSubmission, SessionState, State, Language, Error) {
|
||||
logger = j.logger("SubmitEmail", session, logger)
|
||||
|
||||
update := map[string]any{
|
||||
EmailPropertyKeywords + "/" + JmapKeywordDraft: nil, // unmark as draft
|
||||
EmailPropertyKeywords + "/" + JmapKeywordSeen: true, // mark as seen (read)
|
||||
}
|
||||
if move != nil && move.FromMailboxId != "" && move.ToMailboxId != "" && move.FromMailboxId != move.ToMailboxId {
|
||||
update[EmailPropertyMailboxIds+"/"+move.FromMailboxId] = nil
|
||||
update[EmailPropertyMailboxIds+"/"+move.ToMailboxId] = true
|
||||
}
|
||||
|
||||
id := "s0"
|
||||
|
||||
set := EmailSubmissionSetCommand{
|
||||
AccountId: accountId,
|
||||
Create: map[string]EmailSubmissionCreate{
|
||||
"s0": {
|
||||
id: {
|
||||
IdentityId: identityId,
|
||||
EmailId: emailId,
|
||||
// leaving Envelope empty
|
||||
},
|
||||
},
|
||||
OnSuccessUpdateEmail: map[string]PatchObject{
|
||||
"#s0": {
|
||||
EmailPropertyKeywords + "/" + JmapKeywordDraft: nil,
|
||||
},
|
||||
"#" + id: update,
|
||||
},
|
||||
}
|
||||
|
||||
get := EmailSubmissionGetRefCommand{
|
||||
get := EmailSubmissionGetCommand{
|
||||
AccountId: accountId,
|
||||
IdRef: &ResultReference{
|
||||
ResultOf: "0",
|
||||
Name: CommandEmailSubmissionSet,
|
||||
Path: "/created/s0/" + EmailPropertyId,
|
||||
},
|
||||
Ids: []string{"#" + id},
|
||||
/*
|
||||
IdRef: &ResultReference{
|
||||
ResultOf: "0",
|
||||
Name: CommandEmailSubmissionSet,
|
||||
Path: ["#"]"/created/" + "#" + id + "/" + EmailPropertyId,
|
||||
},
|
||||
*/
|
||||
}
|
||||
|
||||
cmd, err := j.request(session, logger,
|
||||
@@ -813,14 +835,14 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
|
||||
invocation(CommandEmailSubmissionGet, get, "1"),
|
||||
)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, "", "", "", err
|
||||
return EmailSubmission{}, "", "", "", err
|
||||
}
|
||||
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (SubmittedEmail, State, Error) {
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, acceptLanguage, func(body *Response) (EmailSubmission, State, Error) {
|
||||
var submissionResponse EmailSubmissionSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionSet, "0", &submissionResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, "", err
|
||||
return EmailSubmission{}, "", err
|
||||
}
|
||||
|
||||
if len(submissionResponse.NotCreated) > 0 {
|
||||
@@ -836,31 +858,28 @@ func (j *Client) SubmitEmail(accountId string, identityId string, emailId string
|
||||
var setResponse EmailSetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSet, "0", &setResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, "", err
|
||||
return EmailSubmission{}, "", err
|
||||
}
|
||||
|
||||
var getResponse EmailSubmissionGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return SubmittedEmail{}, "", err
|
||||
if emailId := structs.FirstKey(setResponse.Updated); emailId != nil && len(setResponse.Updated) == 1 {
|
||||
var getResponse EmailSubmissionGetResponse
|
||||
err = retrieveResponseMatchParameters(logger, body, CommandEmailSubmissionGet, "1", &getResponse)
|
||||
if err != nil {
|
||||
return EmailSubmission{}, "", err
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
// for some reason (error?)...
|
||||
// TODO(pbleser-oc) handle absence of emailsubmission
|
||||
}
|
||||
|
||||
submission := getResponse.List[0]
|
||||
|
||||
return submission, setResponse.NewState, nil
|
||||
} else {
|
||||
err = simpleError(fmt.Errorf("failed to submit email: updated is empty"), 0) // TODO proper error handling
|
||||
return EmailSubmission{}, "", err
|
||||
}
|
||||
|
||||
if len(getResponse.List) != 1 {
|
||||
// for some reason (error?)...
|
||||
// TODO(pbleser-oc) handle absence of emailsubmission
|
||||
}
|
||||
|
||||
submission := getResponse.List[0]
|
||||
|
||||
return SubmittedEmail{
|
||||
Id: submission.Id,
|
||||
SendAt: submission.SendAt,
|
||||
ThreadId: submission.ThreadId,
|
||||
UndoStatus: submission.UndoStatus,
|
||||
Envelope: submission.Envelope,
|
||||
DsnBlobIds: submission.DsnBlobIds,
|
||||
MdnBlobIds: submission.MdnBlobIds,
|
||||
}, setResponse.NewState, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2540,6 +2540,24 @@ type EmailSubmission struct {
|
||||
MdnBlobIds []string `json:"mdnBlobIds,omitempty"`
|
||||
}
|
||||
|
||||
type EmailSubmissionGetCommand struct {
|
||||
// The id of the account to use.
|
||||
AccountId string `json:"accountId"`
|
||||
|
||||
// The ids of the EmailSubmission objects to return.
|
||||
//
|
||||
// If null, then all records of the data type are returned, if this is supported for that data
|
||||
// type and the number of records does not exceed the maxObjectsInGet limit.
|
||||
Ids []string `json:"ids,omitempty"`
|
||||
|
||||
// If supplied, only the properties listed in the array are returned for each EmailSubmission object.
|
||||
//
|
||||
// If null, all properties of the object are returned. The id property of the object is always returned,
|
||||
// even if not explicitly requested. If an invalid property is requested, the call MUST be rejected
|
||||
// with an invalidArguments error.
|
||||
Properties []string `json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
type EmailSubmissionGetRefCommand struct {
|
||||
// The id of the account to use.
|
||||
AccountId string `json:"accountId"`
|
||||
@@ -2952,6 +2970,18 @@ type EmailCreate struct {
|
||||
// ["From:" field]: https://www.rfc-editor.org/rfc/rfc5322.html#section-3.6.2
|
||||
From []EmailAddress `json:"from,omitempty"`
|
||||
|
||||
// The value is identical to the value of header:To:asAddresses.
|
||||
To []EmailAddress `json:"to,omitempty"`
|
||||
|
||||
// The value is identical to the value of header:Cc:asAddresses.
|
||||
Cc []EmailAddress `json:"cc,omitempty"`
|
||||
|
||||
// The value is identical to the value of header:Bcc:asAddresses.
|
||||
Bcc []EmailAddress `json:"bcc,omitempty"`
|
||||
|
||||
// The value is identical to the value of header:Reply-To:asAddresses.
|
||||
ReplyTo []EmailAddress `json:"replyTo,omitempty"`
|
||||
|
||||
// The "Subject:" field contains a short string identifying the topic of the message.
|
||||
Subject string `json:"subject,omitempty"`
|
||||
|
||||
@@ -2982,6 +3012,31 @@ type EmailCreate struct {
|
||||
|
||||
// This is a map of partId to an EmailBodyValue object for none, some, or all text/* parts.
|
||||
BodyValues map[string]EmailBodyValue `json:"bodyValues,omitempty"`
|
||||
|
||||
// A list of text/plain, text/html, image/*, audio/*, and/or video/* parts to display (sequentially) as the
|
||||
// message body, with a preference for text/plain when alternative versions are available.
|
||||
TextBody []EmailBodyPart `json:"textBody,omitempty"`
|
||||
|
||||
// A list of text/plain, text/html, image/*, audio/*, and/or video/* parts to display (sequentially) as the
|
||||
// message body, with a preference for text/html when alternative versions are available.
|
||||
HtmlBody []EmailBodyPart `json:"htmlBody,omitempty"`
|
||||
|
||||
// A list, traversing depth-first, of all parts in bodyStructure.
|
||||
//
|
||||
// They must satisfy either of the following conditions:
|
||||
//
|
||||
// - not of type multipart/* and not included in textBody or htmlBody
|
||||
// - of type image/*, audio/*, or video/* and not in both textBody and htmlBody
|
||||
//
|
||||
// None of these parts include subParts, including message/* types.
|
||||
//
|
||||
// Attached messages may be fetched using the Email/parse method and the blobId.
|
||||
//
|
||||
// Note that a text/html body part HTML may reference image parts in attachments by using cid:
|
||||
// links to reference the Content-Id, as defined in [RFC2392], or by referencing the Content-Location.
|
||||
//
|
||||
// [RFC2392]: https://www.rfc-editor.org/rfc/rfc2392.html
|
||||
Attachments []EmailBodyPart `json:"attachments,omitempty"`
|
||||
}
|
||||
|
||||
type EmailUpdate map[string]any
|
||||
|
||||
@@ -137,3 +137,10 @@ func Missing[E comparable](expected, actual []E) []E {
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
func FirstKey[K comparable, V any](m map[K]V) *K {
|
||||
for k := range m {
|
||||
return &k
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -887,24 +887,13 @@ func (g *Groupware) CreateEmail(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
logger = log.From(logger.With().Str(logAccountId, log.SafeString(accountId)))
|
||||
|
||||
var body jmap.Email
|
||||
var body jmap.EmailCreate
|
||||
err := req.body(&body)
|
||||
if err != nil {
|
||||
return errorResponse(err)
|
||||
}
|
||||
|
||||
create := jmap.EmailCreate{
|
||||
MailboxIds: body.MailboxIds,
|
||||
Keywords: body.Keywords,
|
||||
From: body.From,
|
||||
Subject: body.Subject,
|
||||
ReceivedAt: body.ReceivedAt,
|
||||
SentAt: body.SentAt,
|
||||
BodyStructure: body.BodyStructure,
|
||||
BodyValues: body.BodyValues,
|
||||
}
|
||||
|
||||
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, create, "", req.session, req.ctx, logger, req.language())
|
||||
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, "", req.session, req.ctx, logger, req.language())
|
||||
if jerr != nil {
|
||||
return req.errorResponseFromJmap(jerr)
|
||||
}
|
||||
@@ -926,24 +915,13 @@ func (g *Groupware) ReplaceEmail(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
logger = log.From(logger.With().Str(logAccountId, log.SafeString(accountId)))
|
||||
|
||||
var body jmap.Email
|
||||
var body jmap.EmailCreate
|
||||
err := req.body(&body)
|
||||
if err != nil {
|
||||
return errorResponse(err)
|
||||
}
|
||||
|
||||
create := jmap.EmailCreate{
|
||||
MailboxIds: body.MailboxIds,
|
||||
Keywords: body.Keywords,
|
||||
From: body.From,
|
||||
Subject: body.Subject,
|
||||
ReceivedAt: body.ReceivedAt,
|
||||
SentAt: body.SentAt,
|
||||
BodyStructure: body.BodyStructure,
|
||||
BodyValues: body.BodyValues,
|
||||
}
|
||||
|
||||
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, create, replaceId, req.session, req.ctx, logger, req.language())
|
||||
created, sessionState, state, lang, jerr := g.jmap.CreateEmail(accountId, body, replaceId, req.session, req.ctx, logger, req.language())
|
||||
if jerr != nil {
|
||||
return req.errorResponseFromJmap(jerr)
|
||||
}
|
||||
@@ -1305,6 +1283,58 @@ func (g *Groupware) DeleteEmails(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Groupware) SendEmail(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
emailId := chi.URLParam(r, UriParamEmailId)
|
||||
|
||||
l := req.logger.With()
|
||||
l.Str(UriParamEmailId, log.SafeString(emailId))
|
||||
|
||||
identityId, err := req.getMandatoryStringParam(QueryParamIdentityId)
|
||||
if err != nil {
|
||||
return errorResponse(err)
|
||||
}
|
||||
l.Str(QueryParamIdentityId, log.SafeString(identityId))
|
||||
|
||||
var move *jmap.MoveMail = nil
|
||||
{
|
||||
moveFromMailboxId, _ := req.getStringParam(QueryParamMoveFromMailboxId, "")
|
||||
moveToMailboxId, _ := req.getStringParam(QueryParamMoveToMailboxId, "")
|
||||
if moveFromMailboxId != "" && moveToMailboxId != "" {
|
||||
move = &jmap.MoveMail{FromMailboxId: moveFromMailboxId, ToMailboxId: moveToMailboxId}
|
||||
l.Str(QueryParamMoveFromMailboxId, log.SafeString(moveFromMailboxId)).Str(QueryParamMoveToMailboxId, log.SafeString(moveFromMailboxId))
|
||||
} else if moveFromMailboxId == "" && moveToMailboxId == "" {
|
||||
// nothing to change
|
||||
} else {
|
||||
missing := moveFromMailboxId
|
||||
if moveFromMailboxId == "" {
|
||||
missing = moveFromMailboxId
|
||||
}
|
||||
// only one is set
|
||||
msg := fmt.Sprintf("Missing required value for query parameter '%v'", missing)
|
||||
return errorResponse(req.observedParameterError(ErrorMissingMandatoryRequestParameter,
|
||||
withDetail(msg),
|
||||
withSource(&ErrorSource{Parameter: missing})))
|
||||
}
|
||||
}
|
||||
|
||||
accountId, gwerr := req.GetAccountIdForMail()
|
||||
if gwerr != nil {
|
||||
return errorResponse(gwerr)
|
||||
}
|
||||
l.Str(logAccountId, accountId)
|
||||
|
||||
logger := log.From(l)
|
||||
|
||||
resp, sessionState, state, lang, jerr := g.jmap.SubmitEmail(accountId, identityId, emailId, move, req.session, req.ctx, logger, req.language())
|
||||
if jerr != nil {
|
||||
return req.errorResponseFromJmap(jerr)
|
||||
}
|
||||
|
||||
return etagResponse(resp, sessionState, state, lang)
|
||||
})
|
||||
}
|
||||
|
||||
type AboutEmailsEvent struct {
|
||||
Id string `json:"id"`
|
||||
Source string `json:"source"`
|
||||
@@ -1833,35 +1863,6 @@ func filterFromNotKeywords(keywords []string) jmap.EmailFilterElement {
|
||||
}
|
||||
}
|
||||
|
||||
func squashQueryState[V any](all map[string]V, mapper func(V) jmap.State) jmap.State {
|
||||
n := len(all)
|
||||
if n == 0 {
|
||||
return jmap.State("")
|
||||
}
|
||||
if n == 1 {
|
||||
for _, v := range all {
|
||||
return mapper(v)
|
||||
}
|
||||
}
|
||||
|
||||
parts := make([]string, n)
|
||||
sortedKeys := make([]string, n)
|
||||
i := 0
|
||||
for k := range all {
|
||||
sortedKeys[i] = k
|
||||
i++
|
||||
}
|
||||
slices.Sort(sortedKeys)
|
||||
for i, k := range sortedKeys {
|
||||
if v, ok := all[k]; ok {
|
||||
parts[i] = k + ":" + string(mapper(v))
|
||||
} else {
|
||||
parts[i] = k + ":"
|
||||
}
|
||||
}
|
||||
return jmap.State(strings.Join(parts, ","))
|
||||
}
|
||||
|
||||
var sanitizationPolicy *bluemonday.Policy = bluemonday.UGCPolicy()
|
||||
|
||||
var sanitizableMediaTypes = []string{
|
||||
|
||||
@@ -174,6 +174,7 @@ const (
|
||||
ErrorCodeInvalidRequestPayload = "INVRQP"
|
||||
ErrorCodeInvalidResponsePayload = "INVRSP"
|
||||
ErrorCodeInvalidRequestParameter = "INVPAR"
|
||||
ErrorCodeMissingMandatoryRequestParameter = "MISMPA"
|
||||
ErrorCodeInvalidRequestBody = "INVBDY"
|
||||
ErrorCodeNonExistingAccount = "INVACC"
|
||||
ErrorCodeIndeterminateAccount = "INDACC"
|
||||
@@ -297,6 +298,12 @@ var (
|
||||
Title: "Invalid Request Parameter",
|
||||
Detail: "At least one of the parameters in the request is invalid.",
|
||||
}
|
||||
ErrorMissingMandatoryRequestParameter = GroupwareError{
|
||||
Status: http.StatusBadRequest,
|
||||
Code: ErrorCodeMissingMandatoryRequestParameter,
|
||||
Title: "Missing Mandatory Request Parameter",
|
||||
Detail: "A mandatory request parameter is missing.",
|
||||
}
|
||||
ErrorInvalidRequestBody = GroupwareError{
|
||||
Status: http.StatusBadRequest,
|
||||
Code: ErrorCodeInvalidRequestBody,
|
||||
|
||||
@@ -168,6 +168,34 @@ func (r Request) parameterErrorResponse(param string, detail string) Response {
|
||||
return errorResponse(r.parameterError(param, detail))
|
||||
}
|
||||
|
||||
func (r Request) getStringParam(param string, defaultValue string) (string, bool) {
|
||||
q := r.r.URL.Query()
|
||||
if !q.Has(param) {
|
||||
return defaultValue, false
|
||||
}
|
||||
str := q.Get(param)
|
||||
if str == "" {
|
||||
return defaultValue, false
|
||||
}
|
||||
return str, true
|
||||
}
|
||||
|
||||
func (r Request) getMandatoryStringParam(param string) (string, *Error) {
|
||||
str := ""
|
||||
q := r.r.URL.Query()
|
||||
if q.Has(param) {
|
||||
str = q.Get(param)
|
||||
}
|
||||
if str == "" {
|
||||
msg := fmt.Sprintf("Missing required value for query parameter '%v'", param)
|
||||
return "", r.observedParameterError(ErrorMissingMandatoryRequestParameter,
|
||||
withDetail(msg),
|
||||
withSource(&ErrorSource{Parameter: param}),
|
||||
)
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func (r Request) parseIntParam(param string, defaultValue int) (int, bool, *Error) {
|
||||
q := r.r.URL.Query()
|
||||
if !q.Has(param) {
|
||||
|
||||
@@ -30,6 +30,9 @@ const (
|
||||
QueryParamSince = "since"
|
||||
QueryParamMaxChanges = "maxchanges"
|
||||
QueryParamMailboxId = "mailbox"
|
||||
QueryParamIdentityId = "identity"
|
||||
QueryParamMoveFromMailboxId = "move-from"
|
||||
QueryParamMoveToMailboxId = "move-to"
|
||||
QueryParamNotInMailboxId = "notmailbox"
|
||||
QueryParamSearchText = "text"
|
||||
QueryParamSearchFrom = "from"
|
||||
@@ -74,43 +77,58 @@ func (g *Groupware) Route(r chi.Router) {
|
||||
r.Get("/", g.GetEmailsForAllAccounts)
|
||||
r.Get("/latest/summary", g.GetLatestEmailsSummaryForAllAccounts) // ?limit=10&seen=true&undesirable=true
|
||||
})
|
||||
r.Get("/quota", g.GetQuotaForAllAccounts)
|
||||
r.Route("/quota", func(r chi.Router) {
|
||||
r.Get("/", g.GetQuotaForAllAccounts)
|
||||
})
|
||||
})
|
||||
r.Route("/{accountid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetAccount)
|
||||
r.Route("/identities", func(r chi.Router) {
|
||||
r.Get("/", g.GetIdentities)
|
||||
r.Get("/{identityid}", g.GetIdentityById)
|
||||
r.Post("/", g.AddIdentity)
|
||||
r.Patch("/{identityid}", g.ModifyIdentity)
|
||||
r.Delete("/{identityid}", g.DeleteIdentity)
|
||||
r.Route("/{identityid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetIdentityById)
|
||||
r.Patch("/", g.ModifyIdentity)
|
||||
r.Delete("/", g.DeleteIdentity)
|
||||
})
|
||||
})
|
||||
r.Get("/vacation", g.GetVacation)
|
||||
r.Put("/vacation", g.SetVacation)
|
||||
r.Get("/quota", g.GetQuota)
|
||||
r.Route("/mailboxes", func(r chi.Router) {
|
||||
r.Get("/", g.GetMailboxes) // ?name=&role=&subcribed=
|
||||
r.Get("/{mailboxid}", g.GetMailbox)
|
||||
r.Get("/{mailboxid}/emails", g.GetAllEmailsInMailbox)
|
||||
r.Get("/{mailboxid}/changes", g.GetMailboxChanges)
|
||||
r.Post("/", g.CreateMailbox)
|
||||
r.Patch("/{mailboxid}", g.UpdateMailbox)
|
||||
r.Delete("/{mailboxid}", g.DeleteMailbox)
|
||||
r.Route("/{mailboxid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetMailbox)
|
||||
r.Get("/emails", g.GetAllEmailsInMailbox)
|
||||
r.Get("/changes", g.GetMailboxChanges)
|
||||
r.Patch("/", g.UpdateMailbox)
|
||||
r.Delete("/", g.DeleteMailbox)
|
||||
})
|
||||
})
|
||||
r.Route("/emails", func(r chi.Router) {
|
||||
r.Get("/", g.GetEmails) // ?fetchemails=true&fetchbodies=true&text=&subject=&body=&keyword=&keyword=&...
|
||||
r.Post("/", g.CreateEmail)
|
||||
r.Delete("/", g.DeleteEmails)
|
||||
r.Get("/{emailid}", g.GetEmailsById) // Accept:message/rfc822
|
||||
r.Put("/{emailid}", g.ReplaceEmail)
|
||||
r.Patch("/{emailid}", g.UpdateEmail)
|
||||
r.Patch("/{emailid}/keywords", g.UpdateEmailKeywords)
|
||||
r.Post("/{emailid}/keywords", g.AddEmailKeywords)
|
||||
r.Delete("/{emailid}/keywords", g.RemoveEmailKeywords)
|
||||
r.Delete("/{emailid}", g.DeleteEmail)
|
||||
Report(r, "/{emailid}", g.RelatedToEmail)
|
||||
r.Get("/{emailid}/related", g.RelatedToEmail)
|
||||
r.Get("/{emailid}/attachments", g.GetEmailAttachments) // ?partId=&name=?&blobId=?
|
||||
r.Route("/{emailid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetEmailsById) // Accept:message/rfc822
|
||||
r.Put("/", g.ReplaceEmail)
|
||||
r.Post("/", g.SendEmail)
|
||||
r.Patch("/", g.UpdateEmail)
|
||||
r.Delete("/", g.DeleteEmail)
|
||||
Report(r, "/", g.RelatedToEmail)
|
||||
r.Route("/related", func(r chi.Router) {
|
||||
r.Get("/", g.RelatedToEmail)
|
||||
})
|
||||
r.Route("/keywords", func(r chi.Router) {
|
||||
r.Patch("/", g.UpdateEmailKeywords)
|
||||
r.Post("/", g.AddEmailKeywords)
|
||||
r.Delete("/", g.RemoveEmailKeywords)
|
||||
})
|
||||
r.Route("/attachments", func(r chi.Router) {
|
||||
r.Get("/", g.GetEmailAttachments) // ?partId=&name=?&blobId=?
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Route("/blobs", func(r chi.Router) {
|
||||
r.Get("/{blobid}", g.GetBlobMeta)
|
||||
@@ -121,8 +139,10 @@ func (g *Groupware) Route(r chi.Router) {
|
||||
})
|
||||
r.Route("/addressbooks", func(r chi.Router) {
|
||||
r.Get("/", g.GetAddressbooks)
|
||||
r.Get("/{addressbookid}", g.GetAddressbook)
|
||||
r.Get("/{addressbookid}/contacts", g.GetContactsInAddressbook)
|
||||
r.Route("/{addressbookid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetAddressbook)
|
||||
r.Get("/contacts", g.GetContactsInAddressbook)
|
||||
})
|
||||
r.Route("/contacts", func(r chi.Router) {
|
||||
r.Post("/", g.CreateContact)
|
||||
r.Delete("/{contactid}", g.DeleteContact)
|
||||
@@ -130,13 +150,17 @@ func (g *Groupware) Route(r chi.Router) {
|
||||
})
|
||||
r.Route("/calendars", func(r chi.Router) {
|
||||
r.Get("/", g.GetCalendars)
|
||||
r.Get("/{calendarid}", g.GetCalendarById)
|
||||
r.Get("/{calendarid}/events", g.GetEventsInCalendar)
|
||||
r.Route("/{calendarid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetCalendarById)
|
||||
r.Get("/events", g.GetEventsInCalendar)
|
||||
})
|
||||
})
|
||||
r.Route("/tasklists", func(r chi.Router) {
|
||||
r.Get("/", g.GetTaskLists)
|
||||
r.Get("/{tasklistid}", g.GetTaskListById)
|
||||
r.Get("/{tasklistid}/tasks", g.GetTasksInTaskList)
|
||||
r.Route("/{tasklistid}", func(r chi.Router) {
|
||||
r.Get("/", g.GetTaskListById)
|
||||
r.Get("/tasks", g.GetTasksInTaskList)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user