Esempio n. 1
0
 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();
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        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}");
        }
Esempio n. 9
0
        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();
        }
Esempio n. 12
0
        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);
        }
Esempio n. 14
0
        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);
        }
Esempio n. 15
0
 public Message(string id, object data, TracingProperties tracingProperties)
 {
     Id   = id;
     Data = data;
     TracingProperties = tracingProperties;
 }