< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.ServiceModel.Channels.KafkaOutputChannel
Assembly: CoreWCF.Kafka.Client
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Kafka.Client/src/CoreWCF/ServiceModel/Channels/KafkaOutputChannel.cs
Line coverage
85%
Covered lines: 69
Uncovered lines: 12
Coverable lines: 81
Total lines: 204
Line coverage: 85.1%
Branch coverage
54%
Covered branches: 12
Total branches: 22
Branch coverage: 54.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor(...)50%121286.66%
OnOpen(...)100%11100%
OnOpenAsync()100%11100%
OnBeginOpen(...)100%11100%
OnEndOpen(...)100%11100%
OnAbort()100%110%
OnClose(...)100%11100%
OnCloseAsync(...)100%11100%
OnBeginClose(...)100%11100%
OnEndClose(...)100%11100%
EncodeMessage(...)100%11100%
SendAsync()100%11100%
ApplyKafkaMessageProperty(...)100%66100%
Send(...)100%110%
Send(...)100%11100%
BeginSend(...)100%11100%
BeginSend(...)100%11100%
EndSend(...)100%11100%
GetProperty()0%440%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Kafka.Client/src/CoreWCF/ServiceModel/Channels/KafkaOutputChannel.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.ServiceModel;
 6using System.ServiceModel.Channels;
 7using System.Text.RegularExpressions;
 8using System.Threading;
 9using System.Threading.Tasks;
 10using Confluent.Kafka;
 11using CoreWCF.Runtime;
 12
 13namespace CoreWCF.ServiceModel.Channels
 14{
 15    internal class KafkaOutputChannel : ChannelBase, IOutputChannel
 16    {
 17        private readonly MessageEncoder _encoder;
 18        private readonly KafkaChannelFactory _parent;
 19        private readonly string _topic;
 20        private readonly ProducerBuilder<byte[], byte[]> _producerBuilder;
 121        private static readonly Regex s_topicNameRegex =
 122            new(@"^[a-zA-Z0-9\.\-_]{1,255}$", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100));
 23        private IProducer<byte[], byte[]> _producer;
 24
 25        public KafkaOutputChannel(KafkaChannelFactory factory, EndpointAddress address, Uri via, MessageEncoder encoder,
 3826            KafkaTransportBindingElement transportBindingElement) : base(factory)
 27        {
 3828            _parent = factory;
 3829            RemoteAddress = address ?? throw new ArgumentNullException(nameof(address));
 3830            Via = via ?? throw new ArgumentNullException(nameof(via));
 3831            _encoder = encoder ?? throw new ArgumentNullException(nameof(encoder));
 32
 3833            if (!string.Equals(address.Uri.Scheme, KafkaConstants.Scheme, StringComparison.OrdinalIgnoreCase))
 34            {
 035                throw new InvalidOperationException();
 36            }
 37
 3838            string bootstrapServer = address.Uri.Authority;
 3839            _topic = address.Uri.PathAndQuery.Substring(1, address.Uri.PathAndQuery.Length - 1);
 3840            if (string.IsNullOrEmpty(_topic) || !s_topicNameRegex.IsMatch(_topic))
 41            {
 042                throw new NotSupportedException(string.Format(SR.InvalidTopicName, _topic));
 43            }
 3844            ProducerConfig producerConfig = transportBindingElement.Config;
 3845            producerConfig.BootstrapServers = bootstrapServer;
 46
 3847            _producerBuilder = new ProducerBuilder<byte[], byte[]>(producerConfig);
 3848        }
 49
 50        protected override void OnOpen(TimeSpan timeout)
 51        {
 2152            _producer = _producerBuilder.Build();
 2153        }
 54
 55        private Task OnOpenAsync()
 56        {
 1757            _producer = _producerBuilder.Build();
 1758            return Task.CompletedTask;
 59        }
 60
 61        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
 62        {
 1763            return OnOpenAsync().ToApm(callback, state);
 64        }
 65
 66        protected override void OnEndOpen(IAsyncResult result)
 67        {
 1768            result.ToApmEnd();
 1769        }
 70
 71        protected override void OnAbort()
 72        {
 073            OnClose(DefaultCloseTimeout);
 074        }
 75
 76        protected override void OnClose(TimeSpan timeout)
 77        {
 1278            CancellationTokenSource cts = new(timeout);
 79            try
 80            {
 1281                _producer.Flush(cts.Token);
 1282            }
 83            finally
 84            {
 1285                cts.Dispose();
 1286                _producer.Dispose();
 1287            }
 1288        }
 89
 90        private Task OnCloseAsync(TimeSpan timeout)
 91        {
 192            OnClose(timeout);
 193            return Task.CompletedTask;
 94        }
 95
 96        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
 97        {
 198            return OnCloseAsync(timeout).ToApm(callback, state);
 99        }
 100
 101        protected override void OnEndClose(IAsyncResult result)
 102        {
 1103            result.ToApmEnd();
 1104        }
 105
 106        private ArraySegment<byte> EncodeMessage(Message message)
 107        {
 108            try
 109            {
 472110                RemoteAddress.ApplyTo(message);
 472111                return _encoder.WriteMessage(message, int.MaxValue, _parent.BufferManager);
 112            }
 113            finally
 114            {
 472115                message.Close();
 472116            }
 472117        }
 118
 119        private async Task SendAsync(Message message, TimeSpan timeout)
 120        {
 472121            Message<byte[], byte[]> kafkaMessage = new();
 472122            ApplyKafkaMessageProperty(message, kafkaMessage);
 472123            ArraySegment<byte> messageBuffer = EncodeMessage(message);
 472124            CancellationTokenSource cts = new(timeout);
 125            try
 126            {
 472127                kafkaMessage.Value = new Span<byte>(messageBuffer.Array, messageBuffer.Offset, messageBuffer.Count).ToAr
 472128                await _producer.ProduceAsync(_topic, kafkaMessage, cts.Token);
 470129            }
 130            catch (KafkaException kafkaException)
 131            {
 1132                throw KafkaChannelHelpers.ConvertKafkaException(kafkaException);
 133            }
 1134            catch (OperationCanceledException)
 135            {
 1136                throw new TimeoutException(string.Format(SR.KafkaSendTimeoutExceeded, timeout));
 137            }
 138            finally
 139            {
 472140                cts.Dispose();
 472141                _parent.BufferManager.ReturnBuffer(messageBuffer.Array);
 142            }
 470143        }
 144
 145        private static void ApplyKafkaMessageProperty(Message message, Message<byte[], byte[]> kafkaMessage)
 146        {
 472147            KafkaMessageProperty kafkaMessageProperty =
 472148                message.Properties.TryGetValue(KafkaMessageProperty.Name, out object value) &&
 472149                value is KafkaMessageProperty property
 472150                    ? property
 472151                    : new KafkaMessageProperty();
 472152            kafkaMessage.Headers = new();
 948153            foreach (KafkaMessageHeader kafkaMessageHeader in kafkaMessageProperty.Headers)
 154            {
 2155                kafkaMessage.Headers.Add(kafkaMessageHeader.Key, kafkaMessageHeader.Value);
 156            }
 157
 472158            kafkaMessage.Key = kafkaMessageProperty.PartitionKey;
 472159        }
 160
 0161        public void Send(Message message) => Send(message, DefaultSendTimeout);
 162
 163        public void Send(Message message, TimeSpan timeout)
 164        {
 455165            SendAsync(message, timeout).GetAwaiter().GetResult();
 455166        }
 167
 168        public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
 169        {
 17170            return SendAsync(message, DefaultSendTimeout).ToApm(callback, state);
 171        }
 172
 173        public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
 174        {
 17175            return BeginSend(message, callback, state);
 176        }
 177
 178        public void EndSend(IAsyncResult result)
 179        {
 17180            result.ToApmEnd();
 15181        }
 182
 472183        public EndpointAddress RemoteAddress { get; }
 184
 0185        public Uri Via { get; }
 186
 187        public override T GetProperty<T>()
 188        {
 0189            if (typeof(T) == typeof(IOutputChannel))
 190            {
 0191                return (T)(object)this;
 192            }
 193
 0194            T messageEncoderProperty = _encoder.GetProperty<T>();
 0195            if (messageEncoderProperty != null)
 196            {
 0197                return messageEncoderProperty;
 198            }
 199
 0200            return base.GetProperty<T>();
 201        }
 202    }
 203}
 204