zitadel/internal/query/event.go
Silvan 386addc718
chore: remove bloating span (#7780)
* fix(query): query event editors only once per call

* remove span
2024-04-16 11:19:17 +00:00

135 lines
3.9 KiB
Go

package query
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/call"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
type Event struct {
Editor *EventEditor
Aggregate *eventstore.Aggregate
Sequence uint64
CreationDate time.Time
Type string
Payload []byte
}
type EventEditor struct {
ID string
DisplayName string
Service string
PreferedLoginName string
AvatarKey string
}
type eventsReducer struct {
ctx context.Context
q *Queries
events []*Event
editors map[string]*EventEditor
}
func (r *eventsReducer) AppendEvents(events ...eventstore.Event) {
r.events = append(r.events, r.convertEvents(r.ctx, events)...)
}
func (r *eventsReducer) Reduce() error { return nil }
func (q *Queries) SearchEvents(ctx context.Context, query *eventstore.SearchQueryBuilder) (_ []*Event, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
auditLogRetention := q.defaultAuditLogRetention
if instanceAuditLogRetention := authz.GetInstance(ctx).AuditLogRetention(); instanceAuditLogRetention != nil {
auditLogRetention = *instanceAuditLogRetention
}
if auditLogRetention != 0 {
query = filterAuditLogRetention(ctx, auditLogRetention, query)
}
reducer := &eventsReducer{ctx: ctx, q: q, editors: make(map[string]*EventEditor, query.GetLimit())}
if err = q.eventstore.FilterToReducer(ctx, query, reducer); err != nil {
return nil, err
}
return reducer.events, nil
}
func filterAuditLogRetention(ctx context.Context, auditLogRetention time.Duration, builder *eventstore.SearchQueryBuilder) *eventstore.SearchQueryBuilder {
callTime := call.FromContext(ctx)
if callTime.IsZero() {
callTime = time.Now()
}
oldestAllowed := callTime.Add(-auditLogRetention)
// The audit log retention time should overwrite the creation date after query only if it is older
// For example API calls should still be able to restrict the creation date after to a more recent date
if builder.GetCreationDateAfter().Before(oldestAllowed) {
return builder.CreationDateAfter(oldestAllowed)
}
return builder
}
func (q *Queries) SearchEventTypes(ctx context.Context) []string {
return q.eventstore.EventTypes()
}
func (q *Queries) SearchAggregateTypes(ctx context.Context) []string {
return q.eventstore.AggregateTypes()
}
func (er *eventsReducer) convertEvents(ctx context.Context, events []eventstore.Event) []*Event {
result := make([]*Event, len(events))
for i, event := range events {
result[i] = er.convertEvent(ctx, event)
}
return result
}
func (er *eventsReducer) convertEvent(ctx context.Context, event eventstore.Event) *Event {
editor, ok := er.editors[event.Creator()]
if !ok {
editor = er.q.editorUserByID(ctx, event.Creator())
er.editors[event.Creator()] = editor
}
return &Event{
Editor: &EventEditor{
ID: event.Creator(),
Service: "zitadel",
DisplayName: editor.DisplayName,
PreferedLoginName: editor.PreferedLoginName,
AvatarKey: editor.AvatarKey,
},
Aggregate: event.Aggregate(),
Sequence: event.Sequence(),
CreationDate: event.CreatedAt(),
Type: string(event.Type()),
Payload: event.DataAsBytes(),
}
}
func (q *Queries) editorUserByID(ctx context.Context, userID string) *EventEditor {
user, err := q.GetUserByID(ctx, false, userID)
if err != nil {
return &EventEditor{ID: userID}
}
if user.Human != nil {
return &EventEditor{
ID: user.ID,
DisplayName: user.Human.DisplayName,
PreferedLoginName: user.PreferredLoginName,
AvatarKey: user.Human.AvatarKey,
}
} else if user.Machine != nil {
return &EventEditor{
ID: user.ID,
DisplayName: user.Machine.Name,
PreferedLoginName: user.PreferredLoginName,
}
}
return &EventEditor{ID: userID}
}