mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:27:31 +00:00
push milestones
This commit is contained in:
@@ -15,7 +15,9 @@ Tracing:
|
|||||||
MetricPrefix: zitadel
|
MetricPrefix: zitadel
|
||||||
|
|
||||||
Telemetry:
|
Telemetry:
|
||||||
# As long as Enabled is true, usage data is marked to be sent to the configured Telemetry.Endpoints
|
# As long as Enabled is true, usage data that is marked as due is also tried to be sent to the configured Telemetry.Endpoints.
|
||||||
|
# Data is marked as due, even if Enabled is false.
|
||||||
|
# This means that switching this to true makes ZITADEL try to send past data that was marked to send.
|
||||||
Enabled: false
|
Enabled: false
|
||||||
# Push telemetry data to all these endpoints at least once.
|
# Push telemetry data to all these endpoints at least once.
|
||||||
# If one endpoint returns an unsuccessful response code or times out,
|
# If one endpoint returns an unsuccessful response code or times out,
|
||||||
|
@@ -3,6 +3,8 @@ package start
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/notification/handlers"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
@@ -65,6 +67,7 @@ type Config struct {
|
|||||||
Eventstore *eventstore.Config
|
Eventstore *eventstore.Config
|
||||||
LogStore *logstore.Configs
|
LogStore *logstore.Configs
|
||||||
Quotas *QuotasConfig
|
Quotas *QuotasConfig
|
||||||
|
Telemetry *handlers.TelemetryPusherConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type QuotasConfig struct {
|
type QuotasConfig struct {
|
||||||
|
@@ -201,14 +201,14 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usageReporter := logstore.UsageReporterFunc(commands.ReportUsage)
|
usageReporter := logstore.UsageReporterFunc(commands.ReportQuotaUsage)
|
||||||
actionsLogstoreSvc := logstore.New(queries, usageReporter, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter)
|
actionsLogstoreSvc := logstore.New(queries, usageReporter, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter)
|
||||||
if actionsLogstoreSvc.Enabled() {
|
if actionsLogstoreSvc.Enabled() {
|
||||||
logging.Warn("execution logs are currently in beta")
|
logging.Warn("execution logs are currently in beta")
|
||||||
}
|
}
|
||||||
actions.SetLogstoreService(actionsLogstoreSvc)
|
actions.SetLogstoreService(actionsLogstoreSvc)
|
||||||
|
|
||||||
notification.Start(ctx, config.Projections.Customizations["notifications"], config.Projections.Customizations["notificationsquotas"], config.ExternalPort, config.ExternalSecure, commands, queries, eventstoreClient, assets.AssetAPIFromDomain(config.ExternalSecure, config.ExternalPort), config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS)
|
notification.Start(ctx, config.Projections.Customizations["notifications"], config.Projections.Customizations["notificationsquotas"], config.Projections.Customizations["telemetry"], *config.Telemetry, config.ExternalPort, config.ExternalSecure, commands, queries, eventstoreClient, assets.AssetAPIFromDomain(config.ExternalSecure, config.ExternalPort), config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS)
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
tlsConfig, err := config.TLS.Config()
|
tlsConfig, err := config.TLS.Config()
|
||||||
|
@@ -63,11 +63,11 @@ For example, systemd has journald and kubernetes has fluentd and fluentbit.
|
|||||||
|
|
||||||
## Telemetry
|
## Telemetry
|
||||||
|
|
||||||
If you want to have some usage data pushed to external systems, enable telemetry in the ZITADEL configuration.
|
If you want to have some data about reached usage milestones pushed to external systems, enable telemetry in the ZITADEL configuration.
|
||||||
Include https://zitadel.com/usage in the list of endpoints if you want to help the ZITADEL maintainers improve the product's usability by giving them some insights.
|
Include https://zitadel.com/usage in the list of endpoints if you want to help the ZITADEL maintainers improve the product's usability by giving them some insights.
|
||||||
Don't forget to opt in by setting Telemetry.Enabled to true.
|
Don't forget to opt in by setting Telemetry.Enabled to true.
|
||||||
|
|
||||||
The following table describes the data points that are sent to the endpoints:
|
The following table describes the milestones that are sent to the endpoints:
|
||||||
|
|
||||||
| Trigger | Description |
|
| Trigger | Description |
|
||||||
|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
@@ -7,8 +7,8 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/repository/quota"
|
"github.com/zitadel/zitadel/internal/repository/quota"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReportUsage calls notification hooks and emits the notified events
|
// ReportQuotaUsage writes a slice of *quota.NotificationDueEvent directly to the eventstore
|
||||||
func (c *Commands) ReportUsage(ctx context.Context, dueNotifications []*quota.NotificationDueEvent) error {
|
func (c *Commands) ReportQuotaUsage(ctx context.Context, dueNotifications []*quota.NotificationDueEvent) error {
|
||||||
cmds := make([]eventstore.Command, len(dueNotifications))
|
cmds := make([]eventstore.Command, len(dueNotifications))
|
||||||
for idx, notification := range dueNotifications {
|
for idx, notification := range dueNotifications {
|
||||||
cmds[idx] = notification
|
cmds[idx] = notification
|
||||||
|
27
internal/command/telemetry.go
Normal file
27
internal/command/telemetry.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/milestone"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReportTelemetryUsage writes one or many *telemetry.PushDueEvent directly to the eventstore
|
||||||
|
func (c *Commands) ReportTelemetryUsage(ctx context.Context, dueEvent ...*milestone.ReachedEvent) error {
|
||||||
|
cmds := make([]eventstore.Command, len(dueEvent))
|
||||||
|
for idx, notification := range dueEvent {
|
||||||
|
cmds[idx] = notification
|
||||||
|
}
|
||||||
|
_, err := c.eventstore.Push(ctx, cmds...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) TelemetryPushed(ctx context.Context, dueEvent *milestone.ReachedEvent, endpoints []string) error {
|
||||||
|
_, err := c.eventstore.Push(
|
||||||
|
ctx,
|
||||||
|
milestone.NewPushedEvent(ctx, dueEvent, endpoints),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
@@ -4,16 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (n *NotificationQueries) IsAlreadyHandled(ctx context.Context, event eventstore.Event, data map[string]interface{}, eventTypes ...eventstore.EventType) (bool, error) {
|
func (n *NotificationQueries) IsAlreadyHandled(ctx context.Context, event eventstore.Event, data map[string]interface{}, aggregateType eventstore.AggregateType, eventTypes ...eventstore.EventType) (bool, error) {
|
||||||
events, err := n.es.Filter(
|
events, err := n.es.Filter(
|
||||||
ctx,
|
ctx,
|
||||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||||
InstanceID(event.Aggregate().InstanceID).
|
InstanceID(event.Aggregate().InstanceID).
|
||||||
AddQuery().
|
AddQuery().
|
||||||
AggregateTypes(user.AggregateType).
|
AggregateTypes(aggregateType).
|
||||||
AggregateIDs(event.Aggregate().ID).
|
AggregateIDs(event.Aggregate().ID).
|
||||||
SequenceGreater(event.Sequence()).
|
SequenceGreater(event.Sequence()).
|
||||||
EventTypes(eventTypes...).
|
EventTypes(eventTypes...).
|
||||||
|
@@ -68,7 +68,7 @@ func (u *quotaNotifier) reduceNotificationDue(event eventstore.Event) (*handler.
|
|||||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DLxdE", "reduce.wrong.event.type %s", quota.NotificationDueEventType)
|
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DLxdE", "reduce.wrong.event.type %s", quota.NotificationDueEventType)
|
||||||
}
|
}
|
||||||
ctx := HandlerContext(event.Aggregate())
|
ctx := HandlerContext(event.Aggregate())
|
||||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, map[string]interface{}{"dueEventID": e.ID}, quota.NotifiedEventType)
|
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, map[string]interface{}{"dueEventID": e.ID}, quota.AggregateType, quota.NotifiedEventType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
113
internal/notification/handlers/telemetrypusher.go
Normal file
113
internal/notification/handlers/telemetrypusher.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/milestone"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore/handler"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
|
||||||
|
"github.com/zitadel/zitadel/internal/notification/channels/webhook"
|
||||||
|
_ "github.com/zitadel/zitadel/internal/notification/statik"
|
||||||
|
"github.com/zitadel/zitadel/internal/notification/types"
|
||||||
|
"github.com/zitadel/zitadel/internal/query/projection"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TelemetryProjectionTable = "projections.telemetry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TelemetryPusherConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
Endpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type telemetryPusher struct {
|
||||||
|
cfg TelemetryPusherConfig
|
||||||
|
crdb.StatementHandler
|
||||||
|
commands *command.Commands
|
||||||
|
queries *NotificationQueries
|
||||||
|
metricSuccessfulDeliveriesJSON string
|
||||||
|
metricFailedDeliveriesJSON string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTelemetryPusher(
|
||||||
|
ctx context.Context,
|
||||||
|
telemetryCfg TelemetryPusherConfig,
|
||||||
|
handlerCfg crdb.StatementHandlerConfig,
|
||||||
|
commands *command.Commands,
|
||||||
|
queries *NotificationQueries,
|
||||||
|
metricSuccessfulDeliveriesJSON,
|
||||||
|
metricFailedDeliveriesJSON string,
|
||||||
|
) *telemetryPusher {
|
||||||
|
p := new(telemetryPusher)
|
||||||
|
handlerCfg.ProjectionName = TelemetryProjectionTable
|
||||||
|
handlerCfg.Reducers = p.reducers()
|
||||||
|
p.cfg = telemetryCfg
|
||||||
|
p.StatementHandler = crdb.NewStatementHandler(ctx, handlerCfg)
|
||||||
|
p.commands = commands
|
||||||
|
p.queries = queries
|
||||||
|
p.metricSuccessfulDeliveriesJSON = metricSuccessfulDeliveriesJSON
|
||||||
|
p.metricFailedDeliveriesJSON = metricFailedDeliveriesJSON
|
||||||
|
projection.TelemetryPusherProjection = p
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *telemetryPusher) reducers() []handler.AggregateReducer {
|
||||||
|
if !t.cfg.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []handler.AggregateReducer{
|
||||||
|
{
|
||||||
|
Aggregate: milestone.AggregateType,
|
||||||
|
EventRedusers: []handler.EventReducer{
|
||||||
|
{
|
||||||
|
Event: milestone.ReachedEventType,
|
||||||
|
Reduce: t.reduceTelemetryPushDue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *telemetryPusher) reduceTelemetryPushDue(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
e, ok := event.(*milestone.ReachedEvent)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-UjA3E", "reduce.wrong.event.type %s", milestone.ReachedEventType)
|
||||||
|
}
|
||||||
|
ctx := HandlerContext(event.Aggregate())
|
||||||
|
alreadyHandled, err := t.queries.IsAlreadyHandled(ctx, event, nil, milestone.AggregateType, milestone.PushedEventType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if alreadyHandled {
|
||||||
|
return crdb.NewNoOpStatement(e), nil
|
||||||
|
}
|
||||||
|
for _, endpoint := range t.cfg.Endpoints {
|
||||||
|
if err = types.SendJSON(
|
||||||
|
ctx,
|
||||||
|
webhook.Config{
|
||||||
|
CallURL: endpoint,
|
||||||
|
Method: http.MethodPost,
|
||||||
|
},
|
||||||
|
t.queries.GetFileSystemProvider,
|
||||||
|
t.queries.GetLogProvider,
|
||||||
|
e,
|
||||||
|
e,
|
||||||
|
t.metricSuccessfulDeliveriesJSON,
|
||||||
|
t.metricFailedDeliveriesJSON,
|
||||||
|
).WithoutTemplate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.commands.TelemetryPushed(ctx, e, t.cfg.Endpoints)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return crdb.NewNoOpStatement(e), nil
|
||||||
|
}
|
@@ -2,8 +2,13 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/project"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
@@ -106,11 +111,67 @@ func (u *userNotifier) reducers() []handler.AggregateReducer {
|
|||||||
Event: user.HumanPasswordChangedType,
|
Event: user.HumanPasswordChangedType,
|
||||||
Reduce: u.reducePasswordChanged,
|
Reduce: u.reducePasswordChanged,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Event: user.UserTokenAddedType,
|
||||||
|
Reduce: u.reduceUserTokenAdded,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Aggregate: instance.AggregateType,
|
||||||
|
EventRedusers: []handler.EventReducer{
|
||||||
|
{
|
||||||
|
Event: instance.InstanceAddedEventType,
|
||||||
|
Reduce: u.reduceInstanceAdded,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Event: instance.InstanceRemovedEventType,
|
||||||
|
Reduce: u.reduceInstanceRemoved,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Aggregate: project.AggregateType,
|
||||||
|
EventRedusers: []handler.EventReducer{
|
||||||
|
{
|
||||||
|
Event: project.ProjectAddedType,
|
||||||
|
Reduce: u.reduceProjectAdded,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Event: project.ApplicationAddedType,
|
||||||
|
Reduce: u.reduceApplicationAdded,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceInstanceAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
fmt.Println("reduceInstanceAdded")
|
||||||
|
return crdb.NewNoOpStatement(event), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceProjectAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
// ignore instance.ProjectSetEventType
|
||||||
|
fmt.Println("reduceProjectAdded")
|
||||||
|
return crdb.NewNoOpStatement(event), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceApplicationAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
fmt.Println("reduceApplicationAdded")
|
||||||
|
return crdb.NewNoOpStatement(event), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceUserTokenAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
fmt.Println("reduceUserTokenAdded")
|
||||||
|
return crdb.NewNoOpStatement(event), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceInstanceRemoved(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
fmt.Println("reduceInstanceRemoved")
|
||||||
|
return crdb.NewNoOpStatement(event), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Statement, error) {
|
func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
e, ok := event.(*user.HumanInitialCodeAddedEvent)
|
e, ok := event.(*user.HumanInitialCodeAddedEvent)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -334,7 +395,7 @@ func (u *userNotifier) reduceDomainClaimed(event eventstore.Event) (*handler.Sta
|
|||||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Drh5w", "reduce.wrong.event.type %s", user.UserDomainClaimedType)
|
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Drh5w", "reduce.wrong.event.type %s", user.UserDomainClaimedType)
|
||||||
}
|
}
|
||||||
ctx := HandlerContext(event.Aggregate())
|
ctx := HandlerContext(event.Aggregate())
|
||||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil,
|
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType,
|
||||||
user.UserDomainClaimedType, user.UserDomainClaimedSentType)
|
user.UserDomainClaimedType, user.UserDomainClaimedSentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -462,7 +523,7 @@ func (u *userNotifier) reducePasswordChanged(event eventstore.Event) (*handler.S
|
|||||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType)
|
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType)
|
||||||
}
|
}
|
||||||
ctx := HandlerContext(event.Aggregate())
|
ctx := HandlerContext(event.Aggregate())
|
||||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.HumanPasswordChangeSentType)
|
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType, user.HumanPasswordChangeSentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -591,5 +652,5 @@ func (u *userNotifier) checkIfCodeAlreadyHandledOrExpired(ctx context.Context, e
|
|||||||
if event.CreationDate().Add(expiry).Before(time.Now().UTC()) {
|
if event.CreationDate().Add(expiry).Before(time.Now().UTC()) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return u.queries.IsAlreadyHandled(ctx, event, data, eventTypes...)
|
return u.queries.IsAlreadyHandled(ctx, event, data, user.AggregateType, eventTypes...)
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,8 @@ func Start(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userHandlerCustomConfig projection.CustomConfig,
|
userHandlerCustomConfig projection.CustomConfig,
|
||||||
quotaHandlerCustomConfig projection.CustomConfig,
|
quotaHandlerCustomConfig projection.CustomConfig,
|
||||||
|
telemetryHandlerCustomConfig projection.CustomConfig,
|
||||||
|
telemetryCfg handlers.TelemetryPusherConfig,
|
||||||
externalPort uint16,
|
externalPort uint16,
|
||||||
externalSecure bool,
|
externalSecure bool,
|
||||||
commands *command.Commands,
|
commands *command.Commands,
|
||||||
@@ -74,4 +76,13 @@ func Start(
|
|||||||
metricSuccessfulDeliveriesJSON,
|
metricSuccessfulDeliveriesJSON,
|
||||||
metricFailedDeliveriesJSON,
|
metricFailedDeliveriesJSON,
|
||||||
).Start()
|
).Start()
|
||||||
|
handlers.NewTelemetryPusher(
|
||||||
|
ctx,
|
||||||
|
telemetryCfg,
|
||||||
|
projection.ApplyCustomConfig(telemetryHandlerCustomConfig),
|
||||||
|
commands,
|
||||||
|
q,
|
||||||
|
metricSuccessfulDeliveriesJSON,
|
||||||
|
metricFailedDeliveriesJSON,
|
||||||
|
).Start()
|
||||||
}
|
}
|
||||||
|
@@ -64,6 +64,7 @@ var (
|
|||||||
NotificationPolicyProjection *notificationPolicyProjection
|
NotificationPolicyProjection *notificationPolicyProjection
|
||||||
NotificationsProjection interface{}
|
NotificationsProjection interface{}
|
||||||
NotificationsQuotaProjection interface{}
|
NotificationsQuotaProjection interface{}
|
||||||
|
TelemetryPusherProjection interface{}
|
||||||
DeviceAuthProjection *deviceAuthProjection
|
DeviceAuthProjection *deviceAuthProjection
|
||||||
SessionProjection *sessionProjection
|
SessionProjection *sessionProjection
|
||||||
)
|
)
|
||||||
|
27
internal/repository/milestone/aggregate.go
Normal file
27
internal/repository/milestone/aggregate.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package milestone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AggregateType = "milestone"
|
||||||
|
AggregateVersion = "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Aggregate struct {
|
||||||
|
eventstore.Aggregate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each data point receives its own aggregate
|
||||||
|
func newAggregate(id, instanceId, resourceOwner string) *Aggregate {
|
||||||
|
return &Aggregate{
|
||||||
|
Aggregate: eventstore.Aggregate{
|
||||||
|
Type: AggregateType,
|
||||||
|
Version: AggregateVersion,
|
||||||
|
ID: id,
|
||||||
|
InstanceID: instanceId,
|
||||||
|
ResourceOwner: resourceOwner,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
102
internal/repository/milestone/events.go
Normal file
102
internal/repository/milestone/events.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package milestone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
eventTypePrefix = eventstore.EventType("milestone.")
|
||||||
|
ReachedEventType = eventTypePrefix + "reached"
|
||||||
|
PushedEventType = eventTypePrefix + "pushed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReachedEvent struct {
|
||||||
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
MilestoneEvent SerializableEvent `json:"milestoneEvent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ReachedEvent) Data() interface{} {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ReachedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReachedEvent(
|
||||||
|
ctx context.Context,
|
||||||
|
newAggregateID string,
|
||||||
|
milestoneEvent eventstore.BaseEvent,
|
||||||
|
) *ReachedEvent {
|
||||||
|
triggeringEventsAggregate := milestoneEvent.Aggregate()
|
||||||
|
return &ReachedEvent{
|
||||||
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
&newAggregate(newAggregateID, triggeringEventsAggregate.InstanceID, triggeringEventsAggregate.ResourceOwner).Aggregate,
|
||||||
|
ReachedEventType,
|
||||||
|
),
|
||||||
|
MilestoneEvent: newSerializableEvent(milestoneEvent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReachedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||||
|
e := &ReachedEvent{
|
||||||
|
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal(event.Data, e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUOTA-k56rT", "unable to unmarshal milestone reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushedEvent struct {
|
||||||
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
ReachedEventSequence uint64 `json:"reachedEventSequence"`
|
||||||
|
Endpoints []string `json:"endpoints"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PushedEvent) Data() interface{} {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PushedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPushedEvent(
|
||||||
|
ctx context.Context,
|
||||||
|
reachedEvent *ReachedEvent,
|
||||||
|
endpoints []string,
|
||||||
|
) *PushedEvent {
|
||||||
|
aggregate := reachedEvent.Aggregate()
|
||||||
|
return &PushedEvent{
|
||||||
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
&aggregate,
|
||||||
|
PushedEventType,
|
||||||
|
),
|
||||||
|
ReachedEventSequence: reachedEvent.Sequence(),
|
||||||
|
Endpoints: endpoints,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PushedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||||
|
e := &PushedEvent{
|
||||||
|
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal(event.Data, e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUOTA-4n8vs", "unable to unmarshal milestone pushed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, nil
|
||||||
|
}
|
10
internal/repository/milestone/eventstore.go
Normal file
10
internal/repository/milestone/eventstore.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package milestone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||||
|
es.RegisterFilterEventMapper(AggregateType, ReachedEventType, ReachedEventMapper).
|
||||||
|
RegisterFilterEventMapper(AggregateType, PushedEventType, PushedEventMapper)
|
||||||
|
}
|
15
internal/repository/milestone/serializable_event.go
Normal file
15
internal/repository/milestone/serializable_event.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package milestone
|
||||||
|
|
||||||
|
import "github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
|
||||||
|
type SerializableEvent struct {
|
||||||
|
eventstore.BaseEvent `json:",inline"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSerializableEvent(triggeringEvent eventstore.BaseEvent) SerializableEvent {
|
||||||
|
return SerializableEvent{
|
||||||
|
BaseEvent: triggeringEvent,
|
||||||
|
Data: triggeringEvent.DataAsBytes(),
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user