8
8
using Microsoft . Extensions . Logging ;
9
9
10
10
namespace Eventuous . Shovel {
11
- public delegate ValueTask < ShovelMessage > RouteAndTransform ( object message ) ;
12
-
13
- public record ShovelMessage ( string TargetStream , object Message ) ;
14
-
15
11
/// <summary>
16
12
/// Super-simple shovel, which allows to use a subscription to receive events, and then
17
13
/// shovel them as-is, or after a transformation, to a producer. For example, you can
@@ -27,10 +23,14 @@ public class ShovelService<TSubscription, TProducer> : IHostedService
27
23
readonly TSubscription _subscription ;
28
24
readonly TProducer _producer ;
29
25
26
+ public record ShovelMessage ( string TargetStream , object ? Message ) ;
27
+
28
+ public delegate ValueTask < ShovelMessage > RouteAndTransform ( object message ) ;
29
+
30
30
public delegate TSubscription CreateSubscription (
31
31
string subscriptionId ,
32
- IEventSerializer serializer ,
33
32
IEnumerable < IEventHandler > eventHandlers ,
33
+ IEventSerializer ? serializer ,
34
34
ILoggerFactory ? loggerFactory ,
35
35
SubscriptionGapMeasure ? measure
36
36
) ;
@@ -47,21 +47,61 @@ public delegate TSubscription CreateSubscription(
47
47
/// <param name="measure">Subscription gap measurement</param>
48
48
public ShovelService (
49
49
string subscriptionId ,
50
- IEventSerializer eventSerializer ,
51
50
CreateSubscription createSubscription ,
52
51
TProducer producer ,
53
52
RouteAndTransform routeAndTransform ,
54
- ILoggerFactory ? loggerFactory = null ,
55
- SubscriptionGapMeasure ? measure = null
53
+ IEventSerializer ? eventSerializer = null ,
54
+ ILoggerFactory ? loggerFactory = null ,
55
+ SubscriptionGapMeasure ? measure = null
56
+ ) {
57
+ _producer = Ensure . NotNull ( producer , nameof ( producer ) ) ;
58
+
59
+ _subscription = createSubscription (
60
+ Ensure . NotEmptyString ( subscriptionId , nameof ( subscriptionId ) ) ,
61
+ new [ ] {
62
+ new ShovelHandler < TSubscription , TProducer > (
63
+ subscriptionId ,
64
+ producer ,
65
+ Ensure . NotNull ( routeAndTransform , nameof ( routeAndTransform ) )
66
+ )
67
+ } ,
68
+ eventSerializer ,
69
+ loggerFactory ,
70
+ measure
71
+ ) ;
72
+ }
73
+
74
+ /// <summary>
75
+ /// Creates a shovel service instance, which must be registered as a hosted service
76
+ /// </summary>
77
+ /// <param name="subscriptionId">Shovel subscription id</param>
78
+ /// <param name="createSubscription">Function to create a subscription</param>
79
+ /// <param name="targetStream">The stream where events will be produced</param>
80
+ /// <param name="producer">Producer instance</param>
81
+ /// <param name="eventSerializer">Event serializer</param>
82
+ /// <param name="loggerFactory">Logger factory</param>
83
+ /// <param name="measure">Subscription gap measurement</param>
84
+ public ShovelService (
85
+ string subscriptionId ,
86
+ CreateSubscription createSubscription ,
87
+ TProducer producer ,
88
+ string targetStream ,
89
+ IEventSerializer ? eventSerializer = null ,
90
+ ILoggerFactory ? loggerFactory = null ,
91
+ SubscriptionGapMeasure ? measure = null
56
92
) {
57
93
_producer = Ensure . NotNull ( producer , nameof ( producer ) ) ;
58
94
59
95
_subscription = createSubscription (
60
96
Ensure . NotEmptyString ( subscriptionId , nameof ( subscriptionId ) ) ,
61
- Ensure . NotNull ( eventSerializer , nameof ( eventSerializer ) ) ,
62
97
new [ ] {
63
- new ShovelHandler < TProducer > ( subscriptionId , producer , Ensure . NotNull ( routeAndTransform , nameof ( routeAndTransform ) ) )
98
+ new ShovelHandler < TSubscription , TProducer > (
99
+ subscriptionId ,
100
+ producer ,
101
+ new DefaultRoute ( Ensure . NotNull ( targetStream , nameof ( targetStream ) ) ) . Route
102
+ )
64
103
} ,
104
+ eventSerializer ?? DefaultEventSerializer . Instance ,
65
105
loggerFactory ,
66
106
measure
67
107
) ;
@@ -76,18 +116,28 @@ public async Task StopAsync(CancellationToken cancellationToken) {
76
116
await _subscription . StopAsync ( cancellationToken ) . NoContext ( ) ;
77
117
await _producer . Shutdown ( cancellationToken ) . NoContext ( ) ;
78
118
}
119
+
120
+ public class DefaultRoute {
121
+ readonly string _targetStream ;
122
+
123
+ public DefaultRoute ( string targetStream ) => _targetStream = targetStream ;
124
+
125
+ public ValueTask < ShovelMessage > Route ( object message ) => new ( new ShovelMessage ( _targetStream , message ) ) ;
126
+ }
79
127
}
80
128
81
- class ShovelHandler < TProducer > : IEventHandler where TProducer : IEventProducer {
82
- readonly TProducer _eventProducer ;
83
- readonly RouteAndTransform _transform ;
129
+ class ShovelHandler < TSubscription , TProducer > : IEventHandler
130
+ where TProducer : class , IEventProducer
131
+ where TSubscription : SubscriptionService {
132
+ readonly TProducer _eventProducer ;
133
+ readonly ShovelService < TSubscription , TProducer > . RouteAndTransform _transform ;
84
134
85
135
public string SubscriptionId { get ; }
86
136
87
137
public ShovelHandler (
88
- string subscriptionId ,
89
- TProducer eventProducer ,
90
- RouteAndTransform transform
138
+ string subscriptionId ,
139
+ TProducer eventProducer ,
140
+ ShovelService < TSubscription , TProducer > . RouteAndTransform transform
91
141
) {
92
142
_eventProducer = eventProducer ;
93
143
_transform = transform ;
@@ -96,16 +146,8 @@ RouteAndTransform transform
96
146
97
147
public async Task HandleEvent ( object evt , long ? position , CancellationToken cancellationToken ) {
98
148
var ( targetStream , message ) = await _transform ( evt ) . NoContext ( ) ;
149
+ if ( message == null ) return ;
99
150
await _eventProducer . Produce ( targetStream , new [ ] { message } , cancellationToken ) . NoContext ( ) ;
100
151
}
101
152
}
102
-
103
- [ PublicAPI ]
104
- public class DefaultRoute {
105
- readonly string _targetStream ;
106
-
107
- public DefaultRoute ( string targetStream ) => _targetStream = targetStream ;
108
-
109
- public ValueTask < ShovelMessage > Route ( object message ) => new ( new ShovelMessage ( _targetStream , message ) ) ;
110
- }
111
153
}
0 commit comments