tavern/storage/activity.go

87 lines
2.6 KiB
Go

package storage
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"github.com/gofrs/uuid"
"github.com/spaolacci/murmur3"
"github.com/ngerakines/tavern/errors"
)
type ActivityStorage interface {
RecordActivity(ctx context.Context, rowID uuid.UUID, activityID, payload string) error
GetActivitiesByIDs(ctx context.Context, activityIDs []uuid.UUID) ([]Activity, error)
RecordUserActivity(ctx context.Context, rowID, userID, activityID uuid.UUID) error
}
type Activity struct {
ID uuid.UUID
Payload string
CreatedAt time.Time
}
func (s pgStorage) RecordActivity(ctx context.Context, rowID uuid.UUID, activityID, payload string) error {
checksum := ActivityChecksum([]byte(payload))
_, err := s.db.ExecContext(ctx, "INSERT INTO activities (id, activity_id, checksum, payload, created_at) VALUES ($1, $2, $3, $4, $5)", rowID, activityID, checksum, payload, s.now())
return errors.WrapInsertQueryFailedError(err)
}
func (s pgStorage) RecordUserActivity(ctx context.Context, rowID, userID, activityID uuid.UUID) error {
_, err := s.db.ExecContext(ctx, "INSERT INTO user_activities (id, user_id, activity_id, created_at) VALUES ($1, $2, $3, $4)", rowID, userID, activityID, s.now())
return errors.WrapInsertQueryFailedError(err)
}
func (s pgStorage) GetActivitiesByIDs(ctx context.Context, activityIDs []uuid.UUID) ([]Activity, error) {
if len(activityIDs) == 0 {
return nil, nil
}
params := make([]string, len(activityIDs))
args := make([]interface{}, len(activityIDs))
for i, id := range activityIDs {
params[i] = fmt.Sprintf("$%d", i+1)
args[i] = id
}
query := fmt.Sprintf("SELECT id, payload, created_at FROM activities WHERE id IN (%s) ORDER BY created_at ASC", strings.Join(params, ", "))
return s.activitiesQuery(ctx, query, args...)
}
func (s pgStorage) activitiesQuery(ctx context.Context, query string, args ...interface{}) ([]Activity, error) {
var activities []Activity
rows, err := s.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, errors.NewSelectQueryFailedError(err)
}
defer rows.Close()
for rows.Next() {
var activity Activity
if err := rows.Scan(&activity.ID, &activity.Payload, &activity.CreatedAt); err != nil {
return nil, errors.NewSelectQueryFailedError(err)
}
activities = append(activities, activity)
}
return activities, nil
}
func ActivityChecksum(raw []byte) string {
h := murmur3.New64()
h.Write(raw)
return strconv.FormatUint(h.Sum64(), 10)
}
func ValidateActivity(activity Payload) (string, error) {
id, ok := JSONString(activity, "id")
if !ok {
return "", fmt.Errorf("missing activity attribute: id")
}
return id, nil
}