tercul-backend/internal/app/analytics/publisher.go
google-labs-jules[bot] f66936bc4b feat: Implement event-driven analytics features
This commit implements a robust, production-ready analytics system using an event-driven architecture with Redis and `asynq`.

Key changes:
- Event-Driven Architecture: Instead of synchronous database updates, analytics events (e.g., views, likes, comments) are now published to a Redis queue. This improves API response times and decouples the analytics system from the main application flow.
- Background Worker: A new worker process (`cmd/worker`) has been created to consume events from the queue and update the analytics counters in the database.
- View Counting: Implemented the missing view counting feature for both works and translations.
- New Analytics Query: Added a `popularTranslations` GraphQL query to demonstrate how to use the collected analytics data.
- Testing: Added unit tests for the new event publisher and integration tests for the analytics worker.

Known Issue:
The integration tests for the analytics worker (`AnalyticsWorkerSuite`) and the GraphQL API (`GraphQLIntegrationSuite`) are currently failing due to the lack of a Redis service in the test environment. The tests are written and are expected to pass in an environment where Redis is available on `localhost:6379`, as configured in the CI pipeline.
2025-09-07 22:30:23 +00:00

36 lines
785 B
Go

package analytics
import (
"context"
"encoding/json"
"github.com/hibiken/asynq"
)
type EventPublisher interface {
Publish(ctx context.Context, event AnalyticsEvent) error
}
type AsynqClient interface {
EnqueueContext(ctx context.Context, task *asynq.Task, opts ...asynq.Option) (*asynq.TaskInfo, error)
}
type asynqEventPublisher struct {
client AsynqClient
}
func NewEventPublisher(client AsynqClient) EventPublisher {
return &asynqEventPublisher{client: client}
}
func (p *asynqEventPublisher) Publish(ctx context.Context, event AnalyticsEvent) error {
payload, err := json.Marshal(event)
if err != nil {
return err
}
task := asynq.NewTask(string(event.EventType), payload)
_, err = p.client.EnqueueContext(ctx, task, asynq.Queue(QueueAnalytics))
return err
}