github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/cqrs/event_processor.go (about)

     1  package cqrs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/pkg/errors"
     7  	"go.uber.org/multierr"
     8  
     9  	"github.com/wfusion/gofusion/common/infra/watermill"
    10  	"github.com/wfusion/gofusion/common/infra/watermill/message"
    11  )
    12  
    13  type EventProcessorConfig struct {
    14  	// GenerateSubscribeTopic is used to generate topic for subscribing to events.
    15  	// If event processor is using handler groups, GenerateSubscribeTopic is used instead.
    16  	GenerateSubscribeTopic EventProcessorGenerateSubscribeTopicFn
    17  
    18  	// SubscriberConstructor is used to create subscriber for EventHandler.
    19  	//
    20  	// This function is called for every EventHandler instance.
    21  	// If you want to re-use one subscriber for multiple handlers, use GroupEventProcessor instead.
    22  	SubscriberConstructor EventProcessorSubscriberConstructorFn
    23  
    24  	// OnHandle is called before handling event.
    25  	// OnHandle works in a similar way to middlewares:
    26  	// you can inject additional logic before and after handling a event.
    27  	//
    28  	// Because of that, you need to explicitly call params.Handler.Handle() to handle the event.
    29  	//
    30  	//  func(params EventProcessorOnHandleParams) (err error) {
    31  	//      // logic before handle
    32  	//      //  (...)
    33  	//
    34  	//      err := params.Handler.Handle(params.Message.Context(), params.Event)
    35  	//
    36  	//      // logic after handle
    37  	//      //  (...)
    38  	//
    39  	//      return err
    40  	//  }
    41  	//
    42  	// This option is not required.
    43  	OnHandle EventProcessorOnHandleFn
    44  
    45  	// AckOnUnknownEvent is used to decide if message should be acked if event has no handler defined.
    46  	AckOnUnknownEvent bool
    47  
    48  	// Marshaler is used to marshal and unmarshal events.
    49  	// It is required.
    50  	Marshaler CommandEventMarshaler
    51  
    52  	// Logger instance used to log.
    53  	// If not provided, watermill.NopLogger is used.
    54  	Logger watermill.LoggerAdapter
    55  
    56  	// disableRouterAutoAddHandlers is used to keep backwards compatibility.
    57  	// it is set when EventProcessor is created by NewEventProcessor.
    58  	// Deprecated: please migrate to NewEventProcessorWithConfig.
    59  	disableRouterAutoAddHandlers bool
    60  }
    61  
    62  func (c *EventProcessorConfig) setDefaults() {
    63  	if c.Logger == nil {
    64  		c.Logger = watermill.NopLogger{}
    65  	}
    66  }
    67  
    68  func (c EventProcessorConfig) Validate() error {
    69  	var err error
    70  
    71  	if c.Marshaler == nil {
    72  		err = multierr.Append(err, errors.New("missing Marshaler"))
    73  	}
    74  
    75  	if c.GenerateSubscribeTopic == nil {
    76  		err = multierr.Append(err, errors.New("missing GenerateHandlerTopic"))
    77  	}
    78  	if c.SubscriberConstructor == nil {
    79  		err = multierr.Append(err, errors.New("missing SubscriberConstructor"))
    80  	}
    81  
    82  	return err
    83  }
    84  
    85  type EventProcessorGenerateSubscribeTopicFn func(EventProcessorGenerateSubscribeTopicParams) (string, error)
    86  
    87  type EventProcessorGenerateSubscribeTopicParams struct {
    88  	EventName    string
    89  	EventHandler EventHandler
    90  }
    91  
    92  type EventProcessorSubscriberConstructorFn func(EventProcessorSubscriberConstructorParams) (message.Subscriber, error)
    93  
    94  type EventProcessorSubscriberConstructorParams struct {
    95  	HandlerName  string
    96  	EventHandler EventHandler
    97  }
    98  
    99  type EventProcessorOnHandleFn func(params EventProcessorOnHandleParams) error
   100  
   101  type EventProcessorOnHandleParams struct {
   102  	Handler EventHandler
   103  
   104  	Event     any
   105  	EventName string
   106  
   107  	// Message is never nil and can be modified.
   108  	Message *message.Message
   109  }
   110  
   111  // EventProcessor determines which EventHandler should handle event received from event bus.
   112  type EventProcessor struct {
   113  	router   *message.Router
   114  	handlers []EventHandler
   115  	config   EventProcessorConfig
   116  }
   117  
   118  // NewEventProcessorWithConfig creates a new EventProcessor.
   119  func NewEventProcessorWithConfig(router *message.Router, config EventProcessorConfig) (*EventProcessor, error) {
   120  	config.setDefaults()
   121  
   122  	if err := config.Validate(); err != nil {
   123  		return nil, errors.Wrap(err, "invalid config EventProcessor")
   124  	}
   125  	if router == nil && !config.disableRouterAutoAddHandlers {
   126  		return nil, errors.New("missing router")
   127  	}
   128  
   129  	return &EventProcessor{
   130  		router: router,
   131  		config: config,
   132  	}, nil
   133  }
   134  
   135  // NewEventProcessor creates a new EventProcessor.
   136  // Deprecated. Use NewEventProcessorWithConfig instead.
   137  func NewEventProcessor(
   138  	individualHandlers []EventHandler,
   139  	generateTopic func(eventName string) string,
   140  	subscriberConstructor EventsSubscriberConstructor,
   141  	marshaler CommandEventMarshaler,
   142  	logger watermill.LoggerAdapter,
   143  ) (*EventProcessor, error) {
   144  	if len(individualHandlers) == 0 {
   145  		return nil, errors.New("missing handlers")
   146  	}
   147  	if generateTopic == nil {
   148  		return nil, errors.New("nil generateTopic")
   149  	}
   150  	if subscriberConstructor == nil {
   151  		return nil, errors.New("missing subscriberConstructor")
   152  	}
   153  	if marshaler == nil {
   154  		return nil, errors.New("missing marshaler")
   155  	}
   156  	if logger == nil {
   157  		logger = watermill.NopLogger{}
   158  	}
   159  
   160  	eventProcessorConfig := EventProcessorConfig{
   161  		AckOnUnknownEvent: true, // this is the previous default behaviour - keeping backwards compatibility
   162  		GenerateSubscribeTopic: func(params EventProcessorGenerateSubscribeTopicParams) (string, error) {
   163  			return generateTopic(params.EventName), nil
   164  		},
   165  		SubscriberConstructor: func(params EventProcessorSubscriberConstructorParams) (message.Subscriber, error) {
   166  			return subscriberConstructor(params.HandlerName)
   167  		},
   168  		Marshaler:                    marshaler,
   169  		Logger:                       logger,
   170  		disableRouterAutoAddHandlers: true,
   171  	}
   172  	eventProcessorConfig.setDefaults()
   173  
   174  	ep, err := NewEventProcessorWithConfig(nil, eventProcessorConfig)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	for _, handler := range individualHandlers {
   180  		if err := ep.AddHandlers(handler); err != nil {
   181  			return nil, err
   182  		}
   183  	}
   184  
   185  	return ep, nil
   186  }
   187  
   188  // EventsSubscriberConstructor creates a subscriber for EventHandler.
   189  // It allows you to create separated customized Subscriber for every command handler.
   190  //
   191  // When handler groups are used, handler group is passed as handlerName.
   192  // Deprecated: please use EventProcessorSubscriberConstructorFn instead.
   193  type EventsSubscriberConstructor func(handlerName string) (message.Subscriber, error)
   194  
   195  // AddHandlers adds a new EventHandler to the EventProcessor and adds it to the router.
   196  func (p *EventProcessor) AddHandlers(handlers ...EventHandler) error {
   197  	if p.config.disableRouterAutoAddHandlers {
   198  		p.handlers = append(p.handlers, handlers...)
   199  		return nil
   200  	}
   201  
   202  	for _, handler := range handlers {
   203  		if err := p.addHandlerToRouter(p.router, handler); err != nil {
   204  			return err
   205  		}
   206  
   207  		p.handlers = append(p.handlers, handler)
   208  	}
   209  
   210  	return nil
   211  }
   212  
   213  // AddHandlersToRouter adds the EventProcessor's handlers to the given router.
   214  // It should be called only once per EventProcessor instance.
   215  //
   216  // It is required to call AddHandlersToRouter only if command processor
   217  // is created with NewEventProcessor (disableRouterAutoAddHandlers is set to true).
   218  // Deprecated: please migrate to event processor created by NewEventProcessorWithConfig.
   219  func (p EventProcessor) AddHandlersToRouter(r *message.Router) error {
   220  	if !p.config.disableRouterAutoAddHandlers {
   221  		return errors.New("AddHandlersToRouter should be called only when using deprecated NewEventProcessor")
   222  	}
   223  
   224  	for i := range p.handlers {
   225  		handler := p.handlers[i]
   226  
   227  		if err := p.addHandlerToRouter(r, handler); err != nil {
   228  			return err
   229  		}
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  func (p EventProcessor) addHandlerToRouter(r *message.Router, handler EventHandler) error {
   236  	if err := validateEvent(handler.NewEvent()); err != nil {
   237  		return errors.Wrapf(err, "invalid event for handler %s", handler.HandlerName())
   238  	}
   239  
   240  	handlerName := handler.HandlerName()
   241  	eventName := p.config.Marshaler.Name(handler.NewEvent())
   242  
   243  	topicName, err := p.config.GenerateSubscribeTopic(EventProcessorGenerateSubscribeTopicParams{
   244  		EventName:    eventName,
   245  		EventHandler: handler,
   246  	})
   247  	if err != nil {
   248  		return errors.Wrapf(err, "cannot generate topic name for handler %s", handlerName)
   249  	}
   250  
   251  	logger := p.config.Logger.With(watermill.LogFields{
   252  		"event_handler_name": handlerName,
   253  		"topic":              topicName,
   254  	})
   255  
   256  	handlerFunc, err := p.routerHandlerFunc(handler, logger)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	if p.config.SubscriberConstructor == nil {
   262  		return errors.New("missing SubscriberConstructor config option")
   263  	}
   264  
   265  	subscriber, err := p.config.SubscriberConstructor(EventProcessorSubscriberConstructorParams{
   266  		HandlerName:  handlerName,
   267  		EventHandler: handler,
   268  	})
   269  	if err != nil {
   270  		return errors.Wrap(err, "cannot create subscriber for event processor")
   271  	}
   272  
   273  	if err := addHandlerToRouter(p.config.Logger, r, handlerName, topicName, handlerFunc, subscriber); err != nil {
   274  		return err
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  func (p EventProcessor) Handlers() []EventHandler {
   281  	return p.handlers
   282  }
   283  
   284  func addHandlerToRouter(logger watermill.LoggerAdapter, r *message.Router, handlerName string, topicName string,
   285  	handlerFunc message.NoPublishHandlerFunc, subscriber message.Subscriber) error {
   286  	logger = logger.With(watermill.LogFields{
   287  		"event_handler_name": handlerName,
   288  		"topic":              topicName,
   289  	})
   290  
   291  	logger.Debug("Adding CQRS event handler to router", nil)
   292  
   293  	r.AddNoPublisherHandler(
   294  		handlerName,
   295  		topicName,
   296  		subscriber,
   297  		handlerFunc,
   298  	)
   299  
   300  	return nil
   301  }
   302  
   303  func (p EventProcessor) routerHandlerFunc(handler EventHandler,
   304  	logger watermill.LoggerAdapter) (message.NoPublishHandlerFunc, error) {
   305  	initEvent := handler.NewEvent()
   306  	expectedEventName := p.config.Marshaler.Name(initEvent)
   307  
   308  	if err := validateEvent(initEvent); err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	return func(msg *message.Message) error {
   313  		event := handler.NewEvent()
   314  		messageEventName := p.config.Marshaler.NameFromMessage(msg)
   315  
   316  		if messageEventName != expectedEventName {
   317  			if !p.config.AckOnUnknownEvent {
   318  				return fmt.Errorf("received unexpected event type %s, expected %s", messageEventName, expectedEventName)
   319  			} else {
   320  				logger.Trace("Received different event type than expected, ignoring", watermill.LogFields{
   321  					"message_uuid":        msg.UUID,
   322  					"expected_event_type": expectedEventName,
   323  					"received_event_type": messageEventName,
   324  				})
   325  				return nil
   326  			}
   327  		}
   328  
   329  		logger.Debug("Handling event", watermill.LogFields{
   330  			"message_uuid":        msg.UUID,
   331  			"received_event_type": messageEventName,
   332  		})
   333  
   334  		ctx := CtxWithOriginalMessage(msg.Context(), msg)
   335  		msg.SetContext(ctx)
   336  
   337  		if err := p.config.Marshaler.Unmarshal(msg, event); err != nil {
   338  			return err
   339  		}
   340  
   341  		handle := func(params EventProcessorOnHandleParams) error {
   342  			return params.Handler.Handle(ctx, params.Event)
   343  		}
   344  		if p.config.OnHandle != nil {
   345  			handle = p.config.OnHandle
   346  		}
   347  
   348  		err := handle(EventProcessorOnHandleParams{
   349  			Handler:   handler,
   350  			Event:     event,
   351  			EventName: messageEventName,
   352  			Message:   msg,
   353  		})
   354  		if err != nil {
   355  			logger.Debug("Error when handling event", watermill.LogFields{"err": err})
   356  			return err
   357  		}
   358  
   359  		return nil
   360  	}, nil
   361  }
   362  
   363  func validateEvent(event any) error {
   364  	// EventHandler's NewEvent must return a pointer, because it is used to unmarshal
   365  	if err := isPointer(event); err != nil {
   366  		return errors.Wrap(err, "command must be a non-nil pointer")
   367  	}
   368  
   369  	return nil
   370  }