191 lines
4.6 KiB
Go
191 lines
4.6 KiB
Go
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
|
|
}
|