snapshot of https://github.com/ThreeDotsLabs/watermill.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
master / ci (push) Failing after 0s
Some checks failed
master / ci (push) Failing after 0s
This commit is contained in:
190
_examples/real-world-examples/server-sent-events-htmx/events.go
Normal file
190
_examples/real-world-examples/server-sent-events-htmx/events.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill-googlecloud/v2/pkg/googlecloud"
|
||||
"github.com/ThreeDotsLabs/watermill-http/v2/pkg/http"
|
||||
"github.com/ThreeDotsLabs/watermill/components/cqrs"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
"github.com/ThreeDotsLabs/watermill/message/router/middleware"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
|
||||
type PostViewed struct {
|
||||
PostID int `json:"post_id"`
|
||||
}
|
||||
|
||||
type PostReactionAdded struct {
|
||||
PostID int `json:"post_id"`
|
||||
ReactionID string `json:"reaction_id"`
|
||||
}
|
||||
|
||||
type PostStatsUpdated struct {
|
||||
PostID int `json:"post_id"`
|
||||
Views int `json:"views"`
|
||||
ViewsUpdated bool `json:"views_updated"`
|
||||
Reactions map[string]int `json:"reactions"`
|
||||
ReactionUpdated *string `json:"reaction_updated"`
|
||||
}
|
||||
|
||||
type Routers struct {
|
||||
EventsRouter *message.Router
|
||||
SSERouter http.SSERouter
|
||||
EventBus *cqrs.EventBus
|
||||
}
|
||||
|
||||
func NewRouters(cfg config, repo *Repository) (Routers, error) {
|
||||
logger := watermill.NewStdLogger(false, false)
|
||||
|
||||
publisher, err := googlecloud.NewPublisher(
|
||||
googlecloud.PublisherConfig{
|
||||
ProjectID: cfg.PubSubProjectID,
|
||||
},
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
eventBus, err := cqrs.NewEventBusWithConfig(
|
||||
publisher,
|
||||
cqrs.EventBusConfig{
|
||||
GeneratePublishTopic: func(params cqrs.GenerateEventPublishTopicParams) (string, error) {
|
||||
return params.EventName, nil
|
||||
},
|
||||
Marshaler: cqrs.JSONMarshaler{},
|
||||
Logger: logger,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
eventsRouter, err := message.NewRouter(message.RouterConfig{}, logger)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
eventsRouter.AddMiddleware(middleware.Recoverer)
|
||||
|
||||
eventProcessor, err := cqrs.NewEventProcessorWithConfig(
|
||||
eventsRouter,
|
||||
cqrs.EventProcessorConfig{
|
||||
GenerateSubscribeTopic: func(params cqrs.EventProcessorGenerateSubscribeTopicParams) (string, error) {
|
||||
return params.EventName, nil
|
||||
},
|
||||
SubscriberConstructor: func(params cqrs.EventProcessorSubscriberConstructorParams) (message.Subscriber, error) {
|
||||
return googlecloud.NewSubscriber(
|
||||
googlecloud.SubscriberConfig{
|
||||
ProjectID: cfg.PubSubProjectID,
|
||||
GenerateSubscriptionName: func(topic string) string {
|
||||
return fmt.Sprintf("%v_%v", topic, params.HandlerName)
|
||||
},
|
||||
},
|
||||
logger,
|
||||
)
|
||||
},
|
||||
Marshaler: cqrs.JSONMarshaler{},
|
||||
Logger: logger,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
err = eventProcessor.AddHandlers(
|
||||
cqrs.NewEventHandler(
|
||||
"UpdateViews",
|
||||
func(ctx context.Context, event *PostViewed) error {
|
||||
var views int
|
||||
var reactions map[string]int
|
||||
err = repo.UpdatePost(ctx, event.PostID, func(post *Post) {
|
||||
post.Views++
|
||||
views = post.Views
|
||||
reactions = post.Reactions
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statsUpdated := PostStatsUpdated{
|
||||
PostID: event.PostID,
|
||||
ViewsUpdated: true,
|
||||
Views: views,
|
||||
Reactions: reactions,
|
||||
}
|
||||
|
||||
return eventBus.Publish(ctx, statsUpdated)
|
||||
},
|
||||
),
|
||||
cqrs.NewEventHandler(
|
||||
"UpdateReactions",
|
||||
func(ctx context.Context, event *PostReactionAdded) error {
|
||||
var views int
|
||||
var reactions map[string]int
|
||||
err := repo.UpdatePost(ctx, event.PostID, func(post *Post) {
|
||||
post.Reactions[event.ReactionID]++
|
||||
views = post.Views
|
||||
reactions = post.Reactions
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statsUpdated := PostStatsUpdated{
|
||||
PostID: event.PostID,
|
||||
Views: views,
|
||||
ReactionUpdated: &event.ReactionID,
|
||||
Reactions: reactions,
|
||||
}
|
||||
|
||||
return eventBus.Publish(ctx, statsUpdated)
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
sseSubscriber, err := googlecloud.NewSubscriber(
|
||||
googlecloud.SubscriberConfig{
|
||||
ProjectID: cfg.PubSubProjectID,
|
||||
GenerateSubscriptionName: func(topic string) string {
|
||||
return fmt.Sprintf("%v_%v", topic, watermill.NewShortUUID())
|
||||
},
|
||||
GenerateSubscription: func(params googlecloud.GenerateSubscriptionParams) *pubsubpb.Subscription {
|
||||
return &pubsubpb.Subscription{
|
||||
ExpirationPolicy: &pubsubpb.ExpirationPolicy{
|
||||
Ttl: durationpb.New(time.Hour * 24),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
sseRouter, err := http.NewSSERouter(
|
||||
http.SSERouterConfig{
|
||||
UpstreamSubscriber: sseSubscriber,
|
||||
Marshaler: http.StringSSEMarshaler{},
|
||||
},
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
return Routers{}, err
|
||||
}
|
||||
|
||||
return Routers{
|
||||
EventsRouter: eventsRouter,
|
||||
SSERouter: sseRouter,
|
||||
EventBus: eventBus,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user