From e5b8d8bd2d629e363af5b069c2628d3df504ca21 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 13 Sep 2023 21:25:05 +0200 Subject: [PATCH] feat(webhooks): register task and project events as webhook --- pkg/models/events.go | 72 ++++++++++++++++++++++++++++++++++++++--- pkg/models/listeners.go | 36 +++++++++++++++++++++ pkg/models/webhooks.go | 22 +++++++++++++ 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/pkg/models/events.go b/pkg/models/events.go index fb75be8143b..467f8cf3ff5 100644 --- a/pkg/models/events.go +++ b/pkg/models/events.go @@ -36,6 +36,10 @@ func (t *TaskCreatedEvent) Name() string { return "task.created" } +func (t *TaskCreatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskUpdatedEvent represents an event where a task has been updated type TaskUpdatedEvent struct { Task *Task @@ -47,6 +51,10 @@ func (t *TaskUpdatedEvent) Name() string { return "task.updated" } +func (t *TaskUpdatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskDeletedEvent represents a TaskDeletedEvent event type TaskDeletedEvent struct { Task *Task @@ -58,6 +66,10 @@ func (t *TaskDeletedEvent) Name() string { return "task.deleted" } +func (t *TaskDeletedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskAssigneeCreatedEvent represents an event where a task has been assigned to a user type TaskAssigneeCreatedEvent struct { Task *Task @@ -70,6 +82,10 @@ func (t *TaskAssigneeCreatedEvent) Name() string { return "task.assignee.created" } +func (t *TaskAssigneeCreatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskAssigneeDeletedEvent represents a TaskAssigneeDeletedEvent event type TaskAssigneeDeletedEvent struct { Task *Task @@ -82,6 +98,10 @@ func (t *TaskAssigneeDeletedEvent) Name() string { return "task.assignee.deleted" } +func (t *TaskAssigneeDeletedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskCommentCreatedEvent represents an event where a task comment has been created type TaskCommentCreatedEvent struct { Task *Task @@ -94,6 +114,10 @@ func (t *TaskCommentCreatedEvent) Name() string { return "task.comment.created" } +func (t *TaskCommentCreatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskCommentUpdatedEvent represents a TaskCommentUpdatedEvent event type TaskCommentUpdatedEvent struct { Task *Task @@ -106,6 +130,10 @@ func (t *TaskCommentUpdatedEvent) Name() string { return "task.comment.edited" } +func (t *TaskCommentUpdatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskCommentDeletedEvent represents a TaskCommentDeletedEvent event type TaskCommentDeletedEvent struct { Task *Task @@ -118,6 +146,10 @@ func (t *TaskCommentDeletedEvent) Name() string { return "task.comment.deleted" } +func (t *TaskCommentDeletedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskAttachmentCreatedEvent represents a TaskAttachmentCreatedEvent event type TaskAttachmentCreatedEvent struct { Task *Task @@ -130,6 +162,10 @@ func (t *TaskAttachmentCreatedEvent) Name() string { return "task.attachment.created" } +func (t *TaskAttachmentCreatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskAttachmentDeletedEvent represents a TaskAttachmentDeletedEvent event type TaskAttachmentDeletedEvent struct { Task *Task @@ -142,6 +178,10 @@ func (t *TaskAttachmentDeletedEvent) Name() string { return "task.attachment.deleted" } +func (t *TaskAttachmentDeletedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskRelationCreatedEvent represents a TaskRelationCreatedEvent event type TaskRelationCreatedEvent struct { Task *Task @@ -154,6 +194,10 @@ func (t *TaskRelationCreatedEvent) Name() string { return "task.relation.created" } +func (t *TaskRelationCreatedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + // TaskRelationDeletedEvent represents a TaskRelationDeletedEvent event type TaskRelationDeletedEvent struct { Task *Task @@ -166,6 +210,10 @@ func (t *TaskRelationDeletedEvent) Name() string { return "task.relation.deleted" } +func (t *TaskRelationDeletedEvent) ProjectID() int64 { + return t.Task.ProjectID +} + //////////////////// // Project Events // //////////////////// @@ -188,10 +236,14 @@ type ProjectUpdatedEvent struct { } // Name defines the name for ProjectUpdatedEvent -func (l *ProjectUpdatedEvent) Name() string { +func (p *ProjectUpdatedEvent) Name() string { return "project.updated" } +func (p *ProjectUpdatedEvent) ProjectID() int64 { + return p.Project.ID +} + // ProjectDeletedEvent represents an event where a project has been deleted type ProjectDeletedEvent struct { Project *Project @@ -199,10 +251,14 @@ type ProjectDeletedEvent struct { } // Name defines the name for ProjectDeletedEvent -func (t *ProjectDeletedEvent) Name() string { +func (p *ProjectDeletedEvent) Name() string { return "project.deleted" } +func (p *ProjectDeletedEvent) ProjectID() int64 { + return p.Project.ID +} + //////////////////// // Sharing Events // //////////////////// @@ -215,10 +271,14 @@ type ProjectSharedWithUserEvent struct { } // Name defines the name for ProjectSharedWithUserEvent -func (l *ProjectSharedWithUserEvent) Name() string { +func (p *ProjectSharedWithUserEvent) Name() string { return "project.shared.user" } +func (p *ProjectSharedWithUserEvent) ProjectID() int64 { + return p.Project.ID +} + // ProjectSharedWithTeamEvent represents an event where a project has been shared with a team type ProjectSharedWithTeamEvent struct { Project *Project @@ -227,10 +287,14 @@ type ProjectSharedWithTeamEvent struct { } // Name defines the name for ProjectSharedWithTeamEvent -func (l *ProjectSharedWithTeamEvent) Name() string { +func (p *ProjectSharedWithTeamEvent) Name() string { return "project.shared.team" } +func (p *ProjectSharedWithTeamEvent) ProjectID() int64 { + return p.Project.ID +} + ///////////////// // Team Events // ///////////////// diff --git a/pkg/models/listeners.go b/pkg/models/listeners.go index fcc23179ee1..f0d41f703f1 100644 --- a/pkg/models/listeners.go +++ b/pkg/models/listeners.go @@ -65,6 +65,22 @@ func RegisterListeners() { events.RegisterListener((&TaskDeletedEvent{}).Name(), &RemoveTaskFromTypesense{}) events.RegisterListener((&TaskCreatedEvent{}).Name(), &AddTaskToTypesense{}) } + RegisterEventForWebhook(&TaskCreatedEvent{}) + RegisterEventForWebhook(&TaskUpdatedEvent{}) + RegisterEventForWebhook(&TaskDeletedEvent{}) + RegisterEventForWebhook(&TaskAssigneeCreatedEvent{}) + RegisterEventForWebhook(&TaskAssigneeDeletedEvent{}) + RegisterEventForWebhook(&TaskCommentCreatedEvent{}) + RegisterEventForWebhook(&TaskCommentUpdatedEvent{}) + RegisterEventForWebhook(&TaskCommentDeletedEvent{}) + RegisterEventForWebhook(&TaskAttachmentCreatedEvent{}) + RegisterEventForWebhook(&TaskAttachmentDeletedEvent{}) + RegisterEventForWebhook(&TaskRelationCreatedEvent{}) + RegisterEventForWebhook(&TaskRelationDeletedEvent{}) + RegisterEventForWebhook(&ProjectUpdatedEvent{}) + RegisterEventForWebhook(&ProjectDeletedEvent{}) + RegisterEventForWebhook(&ProjectSharedWithUserEvent{}) + RegisterEventForWebhook(&ProjectSharedWithTeamEvent{}) } ////// @@ -609,6 +625,26 @@ func (s *SendProjectCreatedNotification) Handle(msg *message.Message) (err error return nil } +// WebhookListener represents a listener +type WebhookListener struct { +} + +// Name defines the name for the WebhookListener listener +func (s *WebhookListener) Name() string { + return "webhook.listener" +} + +// Handle is executed when the event WebhookListener listens on is fired +func (s *WebhookListener) Handle(msg *message.Message) (err error) { + event := &ProjectUpdatedEvent{} + err = json.Unmarshal(msg.Payload, event) + if err != nil { + return err + } + + return nil +} + /////// // Team Events diff --git a/pkg/models/webhooks.go b/pkg/models/webhooks.go index 561b7a3611e..38db349a51c 100644 --- a/pkg/models/webhooks.go +++ b/pkg/models/webhooks.go @@ -17,8 +17,10 @@ package models import ( + "code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/user" "code.vikunja.io/web" + "sync" "time" "xorm.io/xorm" ) @@ -46,6 +48,26 @@ func (w *Webhook) TableName() string { return "webhooks" } +type WebhookEvent interface { + events.Event + ProjectID() int64 +} + +var availableWebhookEvents map[string]bool +var availableWebhookEventsLock *sync.Mutex + +func init() { + availableWebhookEvents = make(map[string]bool) + availableWebhookEventsLock = &sync.Mutex{} +} + +func RegisterEventForWebhook(event WebhookEvent) { + availableWebhookEventsLock.Lock() + defer availableWebhookEventsLock.Unlock() + + availableWebhookEvents[event.Name()] = true +} + func (w *Webhook) Create(s *xorm.Session, a web.Auth) (err error) { // TODO: check valid webhook events w.CreatedByID = a.GetID()