public Task CollectEvents(Guid streamId, long startVersion, IEnumerable <object> events, TracingProperties tracingProperties = default) { return(SaveAndPublish(stateType : _typeResolver.ResolveTypeName <T>(), transaction : Guid.NewGuid(), streamId, startVersion, events.ToImmutableArray(), tracingProperties)); }
private async Task SaveAndPublish(string stateType, Guid transaction, Guid streamId, long startVersion, IReadOnlyList <object> events, TracingProperties tracingProperties) { if (events.Count == 0) { return; } await SaveQueueTicket().ConfigureAwait(continueOnCapturedContext: false); await SaveEvents().ConfigureAwait(continueOnCapturedContext: false); await PublishPendingEvents().ConfigureAwait(continueOnCapturedContext: false); Task SaveQueueTicket() { var queueTicket = new QueueTicket(stateType, streamId, startVersion, events.Count, transaction); return(_table.ExecuteAsync(TableOperation.Insert(queueTicket))); } Task SaveEvents() { var batch = new TableBatchOperation(); for (int i = 0; i < events.Count; i++) { object source = events[i]; var streamEvent = new StreamEvent( stateType, streamId, version: startVersion + i, raisedTimeUtc: DateTime.UtcNow, eventType: _typeResolver.ResolveTypeName(source.GetType()), payload: _jsonProcessor.ToJson(source), messageId: $"{Guid.NewGuid()}", tracingProperties.OperationId, tracingProperties.Contributor, tracingProperties.ParentId, transaction); batch.Insert(streamEvent); } return(_table.ExecuteBatchAsync(batch)); } Task PublishPendingEvents() => _publisher.PublishEvents(stateType, streamId); }
public void CanHandle_returns_false_for_non_FlushTableEvents_command_message( string id, object data, TracingProperties tracingProperties, IMessageBus eventBus) { var message = new Message(id, data, tracingProperties); EntityFlushEventsCommandExecutor sut = GenerateSut(eventBus); bool actual = sut.CanHandle(message); actual.Should().BeFalse(); }
public void CanHandle_returns_true_for_FlushEntityFrameworkEvents_command_message( string commandId, FlushEvents command, TracingProperties tracingProperties, IMessageBus eventBus) { var message = new Message(id: commandId, data: command, tracingProperties); EntityFlushEventsCommandExecutor sut = GenerateSut(eventBus); bool actual = sut.CanHandle(message); actual.Should().BeTrue(); }
public void TryConvertToMessage_deserializes_data_correctly( string id, MessageData1 data, TracingProperties tracingProperties, EventConverter sut) { var message = new Message(id, data, tracingProperties); EventData eventData = sut.ConvertToEvent(message); Message actual = sut.TryConvertToMessage(eventData); actual.Data.Should().BeEquivalentTo(data); }
public void ConvertToEvent_serializes_data_correctly( string id, MessageData1 data, TracingProperties tracingProperties, [Frozen] IJsonProcessor jsonProcessor, EventConverter sut) { var message = new Message(id, data, tracingProperties); EventData actual = sut.ConvertToEvent(message); string body = Encoding.UTF8.GetString(actual.Body.Array); MessageData1 content = jsonProcessor.FromJson <MessageData1>(body); content.Should().BeEquivalentTo(data); }
public async Task sut_processes_event_correctly( IEventConverter converter, LoggingMessageHandler spy, string id, MessageData1 data, TracingProperties tracingProperties) { EventProcessor sut = new EventProcessorBuilder(converter, spy).Build(); var message = new Message(id, data, tracingProperties); EventData eventData = converter.ConvertToEvent(message); await sut.Process(new[] { eventData }); spy.Log.Should().ContainSingle(); spy.Log.Single().Should().BeEquivalentTo(message); }
public async Task CollectEvents_sends_messages_correctly( Guid streamId, MessageBusDouble spy, int startVersion, Event1 evt1, Event2 evt2, Event3 evt3, Event4 evt4, TracingProperties tracingProperties) { // Arrange T sut = GenerateEventStore(eventBus: spy); object[] events = new object[] { evt1, evt2, evt3, evt4 }; // Act await sut.CollectEvents(streamId, startVersion, events, tracingProperties); // Assert spy.Calls.Should().ContainSingle(); (ImmutableArray <Message> msgs, string pk) = spy.Calls.Single(); msgs.Should() .HaveCount(events.Length) .And.OnlyContain(x => x.TracingProperties == tracingProperties); VerifyData(msgs[0].Data, startVersion + 0, evt1); VerifyData(msgs[1].Data, startVersion + 1, evt2); VerifyData(msgs[2].Data, startVersion + 2, evt3); VerifyData(msgs[3].Data, startVersion + 3, evt4); void VerifyData <TPayload>(object source, long expectedVersion, TPayload expectedPayload) { source.Should().BeOfType <StreamEvent <TPayload> >(); var data = (StreamEvent <TPayload>)source; data.StreamId.Should().Be(streamId); data.Version.Should().Be(expectedVersion); data.Payload.Should().BeEquivalentTo(expectedPayload); } pk.Should().Be($"{streamId}"); }
public void ConvertToEvent_sets_properties_correctly( string id, MessageData1 data, TracingProperties tracingProperties, [Frozen] TypeResolver typeResolver, EventConverter sut) { var message = new Message(id, data, tracingProperties); EventData actual = sut.ConvertToEvent(message); IDictionary <string, object> properties = actual.Properties; properties.Should().Contain("Id", id); properties.Should().Contain("Type", typeResolver.ResolveTypeName <MessageData1>()); properties.Should().Contain("OperationId", tracingProperties.OperationId); properties.Should().Contain("Contributor", tracingProperties.Contributor); properties.Should().Contain("ParentId", tracingProperties.ParentId); }
private async Task SaveAndPublish(string stateType, Guid transaction, Guid streamId, long startVersion, ImmutableArray <object> events, TracingProperties tracingProperties = default) { await SaveEvents().ConfigureAwait(continueOnCapturedContext: false); await PublishPendingEvents().ConfigureAwait(continueOnCapturedContext: false); async Task SaveEvents() { using (EventStoreContext context = _contextFactory.Invoke()) { for (int i = 0; i < events.Length; i++) { object source = events[i]; var streamEvent = new StreamEvent( stateType, streamId, version: startVersion + i, raisedTimeUtc: DateTime.UtcNow, eventType: _typeResolver.ResolveTypeName(source.GetType()), payload: _jsonProcessor.ToJson(source), messageId: $"{Guid.NewGuid()}", tracingProperties.OperationId, tracingProperties.Contributor, tracingProperties.ParentId, transaction); context.Add(streamEvent); context.Add(new PendingEvent(streamEvent)); } await context.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false); } } Task PublishPendingEvents() => _publisher.PublishEvents(stateType, streamId); }
public async Task sut_sets_tracing_properties_correctly( Guid streamId, long startVersion, Event1[] events, MessageBusDouble commandBus) { // Arrange var eventBus = new MessageBusDouble(errors: 1); EntityEventStore <State1> eventStore = GenerateEventStore <State1>(eventBus); await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events)); var sut = new EntityPendingEventScanner(ContextFactory, commandBus); // Act await sut.ScanPendingEvents(); // Assert TracingProperties actual = commandBus.Calls.Single().messages.Single().TracingProperties; actual.OperationId.Should().NotBeNullOrWhiteSpace(); actual.Contributor.Should().Be("Loom.EventSourcing.EntityFrameworkCore.EntityPendingEventScanner"); actual.ParentId.Should().BeNull(); }
public EventData ConvertToEvent(Message message) { if (message is null) { throw new ArgumentNullException(nameof(message)); } object data = message.Data; TracingProperties tracingProperties = message.TracingProperties; byte[] array = Encoding.UTF8.GetBytes(_jsonProcessor.ToJson(data)); return(new EventData(array) { Properties = { ["Id"] = message.Id, ["Type"] = _typeResolver.ResolveTypeName(data.GetType()), ["OperationId"] = tracingProperties.OperationId, ["Contributor"] = tracingProperties.Contributor, ["ParentId"] = tracingProperties.ParentId, }, }); }
public async Task Handle_publishes_all_pending_events( Guid streamId, long startVersion, Event1[] events, string commandId, TracingProperties tracingProperties, MessageBusDouble eventBus) { // Arrange var brokenEventBus = new MessageBusDouble(errors: 1); EntityEventStore <State1> eventStore = GenerateEventStore <State1>(brokenEventBus); await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events)); EntityFlushEventsCommandExecutor sut = GenerateSut(eventBus); var command = new FlushEvents(TypeResolver.ResolveTypeName <State1>(), streamId); var message = new Message(id: commandId, data: command, tracingProperties); // Act await sut.Handle(message); // Assert eventBus.Calls.Should().BeEquivalentTo(brokenEventBus.Calls); }
private async Task SaveAndPublish(string stateType, Guid transaction, Guid streamId, long startVersion, ImmutableArray <object> events, TracingProperties tracingProperties = default) { await SaveEvents().ConfigureAwait(continueOnCapturedContext: false); await PublishPendingEvents().ConfigureAwait(continueOnCapturedContext: false); async Task SaveEvents() { using EventStoreContext context = _contextFactory.Invoke(); for (int i = 0; i < events.Length; i++) { object source = events[i]; var streamEvent = new StreamEvent( stateType, streamId, version: startVersion + i, raisedTimeUtc: DateTime.UtcNow, eventType: _typeResolver.ResolveTypeName(source.GetType()), payload: _jsonProcessor.ToJson(source), messageId: $"{Guid.NewGuid()}", tracingProperties.OperationId, tracingProperties.Contributor, tracingProperties.ParentId, transaction); context.Add(streamEvent); context.Add(PendingEvent.Create(streamEvent)); } List <UniqueProperty> uniqueProperties = await context .Set <UniqueProperty>() .Where(p => p.StateType == stateType && p.StreamId == streamId) .AsNoTracking() .ToListAsync() .ConfigureAwait(continueOnCapturedContext: false); foreach ((string name, string value) in GetUniqueProperties(events)) { UniqueProperty property = uniqueProperties.Find(p => p.Name == name); if (value == null) { if (property != null) { context.UniqueProperties.Remove(property); } } else { if (property == null) { context.Add(new UniqueProperty(stateType, streamId, name, value)); } else { property.SetValue(value); context.Update(property); } } } await context.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false); } Task PublishPendingEvents() => _publisher.PublishEvents(stateType, streamId); }
public Message(string id, object data, TracingProperties tracingProperties) { Id = id; Data = data; TracingProperties = tracingProperties; }