mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-24 02:56:52 -05:00
groupware: add mock endpoints for tasklists and tasks
This commit is contained in:
@@ -799,7 +799,7 @@ func (j *Client) QueryEmailSummaries(accountIds []string, session *Session, ctx
|
||||
invocations[i*2+0] = invocation(CommandEmailQuery, EmailQueryCommand{
|
||||
AccountId: accountId,
|
||||
Filter: filter,
|
||||
Sort: []EmailComparator{EmailComparator{Property: emailSortByReceivedAt, IsAscending: false}},
|
||||
Sort: []EmailComparator{{Property: emailSortByReceivedAt, IsAscending: false}},
|
||||
Limit: limit,
|
||||
//CalculateTotal: false,
|
||||
}, mcid(accountId, "0"))
|
||||
|
||||
@@ -114,7 +114,20 @@ type ResourceType string
|
||||
// The Scope data type is used to represent the entities the quota applies to.
|
||||
type Scope string
|
||||
|
||||
// Determines which action must be performed by the MUA or MTA upon receiption.
|
||||
//
|
||||
// !- `manual-action`: the disposition described by the disposition type was a result of an
|
||||
// explicit instruction by the user rather than some sort of automatically performed action.
|
||||
// (This might include the case when the user has manually configured her MUA to automatically
|
||||
// respond to valid MDN requests.) Unless prescribed otherwise in a particular mail environment,
|
||||
// in order to preserve the user's privacy, this MUST be the default for MUAs.
|
||||
// !- `automatic-action`: the disposition described by the disposition type was a result of an
|
||||
// automatic action rather than an explicit instruction by the user for this message. This
|
||||
// is typically generated by a Mail Delivery Agent (e.g., MDN generations by Sieve reject action
|
||||
// [RFC5429], Fax-over-Email [RFC3249], voice message system (see Voice Profile for Internet
|
||||
// Mail (VPIM) [RFC3801]), or upon delivery to a mailing list).
|
||||
type ActionMode string
|
||||
|
||||
type SendingMode string
|
||||
type DispositionTypeOption string
|
||||
|
||||
@@ -184,11 +197,16 @@ const (
|
||||
CalendarEventNotificationTypeOptionUpdated = CalendarEventNotificationTypeOption("updated")
|
||||
CalendarEventNotificationTypeOptionDestroyed = CalendarEventNotificationTypeOption("destroyed")
|
||||
|
||||
// This represents a single person.
|
||||
PrincipalTypeOptionIndividual = PrincipalTypeOption("individual")
|
||||
PrincipalTypeOptionGroup = PrincipalTypeOption("group")
|
||||
PrincipalTypeOptionResource = PrincipalTypeOption("resource")
|
||||
PrincipalTypeOptionLocation = PrincipalTypeOption("location")
|
||||
PrincipalTypeOptionOther = PrincipalTypeOption("other")
|
||||
// This represents a group of people.
|
||||
PrincipalTypeOptionGroup = PrincipalTypeOption("group")
|
||||
// This represents some resource, e.g. a projector.
|
||||
PrincipalTypeOptionResource = PrincipalTypeOption("resource")
|
||||
// This represents a location.
|
||||
PrincipalTypeOptionLocation = PrincipalTypeOption("location")
|
||||
// This represents some other undefined principal.
|
||||
PrincipalTypeOptionOther = PrincipalTypeOption("other")
|
||||
|
||||
HttpDigestAlgorithmAdler32 = HttpDigestAlgorithm("adler32")
|
||||
HttpDigestAlgorithmCrc32c = HttpDigestAlgorithm("crc32c")
|
||||
@@ -216,20 +234,64 @@ const (
|
||||
// The quota information applies to all accounts belonging to the server.
|
||||
ScopeGlobal = Scope("global")
|
||||
|
||||
ActionModeManualAction = ActionMode("manual-action")
|
||||
// The disposition described by the disposition type was a result of an explicit instruction by the
|
||||
// user rather than some sort of automatically performed action.
|
||||
//
|
||||
// (This might include the case when the user has manually configured her MUA to automatically
|
||||
// respond to valid MDN requests.)
|
||||
//
|
||||
// Unless prescribed otherwise in a particular mail environment, in order to preserve the user's
|
||||
// privacy, this MUST be the default for MUAs.
|
||||
ActionModeManualAction = ActionMode("manual-action")
|
||||
|
||||
// The disposition described by the disposition type was a result of an automatic action rather than
|
||||
// an explicit instruction by the user for this message.
|
||||
//
|
||||
// This is typically generated by a Mail Delivery Agent (e.g., MDN generations by Sieve reject
|
||||
// action [RFC5429], Fax-over-Email [RFC3249], voice message system (see Voice Profile
|
||||
// for Internet Mail (VPIM) [RFC3801]), or upon delivery to a mailing list).
|
||||
ActionModeAutomaticAction = ActionMode("automatic-action")
|
||||
|
||||
SendingModeMdnSentManually = SendingMode("mdn-sent-manually")
|
||||
// The user explicitly gave permission for this particular MDN to be sent.
|
||||
//
|
||||
// Unless prescribed otherwise in a particular mail environment, in order to preserve the
|
||||
// user's privacy, this MUST be the default for MUAs.
|
||||
SendingModeMdnSentManually = SendingMode("mdn-sent-manually")
|
||||
|
||||
// The MDN was sent because the MUA had previously been configured to do so automatically.
|
||||
SendingModeMdnSentAutomatically = SendingMode("mdn-sent-automatically")
|
||||
|
||||
DispositionTypeOptionDeleted = DispositionTypeOption("deleted")
|
||||
DispositionTypeOptionDispatched = DispositionTypeOption("dispatched")
|
||||
DispositionTypeOptionDisplayed = DispositionTypeOption("displayed")
|
||||
DispositionTypeOptionProcessed = DispositionTypeOption("processed")
|
||||
// The message has been deleted.
|
||||
//
|
||||
// The recipient may or may not have seen the message.
|
||||
//
|
||||
// The recipient might "undelete" the message at a later time and read the message.
|
||||
DispositionTypeOptionDeleted = DispositionTypeOption("deleted")
|
||||
|
||||
IncludeInAvailabilityAll = IncludeInAvailability("all")
|
||||
// The message has been sent somewhere in some manner (e.g., printed, faxed, forwarded) without
|
||||
// necessarily having been previously displayed to the user.
|
||||
//
|
||||
// The user may or may not see the message later.
|
||||
DispositionTypeOptionDispatched = DispositionTypeOption("dispatched")
|
||||
|
||||
// The message has been displayed by the MUA to someone reading the recipient's mailbox.
|
||||
//
|
||||
// There is no guarantee that the content has been read or understood.
|
||||
DispositionTypeOptionDisplayed = DispositionTypeOption("displayed")
|
||||
|
||||
// The message has been processed in some manner (i.e., by some sort of rules or server)
|
||||
// without being displayed to the user.
|
||||
//
|
||||
// The user may or may not see the message later, or there may not even be a human user
|
||||
// associated with the mailbox.
|
||||
DispositionTypeOptionProcessed = DispositionTypeOption("processed")
|
||||
|
||||
// All events are considered.
|
||||
IncludeInAvailabilityAll = IncludeInAvailability("all")
|
||||
// Events the user is a confirmed or tentative participant of are considered.
|
||||
IncludeInAvailabilityAttending = IncludeInAvailability("attending")
|
||||
IncludeInAvailabilityNone = IncludeInAvailability("none")
|
||||
// All events are ignored (but may be considered if also in another calendar).
|
||||
IncludeInAvailabilityNone = IncludeInAvailability("none")
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -540,16 +602,16 @@ type SessionMDNAccountCapabilities struct {
|
||||
}
|
||||
|
||||
type SessionAccountCapabilities struct {
|
||||
Mail SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail"`
|
||||
Submission SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission"`
|
||||
VacationResponse SessionVacationResponseAccountCapabilities `json:"urn:ietf:params:jmap:vacationresponse"`
|
||||
Sieve SessionSieveAccountCapabilities `json:"urn:ietf:params:jmap:sieve"`
|
||||
Blob SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob"`
|
||||
Quota SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota"`
|
||||
Contacts SessionContactsAccountCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
@@ -558,7 +620,8 @@ type Account struct {
|
||||
// This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them.
|
||||
IsPersonal bool `json:"isPersonal"`
|
||||
// This is true if the entire account is read-only.
|
||||
IsReadOnly bool `json:"isReadOnly"`
|
||||
IsReadOnly bool `json:"isReadOnly"`
|
||||
// Account capabilities.
|
||||
AccountCapabilities SessionAccountCapabilities `json:"accountCapabilities"`
|
||||
}
|
||||
|
||||
@@ -4119,10 +4182,45 @@ type Principal struct {
|
||||
Accounts map[string]Account `json:"accounts,omitempty"`
|
||||
}
|
||||
|
||||
// TODO https://jmap.io/spec-sharing.html#object-properties
|
||||
type ShareNotification struct {
|
||||
type ShareChangePerson struct {
|
||||
// The name of the person who made the change.
|
||||
Name string `json:"name"`
|
||||
// The email of the person who made the change, or null if no email is available.
|
||||
Email string `json:"email,omitempty"`
|
||||
// The id of the Principal corresponding to the person who made the change, or null if no associated principal.
|
||||
PrincipalId string `json:"principalId,omitempty"`
|
||||
}
|
||||
|
||||
type ShareNotification struct {
|
||||
// The id of the `ShareNotification`.
|
||||
Id string `json:"id"`
|
||||
|
||||
// The time this notification was created.
|
||||
Created UTCDate `json:"created,omitzero"`
|
||||
|
||||
// Who made the change.
|
||||
ChangedBy ShareChangePerson `json:"changedBy"`
|
||||
|
||||
// The name of the data type for the object whose permissions have changed, e.g. `Calendar` or `Mailbox`.
|
||||
ObjectType ObjectType `json:"objectType"`
|
||||
|
||||
// The id of the account where this object exists.
|
||||
ObjectAccountId string `json:"objectAccountId"`
|
||||
|
||||
// The id of the object that this notification is about.
|
||||
ObjectId string `json:"objectId"`
|
||||
|
||||
// The name of the object at the time the notification was made.
|
||||
Name string `json:"name"`
|
||||
|
||||
// The `myRights` property of the object for the user before the change.
|
||||
OldRights map[string]bool `json:"oldRights,omitempty"`
|
||||
|
||||
// The `myRights` property of the object for the user after the change.
|
||||
NewRights map[string]bool `json:"newRights,omitempty"`
|
||||
}
|
||||
|
||||
// TODO unused
|
||||
type Shareable struct {
|
||||
// Has the user indicated they wish to see this data?
|
||||
//
|
||||
|
||||
@@ -9,3 +9,5 @@ params:
|
||||
description: The identifier of the AddressBook to perform this operation on
|
||||
calendarid:
|
||||
description: The identifier of the Calendar to perform this operation on
|
||||
tasklistid:
|
||||
description: The identifier of the TaskList to perform this operation on
|
||||
|
||||
@@ -29,6 +29,12 @@ tags:
|
||||
- name: event
|
||||
x-displayName: Events
|
||||
description: APIs about calendar events
|
||||
- name: tasklist
|
||||
x-displayName: TaskLists
|
||||
description: APIs about task lists
|
||||
- name: task
|
||||
x-displayName: Tasks
|
||||
description: APIs about tasks
|
||||
- name: vacation
|
||||
x-displayName: Vacation Responses
|
||||
description: APIs about vacation responses
|
||||
@@ -53,6 +59,10 @@ x-tagGroups:
|
||||
tags:
|
||||
- calendar
|
||||
- event
|
||||
- name: Tasks
|
||||
tags:
|
||||
- tasklist
|
||||
- task
|
||||
components:
|
||||
securitySchemes:
|
||||
api:
|
||||
|
||||
@@ -39,7 +39,7 @@ type SwaggerGetCalendarById200 struct {
|
||||
}
|
||||
|
||||
// swagger:route GET /groupware/accounts/{account}/calendars/{calendarid} calendar calendar_by_id
|
||||
// Get all calendars of an account.
|
||||
// Get a calendar of an account by its identifier.
|
||||
//
|
||||
// responses:
|
||||
//
|
||||
@@ -68,7 +68,7 @@ type SwaggerGetEventsInCalendar200 struct {
|
||||
}
|
||||
|
||||
// swagger:route GET /groupware/accounts/{account}/calendars/{calendarid}/events event events_in_addressbook
|
||||
// Get all the events in a calendarof an account by its identifier.
|
||||
// Get all the events in a calendar of an account by its identifier.
|
||||
//
|
||||
// responses:
|
||||
//
|
||||
|
||||
89
services/groupware/pkg/groupware/groupware_api_tasklists.go
Normal file
89
services/groupware/pkg/groupware/groupware_api_tasklists.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package groupware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/opencloud-eu/opencloud/pkg/jmap"
|
||||
)
|
||||
|
||||
// When the request succeeds.
|
||||
// swagger:response GetTaskLists200
|
||||
type SwaggerGetTaskLists200 struct {
|
||||
// in: body
|
||||
Body []jmap.TaskList
|
||||
}
|
||||
|
||||
// swagger:route GET /groupware/accounts/{account}/tasklists tasklist tasklists
|
||||
// Get all tasklists of an account.
|
||||
//
|
||||
// responses:
|
||||
//
|
||||
// 200: GetTaskLists200
|
||||
// 400: ErrorResponse400
|
||||
// 404: ErrorResponse404
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTaskLists(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
return response(AllTaskLists, req.session.State)
|
||||
})
|
||||
}
|
||||
|
||||
// When the request succeeds.
|
||||
// swagger:response GetTaskListById200
|
||||
type SwaggerGetTaskListById200 struct {
|
||||
// in: body
|
||||
Body struct {
|
||||
*jmap.TaskList
|
||||
}
|
||||
}
|
||||
|
||||
// swagger:route GET /groupware/accounts/{account}/tasklists/{tasklistid} tasklist tasklist_by_id
|
||||
// Get a tasklist by its identifier.
|
||||
//
|
||||
// responses:
|
||||
//
|
||||
// 200: GetTaskListById200
|
||||
// 400: ErrorResponse400
|
||||
// 404: ErrorResponse404
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTaskListById(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
tasklistId := chi.URLParam(r, UriParamTaskListId)
|
||||
// TODO replace with proper implementation
|
||||
for _, tasklist := range AllTaskLists {
|
||||
if tasklist.Id == tasklistId {
|
||||
return response(tasklist, req.session.State)
|
||||
}
|
||||
}
|
||||
return notFoundResponse(req.session.State)
|
||||
})
|
||||
}
|
||||
|
||||
// When the request succeeds.
|
||||
// swagger:response GetTasksInTaskList200
|
||||
type SwaggerGetTasksInTaskList200 struct {
|
||||
// in: body
|
||||
Body []jmap.Task
|
||||
}
|
||||
|
||||
// swagger:route GET /groupware/accounts/{account}/tasklists/{tasklistid}/tasks task tasks_in_tasklist
|
||||
// Get all the tasks in a tasklist of an account by its identifier.
|
||||
//
|
||||
// responses:
|
||||
//
|
||||
// 200: GetTasksInTaskList200
|
||||
// 400: ErrorResponse400
|
||||
// 404: ErrorResponse404
|
||||
// 500: ErrorResponse500
|
||||
func (g *Groupware) GetTasksInTaskList(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
tasklistId := chi.URLParam(r, UriParamTaskListId)
|
||||
// TODO replace with proper implementation
|
||||
tasks, ok := TaskMapByTaskListId[tasklistId]
|
||||
if !ok {
|
||||
return notFoundResponse(req.session.State)
|
||||
}
|
||||
return response(tasks, req.session.State)
|
||||
})
|
||||
}
|
||||
@@ -497,23 +497,6 @@ func (g *Groupware) serveError(w http.ResponseWriter, r *http.Request, error *Er
|
||||
render.Render(w, r, errorResponses(*error))
|
||||
}
|
||||
|
||||
func (g *Groupware) getSession(user user) (*jmap.Session, *GroupwareError) {
|
||||
session, ok, gwerr := g.session(user, g.logger)
|
||||
if gwerr != nil {
|
||||
g.metrics.SessionFailureCounter.Inc()
|
||||
g.logger.Error().Str("code", gwerr.Code).Str("error", gwerr.Title).Str("detail", gwerr.Detail).Msg("failed to determine JMAP session")
|
||||
return nil, gwerr
|
||||
}
|
||||
if !ok {
|
||||
// no session = authentication failed
|
||||
gwerr = &ErrorInvalidAuthentication
|
||||
g.metrics.SessionFailureCounter.Inc()
|
||||
g.logger.Error().Msg("could not authenticate, failed to find Session")
|
||||
return nil, gwerr
|
||||
}
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
func (g *Groupware) withSession(w http.ResponseWriter, r *http.Request, handler func(r Request) Response) (Response, bool) {
|
||||
ctx := r.Context()
|
||||
sl := g.logger.SubloggerWithRequestID(ctx)
|
||||
|
||||
207
services/groupware/pkg/groupware/groupware_mock_tasks.go
Normal file
207
services/groupware/pkg/groupware/groupware_mock_tasks.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package groupware
|
||||
|
||||
import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/jmap"
|
||||
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
|
||||
)
|
||||
|
||||
var TL1 = jmap.TaskList{
|
||||
Id: "aemua9ai",
|
||||
Role: jmap.TaskListRoleInbox,
|
||||
Name: "Your Tasks",
|
||||
Description: "Your default list of tasks",
|
||||
Color: "purple",
|
||||
KeywordColors: map[string]string{
|
||||
"todo": "blue",
|
||||
"done": "green",
|
||||
},
|
||||
CategoryColors: map[string]string{
|
||||
"work": "magenta",
|
||||
},
|
||||
SortOrder: 1,
|
||||
IsSubscribed: true,
|
||||
TimeZone: "CEST",
|
||||
WorkflowStatuses: []string{
|
||||
"new", "todo", "in-progress", "done",
|
||||
},
|
||||
ShareWith: map[string]jmap.TaskRights{
|
||||
"eefeeb4p": {
|
||||
MayReadItems: true,
|
||||
MayWriteAll: false,
|
||||
MayWriteOwn: true,
|
||||
MayUpdatePrivate: false,
|
||||
MayRSVP: false,
|
||||
MayAdmin: false,
|
||||
MayDelete: false,
|
||||
},
|
||||
},
|
||||
MyRights: &jmap.TaskRights{
|
||||
MayReadItems: true,
|
||||
MayWriteAll: true,
|
||||
MayWriteOwn: true,
|
||||
MayUpdatePrivate: true,
|
||||
MayRSVP: true,
|
||||
MayAdmin: false,
|
||||
MayDelete: false,
|
||||
},
|
||||
DefaultAlertsWithTime: map[string]jscalendar.Alert{
|
||||
"saenee7a": {
|
||||
Type: jscalendar.AlertType,
|
||||
Trigger: jscalendar.OffsetTrigger{
|
||||
Type: jscalendar.OffsetTriggerType,
|
||||
Offset: "-PT10M",
|
||||
RelativeTo: jscalendar.RelativeToStart,
|
||||
},
|
||||
Action: jscalendar.AlertActionEmail,
|
||||
},
|
||||
},
|
||||
DefaultAlertsWithoutTime: map[string]jscalendar.Alert{
|
||||
"xiipaew9": {
|
||||
Type: jscalendar.AlertType,
|
||||
Trigger: jscalendar.OffsetTrigger{
|
||||
Type: jscalendar.OffsetTriggerType,
|
||||
Offset: "-PT12H",
|
||||
RelativeTo: jscalendar.RelativeToStart,
|
||||
},
|
||||
Action: jscalendar.AlertActionDisplay,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var T1 = jmap.Task{
|
||||
Id: "laoj0ahk",
|
||||
TaskListId: TL1.Id,
|
||||
IsDraft: false,
|
||||
UtcStart: jmap.UTCDate{Time: mustParseTime("2025-10-02T10:00:00Z")},
|
||||
UtcDue: jmap.UTCDate{Time: mustParseTime("2025-10-12T18:00:00Z")},
|
||||
SortOrder: 1,
|
||||
WorkflowStatus: "new",
|
||||
Task: jscalendar.Task{
|
||||
Type: jscalendar.TaskType,
|
||||
Object: jscalendar.Object{
|
||||
CommonObject: jscalendar.CommonObject{
|
||||
Uid: "7da0d4a2-385c-430f-9022-61db302734d9",
|
||||
ProdId: "Mock 0.0",
|
||||
Created: mustParseTime("2025-10-01T17:31:49Z"),
|
||||
Updated: mustParseTime("2025-10-01T17:35:12Z"),
|
||||
Title: "Crossing the Ring",
|
||||
Description: "We need to cross the Ring the protomolecule opened.",
|
||||
DescriptionContentType: "text/plain",
|
||||
Links: map[string]jscalendar.Link{
|
||||
"theisha5": {
|
||||
Type: jscalendar.LinkType,
|
||||
Href: "https://static.wikia.nocookie.net/expanse/images/e/ed/S03E09-SlowZone_01.jpg/revision/latest/scale-to-width-down/1000?cb=20180611184722",
|
||||
ContentType: "image/jpeg",
|
||||
Size: 109212,
|
||||
Rel: jscalendar.RelIcon,
|
||||
Display: "sol gate",
|
||||
Title: "The Sol Ring Gate",
|
||||
},
|
||||
},
|
||||
Locale: "en-GB",
|
||||
Keywords: map[string]bool{
|
||||
"todo": true,
|
||||
},
|
||||
Categories: map[string]bool{
|
||||
"work": true,
|
||||
},
|
||||
Color: "yellow",
|
||||
},
|
||||
Sequence: 1,
|
||||
ShowWithoutTime: false,
|
||||
Locations: map[string]jscalendar.Location{
|
||||
"ruoth5uu": {
|
||||
Type: jscalendar.LocationType,
|
||||
Name: "Sol Gate",
|
||||
Description: "We meet at the Sol gate",
|
||||
LocationTypes: map[jscalendar.LocationTypeOption]bool{
|
||||
jscalendar.LocationTypeOptionLandmarkAddress: true,
|
||||
},
|
||||
RelativeTo: jscalendar.LocationRelationStart,
|
||||
TimeZone: "UTC",
|
||||
Coordinates: "geo:40.4165583,-3.7063595",
|
||||
Links: map[string]jscalendar.Link{
|
||||
"jeeshei5": {
|
||||
Type: jscalendar.LinkType,
|
||||
Href: "https://expanse.fandom.com/wiki/Sol_gate",
|
||||
ContentType: "text/html",
|
||||
Title: "The Sol Gate",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Priority: 1,
|
||||
FreeBusyStatus: jscalendar.FreeBusyStatusBusy,
|
||||
Privacy: jscalendar.PrivacySecret,
|
||||
Alerts: map[string]jscalendar.Alert{
|
||||
"eiphuw4a": {
|
||||
Type: jscalendar.AlertType,
|
||||
Trigger: jscalendar.AbsoluteTrigger{
|
||||
Type: jscalendar.AbsoluteTriggerType,
|
||||
When: mustParseTime("2025-12-01T10:11:12Z"),
|
||||
},
|
||||
Action: jscalendar.AlertActionDisplay,
|
||||
},
|
||||
},
|
||||
TimeZone: "UTC",
|
||||
MayInviteSelf: true,
|
||||
MayInviteOthers: true,
|
||||
HideAttendees: true,
|
||||
},
|
||||
Due: jscalendar.LocalDateTime{Time: mustParseTime("2025-12-01T10:11:12Z")},
|
||||
Start: jscalendar.LocalDateTime{Time: mustParseTime("2025-10-01T08:00:00Z")},
|
||||
EstimatedDuration: "PT8W",
|
||||
PercentComplete: 5,
|
||||
Progress: jscalendar.ProgressNeedsAction,
|
||||
ProgressUpdated: mustParseTime("2025-10-01T08:12:39Z"),
|
||||
},
|
||||
EstimatedWork: 4,
|
||||
Impact: "block",
|
||||
IsOrigin: true,
|
||||
MayInviteSelf: true,
|
||||
MayInviteOthers: true,
|
||||
HideAttendees: false,
|
||||
Checklists: map[string]jmap.Checklist{
|
||||
"sae9aimu": {
|
||||
Type: jmap.ChecklistType,
|
||||
Title: "Prerequisites",
|
||||
CheckItems: []jmap.CheckItem{
|
||||
{
|
||||
Type: jmap.CheckItemType,
|
||||
Title: "Control Medina Station",
|
||||
SortOrder: 1,
|
||||
IsComplete: true,
|
||||
Updated: jmap.UTCDate{Time: mustParseTime("2025-04-01T09:32:10Z")},
|
||||
Assignee: &jmap.TaskPerson{
|
||||
Type: jmap.TaskPersonType,
|
||||
Name: "Fred Johnson",
|
||||
Uri: "mailto:johnson@opa.org",
|
||||
PrincipalId: "nae5hu9t",
|
||||
},
|
||||
Comments: map[string]jmap.Comment{
|
||||
"ooze1iet": {
|
||||
Type: jmap.CommentType,
|
||||
Message: "We first need to control Medina Station before we can get through the Sol Gate",
|
||||
Created: jmap.UTCDate{Time: mustParseTime("2025-04-01T12:11:10Z")},
|
||||
Updated: jmap.UTCDate{Time: mustParseTime("2025-04-01T12:29:19Z")},
|
||||
Author: &jmap.TaskPerson{
|
||||
Type: jmap.TaskPersonType,
|
||||
Name: "Anderson Dawes",
|
||||
Uri: "mailto:adawes@opa.org",
|
||||
PrincipalId: "eshi9oot",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var AllTaskLists = []jmap.TaskList{TL1}
|
||||
|
||||
var TaskMapByTaskListId = map[string][]jmap.Task{
|
||||
TL1.Id: {
|
||||
T1,
|
||||
},
|
||||
}
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
UriParamRole = "role"
|
||||
UriParamAddressBookId = "addressbookid"
|
||||
UriParamCalendarId = "calendarid"
|
||||
UriParamTaskListId = "tasklistid"
|
||||
QueryParamMailboxSearchName = "name"
|
||||
QueryParamMailboxSearchRole = "role"
|
||||
QueryParamMailboxSearchSubscribed = "subscribed"
|
||||
@@ -101,6 +102,11 @@ func (g *Groupware) Route(r chi.Router) {
|
||||
r.Get("/{calendarid}", g.GetCalendarById)
|
||||
r.Get("/{calendarid}/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.HandleFunc("/events/{stream}", g.ServeSSE)
|
||||
|
||||
Reference in New Issue
Block a user