2022-10-13 15:32:25 -06:00
|
|
|
// package projecter provides a driver middleware to derive new events from other events.
|
2022-09-06 10:35:14 -06:00
|
|
|
package projecter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"strings"
|
|
|
|
|
2023-07-12 17:35:02 -06:00
|
|
|
"go.sour.is/pkg/lg"
|
|
|
|
|
2023-02-26 22:33:01 -07:00
|
|
|
"go.sour.is/ev"
|
2023-07-12 17:35:02 -06:00
|
|
|
"go.sour.is/ev/pkg/driver"
|
|
|
|
"go.sour.is/ev/pkg/event"
|
2022-09-06 10:35:14 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type projector struct {
|
2022-09-08 10:33:02 -06:00
|
|
|
up driver.Driver
|
|
|
|
fns []func(event.Event) []event.Event
|
2022-09-06 10:35:14 -06:00
|
|
|
}
|
|
|
|
|
2022-12-19 10:50:38 -07:00
|
|
|
func New(_ context.Context, fns ...func(event.Event) []event.Event) *projector {
|
2022-09-08 10:33:02 -06:00
|
|
|
return &projector{fns: fns}
|
2022-09-06 10:35:14 -06:00
|
|
|
}
|
2023-01-09 11:30:02 -07:00
|
|
|
func (p *projector) Apply(e *ev.EventStore) {
|
2022-12-19 10:50:38 -07:00
|
|
|
|
|
|
|
up := e.Driver
|
|
|
|
for up != nil {
|
|
|
|
if op, ok := up.(*projector); ok {
|
|
|
|
op.AddProjections(p.fns...)
|
|
|
|
p.up = op.up
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-09 11:30:02 -07:00
|
|
|
up = ev.Unwrap(up)
|
2022-12-19 10:50:38 -07:00
|
|
|
}
|
|
|
|
|
2022-09-06 10:35:14 -06:00
|
|
|
p.up = e.Driver
|
|
|
|
e.Driver = p
|
|
|
|
}
|
|
|
|
func (s *projector) Unwrap() driver.Driver {
|
|
|
|
return s.up
|
|
|
|
}
|
|
|
|
func (s *projector) Open(ctx context.Context, dsn string) (driver.Driver, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
return s.up.Open(ctx, dsn)
|
|
|
|
}
|
|
|
|
func (s *projector) EventLog(ctx context.Context, streamID string) (driver.EventLog, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
l, err := s.up.EventLog(ctx, streamID)
|
|
|
|
return &wrapper{l, s}, err
|
|
|
|
}
|
2022-12-19 10:50:38 -07:00
|
|
|
func (s *projector) AddProjections(fns ...func(event.Event) []event.Event) {
|
|
|
|
s.fns = append(s.fns, fns...)
|
|
|
|
}
|
2022-09-06 10:35:14 -06:00
|
|
|
|
|
|
|
type wrapper struct {
|
|
|
|
up driver.EventLog
|
|
|
|
projector *projector
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ driver.EventLog = (*wrapper)(nil)
|
|
|
|
|
2022-11-20 10:15:51 -07:00
|
|
|
func (r *wrapper) Unwrap() driver.EventLog {
|
|
|
|
return r.up
|
|
|
|
}
|
2022-10-25 20:15:57 -06:00
|
|
|
func (w *wrapper) Read(ctx context.Context, after int64, count int64) (event.Events, error) {
|
2022-09-06 10:35:14 -06:00
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
2022-10-25 20:15:57 -06:00
|
|
|
return w.up.Read(ctx, after, count)
|
2022-09-06 10:35:14 -06:00
|
|
|
}
|
2022-10-30 09:18:08 -06:00
|
|
|
func (w *wrapper) ReadN(ctx context.Context, index ...uint64) (event.Events, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
return w.up.ReadN(ctx, index...)
|
|
|
|
}
|
2022-09-06 10:35:14 -06:00
|
|
|
func (w *wrapper) Append(ctx context.Context, events event.Events, version uint64) (uint64, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
i, err := w.up.Append(ctx, events, version)
|
|
|
|
if err != nil {
|
|
|
|
return i, err
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ctx, span := lg.Fork(ctx)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
var pevents []event.Event
|
|
|
|
|
|
|
|
for i := range events {
|
|
|
|
e := events[i]
|
2022-09-08 10:33:02 -06:00
|
|
|
|
|
|
|
for _, fn := range w.projector.fns {
|
|
|
|
pevents = append(
|
|
|
|
pevents,
|
|
|
|
fn(e)...,
|
|
|
|
)
|
|
|
|
}
|
2022-09-06 10:35:14 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range pevents {
|
|
|
|
e := pevents[i]
|
|
|
|
l, err := w.projector.up.EventLog(ctx, event.StreamID(e).StreamID())
|
|
|
|
if err != nil {
|
|
|
|
span.RecordError(err)
|
|
|
|
continue
|
|
|
|
}
|
2023-01-09 11:30:02 -07:00
|
|
|
_, err = l.Append(ctx, event.NewEvents(e), ev.AppendOnly)
|
2022-09-06 10:35:14 -06:00
|
|
|
span.RecordError(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
return i, err
|
|
|
|
}
|
|
|
|
func (w *wrapper) FirstIndex(ctx context.Context) (uint64, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
return w.up.FirstIndex(ctx)
|
|
|
|
}
|
|
|
|
func (w *wrapper) LastIndex(ctx context.Context) (uint64, error) {
|
|
|
|
ctx, span := lg.Span(ctx)
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
return w.up.LastIndex(ctx)
|
|
|
|
}
|
|
|
|
|
2022-09-08 10:33:02 -06:00
|
|
|
func DefaultProjection(e event.Event) []event.Event {
|
|
|
|
m := e.EventMeta()
|
|
|
|
streamID := m.StreamID
|
|
|
|
streamPos := m.Position
|
2022-10-30 09:18:08 -06:00
|
|
|
eventType := event.TypeOf(e)
|
|
|
|
pkg, _, _ := strings.Cut(eventType, ".")
|
2022-09-08 10:33:02 -06:00
|
|
|
|
|
|
|
e1 := event.NewPtr(streamID, streamPos)
|
|
|
|
event.SetStreamID("$all", e1)
|
|
|
|
|
|
|
|
e2 := event.NewPtr(streamID, streamPos)
|
|
|
|
event.SetStreamID("$type-"+eventType, e2)
|
|
|
|
|
|
|
|
e3 := event.NewPtr(streamID, streamPos)
|
|
|
|
event.SetStreamID("$pkg-"+pkg, e3)
|
|
|
|
|
|
|
|
return []event.Event{e1, e2, e3}
|
|
|
|
}
|