Add Event Producer
Update User to carry webhook from circle if assigned Refactor notification handling and update models for webhook support
This commit is contained in:
parent
44cb5501dd
commit
04d1894aea
17 changed files with 351 additions and 101 deletions
164
internal/events/producer.go
Normal file
164
internal/events/producer.go
Normal file
|
@ -0,0 +1,164 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"donetick.com/core/config"
|
||||
chModel "donetick.com/core/internal/chore/model"
|
||||
uModel "donetick.com/core/internal/user/model"
|
||||
"donetick.com/core/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
METHOD_POST = "POST"
|
||||
HEAD_CONTENT_TYPE = "Content-Type"
|
||||
CONTENT_TYPE_JSON = "application/json"
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
||||
const (
|
||||
EventTypeUnknown EventType = ""
|
||||
EventTypeChoreCreated EventType = "CREATED"
|
||||
EventTypeChoreReminder EventType = "REMINDER"
|
||||
EventTypeChoreUpdated EventType = "UPDATED"
|
||||
EventTypeChoreCompleted EventType = "COMPLETED"
|
||||
EventTypeChoreReassigned EventType = "REASSIGNED"
|
||||
EventTypeChoreSkipped EventType = "SKIPPED"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Type EventType `json:"type"`
|
||||
URL string `json:"-"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type ChoreData struct {
|
||||
Chore *chModel.Chore `json:"chore"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
type EventsProducer struct {
|
||||
client *http.Client
|
||||
queue chan Event
|
||||
logger *zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (p *EventsProducer) Start(ctx context.Context) {
|
||||
|
||||
p.logger = logging.FromContext(ctx)
|
||||
|
||||
go func() {
|
||||
for event := range p.queue {
|
||||
p.processEvent(event)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func NewEventsProducer(cfg *config.Config) *EventsProducer {
|
||||
return &EventsProducer{
|
||||
client: &http.Client{
|
||||
Timeout: cfg.WebhookConfig.Timeout,
|
||||
},
|
||||
queue: make(chan Event, cfg.WebhookConfig.QueueSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EventsProducer) publishEvent(event Event) {
|
||||
select {
|
||||
case p.queue <- event:
|
||||
// Successfully added to queue
|
||||
default:
|
||||
log.Println("Webhook queue is full, dropping event")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EventsProducer) processEvent(event Event) {
|
||||
p.logger.Debugw("Sending webhook event", "type", event.Type, "url", event.URL)
|
||||
|
||||
eventJSON, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
p.logger.Errorw("Failed to marshal webhook event", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Pring the event and the url:
|
||||
p.logger.Debug("Sending event to webhook", "url", event.URL, "event", event)
|
||||
p.logger.Debug("Event: ", event)
|
||||
|
||||
req, err := http.NewRequest(METHOD_POST, event.URL, bytes.NewBuffer(eventJSON))
|
||||
if err != nil {
|
||||
p.logger.Errorw("Failed to create webhook request", "error", err)
|
||||
return
|
||||
}
|
||||
req.Header.Set(HEAD_CONTENT_TYPE, CONTENT_TYPE_JSON)
|
||||
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
p.logger.Errorw("Failed to send webhook event", "error", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
p.logger.Errorw("Webhook request failed", "status", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EventsProducer) ChoreCompleted(ctx context.Context, webhookURL *string, chore *chModel.Chore, performer *uModel.User) {
|
||||
if webhookURL == nil {
|
||||
p.logger.Debug("No subscribers for circle, skipping webhook")
|
||||
return
|
||||
}
|
||||
|
||||
event := Event{
|
||||
Type: EventTypeChoreCompleted,
|
||||
URL: *webhookURL,
|
||||
Timestamp: time.Now(),
|
||||
Data: ChoreData{Chore: chore,
|
||||
Username: performer.Username,
|
||||
DisplayName: performer.DisplayName,
|
||||
},
|
||||
}
|
||||
p.publishEvent(event)
|
||||
}
|
||||
|
||||
func (p *EventsProducer) ChoreSkipped(ctx context.Context, webhookURL *string, chore *chModel.Chore, performer *uModel.User) {
|
||||
if webhookURL == nil {
|
||||
p.logger.Debug("No Webhook URL for circle, skipping webhook")
|
||||
return
|
||||
}
|
||||
|
||||
event := Event{
|
||||
Type: EventTypeChoreSkipped,
|
||||
URL: *webhookURL,
|
||||
Timestamp: time.Now(),
|
||||
Data: ChoreData{Chore: chore,
|
||||
Username: performer.Username,
|
||||
DisplayName: performer.DisplayName,
|
||||
},
|
||||
}
|
||||
p.publishEvent(event)
|
||||
}
|
||||
|
||||
func (p *EventsProducer) NotificaitonEvent(ctx context.Context, url string, event interface{}) {
|
||||
// print the event and the url :
|
||||
p.logger.Debug("Sending notification event")
|
||||
|
||||
p.publishEvent(Event{
|
||||
URL: url,
|
||||
Type: EventTypeChoreReminder,
|
||||
Timestamp: time.Now(),
|
||||
Data: event,
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue