コード例 #1
0
        private static string ValidateContext(MessagePublishingContext context)
        {
            var errors = new List <string>();

            if (context.Metadata == null)
            {
                errors.Add(nameof(context.Metadata));
            }
            if (context.Payload == null)
            {
                errors.Add(nameof(context.Payload));
            }
            if (context.Key == null)
            {
                errors.Add(nameof(context.Key));
            }
            if (context.Api == null)
            {
                errors.Add(nameof(context.Api));
            }
            if (context.QueueNames == null)
            {
                errors.Add(nameof(context.QueueNames));
            }
            if (context.Broker == null)
            {
                errors.Add(nameof(context.Broker));
            }

            return(string.Join(", ", errors));
        }
コード例 #2
0
        public void when_publishing_cancelled_it_should_report_about_it()
        {
            var producer = new TestProducer <int, byte[]>(new ProducerConfig(), new Dictionary <string, object>());
            var sut      = new DefaultApiProducer <int, byte[]>(
                producer,
                Mock.Of <IHeaderValueCodec>(),
                Mock.Of <ILogger <DefaultApiProducer <int, byte[]> > >());

            var cancelledToken = new CancellationToken(canceled: true);
            var context        = new MessagePublishingContext
            {
                Key      = 555,
                Payload  = Encoding.UTF8.GetBytes("payload"),
                Api      = Mock.Of <IEgressApi>(),
                Metadata = new Dictionary <string, string> {
                    { "Type", "Message1" }
                },
                QueueNames = new[] { "commands1", "audit2" },
                Broker     = Mock.Of <IMessageBroker>()
            };

            var publish = sut.Publish(context, cancelledToken);

            publish.IsCanceled.Should().BeTrue("publishing was cancelled");
        }
        public void when_executing_without_causation_id_in_context_it_should_fail()
        {
            var step    = new ValidateMessagePublishingContextStep();
            var context = new MessagePublishingContext
            {
                Api           = CreateEgressApi(),
                Message       = new Message1(),
                CorrelationId = ExpectedCorrelationId,
                CausationId   = ExpectedCausationId,
                MessageId     = ExpectedMessageId
            };

            Func <Task> sut = () => step.Execute(context);

            context.CausationId = null;
            sut.Should().Throw <ArgumentException>().Where(
                exception => exception.ParamName.Equals("context") && exception.Message.Contains("Causation ID should"),
                "null is not a valid causation ID");
            context.CausationId = string.Empty;
            sut.Should().Throw <ArgumentException>().Where(
                exception => exception.ParamName.Equals("context") && exception.Message.Contains("Causation ID should"),
                "an empty string is not a valid causation ID");
            context.CausationId = WhitespaceString;
            sut.Should().Throw <ArgumentException>().Where(
                exception => exception.ParamName.Equals("context") && exception.Message.Contains("Causation ID should"),
                "a whitespace string is not a valid causation ID");
        }
コード例 #4
0
        /// <inheritdoc />
        public Task Publish(MessagePublishingContext context, CancellationToken cancellationToken)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var errors = ValidateContext(context);

            if (!string.IsNullOrWhiteSpace(errors))
            {
                throw new PoezdOperationException($"Message publishing context has missing data: {errors}.");
            }

            var headers   = MakeHeaders(context.Metadata);
            var timestamp = new Timestamp(context.Timestamp);
            var brokerId  = context.Broker.Id;
            // TODO: Handle type conversion errors.
            var key = (TKey)context.Key;
            // TODO: Handle type conversion errors.
            var payload = (TValue)context.Payload;
            var message = new Message <TKey, TValue> {
                Key = key, Value = payload, Headers = headers, Timestamp = timestamp
            };
            var publishTasks = context.QueueNames.Select(
                async queueName =>
            {
                try
                {
                    _logger.LogDebug(
                        "Publishing a message with key {Key} to topic {Topic} of broker with ID {BrokerId}.",
                        key,
                        queueName,
                        brokerId);
                    await _producer.ProduceAsync(
                        queueName,
                        message,
                        cancellationToken);
                    _logger.LogDebug(
                        "Successfully published a message with key {Key} to topic {Topic} of broker with ID {BrokerId}.",
                        key,
                        queueName,
                        brokerId);
                }
                catch (Exception exception)
                {
                    _logger.LogDebug(
                        exception,
                        "Failed to publish a message with key {Key} to topic {Topic} of broker with ID {BrokerId}.",
                        key,
                        queueName,
                        brokerId);
                    throw;
                }
            });

            return(Task.WhenAll(publishTasks));
        }
コード例 #5
0
        public void when_executed_it_should_store_queue_name_into_context()
        {
            var context = new MessagePublishingContext {
                Api = CreateEgressApi(), Message = new Message1()
            };
            var sut = new GetTopicNameStep();

            sut.Execute(context);

            context.QueueNames.Should().BeEquivalentTo(new[] { ExpectedQueueName }, "queue name should be set after step executed");
        }
コード例 #6
0
        /// <inheritdoc />
        public Task Publish(MessagePublishingContext context, CancellationToken cancellationToken)
        {
            if (!_isInitialized)
            {
                throw new PoezdOperationException("Kafka driver should be initialized before it can publish messages.");
            }
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return(_producerRegistry.Get(context.Api).Publish(context, cancellationToken));
        }
コード例 #7
0
        public async Task when_executing_it_should_serialize_message_into_byte_array()
        {
            const byte expected = 0x23;
            var        context  = new MessagePublishingContext {
                Api = CreateEgressApi(), Message = new Message1 {
                    Byte = expected
                }
            };
            var sut = new SerializeMessageStep();

            await sut.Execute(context);

            ((byte[])context.Payload)[0].Should().Be(expected, "message content should be serialized");
        }
コード例 #8
0
        public void when_executed_it_should_store_message_key_in_context()
        {
            const byte expected = 0x23;
            var        context  = new MessagePublishingContext {
                Api = CreateEgressApi(), Message = new Message1 {
                    Byte = expected
                }
            };
            var sut = new GetMessageKeyStep();

            sut.Execute(context);

            context.Key.Should().Be(expected, "key should be set after step executed");
        }
コード例 #9
0
        public async Task when_publishing_to_properly_initialized_driver_it_should_publish_messages_and_log()
        {
            var          container  = new Container().AddLogging(_testOutput);
            const string brokerId   = "broker-1";
            var          brokerMock = new Mock <IMessageBroker>();

            brokerMock.SetupGet(broker => broker.Id).Returns(brokerId);

            var          producer = new TestProducer <int, byte[]>(new ProducerConfig(), new Dictionary <string, object>());
            const int    key      = 555;
            const string topic1   = "commands1";
            const string topic2   = "audit2";
            var          context  = new MessagePublishingContext
            {
                Key      = key,
                Payload  = Encoding.UTF8.GetBytes("payload"),
                Api      = Mock.Of <IEgressApi>(),
                Metadata = new Dictionary <string, string> {
                    { "Type", "Message1" }
                },
                QueueNames = new[] { topic1, topic2 },
                Broker     = brokerMock.Object
            };

            var apiProducer = new DefaultApiProducer <int, byte[]>(
                producer,
                Mock.Of <IHeaderValueCodec>(),
                container.GetInstance <ILogger <DefaultApiProducer <int, byte[]> > >());

            await apiProducer.Publish(context, CancellationToken.None);

            InMemorySink.Instance.LogEvents.Should().HaveCount(2 * 2, "message was published to 2 topics");
            InMemorySink.Instance.LogEvents.ElementAt(index: 0).RenderMessage().Should().Contain("Publishing")
            .And.Contain(key.ToString())
            .And.Contain(topic1)
            .And.Contain(brokerId);
            InMemorySink.Instance.LogEvents.ElementAt(index: 1).RenderMessage().Should().Contain("Successfully")
            .And.Contain(key.ToString())
            .And.Contain(topic1)
            .And.Contain(brokerId);
            InMemorySink.Instance.LogEvents.ElementAt(index: 2).RenderMessage().Should().Contain("Publishing")
            .And.Contain(key.ToString())
            .And.Contain(topic2)
            .And.Contain(brokerId);
            InMemorySink.Instance.LogEvents.ElementAt(index: 3).RenderMessage().Should().Contain("Successfully")
            .And.Contain(key.ToString())
            .And.Contain(topic2)
            .And.Contain(brokerId);
        }
コード例 #10
0
        public void when_publishing_and_error_happens_it_should_log_error_and_fail()
        {
            var          container  = new Container().AddLogging(_testOutput);
            const string brokerId   = "broker-1";
            var          brokerMock = new Mock <IMessageBroker>();

            brokerMock.SetupGet(broker => broker.Id).Returns(brokerId);

            var producer = new TestProducer <int, byte[]>(
                new ProducerConfig(),
                new Dictionary <string, object>(),
                new IOException());
            const int    key     = 555;
            const string topic1  = "commands1";
            const string topic2  = "audit2";
            var          context = new MessagePublishingContext
            {
                Key      = key,
                Payload  = Encoding.UTF8.GetBytes("payload"),
                Api      = Mock.Of <IEgressApi>(),
                Metadata = new Dictionary <string, string> {
                    { "Type", "Message1" }
                },
                QueueNames = new[] { topic1, topic2 },
                Broker     = brokerMock.Object
            };

            var apiProducer = new DefaultApiProducer <int, byte[]>(
                producer,
                Mock.Of <IHeaderValueCodec>(),
                container.GetInstance <ILogger <DefaultApiProducer <int, byte[]> > >());

            Func <Task> sut = () => apiProducer.Publish(context, CancellationToken.None);

            sut.Should().ThrowExactly <IOException>("should fail if publishing failed");

            InMemorySink.Instance.LogEvents.Should().HaveCount(2 * 2, "message publishing failed in both topics");
            InMemorySink.Instance.LogEvents.ElementAt(index: 0).RenderMessage().Should().Contain("Publishing")
            .And.Contain(key.ToString())
            .And.Contain(topic1)
            .And.Contain(brokerId);
            InMemorySink.Instance.LogEvents.ElementAt(index: 1).RenderMessage().Should().Contain("Failed")
            .And.Contain(key.ToString())
            .And.Contain(topic1)
            .And.Contain(brokerId);
        }
        public void when_executing_without_message_in_context_it_should_fail()
        {
            var step    = new ValidateMessagePublishingContextStep();
            var context = new MessagePublishingContext
            {
                Api           = CreateEgressApi(),
                Message       = null,
                CorrelationId = ExpectedCorrelationId,
                CausationId   = ExpectedCausationId,
                MessageId     = ExpectedMessageId
            };

            Func <Task> sut = () => step.Execute(context);

            sut.Should().Throw <ArgumentException>()
            .Where(
                exception => exception.ParamName.Equals("context") && exception.Message.Contains("Message should"),
                "message is required");
        }
コード例 #12
0
        public async Task when_create_producer_it_should_create_producer()
        {
            var published = 0;

            var producerMock = new Mock <IProducer <int, string> >();

            producerMock
            .Setup(
                producer => producer.ProduceAsync(
                    It.IsAny <string>(),
                    It.IsAny <Message <int, string> >(),
                    It.IsAny <CancellationToken>()))
            .Callback(() => published++)
            .Returns(() => Task.FromResult <DeliveryResult <int, string> >(new DeliveryReport <int, string>()));
            var producerFactoryMock = new Mock <IProducerFactory>();

            producerFactoryMock.Setup(factory => factory.Create <int, string>(It.IsAny <ProducerConfig>())).Returns(producerMock.Object);
            var loggerFactoryMock = new Mock <ILoggerFactory>();

            loggerFactoryMock.Setup(factory => factory.CreateLogger(It.IsAny <string>())).Returns(Mock.Of <ILogger>);

            var sut = new DefaultApiProducerFactory(
                producerFactoryMock.Object,
                Mock.Of <IHeaderValueCodec>(),
                loggerFactoryMock.Object);

            var apiProducer = sut.Create <int, string>(new ProducerConfig());
            var context     = new MessagePublishingContext
            {
                Key        = 555,
                Payload    = "payload",
                Api        = Mock.Of <IEgressApi>(),
                Metadata   = new Dictionary <string, string>(),
                QueueNames = new[] { "topic1" },
                Broker     = Mock.Of <IMessageBroker>()
            };

            await apiProducer.Publish(context, CancellationToken.None);

            published.Should().Be(expected: 1, "factory should create functional producer");
        }
コード例 #13
0
        public void when_executed_it_should_store_message_metadata_into_context()
        {
            var sut     = new GetBrokerMetadataStep();
            var context = new MessagePublishingContext
            {
                Api           = CreateEgressApi(),
                Message       = new Message1(),
                CorrelationId = ExpectedCorrelationId,
                CausationId   = ExpectedCausationId,
                MessageId     = ExpectedMessageId
            };

            sut.Execute(context);

            var expectedMetadata = new Dictionary <string, string>
            {
                { VentureApi.Headers.MessageTypeName, ExpectedMessageTypeName },
                { VentureApi.Headers.CorrelationId, ExpectedCorrelationId },
                { VentureApi.Headers.CausationId, ExpectedCausationId },
                { VentureApi.Headers.MessageId, ExpectedMessageId }
            };

            context.Metadata.Should().BeEquivalentTo(expectedMetadata, "it should store metadata with expected content");
        }
コード例 #14
0
 public Task Publish(MessagePublishingContext context, CancellationToken cancellationToken)
 {
     _state.PublishingContext = context;
     _state.PublishedMessageCount++;
     return(Task.CompletedTask);
 }
コード例 #15
0
        public async Task when_publishing_to_properly_initialized_driver_it_should_publish_message_to_producer()
        {
            var container         = new Container().AddLogging(_testOutput);
            var publishedMessages = new Dictionary <string, Message <int, byte[]> >();

            var producerMock = new Mock <IProducer <int, byte[]> >();

            producerMock
            .Setup(
                producer => producer.ProduceAsync(
                    It.IsAny <string>(),
                    It.IsAny <Message <int, byte[]> >(),
                    CancellationToken.None))
            .Callback(
                (
                    string topic,
                    Message <int, byte[]> message,
                    CancellationToken token) => publishedMessages.Add(topic, message))
            .Returns(() => Task.FromResult(new DeliveryResult <int, byte[]>()));

            var apiProducer = new DefaultApiProducer <int, byte[]>(
                producerMock.Object,
                new Utf8ByteStringHeaderValueCodec(),
                container.GetInstance <ILogger <DefaultApiProducer <int, byte[]> > >());

            const int    key         = 555;
            var          payload     = Encoding.UTF8.GetBytes("payload");
            const string headerKey   = "Type";
            const string headerValue = "Message1";
            const string topic1      = "commands1";
            const string topic2      = "audit2";

            var context = new MessagePublishingContext
            {
                Key      = key,
                Payload  = payload,
                Api      = Mock.Of <IEgressApi>(),
                Metadata = new Dictionary <string, string> {
                    { headerKey, headerValue }
                },
                QueueNames = new[] { topic1, topic2 },
                Broker     = Mock.Of <IMessageBroker>()
            };

            await apiProducer.Publish(context, CancellationToken.None);

            publishedMessages.Should().HaveCount(expected: 2, "published 1 message to 2 topics");
            publishedMessages.ElementAt(index: 0).Key.Should().Be(topic1, "should publish to topics in order of occurrence");
            publishedMessages.ElementAt(index: 0).Value.As <Message <int, byte[]> >().Key.Should().Be(key, "key should have expected value");
            publishedMessages.ElementAt(index: 0).Value.As <Message <int, byte[]> >().Value.Should()
            .Equal(payload, "payload should have expected value");
            var header = publishedMessages.ElementAt(index: 0).Value.As <Message <int, byte[]> >().Headers.Single();

            header.Key.Should().Be(headerKey, "single header should have expected value");
            header.GetValueBytes().Should().Equal(Encoding.UTF8.GetBytes(headerValue), "single header should have expected value");

            publishedMessages.ElementAt(index: 1).Key.Should().Be(topic2, "should publish to topics in order of occurrence");
            publishedMessages.ElementAt(index: 1).Value.As <Message <int, byte[]> >().Key.Should().Be(key, "key should have expected value");
            publishedMessages.ElementAt(index: 1).Value.As <Message <int, byte[]> >().Value.Should()
            .Equal(payload, "payload should have expected value");
            header = publishedMessages.ElementAt(index: 1).Value.As <Message <int, byte[]> >().Headers.Single();
            header.Key.Should().Be(headerKey, "single header should have expected value");
            header.GetValueBytes().Should().Equal(Encoding.UTF8.GetBytes(headerValue), "single header should have expected value");
        }
コード例 #16
0
        public void when_publishing_with_invalid_context_content_it_should_fail()
        {
            var apiProducer = new DefaultApiProducer <string, string>(
                Mock.Of <IProducer <string, string> >(),
                Mock.Of <IHeaderValueCodec>(),
                Mock.Of <ILogger <DefaultApiProducer <string, string> > >());

            const int key     = 555;
            var       payload = Encoding.UTF8.GetBytes("payload");

            var metadata = new Dictionary <string, string> {
                { "Type", "Message1" }
            };
            var queueNames = new[] { "commands1", "audit2" };
            var broker     = Mock.Of <IMessageBroker>();
            var api        = Mock.Of <IEgressApi>();
            var context    = new MessagePublishingContext
            {
                Key        = key,
                Payload    = payload,
                Api        = api,
                Metadata   = metadata,
                QueueNames = queueNames,
                Broker     = broker
            };

            Func <Task> sut = () => apiProducer.Publish(context, CancellationToken.None);

            context.Metadata = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("metadata", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid metadata");
            context.Metadata = metadata;

            context.QueueNames = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("queueNames", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid queue names");
            context.QueueNames = queueNames;

            context.Key = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("key", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid key");
            context.Key = key;

            context.Payload = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("payload", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid payload");
            context.Payload = payload;

            context.Broker = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("broker", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid broker");
            context.Broker = broker;

            context.Api = null;
            sut.Should().ThrowExactly <PoezdOperationException>().Where(
                exception => exception.Message.Contains("API", StringComparison.InvariantCultureIgnoreCase),
                "null is an invalid API");
        }