├── README.md ├── .vscode └── settings.json ├── src ├── Confluent.Kafka.Core │ ├── Models │ │ ├── Internal │ │ │ ├── KafkaSenderType.cs │ │ │ ├── KafkaSenderConstants.cs │ │ │ ├── KafkaSender.cs │ │ │ ├── MessageValueExtensions.cs │ │ │ ├── HeadersExtensions.cs │ │ │ ├── HeaderExtensions.cs │ │ │ ├── MessageExtensions.cs │ │ │ └── TopicPartitionExtensions.cs │ │ └── TopicPartitionException.cs │ ├── Mapping │ │ └── Internal │ │ │ ├── IMapper`.cs │ │ │ └── MapperExtensions.cs │ ├── Conversion │ │ └── Internal │ │ │ ├── IOptionsConverter`.cs │ │ │ └── OptionsConverterExtensions.cs │ ├── Consumer │ │ ├── Internal │ │ │ ├── IConsumerBuilder``.cs │ │ │ ├── ConsumeResultConstants.cs │ │ │ ├── IConsumerAccessor``.cs │ │ │ ├── KafkaConsumerHandlerFactoryOptions.cs │ │ │ ├── KafkaConsumerInterceptorContext``.cs │ │ │ ├── KafkaConsumerBuilderExtensions.cs │ │ │ ├── IKafkaConsumerFactory.cs │ │ │ ├── IKafkaConsumerHandlerFactory.cs │ │ │ ├── KafkaConsumerConfigExtensions.cs │ │ │ ├── ConsumerExtensions.cs │ │ │ └── KafkaConsumerHandlerFactoryOptionsBuilder.cs │ │ └── KafkaConsumerConfigException.cs │ ├── Producer │ │ ├── Internal │ │ │ ├── IProducerBuilder``.cs │ │ │ ├── IProducerAccessor``.cs │ │ │ ├── KafkaProducerHandlerFactoryOptions.cs │ │ │ ├── IDeliveryReportFactory.cs │ │ │ ├── KafkaProducerConstants.cs │ │ │ ├── KafkaProducerInterceptorContext``.cs │ │ │ ├── ProducerExtensions.cs │ │ │ ├── KafkaProducerBuilderExtensions.cs │ │ │ ├── IKafkaProducerFactory.cs │ │ │ ├── IKafkaProducerHandlerFactory.cs │ │ │ ├── ProduceResult``.cs │ │ │ ├── DeliveryReportFactory.cs │ │ │ ├── KafkaProducerExtensions.cs │ │ │ ├── KafkaProducerOptions``.cs │ │ │ └── KafkaProducerHandlerFactoryOptionsBuilder.cs │ │ └── KafkaProducerConfigException.cs │ ├── Idempotency │ │ └── Internal │ │ │ └── KafkaIdempotencyConstants.cs │ ├── Internal │ │ ├── IObjectFactory.cs │ │ ├── DictionaryExtensions.cs │ │ ├── ExceptionExtensions.cs │ │ ├── AssemblyScanner.cs │ │ └── ObjectFactory.cs │ ├── Hosting │ │ ├── Internal │ │ │ ├── ExecutionResult.cs │ │ │ └── MessageOrderGuaranteeConstants.cs │ │ ├── KafkaConsumerWorkerConfigException.cs │ │ └── Retry │ │ │ └── KafkaRetryConsumerWorkerConfigException.cs │ ├── Diagnostics │ │ ├── Internal │ │ │ ├── OperationNames.cs │ │ │ ├── KafkaActivitySource.cs │ │ │ ├── KafkaDiagnosticsManager.cs │ │ │ ├── PropagationContext.cs │ │ │ ├── IKafkaDiagnosticsManagerFactory.cs │ │ │ ├── KafkaNoopDiagnosticsManager.cs │ │ │ ├── KafkaEnrichmentOptions.cs │ │ │ ├── KafkaActivityAttributes.cs │ │ │ ├── ActivitySourceBase.cs │ │ │ └── ActivityExtensions.cs │ │ └── KafkaDiagnosticsServiceCollectionExtensions.cs │ ├── Threading │ │ ├── Internal │ │ │ ├── IAsyncLockFactory.cs │ │ │ ├── IAsyncSemaphore.cs │ │ │ ├── IAsyncLockOptionsBuilder.cs │ │ │ ├── AsyncLockContext.cs │ │ │ ├── AsyncLockOptionsBuilderExtensions.cs │ │ │ ├── AsyncLockFactory.cs │ │ │ ├── TaskActivity.cs │ │ │ ├── AsyncLockOptionsBuilder.cs │ │ │ └── AsyncLockOptions.cs │ │ └── AsyncLockOptionsException.cs │ ├── Retry │ │ └── Internal │ │ │ └── KafkaRetryConstants.cs │ ├── Serialization │ │ └── Internal │ │ │ ├── SyncOverAsyncSerializerExtensions.cs │ │ │ ├── SyncOverAsyncDeserializerExtensions.cs │ │ │ └── SerializationExtensions.cs │ └── DependencyInjection │ │ └── KafkaServiceCollectionExtensions.cs ├── Confluent.Kafka.Core.Abstractions │ ├── Models │ │ ├── IMessageValue.cs │ │ ├── KafkaTopicPartitionLag.cs │ │ └── KafkaMetadataMessage.cs │ ├── Specifications │ │ ├── ISpecification`.cs │ │ └── ExpressionSpecification`.cs │ ├── Encoding │ │ ├── IEncodingFactory.cs │ │ └── EncodingFactory.cs │ ├── Hosting │ │ ├── Retry │ │ │ ├── IKafkaRetryConsumerWorker.cs │ │ │ ├── IKafkaRetryConsumerWorkerConfig.cs │ │ │ ├── IKafkaRetryConsumerWorkerOptions.cs │ │ │ └── IKafkaRetryConsumerWorkerConfigBuilder.cs │ │ ├── IConsumeResultErrorHandler``.cs │ │ ├── IConsumeResultHandler``.cs │ │ ├── IKafkaConsumerWorker``.cs │ │ ├── IKafkaConsumerLifecycleWorker``.cs │ │ ├── IKafkaConsumerWorkerConfig.cs │ │ ├── IKafkaConsumerWorkerOptions``.cs │ │ └── IKafkaConsumerWorkerConfigBuilder.cs │ ├── Diagnostics │ │ ├── KafkaSyncProductionEnrichmentContext.cs │ │ ├── KafkaProcessingFailureEnrichmentContext.cs │ │ ├── KafkaProductionFailureEnrichmentContext.cs │ │ ├── KafkaProcessingEnrichmentContext.cs │ │ ├── KafkaConsumptionFailureEnrichmentContext.cs │ │ ├── IPropagationContext.cs │ │ ├── IContextPropagator.cs │ │ ├── IKafkaDiagnosticsManager.cs │ │ ├── KafkaConsumptionEnrichmentContext.cs │ │ ├── KafkaAsyncProductionEnrichmentContext.cs │ │ ├── IKafkaEnrichmentOptionsBuilder.cs │ │ └── IKafkaActivityEnricher.cs │ ├── Consumer │ │ ├── IKafkaConsumerInterceptor``.cs │ │ ├── IKafkaConsumerInterceptorContext``.cs │ │ ├── IKafkaConsumerHandlerFactoryOptionsBuilder.cs │ │ ├── IKafkaConsumerConfig.cs │ │ ├── ConsumeExceptionExtensions.cs │ │ ├── IKafkaConsumerHandlerFactory``.cs │ │ ├── IKafkaConsumer``.cs │ │ ├── IKafkaConsumerConfigBuilder.cs │ │ └── IConsumerConfig.cs │ ├── Producer │ │ ├── IKafkaProducerInterceptor``.cs │ │ ├── IKafkaProducerInterceptorContext``.cs │ │ ├── IKafkaProducerHandlerFactoryOptionsBuilder.cs │ │ ├── IKafkaProducerHandlerFactory``.cs │ │ ├── IKafkaProducerConfig.cs │ │ ├── ProduceExceptionExtensions.cs │ │ ├── IKafkaProducerConfigBuilder.cs │ │ ├── IProducerConfig.cs │ │ ├── IKafkaProducerOptions``.cs │ │ ├── IKafkaProducer``.cs │ │ └── IProducerConfigBuilder`.cs │ ├── Client │ │ ├── IConfig.cs │ │ └── IConfigBuilder`.cs │ ├── Idempotency │ │ └── IIdempotencyHandler``.cs │ ├── Retry │ │ ├── IRetryHandler``.cs │ │ └── RetrySpecification.cs │ └── Confluent.Kafka.Core.Abstractions.csproj ├── Shared │ ├── SchemaRegistry │ │ ├── IUnregisteredSchemaBuilder.cs │ │ ├── Internal │ │ │ ├── SchemaRegistryClientConstants.cs │ │ │ ├── ISchemaRegistryClientFactory.cs │ │ │ ├── SchemaBuilder.cs │ │ │ └── SchemaRegistryClientBuilder.cs │ │ ├── ISchemaBuilder.cs │ │ ├── IRegisteredSchemaBuilder.cs │ │ ├── ISchemaBuilder`.cs │ │ ├── ISchemaRegistryClientBuilder.cs │ │ └── SchemaRegistryClientServiceCollectionExtensions.cs │ ├── Builders │ │ ├── IFunctionalBuilder``.cs │ │ ├── IFunctionalBuilder```.cs │ │ └── FunctionalBuilder``.cs │ ├── IsExternalInit.cs │ └── Extensions │ │ ├── LoggerFactoryExtensions.cs │ │ ├── TaskExtensions.cs │ │ └── ValidatableObjectExtensions.cs ├── Confluent.Kafka.Core.Retry.Polly │ ├── Internal │ │ ├── PollyRetryHandlerConstants.cs │ │ ├── LoggerExtensions.cs │ │ └── IPollyRetryHandlerFactory.cs │ ├── PollyRetryHandlerOptionsException.cs │ ├── PollyRetryHandlerKafkaBuilderExtensions.cs │ ├── IPollyRetryHandlerOptionsBuilder.cs │ ├── PollyRetryHandlerKafkaConsumerBuilderExtensions.cs │ ├── PollyRetryHandlerKafkaProducerBuilderExtensions.cs │ ├── PollyRetryHandlerKafkaConsumerWorkerBuilderExtensions.cs │ ├── PollyRetryHandlerKafkaRetryConsumerWorkerBuilderExtensions.cs │ └── PollyRetryHandlerServiceCollectionExtensions.cs ├── Confluent.Kafka.Core.Serialization.ProtobufNet │ ├── ProtobufNetSerializerOptions.cs │ ├── Internal │ │ ├── ProtobufNetSerializerConstants.cs │ │ ├── IProtobufNetSerializerFactory.cs │ │ └── ProtobufNetSerializerOptionsBuilder.cs │ ├── IProtobufNetSerializerOptionsBuilder.cs │ └── ProtobufNetSerializerKafkaBuilderExtensions.cs ├── Confluent.Kafka.Core.Serialization.JsonCore │ ├── Internal │ │ ├── JsonCoreSerializerConstants.cs │ │ ├── IJsonCoreSerializerFactory.cs │ │ ├── JsonCoreSerializer`.cs │ │ └── JsonCoreSerializerFactory.cs │ └── JsonCoreSerializerKafkaBuilderExtensions.cs ├── Confluent.Kafka.Core.Idempotency.Redis │ ├── Internal │ │ ├── RedisIdempotencyHandlerConstants.cs │ │ ├── RedisClientConstants.cs │ │ ├── IRedisClientFactory.cs │ │ └── IRedisIdempotencyHandlerFactory.cs │ ├── IRedisIdempotencyHandlerBuilder``.cs │ ├── RedisIdempotencyHandlerOptionsException``.cs │ ├── IRedisIdempotencyHandlerOptionsBuilder``.cs │ ├── RedisIdempotencyHandlerKafkaBuilderExtensions.cs │ ├── RedisIdempotencyHandlerKafkaConsumerWorkerBuilderExtensions.cs │ ├── RedisIdempotencyHandlerKafkaRetryConsumerWorkerBuilderExtensions.cs │ ├── RedisClientServiceCollectionExtensions.cs │ └── RedisIdempotencyHandlerServiceCollectionExtensions.cs ├── Confluent.Kafka.Core.Serialization.NewtonsoftJson │ ├── Internal │ │ ├── NewtonsoftJsonSerializerConstants.cs │ │ ├── INewtonsoftJsonSerializerFactory.cs │ │ └── NewtonsoftJsonSerializer`.cs │ └── NewtonsoftJsonSerializerKafkaBuilderExtensions.cs ├── Confluent.Kafka.Core.OpenTelemetry │ ├── TracerProviderBuilderExtensions.cs │ └── Confluent.Kafka.Core.OpenTelemetry.csproj ├── Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro │ ├── Internal │ │ ├── SchemaRegistryAvroSerializerConstants.cs │ │ ├── ISchemaRegistryAvroSerializerFactory.cs │ │ └── SchemaRegistryAvroSerializer`.cs │ ├── ISchemaRegistryAvroSerializerBuilder.cs │ ├── IAvroDeserializerConfigBuilder.cs │ ├── SchemaRegistryAvroSerializerKafkaBuilderExtensions.cs │ └── IAvroSerializerConfigBuilder.cs ├── Confluent.Kafka.Core.Serialization.SchemaRegistry.Json │ ├── Internal │ │ ├── SchemaRegistryJsonSerializerConstants.cs │ │ ├── ISchemaRegistryJsonSerializerFactory.cs │ │ └── JsonSchemaGeneratorSettingsBuilder.cs │ ├── IJsonSchemaGeneratorSettingsBuilder.cs │ ├── INewtonsoftJsonSchemaGeneratorSettingsBuilder.cs │ ├── IJsonDeserializerConfigBuilder.cs │ ├── SchemaRegistryJsonSerializerKafkaBuilderExtensions.cs │ ├── ISchemaRegistryJsonSerializerBuilder.cs │ └── IJsonSerializerConfigBuilder.cs └── Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf │ ├── Internal │ ├── SchemaRegistryProtobufSerializerConstants.cs │ └── ISchemaRegistryProtobufSerializerFactory.cs │ ├── ISchemaRegistryProtobufSerializerBuilder.cs │ ├── IProtobufDeserializerConfigBuilder.cs │ ├── SchemaRegistryProtobufSerializerKafkaBuilderExtensions.cs │ └── IProtobufSerializerConfigBuilder.cs ├── tests ├── Confluent.Kafka.Core.Shared.Tests │ └── Stubs │ │ ├── ITestSubject.cs │ │ ├── TestSubject.cs │ │ └── TestFunctionalBuilder.cs └── Confluent.Kafka.Core.Tests │ ├── Serialization │ └── Stubs │ │ ├── protobufMessage.proto │ │ └── AvroMessage.avsc │ ├── Extensions │ ├── ObjectExtensions.cs │ ├── EnumExtensions.cs │ └── MockExtensions.cs │ └── Core │ ├── Diagnostics │ └── KafkaActivityListener.cs │ └── Extensions │ ├── ActivityExtensions.cs │ └── KafkaConsumerExtensions.cs ├── Directory.Build.props ├── NuGet.Config ├── docs ├── Usage.md └── Serialization │ └── Serialization.md ├── .github └── workflows │ ├── build-and-publish.yml │ └── build-and-tests.yml └── LICENSE /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffernandolima/confluent-kafka-core-dotnet/HEAD/README.md -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "omnisharp.organizeImportsOnFormat": true, 3 | "prettyxml.settings.preserveWhiteSpacesInComment": true 4 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/KafkaSenderType.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Models.Internal 2 | { 3 | internal enum KafkaSenderType 4 | { 5 | Consumer, 6 | Hosting 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Shared.Tests/Stubs/ITestSubject.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Shared.Tests.Stubs 2 | { 3 | public interface ITestSubject 4 | { 5 | string SomeProperty { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Models/IMessageValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Models 4 | { 5 | public interface IMessageValue 6 | { 7 | Guid Id { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Specifications/ISpecification`.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Specifications 2 | { 3 | public interface ISpecification 4 | { 5 | bool IsSatisfiedBy(T source); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Mapping/Internal/IMapper`.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Mapping.Internal 2 | { 3 | internal interface IMapper 4 | { 5 | TDestination Map(params object[] args); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Conversion/Internal/IOptionsConverter`.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Conversion.Internal 2 | { 3 | internal interface IOptionsConverter 4 | { 5 | TOptions ToOptions(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/IUnregisteredSchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry 2 | { 3 | public interface IUnregisteredSchemaBuilder : ISchemaBuilder 4 | { } 5 | } 6 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/IConsumerBuilder``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer.Internal 2 | { 3 | internal interface IConsumerBuilder 4 | { 5 | IConsumer Build(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/KafkaSenderConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Models.Internal 2 | { 3 | internal static class KafkaSenderConstants 4 | { 5 | public const string Sender = "Sender"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/IProducerBuilder``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal interface IProducerBuilder 4 | { 5 | IProducer Build(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Serialization/Stubs/protobufMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "Confluent.Kafka.Core.Tests.Serialization"; 4 | 5 | message ProtobufMessage { 6 | int32 id = 1; 7 | string content = 2; 8 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Encoding/IEncodingFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Encoding 2 | { 3 | using System.Text; 4 | 5 | public interface IEncodingFactory 6 | { 7 | Encoding CreateDefault(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/ConsumeResultConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer.Internal 2 | { 3 | internal static class ConsumeResultConstants 4 | { 5 | public const string ConsumeResult = "ConsumeResult"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/IConsumerAccessor``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer.Internal 2 | { 3 | internal interface IConsumerAccessor 4 | { 5 | IConsumer UnderlyingConsumer { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/IProducerAccessor``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal interface IProducerAccessor 4 | { 5 | IProducer UnderlyingProducer { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/KafkaConsumerHandlerFactoryOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer.Internal 2 | { 3 | internal sealed class KafkaConsumerHandlerFactoryOptions 4 | { 5 | public bool EnableLogging { get; set; } = true; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Idempotency/Internal/KafkaIdempotencyConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Idempotency.Internal 2 | { 3 | internal static class KafkaIdempotencyConstants 4 | { 5 | public const string IdempotencyHandler = "IdempotencyHandler"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Internal/IObjectFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Internal 4 | { 5 | internal interface IObjectFactory 6 | { 7 | object TryCreateInstance(IServiceProvider serviceProvider, Type objectType); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerHandlerFactoryOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal sealed class KafkaProducerHandlerFactoryOptions 4 | { 5 | public bool EnableLogging { get; set; } = true; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/Internal/PollyRetryHandlerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Retry.Polly.Internal 2 | { 3 | internal static class PollyRetryHandlerConstants 4 | { 5 | public const string PollyRetryHandlerKey = "PollyRetryHandler"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/ProtobufNetSerializerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.ProtobufNet 2 | { 3 | public sealed class ProtobufNetSerializerOptions 4 | { 5 | public bool AutomaticRuntimeMap { get; set; } = true; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/Retry/IKafkaRetryConsumerWorker.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Models; 2 | 3 | namespace Confluent.Kafka.Core.Hosting.Retry 4 | { 5 | public interface IKafkaRetryConsumerWorker : IKafkaConsumerWorker 6 | { } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaSyncProductionEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Diagnostics 2 | { 3 | public class KafkaSyncProductionEnrichmentContext : KafkaAsyncProductionEnrichmentContext 4 | { 5 | public Error Error { get; init; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Hosting/Internal/ExecutionResult.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Hosting.Internal 2 | { 3 | internal enum ExecutionResult 4 | { 5 | NoAvailableSlots, 6 | NoAvailableMessages, 7 | Dispatched, 8 | UnhandledException 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.JsonCore/Internal/JsonCoreSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.JsonCore.Internal 2 | { 3 | internal static class JsonCoreSerializerConstants 4 | { 5 | public const string JsonCoreSerializerKey = "JsonCoreSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Hosting/Internal/MessageOrderGuaranteeConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Hosting.Internal 2 | { 3 | internal static class MessageOrderGuaranteeConstants 4 | { 5 | public const string MessageOrderGuaranteeKeyHandler = "MessageOrderGuaranteeKeyHandler"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Shared/Builders/IFunctionalBuilder``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Internal 2 | { 3 | internal interface IFunctionalBuilder : IFunctionalBuilder 4 | where TSubject : class 5 | where TSelf : IFunctionalBuilder 6 | { } 7 | } -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/Internal/SchemaRegistryClientConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Internal 2 | { 3 | internal static class SchemaRegistryClientConstants 4 | { 5 | public const string ConfluentSchemaRegistryClientKey = "ConfluentSchemaRegistryClient"; 6 | } 7 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/Internal/RedisIdempotencyHandlerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Idempotency.Redis.Internal 2 | { 3 | internal static class RedisIdempotencyHandlerConstants 4 | { 5 | public const string RedisIdempotencyHandlerKey = "RedisIdempotencyHandler"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/Internal/ProtobufNetSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.ProtobufNet.Internal 2 | { 3 | internal static class ProtobufNetSerializerConstants 4 | { 5 | public const string ProtobufNetSerializerKey = "ProtobufNetSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerInterceptor``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer 2 | { 3 | public interface IKafkaConsumerInterceptor 4 | { 5 | IKafkaConsumerInterceptorContext OnConsume(IKafkaConsumerInterceptorContext context); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerInterceptor``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer 2 | { 3 | public interface IKafkaProducerInterceptor 4 | { 5 | IKafkaProducerInterceptorContext OnProduce(IKafkaProducerInterceptorContext context); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaProcessingFailureEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaProcessingFailureEnrichmentContext : KafkaProcessingEnrichmentContext 6 | { 7 | public Exception Exception { get; init; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaProductionFailureEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaProductionFailureEnrichmentContext : KafkaSyncProductionEnrichmentContext 6 | { 7 | public Exception Exception { get; init; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.NewtonsoftJson/Internal/NewtonsoftJsonSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.NewtonsoftJson.Internal 2 | { 3 | internal static class NewtonsoftJsonSerializerConstants 4 | { 5 | public const string NewtonsoftJsonSerializerKey = "NewtonsoftJsonSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Client/IConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Confluent.Kafka.Core.Client 4 | { 5 | public interface IConfig : IEnumerable> 6 | { 7 | public int CancellationDelayMaxMs { get; } 8 | 9 | public string Get(string key); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerInterceptorContext``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer 2 | { 3 | public interface IKafkaConsumerInterceptorContext 4 | { 5 | ConsumeResult ConsumeResult { get; } 6 | 7 | IKafkaConsumerConfig ConsumerConfig { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.OpenTelemetry/TracerProviderBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace OpenTelemetry.Trace 2 | { 3 | public static class TracerProviderBuilderExtensions 4 | { 5 | public static TracerProviderBuilder AddKafkaCoreInstrumentation(this TracerProviderBuilder builder) 6 | => builder.AddSource("Confluent.Kafka.Core"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/IDeliveryReportFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal interface IDeliveryReportFactory 4 | { 5 | DeliveryReport CreateDefault( 6 | TopicPartition partition, 7 | Message message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Serialization/Stubs/AvroMessage.avsc: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "Confluent.Kafka.Core.Tests.Serialization", 3 | "name": "AvroMessage", 4 | "type": "record", 5 | "fields": [ 6 | { 7 | "name": "id", 8 | "type": "int" 9 | }, 10 | { 11 | "name": "content", 12 | "type": "string" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaProcessingEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Hosting; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaProcessingEnrichmentContext : KafkaConsumptionEnrichmentContext 6 | { 7 | public IKafkaConsumerWorkerConfig WorkerConfig { get; init; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IConsumeResultErrorHandler``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Confluent.Kafka.Core.Hosting 5 | { 6 | public interface IConsumeResultErrorHandler 7 | { 8 | Task HandleAsync(ConsumeResult consumeResult, Exception exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/Internal/SchemaRegistryAvroSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro.Internal 2 | { 3 | internal static class SchemaRegistryAvroSerializerConstants 4 | { 5 | public const string SchemaRegistryAvroSerializerKey = "SchemaRegistryAvroSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/Internal/SchemaRegistryJsonSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json.Internal 2 | { 3 | internal static class SchemaRegistryJsonSerializerConstants 4 | { 5 | public const string SchemaRegistryJsonSerializerKey = "SchemaRegistryJsonSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/IJsonSchemaGeneratorSettingsBuilder.cs: -------------------------------------------------------------------------------- 1 | #if !NET8_0_OR_GREATER 2 | 3 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json 4 | { 5 | public interface IJsonSchemaGeneratorSettingsBuilder : 6 | IJsonSchemaGeneratorSettingsBuilder 7 | { } 8 | } 9 | 10 | #endif -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/OperationNames.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Diagnostics.Internal 2 | { 3 | internal static class OperationNames 4 | { 5 | public const string PublishOperation = "publish"; 6 | public const string ReceiveOperation = "receive"; 7 | public const string ProcessOperation = "process"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/IAsyncLockFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Threading.Internal 4 | { 5 | internal interface IAsyncLockFactory 6 | { 7 | AsyncLock CreateAsyncLock(Action configureOptions); 8 | 9 | AsyncLock CreateAsyncLock(AsyncLockOptions options); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IConsumeResultHandler``.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Confluent.Kafka.Core.Hosting 5 | { 6 | public interface IConsumeResultHandler 7 | { 8 | Task HandleAsync(ConsumeResult consumeResult, CancellationToken cancellationToken); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/Internal/RedisClientConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Idempotency.Redis.Internal 2 | { 3 | internal static class RedisClientConstants 4 | { 5 | public const string StackExchangeRedisClientKey = "StackExchangeRedisClient"; 6 | public const string PreventThreadTheftFeatureFlag = "PreventThreadTheft"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/IAsyncSemaphore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Threading.Internal 6 | { 7 | internal interface IAsyncSemaphore : IDisposable 8 | { 9 | Task WaitAsync(CancellationToken cancellationToken); 10 | 11 | void Release(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaConsumptionFailureEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaConsumptionFailureEnrichmentContext : KafkaConsumptionEnrichmentContext 6 | { 7 | public Error Error { get; init; } 8 | public Exception Exception { get; init; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/Internal/SchemaRegistryProtobufSerializerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf.Internal 2 | { 3 | internal static class SchemaRegistryProtobufSerializerConstants 4 | { 5 | public const string SchemaRegistryProtobufSerializerKey = "SchemaRegistryProtobufSerializer"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/IPropagationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics 5 | { 6 | public interface IPropagationContext 7 | { 8 | ActivityContext ActivityContext { get; } 9 | 10 | IEnumerable> Baggage { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerInterceptorContext``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer 2 | { 3 | public interface IKafkaProducerInterceptorContext 4 | { 5 | TopicPartition TopicPartition { get; } 6 | 7 | Message Message { get; } 8 | 9 | IKafkaProducerConfig ProducerConfig { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal static class KafkaProducerConstants 4 | { 5 | public const string RetryProducer = "RetryProducer"; 6 | public const string DeadLetterProducer = "DeadLetterProducer"; 7 | public const string DeadLetterTopicSuffix = ".DeadLetter"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerHandlerFactoryOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer 2 | { 3 | public interface IKafkaConsumerHandlerFactoryOptionsBuilder 4 | { 5 | IKafkaConsumerHandlerFactoryOptionsBuilder FromConfiguration(string sectionKey); 6 | 7 | IKafkaConsumerHandlerFactoryOptionsBuilder WithEnableLogging(bool enableLogging); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/KafkaConsumerInterceptorContext``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Consumer.Internal 2 | { 3 | internal sealed class KafkaConsumerInterceptorContext : IKafkaConsumerInterceptorContext 4 | { 5 | public ConsumeResult ConsumeResult { get; init; } 6 | public IKafkaConsumerConfig ConsumerConfig { get; init; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Shared/Builders/IFunctionalBuilder```.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Internal 4 | { 5 | internal interface IFunctionalBuilder : IDisposable 6 | where TSubject : class, TSubjectAbs 7 | where TSelf : IFunctionalBuilder 8 | { 9 | TSelf Clear(); 10 | TSubjectAbs Build(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/IProtobufNetSerializerOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.ProtobufNet 2 | { 3 | public interface IProtobufNetSerializerOptionsBuilder 4 | { 5 | IProtobufNetSerializerOptionsBuilder FromConfiguration(string sectionKey); 6 | 7 | IProtobufNetSerializerOptionsBuilder WithAutomaticRuntimeMap(bool automaticRuntimeMap); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Shared.Tests/Stubs/TestSubject.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Shared.Tests.Stubs 2 | { 3 | public class TestSubject : ITestSubject 4 | { 5 | public string SomeProperty { get; set; } 6 | 7 | public TestSubject() 8 | { } 9 | 10 | public TestSubject(string someProperty) 11 | { 12 | SomeProperty = someProperty; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerHandlerFactoryOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer 2 | { 3 | public interface IKafkaProducerHandlerFactoryOptionsBuilder 4 | { 5 | IKafkaProducerHandlerFactoryOptionsBuilder FromConfiguration(string sectionKey); 6 | 7 | IKafkaProducerHandlerFactoryOptionsBuilder WithEnableLogging(bool enableLogging); 8 | } 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Tests.Extensions 2 | { 3 | public static class ObjectExtensions 4 | { 5 | public static TImplementation ToImplementation( 6 | this object service) 7 | { 8 | var implementation = (TImplementation)service; 9 | 10 | return implementation; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/IContextPropagator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics 5 | { 6 | public interface IContextPropagator 7 | { 8 | void InjectContext(Activity activity, IDictionary carrier); 9 | 10 | IPropagationContext ExtractContext(IDictionary carrier); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/KafkaSender.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Models.Internal 2 | { 3 | internal sealed class KafkaSender 4 | { 5 | public object Instance { get; } 6 | public KafkaSenderType Type { get; } 7 | 8 | public KafkaSender(object instance, KafkaSenderType type) 9 | { 10 | Instance = instance; 11 | Type = type; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Retry/Internal/KafkaRetryConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Retry.Internal 2 | { 3 | internal static class KafkaRetryConstants 4 | { 5 | public const string RetryHandler = "RetryHandler"; 6 | public const string RetryCountKey = "X-Retry-Count"; 7 | public const string RetryGroupIdKey = "X-Retry-Group-Id"; 8 | public const string RetryTopicSuffix = ".Retry"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/ISchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry 4 | { 5 | public interface ISchemaBuilder 6 | { 7 | ISchemaBuilder WithRegisteredSchema( 8 | Action configureRegisteredSchema); 9 | 10 | ISchemaBuilder WithUnregisteredSchema( 11 | Action configureUnregisteredSchema); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerInterceptorContext``.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Producer.Internal 2 | { 3 | internal sealed class KafkaProducerInterceptorContext : IKafkaProducerInterceptorContext 4 | { 5 | public TopicPartition TopicPartition { get; init; } 6 | public Message Message { get; init; } 7 | public IKafkaProducerConfig ProducerConfig { get; init; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Idempotency/IIdempotencyHandler``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Idempotency 6 | { 7 | public interface IIdempotencyHandler : IDisposable 8 | { 9 | Task StartAsync(CancellationToken cancellationToken = default); 10 | 11 | Task TryHandleAsync(TValue messageValue, CancellationToken cancellationToken = default); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/IRegisteredSchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry 2 | { 3 | public interface IRegisteredSchemaBuilder : ISchemaBuilder 4 | { 5 | IRegisteredSchemaBuilder WithSubject(string subject); 6 | 7 | IRegisteredSchemaBuilder WithVersion(int version); 8 | 9 | IRegisteredSchemaBuilder WithId(int id); 10 | 11 | IRegisteredSchemaBuilder WithGuid(string guid); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IKafkaConsumerWorker``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Hosting 6 | { 7 | public interface IKafkaConsumerWorker : IDisposable 8 | { 9 | Task StartAsync(CancellationToken cancellationToken); 10 | 11 | Task StopAsync(CancellationToken cancellationToken); 12 | 13 | Task ExecuteAsync(CancellationToken stoppingToken); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/KafkaActivitySource.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics.Internal 4 | { 5 | internal sealed class KafkaActivitySource : ActivitySourceBase 6 | { 7 | private static readonly AssemblyName AssemblyName = typeof(KafkaActivitySource).Assembly.GetName(); 8 | 9 | public KafkaActivitySource() 10 | : base(AssemblyName.Name, AssemblyName.Version!.ToString()) 11 | { } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/IAsyncLockOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Threading.Internal 4 | { 5 | internal interface IAsyncLockOptionsBuilder 6 | { 7 | IAsyncLockOptionsBuilder WithMaxDegreeOfParallelism(int maxDegreeOfParallelism); 8 | 9 | IAsyncLockOptionsBuilder WithHandleLockByKey(bool handleLockByKey); 10 | 11 | IAsyncLockOptionsBuilder WithLockKeyHandler(Func lockKeyHandler); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/Usage.md: -------------------------------------------------------------------------------- 1 | | [Main](/README.md) > Usage | 2 | |----------------------------| 3 | 4 | ### Usage :wrench: 5 | 6 | - [Producers/Consumers](/docs/Core/PubSub.md) 7 | - [Workers](/docs/Core/Workers.md) 8 | - [Serialization](/docs/Serialization/Serialization.md) 9 | - [Blocking Retry](/docs/Retry/PollyBlockingRetry.md) 10 | - [Idempotency](/docs/Idempotency/RedisIdempotency.md) 11 | - [Distributed Tracing and OpenTelemetry](/docs/OpenTelemetry/OpenTelemetry.md) 12 | 13 | | [Go Back](/README.md) | 14 | |-----------------------| -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IKafkaConsumerLifecycleWorker``.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Confluent.Kafka.Core.Hosting 5 | { 6 | public interface IKafkaConsumerLifecycleWorker 7 | { 8 | Task StartAsync(IKafkaConsumerWorkerOptions options, CancellationToken cancellationToken); 9 | 10 | Task StopAsync(IKafkaConsumerWorkerOptions options, CancellationToken cancellationToken); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/MessageValueExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Models.Internal 4 | { 5 | internal static class MessageValueExtensions 6 | { 7 | public static Guid? GetId(this IMessageValue messageValue) 8 | { 9 | var messageId = messageValue is not null && messageValue.Id != Guid.Empty 10 | ? new Guid?(messageValue.Id) 11 | : null; 12 | 13 | return messageId; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Client/IConfigBuilder`.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Confluent.Kafka.Core.Client 4 | { 5 | public interface IConfigBuilder where TBuilder : IConfigBuilder 6 | { 7 | TBuilder WithCancellationDelayMaxMs(int cancellationDelayMaxMs); 8 | 9 | TBuilder WithConfigProperty(KeyValuePair configProperty); 10 | 11 | TBuilder WithConfigProperty(string configPropertyKey, string configPropertyValue); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerHandlerFactory``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Producer 4 | { 5 | public interface IKafkaProducerHandlerFactory 6 | { 7 | Action, string> CreateStatisticsHandler(); 8 | 9 | Action, Error> CreateErrorHandler(); 10 | 11 | Action, LogMessage> CreateLogHandler(); 12 | 13 | Func CreateMessageIdHandler(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/AsyncLockContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Confluent.Kafka.Core.Threading.Internal 4 | { 5 | internal sealed class AsyncLockContext 6 | { 7 | public static readonly AsyncLockContext Empty = new(); 8 | 9 | public IDictionary Items { get; } = new Dictionary(); 10 | 11 | public object this[object key] 12 | { 13 | get => Items[key]; 14 | set => Items[key] = value; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/INewtonsoftJsonSchemaGeneratorSettingsBuilder.cs: -------------------------------------------------------------------------------- 1 | #if NET8_0_OR_GREATER 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json 6 | { 7 | public interface INewtonsoftJsonSchemaGeneratorSettingsBuilder : 8 | IJsonSchemaGeneratorSettingsBuilder 9 | { 10 | INewtonsoftJsonSchemaGeneratorSettingsBuilder WithSerializerSettings(JsonSerializerSettings serializerSettings); 11 | } 12 | } 13 | 14 | #endif -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/ISchemaBuilder`.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry 5 | { 6 | public interface ISchemaBuilder where TBuilder : ISchemaBuilder 7 | { 8 | TBuilder FromConfiguration(string sectionKey); 9 | 10 | TBuilder WithSchemaString(string schemaString); 11 | 12 | TBuilder WithSchemaType(SchemaType schemaType); 13 | 14 | TBuilder WithSchemaReferences(List schemaReferences); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/ISchemaRegistryClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry 5 | { 6 | public interface ISchemaRegistryClientBuilder 7 | { 8 | ISchemaRegistryClientBuilder WithSchemaRegistryConfiguration( 9 | Action configureSchemaRegistry); 10 | 11 | ISchemaRegistryClientBuilder WithAuthenticationHeaderValueProvider( 12 | IAuthenticationHeaderValueProvider authenticationHeaderValueProvider); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Models/KafkaTopicPartitionLag.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Models 2 | { 3 | public sealed class KafkaTopicPartitionLag 4 | { 5 | public string Topic { get; } 6 | public Partition Partition { get; } 7 | public long Lag { get; } 8 | 9 | public TopicPartition TopicPartition => new(Topic, Partition); 10 | 11 | public KafkaTopicPartitionLag(string topic, Partition partition, long lag) 12 | { 13 | Topic = topic; 14 | Partition = partition; 15 | Lag = lag; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/IRedisIdempotencyHandlerBuilder``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Idempotency.Redis 4 | { 5 | public interface IRedisIdempotencyHandlerBuilder 6 | { 7 | IRedisIdempotencyHandlerBuilder WithRedisClient( 8 | Action configureOptions, 9 | object clientKey = null); 10 | 11 | IRedisIdempotencyHandlerBuilder WithHandlerOptions( 12 | Action> configureOptions); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/KafkaDiagnosticsManager.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Diagnostics.Internal 2 | { 3 | internal sealed class KafkaDiagnosticsManager : KafkaDiagnosticsManagerBase 4 | { 5 | protected override ActivitySourceBase ActivitySource { get; } 6 | protected override IKafkaActivityEnricher ActivityEnricher { get; } 7 | 8 | public KafkaDiagnosticsManager(KafkaEnrichmentOptions options) 9 | { 10 | ActivitySource = new KafkaActivitySource(); 11 | ActivityEnricher = new KafkaActivityEnricher(options); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/IKafkaDiagnosticsManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics 5 | { 6 | public interface IKafkaDiagnosticsManager : IContextPropagator, IKafkaActivityEnricher 7 | { 8 | Activity StartActivity(string activityName, ActivityKind activityKind, IPropagationContext propagationContext); 9 | 10 | Activity StartProducerActivity(string activityName, IDictionary carrier); 11 | 12 | Activity StartConsumerActivity(string activityName, IDictionary carrier); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Shared/Builders/FunctionalBuilder``.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace Confluent.Kafka.Core.Internal 4 | { 5 | internal abstract class FunctionalBuilder : 6 | FunctionalBuilder, 7 | IFunctionalBuilder 8 | where TSubject : class 9 | where TSelf : FunctionalBuilder 10 | { 11 | protected FunctionalBuilder( 12 | TSubject seedSubject = null, 13 | IConfiguration configuration = null) 14 | : base(seedSubject: seedSubject, configuration) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaConsumptionEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Consumer; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaConsumptionEnrichmentContext 6 | { 7 | public string Topic { get; init; } 8 | public Partition Partition { get; init; } 9 | public Offset Offset { get; init; } 10 | public object Key { get; init; } 11 | public object Value { get; init; } 12 | public Timestamp Timestamp { get; init; } 13 | public Headers Headers { get; init; } 14 | public IKafkaConsumerConfig ConsumerConfig { get; init; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Confluent.Kafka.Core.Tests.Extensions 5 | { 6 | public static class EnumExtensions 7 | { 8 | public static string GetDescription(this TEnum value) where TEnum : Enum 9 | { 10 | var type = typeof(TEnum); 11 | 12 | var field = type.GetField(value.ToString()); 13 | 14 | var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); 15 | 16 | return attribute is null ? value.ToString() : attribute.Description; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/PropagationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics.Internal 5 | { 6 | internal sealed class PropagationContext : IPropagationContext 7 | { 8 | public ActivityContext ActivityContext { get; } 9 | public IEnumerable> Baggage { get; } = []; 10 | 11 | public PropagationContext(ActivityContext activityContext, IEnumerable> baggage) 12 | { 13 | ActivityContext = activityContext; 14 | Baggage = baggage ?? []; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/HeadersExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Models.Internal 5 | { 6 | using System.Text; 7 | 8 | internal static class HeadersExtensions 9 | { 10 | public static IDictionary ToDictionary(this Headers headers, Encoding encoding = null) 11 | { 12 | if (headers is null) 13 | { 14 | throw new ArgumentNullException(nameof(headers)); 15 | } 16 | 17 | var kafkaHeaders = new KafkaHeaders(headers, encoding); 18 | 19 | return kafkaHeaders; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Encoding/EncodingFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Encoding 4 | { 5 | using System.Text; 6 | 7 | public sealed class EncodingFactory : IEncodingFactory 8 | { 9 | private static readonly Lazy Factory = new( 10 | () => new EncodingFactory(), isThreadSafe: true); 11 | 12 | public static EncodingFactory Instance => Factory.Value; 13 | 14 | private EncodingFactory() 15 | { } 16 | 17 | public Encoding CreateDefault() => new UTF8Encoding( 18 | encoderShouldEmitUTF8Identifier: false, 19 | throwOnInvalidBytes: false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/Serialization/Serialization.md: -------------------------------------------------------------------------------- 1 | | [Main](/README.md) > [Usage](/docs/Usage.md) > Serialization | 2 | |--------------------------------------------------------------| 3 | 4 | ### Serialization :outbox_tray: 5 | 6 | 7 | - [System.Text.Json (JsonCore)](/docs/Serialization/JsonCore.md) 8 | - [Newtonsoft.Json (Json.NET)](/docs/Serialization/JsonNET.md) 9 | - [protobuf-net](/docs/Serialization/ProtobufNet.md) 10 | - [SchemaRegistry.Avro](/docs/Serialization/SchemaRegistryAvro.md) 11 | - [SchemaRegistry.Json](/docs/Serialization/SchemaRegistryJson.md) 12 | - [SchemaRegistry.Protobuf](/docs/Serialization/SchemaRegistryProtobuf.md) 13 | 14 | 15 | | [Go Back](/docs/Usage.md) | 16 | |---------------------------| -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/TopicPartitionException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Models 5 | { 6 | public sealed class TopicPartitionException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(TopicPartition)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public TopicPartitionException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Producer 5 | { 6 | public interface IKafkaProducerConfig : IProducerConfig, IValidatableObject 7 | { 8 | string DefaultTopic { get; } 9 | 10 | Partition DefaultPartition { get; } 11 | 12 | TimeSpan DefaultTimeout { get; } 13 | 14 | bool PollAfterProducing { get; } 15 | 16 | bool EnableLogging { get; } 17 | 18 | bool EnableDiagnostics { get; } 19 | 20 | bool EnableRetryOnFailure { get; } 21 | 22 | bool EnableInterceptorExceptionPropagation { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Retry/IRetryHandler``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Retry 6 | { 7 | public interface IRetryHandler 8 | { 9 | void Handle( 10 | Action executeAction, 11 | Action onRetryAction = null, 12 | CancellationToken cancellationToken = default); 13 | 14 | Task HandleAsync( 15 | Func executeAction, 16 | Action onRetryAction = null, 17 | CancellationToken cancellationToken = default); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/KafkaConsumerConfigException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Consumer 5 | { 6 | public sealed class KafkaConsumerConfigException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(KafkaConsumerConfig)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public KafkaConsumerConfigException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/KafkaProducerConfigException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Producer 5 | { 6 | public sealed class KafkaProducerConfigException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(KafkaProducerConfig)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public KafkaProducerConfigException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Shared/IsExternalInit.cs: -------------------------------------------------------------------------------- 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 | 4 | #if NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 5 | 6 | namespace System.Runtime.CompilerServices 7 | { 8 | using System.ComponentModel; 9 | 10 | /// 11 | /// Reserved to be used by the compiler for tracking metadata. 12 | /// 13 | [EditorBrowsable(EditorBrowsableState.Never)] 14 | internal static class IsExternalInit 15 | { } 16 | } 17 | 18 | #endif -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/KafkaAsyncProductionEnrichmentContext.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Producer; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics 4 | { 5 | public class KafkaAsyncProductionEnrichmentContext 6 | { 7 | public string Topic { get; init; } 8 | public Partition Partition { get; init; } 9 | public Offset Offset { get; init; } 10 | public object Key { get; init; } 11 | public object Value { get; init; } 12 | public Timestamp Timestamp { get; init; } 13 | public Headers Headers { get; init; } 14 | public PersistenceStatus Status { get; init; } 15 | public IKafkaProducerConfig ProducerConfig { get; init; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Hosting/KafkaConsumerWorkerConfigException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Hosting 5 | { 6 | public sealed class KafkaConsumerWorkerConfigException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(KafkaConsumerWorkerConfig)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public KafkaConsumerWorkerConfigException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerOptionsException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Retry.Polly 5 | { 6 | public sealed class PollyRetryHandlerOptionsException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(PollyRetryHandlerOptions)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public PollyRetryHandlerOptionsException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/AsyncLockOptionsException.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Threading.Internal; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Threading 6 | { 7 | public sealed class AsyncLockOptionsException : ValidationException 8 | { 9 | private const string ExceptionMessage = $"One or more errors have occurred while validating an '{nameof(AsyncLockOptions)}' instance."; 10 | 11 | public IEnumerable Results { get; } 12 | 13 | public AsyncLockOptionsException(IEnumerable results) 14 | : base(ExceptionMessage) 15 | { 16 | Results = results ?? []; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/HeaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Models.Internal 4 | { 5 | using System.Text; 6 | 7 | internal static class HeaderExtensions 8 | { 9 | public static string GetStringValue(this IHeader header, Encoding encoding) 10 | { 11 | if (header is null) 12 | { 13 | throw new ArgumentNullException(nameof(header)); 14 | } 15 | 16 | if (encoding is null) 17 | { 18 | throw new ArgumentNullException(nameof(encoding)); 19 | } 20 | 21 | var stringValue = encoding.GetString(header.GetValueBytes()); 22 | 23 | return stringValue; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Hosting/Retry/KafkaRetryConsumerWorkerConfigException.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Hosting.Retry 5 | { 6 | public sealed class KafkaRetryConsumerWorkerConfigException : ValidationException 7 | { 8 | private const string ExceptionMessage = $"One or more errors have occurred while validating a '{nameof(KafkaRetryConsumerWorkerConfig)}' instance."; 9 | 10 | public IEnumerable Results { get; } 11 | 12 | public KafkaRetryConsumerWorkerConfigException(IEnumerable results) 13 | : base(ExceptionMessage) 14 | { 15 | Results = results ?? []; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Mapping/Internal/MapperExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Mapping.Internal 5 | { 6 | internal static class MapperExtensions 7 | { 8 | internal static TDestination Map(this object sourceObject, params object[] args) 9 | { 10 | if (sourceObject is not IMapper mapper) 11 | { 12 | var mapperType = typeof(IMapper).ExtractTypeName(); 13 | 14 | throw new InvalidCastException($"{nameof(sourceObject)} should be of type '{mapperType}'."); 15 | } 16 | 17 | var result = mapper.Map(args); 18 | 19 | return result; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Extensions/MockExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Moq; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Tests.Extensions 6 | { 7 | public static class MockExtensions 8 | { 9 | public static void VerifyLog(this Mock mockLogger, LogLevel logLevel, Times times) 10 | { 11 | mockLogger.Verify( 12 | logger => logger.Log( 13 | logLevel, 14 | It.IsAny(), 15 | It.Is((state, type) => true), 16 | It.IsAny(), 17 | It.Is>((state, type) => true)), 18 | times); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/ProducerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Producer.Internal 5 | { 6 | internal static class ProducerExtensions 7 | { 8 | public static IKafkaProducer ToKafkaProducer(this IProducer producer) 9 | { 10 | if (producer is not IKafkaProducer kafkaProducer) 11 | { 12 | var kafkaProducerType = typeof(IKafkaProducer).ExtractTypeName(); 13 | 14 | throw new InvalidCastException($"{nameof(producer)} should be of type '{kafkaProducerType}'."); 15 | } 16 | 17 | return kafkaProducer; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.JsonCore/Internal/IJsonCoreSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.JsonCore.Internal 5 | { 6 | internal interface IJsonCoreSerializerFactory 7 | { 8 | JsonCoreSerializer GetOrCreateSerializer( 9 | IServiceProvider serviceProvider, 10 | IConfiguration configuration, 11 | Action configureOptions, 12 | object serializerKey); 13 | 14 | JsonCoreSerializer CreateSerializer( 15 | IServiceProvider serviceProvider, 16 | IConfiguration configuration, 17 | Action configureOptions); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/AsyncLockOptionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Threading.Internal 4 | { 5 | internal static class AsyncLockOptionsBuilderExtensions 6 | { 7 | public static IAsyncLockOptionsBuilder WithHandleLockByKey(this IAsyncLockOptionsBuilder builder, Func keyFactory) 8 | { 9 | if (builder is null) 10 | { 11 | throw new ArgumentNullException(nameof(builder)); 12 | } 13 | 14 | if (keyFactory is null) 15 | { 16 | throw new ArgumentNullException(nameof(keyFactory)); 17 | } 18 | 19 | builder.WithHandleLockByKey(keyFactory.Invoke()); 20 | 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Shared/Extensions/LoggerFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Logging.Abstractions; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Internal 6 | { 7 | internal static class LoggerFactoryExtensions 8 | { 9 | public static ILogger CreateLogger(this ILoggerFactory loggerFactory, bool enableLogging, Type categoryType) 10 | { 11 | var logger = (enableLogging, loggerFactory) switch 12 | { 13 | (enableLogging: false, loggerFactory: _) or 14 | (enableLogging: true, loggerFactory: null) => NullLogger.Instance, 15 | (enableLogging: true, loggerFactory: not null) => loggerFactory.CreateLogger(categoryType) 16 | }; 17 | 18 | return logger; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/Internal/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Retry.Polly.Internal 5 | { 6 | internal static partial class LoggerExtensions 7 | { 8 | [LoggerMessage( 9 | Level = LogLevel.Debug, 10 | Message = "The execution will not be retried as the configured filters have ruled out the possibility of retry.")] 11 | public static partial void LogExecutionNotRetriable(this ILogger logger); 12 | 13 | [LoggerMessage( 14 | Level = LogLevel.Error, 15 | Message = "An exception has occurred during the retry attempt {RetryAttempt}.")] 16 | public static partial void LogRetryExecutionFailure(this ILogger logger, Exception exception, int retryAttempt); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Conversion/Internal/OptionsConverterExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Conversion.Internal 5 | { 6 | internal static class OptionsConverterExtensions 7 | { 8 | internal static TOptions ToOptions(this object sourceObject) 9 | { 10 | if (sourceObject is not IOptionsConverter optionsConverter) 11 | { 12 | var optionsConverterType = typeof(IOptionsConverter).ExtractTypeName(); 13 | 14 | throw new InvalidCastException($"{nameof(sourceObject)} should be of type '{optionsConverterType}'."); 15 | } 16 | 17 | var options = optionsConverter.ToOptions(); 18 | 19 | return options; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/Internal/ISchemaRegistryClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Internal 6 | { 7 | internal interface ISchemaRegistryClientFactory 8 | { 9 | ISchemaRegistryClient GetOrCreateSchemaRegistryClient( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | Action configureClient, 13 | object clientKey); 14 | 15 | ISchemaRegistryClient CreateSchemaRegistryClient( 16 | IServiceProvider serviceProvider, 17 | IConfiguration configuration, 18 | Action configureClient); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/Internal/IProtobufNetSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.ProtobufNet.Internal 5 | { 6 | internal interface IProtobufNetSerializerFactory 7 | { 8 | ProtobufNetSerializer GetOrCreateSerializer( 9 | IServiceProvider serviceProvider, 10 | IConfiguration configuration, 11 | Action configureOptions, 12 | object serializerKey); 13 | 14 | ProtobufNetSerializer CreateSerializer( 15 | IServiceProvider serviceProvider, 16 | IConfiguration configuration, 17 | Action configureOptions); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.NewtonsoftJson/Internal/INewtonsoftJsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.NewtonsoftJson.Internal 5 | { 6 | internal interface INewtonsoftJsonSerializerFactory 7 | { 8 | NewtonsoftJsonSerializer GetOrCreateSerializer( 9 | IServiceProvider serviceProvider, 10 | IConfiguration configuration, 11 | Action configureSettings, 12 | object serializerKey); 13 | 14 | NewtonsoftJsonSerializer CreateSerializer( 15 | IServiceProvider serviceProvider, 16 | IConfiguration configuration, 17 | Action configureSettings); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/IKafkaDiagnosticsManagerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Diagnostics.Internal 4 | { 5 | internal interface IKafkaDiagnosticsManagerFactory 6 | { 7 | IKafkaDiagnosticsManager GetOrCreateDiagnosticsManager( 8 | IServiceProvider serviceProvider, 9 | bool enableDiagnostics, 10 | Action configureOptions); 11 | 12 | IKafkaDiagnosticsManager GetOrCreateDiagnosticsManager( 13 | IServiceProvider serviceProvider, 14 | Action configureOptions); 15 | 16 | IKafkaDiagnosticsManager CreateDiagnosticsManager( 17 | IServiceProvider serviceProvider, 18 | Action configureOptions); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/KafkaNoopDiagnosticsManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics.Internal 5 | { 6 | internal sealed class KafkaNoopDiagnosticsManager : KafkaDiagnosticsManagerBase 7 | { 8 | private static readonly Lazy Factory = new( 9 | () => new KafkaNoopDiagnosticsManager(), isThreadSafe: true); 10 | 11 | public static KafkaNoopDiagnosticsManager Instance => Factory.Value; 12 | 13 | protected override ActivitySourceBase ActivitySource { get; } = null; 14 | protected override IKafkaActivityEnricher ActivityEnricher { get; } = null; 15 | protected override DistributedContextPropagator Propagator { get; } = null; 16 | 17 | private KafkaNoopDiagnosticsManager() 18 | { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Consumer 6 | { 7 | public interface IKafkaConsumerConfig : IConsumerConfig, IValidatableObject 8 | { 9 | IEnumerable TopicSubscriptions { get; } 10 | 11 | IEnumerable PartitionAssignments { get; } 12 | 13 | bool CommitAfterConsuming { get; } 14 | 15 | TimeSpan DefaultTimeout { get; } 16 | 17 | int DefaultBatchSize { get; } 18 | 19 | bool EnableLogging { get; } 20 | 21 | bool EnableDiagnostics { get; } 22 | 23 | bool EnableDeadLetterTopic { get; } 24 | 25 | bool EnableRetryOnFailure { get; } 26 | 27 | bool EnableInterceptorExceptionPropagation { get; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/Internal/ISchemaRegistryAvroSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro.Internal 5 | { 6 | internal interface ISchemaRegistryAvroSerializerFactory 7 | { 8 | SchemaRegistryAvroSerializer GetOrCreateSerializer( 9 | IServiceProvider serviceProvider, 10 | IConfiguration configuration, 11 | Action configureSerializer, 12 | object serializerKey); 13 | 14 | SchemaRegistryAvroSerializer CreateSerializer( 15 | IServiceProvider serviceProvider, 16 | IConfiguration configuration, 17 | Action configureSerializer); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisIdempotencyHandlerOptionsException``.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Idempotency.Redis 6 | { 7 | public sealed class RedisIdempotencyHandlerOptionsException : ValidationException 8 | { 9 | private static readonly string ExceptionMessage = 10 | $"One or more errors have occurred while validating a '{typeof(RedisIdempotencyHandlerOptions).ExtractTypeName()}' instance."; 11 | 12 | public IEnumerable Results { get; } 13 | 14 | public RedisIdempotencyHandlerOptionsException(IEnumerable results) 15 | : base(ExceptionMessage) 16 | { 17 | Results = results ?? []; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/ConsumeExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Confluent.Kafka.Core.Consumer 5 | { 6 | public static class ConsumeExceptionExtensions 7 | { 8 | private static readonly ErrorCode[] DeserializationCodes = 9 | [ 10 | ErrorCode.Local_KeyDeserialization, 11 | ErrorCode.Local_ValueDeserialization 12 | ]; 13 | 14 | public static bool IsDeserializationException(this ConsumeException consumeException) 15 | { 16 | if (consumeException is null) 17 | { 18 | throw new ArgumentNullException(nameof(consumeException)); 19 | } 20 | 21 | var isDeserializationException = DeserializationCodes.Contains(consumeException.Error!.Code); 22 | 23 | return isDeserializationException; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/AsyncLockFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Threading.Internal 4 | { 5 | internal sealed class AsyncLockFactory : IAsyncLockFactory 6 | { 7 | private static readonly Lazy Factory = new( 8 | () => new AsyncLockFactory(), isThreadSafe: true); 9 | 10 | public static AsyncLockFactory Instance => Factory.Value; 11 | 12 | private AsyncLockFactory() 13 | { } 14 | 15 | public AsyncLock CreateAsyncLock(Action configureOptions) 16 | { 17 | var options = AsyncLockOptionsBuilder.Build(configureOptions); 18 | 19 | var asyncLock = CreateAsyncLock(options); 20 | 21 | return asyncLock; 22 | } 23 | 24 | public AsyncLock CreateAsyncLock(AsyncLockOptions options) => new(options); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry.Polly; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class PollyRetryHandlerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddPollyRetryHandler( 10 | this IKafkaBuilder builder, 11 | Action configureOptions = null, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | builder.Services!.AddPollyRetryHandler(configureOptions, handlerKey); 20 | 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Serialization/Internal/SyncOverAsyncSerializerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.SyncOverAsync; 2 | using System.Reflection; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.Internal 5 | { 6 | internal static class SyncOverAsyncSerializerExtensions 7 | { 8 | public static IAsyncSerializer GetInnerSerializer(this SyncOverAsyncSerializer syncOverAsyncSerializer) 9 | { 10 | IAsyncSerializer innerSerializer = null; 11 | 12 | if (syncOverAsyncSerializer is not null) 13 | { 14 | innerSerializer = (IAsyncSerializer)typeof(SyncOverAsyncSerializer) 15 | .GetProperty("asyncSerializer", BindingFlags.NonPublic | BindingFlags.Instance) 16 | ?.GetValue(syncOverAsyncSerializer); 17 | } 18 | 19 | return innerSerializer; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/ProduceExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Confluent.Kafka.Core.Producer 5 | { 6 | public static class ProduceExceptionExtensions 7 | { 8 | private static readonly ErrorCode[] SerializationCodes = 9 | [ 10 | ErrorCode.Local_KeySerialization, 11 | ErrorCode.Local_ValueSerialization 12 | ]; 13 | 14 | public static bool IsSerializationException(this ProduceException produceException) 15 | { 16 | if (produceException is null) 17 | { 18 | throw new ArgumentNullException(nameof(produceException)); 19 | } 20 | 21 | var isSerializationException = SerializationCodes.Contains(produceException.Error!.Code); 22 | 23 | return isSerializationException; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.JsonCore/JsonCoreSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.JsonCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class JsonCoreSerializerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddJsonCoreSerializer( 10 | this IKafkaBuilder builder, 11 | Action configureOptions = null, 12 | object serializerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | builder.Services!.AddJsonCoreSerializer(configureOptions, serializerKey); 20 | 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/ISchemaRegistryAvroSerializerBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro 5 | { 6 | public interface ISchemaRegistryAvroSerializerBuilder 7 | { 8 | ISchemaRegistryAvroSerializerBuilder WithSchemaRegistryClient( 9 | Action configureClient, 10 | object clientKey = null); 11 | 12 | ISchemaRegistryAvroSerializerBuilder WithSerializerConfiguration( 13 | Action configureSerializer); 14 | 15 | ISchemaRegistryAvroSerializerBuilder WithDeserializerConfiguration( 16 | Action configureDeserializer); 17 | 18 | ISchemaRegistryAvroSerializerBuilder WithRuleRegistry( 19 | RuleRegistry ruleRegistry); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/Internal/IRedisClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using StackExchange.Redis; 4 | using System; 5 | 6 | namespace Confluent.Kafka.Core.Idempotency.Redis.Internal 7 | { 8 | internal interface IRedisClientFactory 9 | { 10 | IConnectionMultiplexer GetOrCreateRedisClient( 11 | IServiceProvider serviceProvider, 12 | IConfiguration configuration, 13 | ILoggerFactory loggerFactory, 14 | Action configureOptions, 15 | object clientKey); 16 | 17 | IConnectionMultiplexer CreateRedisClient( 18 | IServiceProvider serviceProvider, 19 | IConfiguration configuration, 20 | ILoggerFactory loggerFactory, 21 | Action configureOptions); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Specifications/ExpressionSpecification`.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Confluent.Kafka.Core.Specifications 5 | { 6 | public abstract class ExpressionSpecification : ISpecification 7 | { 8 | private readonly Func _predicate; 9 | 10 | protected Expression> Expression { get; } 11 | 12 | protected ExpressionSpecification(Expression> expression) 13 | { 14 | if (expression is null) 15 | { 16 | throw new ArgumentNullException(nameof(expression)); 17 | } 18 | 19 | _predicate = expression.Compile(); 20 | Expression = expression; 21 | } 22 | 23 | protected virtual bool Evaluate(T source) => _predicate.Invoke(source); 24 | 25 | public bool IsSatisfiedBy(T source) => Evaluate(source); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Internal/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Internal 5 | { 6 | internal static class DictionaryExtensions 7 | { 8 | public static void AddOrUpdate(this IDictionary dictionary, TKey key, TValue value) 9 | where TKey : notnull 10 | { 11 | if (dictionary is null) 12 | { 13 | throw new ArgumentNullException(nameof(dictionary)); 14 | } 15 | 16 | if (dictionary.IsReadOnly) 17 | { 18 | throw new NotSupportedException($"{nameof(dictionary)} cannot be read-only."); 19 | } 20 | 21 | if (key is null) 22 | { 23 | throw new ArgumentNullException(nameof(key)); 24 | } 25 | 26 | dictionary[key] = value; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Serialization/Internal/SyncOverAsyncDeserializerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.SyncOverAsync; 2 | using System.Reflection; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.Internal 5 | { 6 | internal static class SyncOverAsyncDeserializerExtensions 7 | { 8 | public static IAsyncDeserializer GetInnerDeserializer(this SyncOverAsyncDeserializer syncOverAsyncDeserializer) 9 | { 10 | IAsyncDeserializer innerDeserializer = null; 11 | 12 | if (syncOverAsyncDeserializer is not null) 13 | { 14 | innerDeserializer = (IAsyncDeserializer)typeof(SyncOverAsyncDeserializer) 15 | .GetProperty("asyncDeserializer", BindingFlags.NonPublic | BindingFlags.Instance) 16 | ?.GetValue(syncOverAsyncDeserializer); 17 | } 18 | 19 | return innerDeserializer; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/ProtobufNetSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.ProtobufNet; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class ProtobufNetSerializerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddProtobufNetSerializer( 10 | this IKafkaBuilder builder, 11 | Action configureOptions = null, 12 | object serializerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | builder.Services!.AddProtobufNetSerializer(configureOptions, serializerKey); 20 | 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/KafkaConsumerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Consumer.Internal 5 | { 6 | public static partial class KafkaConsumerBuilderExtensions 7 | { 8 | internal static IConsumer BuildUnderlyingConsumer(this IKafkaConsumerBuilder consumerBuilder) 9 | { 10 | if (consumerBuilder is not IConsumerBuilder underlyingConsumerBuilder) 11 | { 12 | var consumerBuilderType = typeof(IConsumerBuilder).ExtractTypeName(); 13 | 14 | throw new InvalidCastException($"{nameof(consumerBuilder)} should be of type '{consumerBuilderType}'."); 15 | } 16 | 17 | var underlyingConsumer = underlyingConsumerBuilder.Build(); 18 | 19 | return underlyingConsumer; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Producer.Internal 5 | { 6 | public static partial class KafkaProducerBuilderExtensions 7 | { 8 | internal static IProducer BuildUnderlyingProducer(this IKafkaProducerBuilder producerBuilder) 9 | { 10 | if (producerBuilder is not IProducerBuilder underlyingProducerBuilder) 11 | { 12 | var producerBuilderType = typeof(IProducerBuilder).ExtractTypeName(); 13 | 14 | throw new InvalidCastException($"{nameof(producerBuilder)} should be of type '{producerBuilderType}'."); 15 | } 16 | 17 | var underlyingProducer = underlyingProducerBuilder.Build(); 18 | 19 | return underlyingProducer; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/Internal/IPollyRetryHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Retry.Polly.Internal 6 | { 7 | internal interface IPollyRetryHandlerFactory 8 | { 9 | IRetryHandler GetOrCreateRetryHandler( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action configureOptions, 14 | object handlerKey); 15 | 16 | IRetryHandler CreateRetryHandler( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action configureOptions); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/Internal/ISchemaRegistryJsonSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json.Internal 5 | { 6 | internal interface ISchemaRegistryJsonSerializerFactory 7 | { 8 | SchemaRegistryJsonSerializer GetOrCreateSerializer( 9 | IServiceProvider serviceProvider, 10 | IConfiguration configuration, 11 | Action configureSerializer, 12 | object serializerKey) 13 | where T : class; 14 | 15 | SchemaRegistryJsonSerializer CreateSerializer( 16 | IServiceProvider serviceProvider, 17 | IConfiguration configuration, 18 | Action configureSerializer) 19 | where T : class; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Internal/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Internal 5 | { 6 | internal static class ExceptionExtensions 7 | { 8 | public static IEnumerable GetInnerExceptions(this Exception exception) 9 | { 10 | if (exception is null) 11 | { 12 | throw new ArgumentNullException(nameof(exception)); 13 | } 14 | 15 | var exceptions = new List(); 16 | 17 | var exceptionInternal = exception; 18 | 19 | do 20 | { 21 | exceptions.Add(exceptionInternal); 22 | 23 | exceptionInternal = exceptionInternal.InnerException; 24 | } 25 | while (exceptionInternal is not null); 26 | 27 | exceptions.Reverse(); 28 | 29 | return exceptions; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.NewtonsoftJson/NewtonsoftJsonSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.NewtonsoftJson; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class NewtonsoftJsonSerializerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddNewtonsoftJsonSerializer( 10 | this IKafkaBuilder builder, 11 | Action configureSettings = null, 12 | object serializerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | builder.Services!.AddNewtonsoftJsonSerializer(configureSettings, serializerKey); 20 | 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/ISchemaRegistryProtobufSerializerBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf 5 | { 6 | public interface ISchemaRegistryProtobufSerializerBuilder 7 | { 8 | ISchemaRegistryProtobufSerializerBuilder WithSchemaRegistryClient( 9 | Action configureClient, 10 | object clientKey = null); 11 | 12 | ISchemaRegistryProtobufSerializerBuilder WithSerializerConfiguration( 13 | Action configureSerializer); 14 | 15 | ISchemaRegistryProtobufSerializerBuilder WithDeserializerConfiguration( 16 | Action configureDeserializer); 17 | 18 | ISchemaRegistryProtobufSerializerBuilder WithRuleRegistry( 19 | RuleRegistry ruleRegistry); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/IAvroDeserializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro 5 | { 6 | public interface IAvroDeserializerConfigBuilder 7 | { 8 | IAvroDeserializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IAvroDeserializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 11 | 12 | IAvroDeserializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 13 | 14 | IAvroDeserializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 15 | 16 | IAvroDeserializerConfigBuilder WithSchemaIdStrategy(SchemaIdDeserializerStrategy? schemaIdStrategy); 17 | 18 | IAvroDeserializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Serialization/Internal/SerializationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.SyncOverAsync; 2 | 3 | namespace Confluent.Kafka.Core.Serialization.Internal 4 | { 5 | internal static class SerializationExtensions 6 | { 7 | public static ISerializer ToSerializer(this object sourceObject) 8 | { 9 | if (sourceObject is ISerializer serializer) 10 | { 11 | return serializer; 12 | } 13 | 14 | if (sourceObject is SyncOverAsyncDeserializer syncDeserializer) 15 | { 16 | var innerDeserializer = syncDeserializer.GetInnerDeserializer(); 17 | 18 | if (innerDeserializer is IAsyncSerializer asyncSerializer) 19 | { 20 | return asyncSerializer.AsSyncOverAsync(); 21 | } 22 | } 23 | 24 | return KafkaSerialization.TryGetSerializer(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/IKafkaConsumerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Consumer.Internal 6 | { 7 | internal interface IKafkaConsumerFactory 8 | { 9 | IKafkaConsumer GetOrCreateConsumer( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action> configureConsumer, 14 | object consumerKey); 15 | 16 | IKafkaConsumer CreateConsumer( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action> configureConsumer, 21 | object consumerKey); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/IKafkaProducerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Producer.Internal 6 | { 7 | internal interface IKafkaProducerFactory 8 | { 9 | IKafkaProducer GetOrCreateProducer( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action> configureProducer, 14 | object producerKey); 15 | 16 | IKafkaProducer CreateProducer( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action> configureProducer, 21 | object producerKey); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerHandlerFactory``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Consumer 5 | { 6 | public interface IKafkaConsumerHandlerFactory 7 | { 8 | Action, string> CreateStatisticsHandler(); 9 | 10 | Action, Error> CreateErrorHandler(); 11 | 12 | Action, LogMessage> CreateLogHandler(); 13 | 14 | Action, List> CreatePartitionsAssignedHandler(); 15 | 16 | Action, List> CreatePartitionsRevokedHandler(); 17 | 18 | Action, List> CreatePartitionsLostHandler(); 19 | 20 | Action, CommittedOffsets> CreateOffsetsCommittedHandler(); 21 | 22 | Func CreateMessageIdHandler(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/IPollyRetryHandlerOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Retry.Polly 5 | { 6 | public interface IPollyRetryHandlerOptionsBuilder 7 | { 8 | IPollyRetryHandlerOptionsBuilder FromConfiguration(string sectionKey); 9 | 10 | IPollyRetryHandlerOptionsBuilder WithRetryCount(int retryCount); 11 | 12 | IPollyRetryHandlerOptionsBuilder WithRetryDelay(TimeSpan retryDelay); 13 | 14 | IPollyRetryHandlerOptionsBuilder WithDelayProvider(Func delayProvider); 15 | 16 | IPollyRetryHandlerOptionsBuilder WithDelays(IEnumerable delays); 17 | 18 | IPollyRetryHandlerOptionsBuilder WithExceptionTypeFilters(string[] exceptionTypeFilters); 19 | 20 | IPollyRetryHandlerOptionsBuilder WithExceptionFilter(Func exceptionFilter); 21 | 22 | IPollyRetryHandlerOptionsBuilder WithEnableLogging(bool enableLogging); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/IKafkaConsumerHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Consumer.Internal 6 | { 7 | internal interface IKafkaConsumerHandlerFactory 8 | { 9 | IKafkaConsumerHandlerFactory GetOrCreateHandlerFactory( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action configureOptions, 14 | object consumerKey); 15 | 16 | IKafkaConsumerHandlerFactory CreateHandlerFactory( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action configureOptions); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/KafkaEnrichmentOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics.Internal 5 | { 6 | internal sealed class KafkaEnrichmentOptions 7 | { 8 | public Action EnrichConsumptionFailure { get; set; } 9 | public Action EnrichConsumption { get; set; } 10 | public Action EnrichProductionFailure { get; set; } 11 | public Action EnrichSyncProduction { get; set; } 12 | public Action EnrichAsyncProduction { get; set; } 13 | public Action EnrichProcessingFailure { get; set; } 14 | public Action EnrichProcessing { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/IKafkaProducerHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Producer.Internal 6 | { 7 | internal interface IKafkaProducerHandlerFactory 8 | { 9 | IKafkaProducerHandlerFactory GetOrCreateHandlerFactory( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action configureOptions, 14 | object producerKey); 15 | 16 | IKafkaProducerHandlerFactory CreateHandlerFactory( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action configureOptions); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/ProduceResult``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Producer.Internal 4 | { 5 | internal sealed class ProduceResult 6 | { 7 | public bool DeliveryHandled { get; private set; } 8 | public DeliveryReport DeliveryReport { get; private set; } 9 | public bool Faulted => DeliveryReport.Error!.IsError; 10 | 11 | public ProduceResult(TopicPartition partition, Message message) 12 | { 13 | DeliveryReport = DeliveryReportFactory.Instance.CreateDefault(partition, message); 14 | } 15 | 16 | public void Complete(DeliveryReport deliveryReport) 17 | { 18 | if (deliveryReport is null) 19 | { 20 | throw new ArgumentNullException(nameof(deliveryReport)); 21 | } 22 | 23 | DeliveryHandled = true; 24 | DeliveryReport = deliveryReport; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/DependencyInjection/KafkaServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.DependencyInjection; 2 | using Confluent.Kafka.Core.DependencyInjection.Internal; 3 | using System; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class KafkaServiceCollectionExtensions 8 | { 9 | public static IServiceCollection AddKafka(this IServiceCollection services, Action configureKafka) 10 | { 11 | if (services is null) 12 | { 13 | throw new ArgumentNullException(nameof(services)); 14 | } 15 | 16 | if (configureKafka is null) 17 | { 18 | throw new ArgumentNullException(nameof(configureKafka)); 19 | } 20 | 21 | var builder = new KafkaBuilder(services); 22 | 23 | configureKafka.Invoke(builder); 24 | 25 | builder.AddKafkaDiagnostics(); 26 | 27 | return services; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/Internal/IRedisIdempotencyHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Idempotency.Redis.Internal 6 | { 7 | internal interface IRedisIdempotencyHandlerFactory 8 | { 9 | IIdempotencyHandler GetOrCreateIdempotencyHandler( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | ILoggerFactory loggerFactory, 13 | Action> configureHandler, 14 | object handlerKey); 15 | 16 | IIdempotencyHandler CreateIdempotencyHandler( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | ILoggerFactory loggerFactory, 20 | Action> configureHandler); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/KafkaConsumerConfigExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Confluent.Kafka.Core.Consumer.Internal 4 | { 5 | internal static class KafkaConsumerConfigExtensions 6 | { 7 | public static bool HasTopicSubscriptions(this IKafkaConsumerConfig consumerConfig) 8 | { 9 | var hasTopicSubscriptions = consumerConfig?.TopicSubscriptions is not null && 10 | consumerConfig.TopicSubscriptions.Any(topic => !string.IsNullOrWhiteSpace(topic)); 11 | 12 | return hasTopicSubscriptions; 13 | } 14 | 15 | public static bool HasPartitionAssignments(this IKafkaConsumerConfig consumerConfig) 16 | { 17 | var hasPartitionAssignments = consumerConfig?.PartitionAssignments is not null && 18 | consumerConfig.PartitionAssignments.Any(assignment => assignment is not null); 19 | 20 | return hasPartitionAssignments; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/Retry/IKafkaRetryConsumerWorkerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Confluent.Kafka.Core.Hosting.Retry 5 | { 6 | public interface IKafkaRetryConsumerWorkerConfig : IValidatableObject 7 | { 8 | int MaxDegreeOfParallelism { get; } 9 | 10 | bool EnableLogging { get; } 11 | 12 | bool EnableDiagnostics { get; } 13 | 14 | bool CommitFaultedMessages { get; } 15 | 16 | bool EnableIdempotency { get; } 17 | 18 | bool EnableRetryOnFailure { get; } 19 | 20 | bool EnableDeadLetterTopic { get; } 21 | 22 | int RetryCount { get; } 23 | 24 | TimeSpan EmptyTopicDelay { get; } 25 | 26 | TimeSpan NotEmptyTopicDelay { get; } 27 | 28 | TimeSpan UnavailableProcessingSlotsDelay { get; } 29 | 30 | TimeSpan ExceptionDelay { get; } 31 | 32 | TimeSpan PendingProcessingDelay { get; } 33 | 34 | TimeSpan RetryTopicDelay { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/IRedisIdempotencyHandlerOptionsBuilder``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Idempotency.Redis 4 | { 5 | public interface IRedisIdempotencyHandlerOptionsBuilder 6 | { 7 | IRedisIdempotencyHandlerOptionsBuilder FromConfiguration(string sectionKey); 8 | 9 | IRedisIdempotencyHandlerOptionsBuilder WithGroupId(string groupId); 10 | 11 | IRedisIdempotencyHandlerOptionsBuilder WithConsumerName(string consumerName); 12 | 13 | IRedisIdempotencyHandlerOptionsBuilder WithExpirationInterval(TimeSpan expirationInterval); 14 | 15 | IRedisIdempotencyHandlerOptionsBuilder WithExpirationDelay(TimeSpan expirationDelay); 16 | 17 | IRedisIdempotencyHandlerOptionsBuilder WithMessageIdHandler(Func messageIdHandler); 18 | 19 | IRedisIdempotencyHandlerOptionsBuilder WithEnableLogging(bool enableLogging); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/IJsonDeserializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json 5 | { 6 | public interface IJsonDeserializerConfigBuilder 7 | { 8 | IJsonDeserializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IJsonDeserializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 11 | 12 | IJsonDeserializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 13 | 14 | IJsonDeserializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 15 | 16 | IJsonDeserializerConfigBuilder WithSchemaIdStrategy(SchemaIdDeserializerStrategy? schemaIdStrategy); 17 | 18 | IJsonDeserializerConfigBuilder WithValidate(bool? validate); 19 | 20 | IJsonDeserializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/Internal/ISchemaRegistryProtobufSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf.Internal 6 | { 7 | internal interface ISchemaRegistryProtobufSerializerFactory 8 | { 9 | SchemaRegistryProtobufSerializer GetOrCreateSerializer( 10 | IServiceProvider serviceProvider, 11 | IConfiguration configuration, 12 | Action configureSerializer, 13 | object serializerKey) 14 | where T : class, IMessage, new(); 15 | 16 | SchemaRegistryProtobufSerializer CreateSerializer( 17 | IServiceProvider serviceProvider, 18 | IConfiguration configuration, 19 | Action configureSerializer) 20 | where T : class, IMessage, new(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Core/Diagnostics/KafkaActivityListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Tests.Core.Diagnostics 5 | { 6 | public static class KafkaActivityListener 7 | { 8 | public static ActivityListener StartListening(Action onListen = null) 9 | { 10 | var activityListener = new ActivityListener 11 | { 12 | ShouldListenTo = source => source.Name == "Confluent.Kafka.Core", 13 | SampleUsingParentId = (ref ActivityCreationOptions activityOptions) => ActivitySamplingResult.AllData, 14 | Sample = (ref ActivityCreationOptions activityOptions) => ActivitySamplingResult.AllData, 15 | ActivityStarted = activity => { }, 16 | ActivityStopped = activity => { onListen?.Invoke(activity); } 17 | }; 18 | 19 | ActivitySource.AddActivityListener(activityListener); 20 | 21 | return activityListener; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisIdempotencyHandlerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Idempotency.Redis; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class RedisIdempotencyHandlerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddRedisIdempotencyHandler( 10 | this IKafkaBuilder builder, 11 | Action> configureHandler, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | if (configureHandler is null) 20 | { 21 | throw new ArgumentNullException(nameof(configureHandler)); 22 | } 23 | 24 | builder.Services!.AddRedisIdempotencyHandler(configureHandler, handlerKey); 25 | 26 | return builder; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/build-and-publish.yml: -------------------------------------------------------------------------------- 1 | name: build-and-publish 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | PROJECT: 7 | required: true 8 | type: string 9 | secrets: 10 | NUGET_API_KEY: 11 | required: true 12 | NUGET_SOURCE: 13 | required: true 14 | 15 | jobs: 16 | build-and-publish: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout the code 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup .NET 8 24 | uses: actions/setup-dotnet@v4 25 | with: 26 | dotnet-version: 8.0.x 27 | 28 | - name: Setup .NET 9 29 | uses: actions/setup-dotnet@v4 30 | with: 31 | dotnet-version: 9.0.x 32 | 33 | - name: Restore dependencies 34 | run: dotnet restore 35 | 36 | - name: Build solution 37 | run: dotnet build --configuration Release --no-restore 38 | 39 | - name: Push packages 40 | run: dotnet nuget push "**/${{ inputs.PROJECT }}*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s ${{ secrets.NUGET_SOURCE }} --skip-duplicate 41 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/IProtobufDeserializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf 5 | { 6 | public interface IProtobufDeserializerConfigBuilder 7 | { 8 | IProtobufDeserializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IProtobufDeserializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 11 | 12 | IProtobufDeserializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 13 | 14 | IProtobufDeserializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 15 | 16 | IProtobufDeserializerConfigBuilder WithSchemaIdStrategy(SchemaIdDeserializerStrategy? schemaIdStrategy); 17 | 18 | IProtobufDeserializerConfigBuilder WithUseDeprecatedFormat(bool? useDeprecatedFormat); 19 | 20 | IProtobufDeserializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Models.Internal 4 | { 5 | internal static class MessageExtensions 6 | { 7 | public static void EnsureDefaultMetadata(this Message message) 8 | { 9 | if (message is null) 10 | { 11 | throw new ArgumentNullException(nameof(message)); 12 | } 13 | 14 | message.Headers ??= []; 15 | 16 | if (message.Timestamp == Timestamp.Default) 17 | { 18 | message.Timestamp = new Timestamp(dateTime: DateTime.UtcNow); 19 | } 20 | } 21 | 22 | public static object GetId(this Message message, Func handler) 23 | { 24 | if (message is null) 25 | { 26 | throw new ArgumentNullException(nameof(message)); 27 | } 28 | 29 | var messageId = handler?.Invoke(message.Value); 30 | 31 | return messageId; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Internal/AssemblyScanner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Confluent.Kafka.Core.Internal 7 | { 8 | internal static class AssemblyScanner 9 | { 10 | public static Type[] Scan(IEnumerable assemblies, Func predicate) 11 | { 12 | if (assemblies is null) 13 | { 14 | throw new ArgumentNullException(nameof(assemblies)); 15 | } 16 | 17 | if (predicate is null) 18 | { 19 | throw new ArgumentNullException(nameof(predicate)); 20 | } 21 | 22 | var assemblyTypes = AppDomain.CurrentDomain 23 | .GetAssemblies() 24 | .Where(loadedAssembly => assemblies.All(assembly => assembly is null) || assemblies.Contains(loadedAssembly)) 25 | .SelectMany(loadedAssembly => loadedAssembly.GetTypes()) 26 | .Where(predicate) 27 | .ToArray(); 28 | 29 | return assemblyTypes; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/DeliveryReportFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Producer.Internal 4 | { 5 | internal sealed class DeliveryReportFactory : IDeliveryReportFactory 6 | { 7 | private static readonly Lazy Factory = new( 8 | () => new DeliveryReportFactory(), isThreadSafe: true); 9 | 10 | public static DeliveryReportFactory Instance => Factory.Value; 11 | 12 | private DeliveryReportFactory() 13 | { } 14 | 15 | public DeliveryReport CreateDefault( 16 | TopicPartition partition, 17 | Message message) 18 | { 19 | var deliveryReport = new DeliveryReport 20 | { 21 | Message = message, 22 | Error = new Error(ErrorCode.NoError), 23 | Status = PersistenceStatus.PossiblyPersisted, 24 | TopicPartitionOffset = new TopicPartitionOffset(partition, Offset.Unset) 25 | }; 26 | 27 | return deliveryReport; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Fernando Luiz de Lima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/SchemaRegistryAvroSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class SchemaRegistryAvroSerializerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddSchemaRegistryAvroSerializer( 10 | this IKafkaBuilder builder, 11 | Action configureSerializer, 12 | object serializerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | if (configureSerializer is null) 20 | { 21 | throw new ArgumentNullException(nameof(configureSerializer)); 22 | } 23 | 24 | builder.Services!.AddSchemaRegistryAvroSerializer(configureSerializer, serializerKey); 25 | 26 | return builder; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Producer 4 | { 5 | public interface IKafkaProducerConfigBuilder : IProducerConfigBuilder, IDisposable 6 | { 7 | IKafkaProducerConfigBuilder FromConfiguration(string sectionKey); 8 | 9 | IKafkaProducerConfigBuilder WithDefaultTopic(string defaultTopic); 10 | 11 | IKafkaProducerConfigBuilder WithDefaultPartition(Partition defaultPartition); 12 | 13 | IKafkaProducerConfigBuilder WithDefaultTimeout(TimeSpan defaultTimeout); 14 | 15 | IKafkaProducerConfigBuilder WithPollAfterProducing(bool pollAfterProducing); 16 | 17 | IKafkaProducerConfigBuilder WithEnableLogging(bool enableLogging); 18 | 19 | IKafkaProducerConfigBuilder WithEnableDiagnostics(bool enableDiagnostics); 20 | 21 | IKafkaProducerConfigBuilder WithEnableRetryOnFailure(bool enableRetryOnFailure); 22 | 23 | IKafkaProducerConfigBuilder WithEnableInterceptorExceptionPropagation(bool enableInterceptorExceptionPropagation); 24 | 25 | IKafkaProducerConfig Build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/Retry/IKafkaRetryConsumerWorkerOptions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Consumer; 2 | using Confluent.Kafka.Core.Diagnostics; 3 | using Confluent.Kafka.Core.Idempotency; 4 | using Confluent.Kafka.Core.Models; 5 | using Confluent.Kafka.Core.Producer; 6 | using Confluent.Kafka.Core.Retry; 7 | using Microsoft.Extensions.Logging; 8 | using System; 9 | 10 | namespace Confluent.Kafka.Core.Hosting.Retry 11 | { 12 | public interface IKafkaRetryConsumerWorkerOptions 13 | { 14 | Type WorkerType { get; } 15 | 16 | ILoggerFactory LoggerFactory { get; } 17 | 18 | IKafkaRetryConsumerWorkerConfig WorkerConfig { get; } 19 | 20 | IKafkaDiagnosticsManager DiagnosticsManager { get; } 21 | 22 | IKafkaConsumer Consumer { get; } 23 | 24 | IRetryHandler RetryHandler { get; } 25 | 26 | IIdempotencyHandler IdempotencyHandler { get; } 27 | 28 | IKafkaProducer SourceProducer { get; } 29 | 30 | IKafkaProducer DeadLetterProducer { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/KafkaDiagnosticsServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Diagnostics; 2 | using Confluent.Kafka.Core.Diagnostics.Internal; 3 | using Microsoft.Extensions.DependencyInjection.Extensions; 4 | using System; 5 | 6 | namespace Microsoft.Extensions.DependencyInjection 7 | { 8 | public static class KafkaDiagnosticsServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddKafkaDiagnostics( 11 | this IServiceCollection services, 12 | Action configureOptions = null) 13 | { 14 | if (services is null) 15 | { 16 | throw new ArgumentNullException(nameof(services)); 17 | } 18 | 19 | services.TryAddSingleton(serviceProvider => 20 | { 21 | var diagnosticsManager = KafkaDiagnosticsManagerFactory.Instance.CreateDiagnosticsManager( 22 | serviceProvider, 23 | configureOptions); 24 | 25 | return diagnosticsManager; 26 | }); 27 | 28 | return services; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/SchemaRegistryJsonSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.SchemaRegistry.Json; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.DependencyInjection 6 | { 7 | public static class SchemaRegistryJsonSerializerKafkaBuilderExtensions 8 | { 9 | public static IKafkaBuilder AddSchemaRegistryJsonSerializer( 10 | this IKafkaBuilder builder, 11 | Action configureSerializer, 12 | object serializerKey = null) 13 | where T : class 14 | { 15 | if (builder is null) 16 | { 17 | throw new ArgumentNullException(nameof(builder)); 18 | } 19 | 20 | if (configureSerializer is null) 21 | { 22 | throw new ArgumentNullException(nameof(configureSerializer)); 23 | } 24 | 25 | builder.Services!.AddSchemaRegistryJsonSerializer(configureSerializer, serializerKey); 26 | 27 | return builder; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Shared/Extensions/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Internal 6 | { 7 | /// https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#cancelling-uncancellable-operations 8 | internal static class TaskExtensions 9 | { 10 | public static async Task WithCancellation(this Task sourceTask, CancellationToken cancellationToken) 11 | { 12 | var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); 13 | 14 | using (cancellationToken.Register(state => ((TaskCompletionSource)state).TrySetResult(result: null), taskCompletionSource)) 15 | { 16 | var completedTask = await Task.WhenAny(sourceTask, taskCompletionSource.Task); 17 | 18 | if (completedTask == taskCompletionSource.Task) 19 | { 20 | throw new OperationCanceledException(cancellationToken); 21 | } 22 | 23 | return await sourceTask; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Internal/ObjectFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Internal 5 | { 6 | internal sealed class ObjectFactory : IObjectFactory 7 | { 8 | private static readonly Lazy Factory = new( 9 | () => new ObjectFactory(), isThreadSafe: true); 10 | 11 | public static ObjectFactory Instance => Factory.Value; 12 | 13 | private ObjectFactory() 14 | { } 15 | 16 | public object TryCreateInstance(IServiceProvider serviceProvider, Type objectType) 17 | { 18 | if (objectType is null) 19 | { 20 | throw new ArgumentNullException(nameof(objectType)); 21 | } 22 | 23 | try 24 | { 25 | var instance = serviceProvider is null 26 | ? Activator.CreateInstance(objectType) 27 | : ActivatorUtilities.CreateInstance(serviceProvider, objectType); 28 | 29 | return instance; 30 | } 31 | catch 32 | { 33 | return null; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Core/Extensions/ActivityExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Diagnostics.Internal; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | namespace Confluent.Kafka.Core.Tests.Core.Extensions 6 | { 7 | public static class ActivityExtensions 8 | { 9 | public static bool HasTopic(this Activity activity, params string[] topics) 10 | { 11 | var topicKey = SemanticConventions.Messaging.DestinationName; 12 | 13 | var topicKvp = activity?.Tags.SingleOrDefault(tag => tag.Key == topicKey) ?? default; 14 | 15 | var hasTopic = topics.Contains(topicKvp.Value); 16 | 17 | return hasTopic; 18 | } 19 | 20 | public static bool IsProducerKind(this Activity activity) 21 | { 22 | var isProducerKind = activity is not null && activity.Kind == ActivityKind.Producer; 23 | 24 | return isProducerKind; 25 | } 26 | 27 | public static bool IsConsumerKind(this Activity activity) 28 | { 29 | var isConsumerKind = activity is not null && activity.Kind == ActivityKind.Consumer; 30 | 31 | return isConsumerKind; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumer``.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | 6 | namespace Confluent.Kafka.Core.Consumer 7 | { 8 | public interface IKafkaConsumer : IConsumer 9 | { 10 | IKafkaConsumerOptions Options { get; } 11 | 12 | ConsumeResult Consume(); 13 | 14 | IEnumerable> ConsumeBatch(); 15 | 16 | IEnumerable> ConsumeBatch(int batchSize); 17 | 18 | IEnumerable> ConsumeBatch(TimeSpan timeout); 19 | 20 | IEnumerable> ConsumeBatch(int batchSize, TimeSpan timeout); 21 | 22 | IEnumerable> ConsumeBatch(CancellationToken cancellationToken); 23 | 24 | IEnumerable> ConsumeBatch(int batchSize, CancellationToken cancellationToken); 25 | 26 | IEnumerable SeekBatch(IEnumerable> results); 27 | 28 | IEnumerable Lag(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerKafkaConsumerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry.Polly; 2 | using Confluent.Kafka.Core.Retry.Polly.Internal; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Consumer 6 | { 7 | public static class PollyRetryHandlerKafkaConsumerBuilderExtensions 8 | { 9 | public static IKafkaConsumerBuilder WithPollyRetryHandler( 10 | this IKafkaConsumerBuilder builder, 11 | Action configureOptions = null, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | var retryHandler = PollyRetryHandlerFactory.Instance.GetOrCreateRetryHandler( 20 | builder.ServiceProvider, 21 | builder.Configuration, 22 | builder.LoggerFactory, 23 | configureOptions, 24 | handlerKey); 25 | 26 | builder.WithRetryHandler(retryHandler); 27 | 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerKafkaProducerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry.Polly; 2 | using Confluent.Kafka.Core.Retry.Polly.Internal; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Producer 6 | { 7 | public static class PollyRetryHandlerKafkaProducerBuilderExtensions 8 | { 9 | public static IKafkaProducerBuilder WithPollyRetryHandler( 10 | this IKafkaProducerBuilder builder, 11 | Action configureOptions = null, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | var retryHandler = PollyRetryHandlerFactory.Instance.GetOrCreateRetryHandler( 20 | builder.ServiceProvider, 21 | builder.Configuration, 22 | builder.LoggerFactory, 23 | configureOptions, 24 | handlerKey); 25 | 26 | builder.WithRetryHandler(retryHandler); 27 | 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerKafkaConsumerWorkerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry.Polly; 2 | using Confluent.Kafka.Core.Retry.Polly.Internal; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Hosting 6 | { 7 | public static class PollyRetryHandlerKafkaConsumerWorkerBuilderExtensions 8 | { 9 | public static IKafkaConsumerWorkerBuilder WithPollyRetryHandler( 10 | this IKafkaConsumerWorkerBuilder builder, 11 | Action configureOptions = null, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | var retryHandler = PollyRetryHandlerFactory.Instance.GetOrCreateRetryHandler( 20 | builder.ServiceProvider, 21 | builder.Configuration, 22 | builder.LoggerFactory, 23 | configureOptions, 24 | handlerKey); 25 | 26 | builder.WithRetryHandler(retryHandler); 27 | 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Shared/Extensions/ValidatableObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Internal 6 | { 7 | internal static class ValidatableObjectExtensions 8 | { 9 | public static void ValidateAndThrow( 10 | this IValidatableObject validatableObject, 11 | ValidationContext validationContext = null, 12 | bool validateAllProperties = false) 13 | where TException : ValidationException 14 | { 15 | if (validatableObject is null) 16 | { 17 | throw new ArgumentNullException(nameof(validatableObject)); 18 | } 19 | 20 | var validationResults = new List(); 21 | 22 | if (!Validator.TryValidateObject( 23 | validatableObject, 24 | validationContext ?? new ValidationContext(validatableObject), 25 | validationResults, 26 | validateAllProperties)) 27 | { 28 | throw (TException)Activator.CreateInstance(typeof(TException), validationResults); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/KafkaActivityAttributes.cs: -------------------------------------------------------------------------------- 1 | namespace Confluent.Kafka.Core.Diagnostics.Internal 2 | { 3 | internal sealed class KafkaActivityAttributes 4 | { 5 | public string System { get; set; } = "kafka"; 6 | public string ClientId { get; set; } = "rdkafka"; 7 | public string Operation { get; set; } 8 | public string MessageKey { get; set; } 9 | public object MessageId { get; set; } 10 | public int? MessageBodySize { get; set; } 11 | public Offset MessageOffset { get; set; } 12 | public bool MessageTombstone { get; set; } 13 | public string ConsumerGroup { get; set; } 14 | public string DestinationName { get; set; } 15 | public Partition? DestinationPartition { get; set; } 16 | public bool ResultIsError { get; set; } 17 | public ErrorCode? ResultErrorCode { get; set; } 18 | public string ResultErrorReason { get; set; } 19 | public string ExceptionType { get; set; } 20 | public string ExceptionMessage { get; set; } 21 | public string ExceptionStackTrace { get; set; } 22 | public string ServerAddress { get; set; } 23 | public string NetworkTransport { get; set; } = "tcp"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/SchemaRegistryProtobufSerializerKafkaBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf; 2 | using Google.Protobuf; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | 6 | namespace Confluent.Kafka.Core.DependencyInjection 7 | { 8 | public static class SchemaRegistryProtobufSerializerKafkaBuilderExtensions 9 | { 10 | public static IKafkaBuilder AddSchemaRegistryProtobufSerializer( 11 | this IKafkaBuilder builder, 12 | Action configureSerializer, 13 | object serializerKey = null) 14 | where T : class, IMessage, new() 15 | { 16 | if (builder is null) 17 | { 18 | throw new ArgumentNullException(nameof(builder)); 19 | } 20 | 21 | if (configureSerializer is null) 22 | { 23 | throw new ArgumentNullException(nameof(configureSerializer)); 24 | } 25 | 26 | builder.Services!.AddSchemaRegistryProtobufSerializer(configureSerializer, serializerKey); 27 | 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IKafkaConsumerWorkerConfig.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Hosting 6 | { 7 | public interface IKafkaConsumerWorkerConfig : IValidatableObject 8 | { 9 | int MaxDegreeOfParallelism { get; } 10 | 11 | bool EnableLogging { get; } 12 | 13 | bool EnableDiagnostics { get; } 14 | 15 | bool CommitFaultedMessages { get; } 16 | 17 | bool EnableIdempotency { get; } 18 | 19 | bool EnableRetryOnFailure { get; } 20 | 21 | bool EnableRetryTopic { get; } 22 | 23 | string[] RetryTopicExceptionTypeFilters { get; } 24 | 25 | Func RetryTopicExceptionFilter { get; } 26 | 27 | RetrySpecification RetryTopicSpecification { get; } 28 | 29 | bool EnableDeadLetterTopic { get; } 30 | 31 | bool EnableMessageOrderGuarantee { get; } 32 | 33 | TimeSpan EmptyTopicDelay { get; } 34 | 35 | TimeSpan NotEmptyTopicDelay { get; } 36 | 37 | TimeSpan UnavailableProcessingSlotsDelay { get; } 38 | 39 | TimeSpan ExceptionDelay { get; } 40 | 41 | TimeSpan PendingProcessingDelay { get; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IProducerConfig.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Client; 2 | 3 | namespace Confluent.Kafka.Core.Producer 4 | { 5 | public interface IProducerConfig : IClientConfig 6 | { 7 | bool? EnableBackgroundPoll { get; } 8 | 9 | bool? EnableDeliveryReports { get; } 10 | 11 | string DeliveryReportFields { get; } 12 | 13 | int? RequestTimeoutMs { get; } 14 | 15 | int? MessageTimeoutMs { get; } 16 | 17 | Partitioner? Partitioner { get; } 18 | 19 | int? CompressionLevel { get; } 20 | 21 | string TransactionalId { get; } 22 | 23 | int? TransactionTimeoutMs { get; } 24 | 25 | bool? EnableIdempotence { get; } 26 | 27 | bool? EnableGaplessGuarantee { get; } 28 | 29 | int? QueueBufferingMaxMessages { get; } 30 | 31 | int? QueueBufferingMaxKbytes { get; } 32 | 33 | double? LingerMs { get; } 34 | 35 | int? MessageSendMaxRetries { get; } 36 | 37 | int? QueueBufferingBackpressureThreshold { get; } 38 | 39 | CompressionType? CompressionType { get; } 40 | 41 | int? BatchNumMessages { get; } 42 | 43 | int? BatchSize { get; } 44 | 45 | int? StickyPartitioningLingerMs { get; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerKafkaRetryConsumerWorkerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Models; 2 | using Confluent.Kafka.Core.Retry.Polly; 3 | using Confluent.Kafka.Core.Retry.Polly.Internal; 4 | using System; 5 | 6 | namespace Confluent.Kafka.Core.Hosting.Retry 7 | { 8 | public static class PollyRetryHandlerKafkaRetryConsumerWorkerBuilderExtensions 9 | { 10 | public static IKafkaRetryConsumerWorkerBuilder WithPollyRetryHandler( 11 | this IKafkaRetryConsumerWorkerBuilder builder, 12 | Action configureOptions = null, 13 | object handlerKey = null) 14 | { 15 | if (builder is null) 16 | { 17 | throw new ArgumentNullException(nameof(builder)); 18 | } 19 | 20 | var retryHandler = PollyRetryHandlerFactory.Instance.GetOrCreateRetryHandler( 21 | builder.ServiceProvider, 22 | builder.Configuration, 23 | builder.LoggerFactory, 24 | configureOptions, 25 | handlerKey); 26 | 27 | builder.WithRetryHandler(retryHandler); 28 | 29 | return builder; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisIdempotencyHandlerKafkaConsumerWorkerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Idempotency.Redis; 2 | using Confluent.Kafka.Core.Idempotency.Redis.Internal; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Hosting 6 | { 7 | public static class RedisIdempotencyHandlerKafkaConsumerWorkerBuilderExtensions 8 | { 9 | public static IKafkaConsumerWorkerBuilder WithRedisIdempotencyHandler( 10 | this IKafkaConsumerWorkerBuilder builder, 11 | Action> configureHandler = null, 12 | object handlerKey = null) 13 | { 14 | if (builder is null) 15 | { 16 | throw new ArgumentNullException(nameof(builder)); 17 | } 18 | 19 | var idempotencyHandler = RedisIdempotencyHandlerFactory.Instance.GetOrCreateIdempotencyHandler( 20 | builder.ServiceProvider, 21 | builder.Configuration, 22 | builder.LoggerFactory, 23 | configureHandler, 24 | handlerKey); 25 | 26 | builder.WithIdempotencyHandler(idempotencyHandler); 27 | 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/IAvroSerializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro 5 | { 6 | public interface IAvroSerializerConfigBuilder 7 | { 8 | IAvroSerializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IAvroSerializerConfigBuilder WithBufferBytes(int? bufferBytes); 11 | 12 | IAvroSerializerConfigBuilder WithAutoRegisterSchemas(bool? autoRegisterSchemas); 13 | 14 | IAvroSerializerConfigBuilder WithNormalizeSchemas(bool? normalizeSchemas); 15 | 16 | IAvroSerializerConfigBuilder WithUseSchemaId(int? useSchemaId); 17 | 18 | IAvroSerializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 19 | 20 | IAvroSerializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 21 | 22 | IAvroSerializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 23 | 24 | IAvroSerializerConfigBuilder WithSchemaIdStrategy(SchemaIdSerializerStrategy? schemaIdStrategy); 25 | 26 | IAvroSerializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducerOptions``.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Diagnostics; 2 | using Confluent.Kafka.Core.Retry; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Confluent.Kafka.Core.Producer 8 | { 9 | public interface IKafkaProducerOptions 10 | { 11 | Type ProducerType { get; } 12 | 13 | ILoggerFactory LoggerFactory { get; } 14 | 15 | IKafkaProducerConfig ProducerConfig { get; } 16 | 17 | IKafkaDiagnosticsManager DiagnosticsManager { get; } 18 | 19 | ISerializer KeySerializer { get; } 20 | 21 | ISerializer ValueSerializer { get; } 22 | 23 | Func MessageIdHandler { get; } 24 | 25 | IRetryHandler RetryHandler { get; } 26 | 27 | IEnumerable> Interceptors { get; } 28 | 29 | Action OAuthBearerTokenRefreshHandler { get; } 30 | 31 | ICollection, string>> StatisticsHandlers { get; } 32 | 33 | ICollection, Error>> ErrorHandlers { get; } 34 | 35 | ICollection, LogMessage>> LogHandlers { get; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/IKafkaEnrichmentOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics 5 | { 6 | public interface IKafkaEnrichmentOptionsBuilder 7 | { 8 | IKafkaEnrichmentOptionsBuilder WithConsumptionEnrichment(Action enrichConsumption); 9 | 10 | IKafkaEnrichmentOptionsBuilder WithConsumptionFailureEnrichment(Action enrichConsumptionFailure); 11 | 12 | IKafkaEnrichmentOptionsBuilder WithSyncProductionEnrichment(Action enrichSyncProduction); 13 | 14 | IKafkaEnrichmentOptionsBuilder WithAsyncProductionEnrichment(Action enrichAsyncProduction); 15 | 16 | IKafkaEnrichmentOptionsBuilder WithProductionFailureEnrichment(Action enrichProductionFailure); 17 | 18 | IKafkaEnrichmentOptionsBuilder WithProcessingEnrichment(Action enrichProcessing); 19 | 20 | IKafkaEnrichmentOptionsBuilder WithProcessingFailureEnrichment(Action enrichProcessingFailure); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisIdempotencyHandlerKafkaRetryConsumerWorkerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Idempotency.Redis; 2 | using Confluent.Kafka.Core.Idempotency.Redis.Internal; 3 | using Confluent.Kafka.Core.Models; 4 | using System; 5 | 6 | namespace Confluent.Kafka.Core.Hosting.Retry 7 | { 8 | public static class RedisIdempotencyHandlerKafkaRetryConsumerWorkerBuilderExtensions 9 | { 10 | public static IKafkaRetryConsumerWorkerBuilder WithRedisIdempotencyHandler( 11 | this IKafkaRetryConsumerWorkerBuilder builder, 12 | Action> configureHandler = null, 13 | object handlerKey = null) 14 | { 15 | if (builder is null) 16 | { 17 | throw new ArgumentNullException(nameof(builder)); 18 | } 19 | 20 | var idempotencyHandler = RedisIdempotencyHandlerFactory.Instance.GetOrCreateIdempotencyHandler( 21 | builder.ServiceProvider, 22 | builder.Configuration, 23 | builder.LoggerFactory, 24 | configureHandler, 25 | handlerKey); 26 | 27 | builder.WithIdempotencyHandler(idempotencyHandler); 28 | 29 | return builder; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/ConsumerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Confluent.Kafka.Core.Consumer.Internal 7 | { 8 | internal static class ConsumerExtensions 9 | { 10 | public static IKafkaConsumer ToKafkaConsumer(this IConsumer consumer) 11 | { 12 | if (consumer is not IKafkaConsumer kafkaConsumer) 13 | { 14 | var kafkaConsumerType = typeof(IKafkaConsumer).ExtractTypeName(); 15 | 16 | throw new InvalidCastException($"{nameof(consumer)} should be of type '{kafkaConsumerType}'."); 17 | } 18 | 19 | return kafkaConsumer; 20 | } 21 | 22 | public static IEnumerable GetCurrentTopics(this IConsumer consumer) 23 | { 24 | var currentTopics = (consumer?.Subscription ?? []) 25 | .Concat(consumer?.Assignment?.Select(partition => partition?.Topic) ?? []) 26 | .Where(topic => !string.IsNullOrWhiteSpace(topic)) 27 | .Distinct(StringComparer.Ordinal) 28 | .ToArray(); 29 | 30 | return currentTopics; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/TaskActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Threading.Internal 6 | { 7 | internal sealed class TaskActivity 8 | { 9 | public Task ExecutingTask { get; private set; } 10 | public Activity TraceActivity { get; private set; } 11 | 12 | public TaskActivity(Task executingTask, Activity traceActivity = null) 13 | { 14 | ExecutingTask = executingTask ?? throw new ArgumentNullException(nameof(executingTask)); 15 | TraceActivity = traceActivity; 16 | } 17 | 18 | public static TaskActivity Run(Func> function) 19 | { 20 | if (function is null) 21 | { 22 | throw new ArgumentNullException(nameof(function)); 23 | } 24 | 25 | var executingTask = Task.Run(function.Invoke); 26 | 27 | var taskActivity = new TaskActivity(executingTask); 28 | 29 | executingTask.ContinueWith(completedTask => 30 | { 31 | taskActivity.TraceActivity = completedTask?.Result; 32 | taskActivity.TraceActivity?.SetEndTime(DateTime.UtcNow); 33 | 34 | }, TaskContinuationOptions.AttachedToParent); 35 | 36 | return taskActivity; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Shared.Tests/Stubs/TestFunctionalBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace Confluent.Kafka.Core.Shared.Tests.Stubs 5 | { 6 | internal class TestFunctionalBuilder : 7 | FunctionalBuilder 8 | { 9 | public TestFunctionalBuilder( 10 | IConfiguration configuration = null, 11 | TestSubject seedSubject = null) 12 | : base(seedSubject, configuration) 13 | { } 14 | 15 | public TestFunctionalBuilder FromConfiguration(string sectionKey) 16 | { 17 | AppendAction(subject => 18 | { 19 | if (!string.IsNullOrWhiteSpace(sectionKey)) 20 | { 21 | subject = Bind(subject, sectionKey); 22 | } 23 | }); 24 | return this; 25 | } 26 | 27 | public TestFunctionalBuilder WithParameter(string someProperty) 28 | { 29 | AppendParameter(parameters => parameters[0] = someProperty); 30 | return this; 31 | } 32 | 33 | public TestFunctionalBuilder WithProperty(string someProperty) 34 | { 35 | AppendAction(subject => subject.SomeProperty = someProperty); 36 | return this; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Diagnostics/IKafkaActivityEnricher.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Consumer; 2 | using Confluent.Kafka.Core.Hosting; 3 | using Confluent.Kafka.Core.Producer; 4 | using System; 5 | using System.Diagnostics; 6 | 7 | namespace Confluent.Kafka.Core.Diagnostics 8 | { 9 | public interface IKafkaActivityEnricher 10 | { 11 | void Enrich(Activity activity, ConsumeException consumeException, IKafkaConsumerConfig consumerConfig); 12 | 13 | void Enrich(Activity activity, ConsumeResult consumeResult, IKafkaConsumerOptions options); 14 | 15 | void Enrich(Activity activity, ProduceException produceException, IKafkaProducerOptions options); 16 | 17 | void Enrich(Activity activity, DeliveryReport deliveryReport, IKafkaProducerOptions options); 18 | 19 | void Enrich(Activity activity, DeliveryResult deliveryResult, IKafkaProducerOptions options); 20 | 21 | void Enrich(Activity activity, Exception exception, ConsumeResult consumeResult, IKafkaConsumerWorkerOptions options); 22 | 23 | void Enrich(Activity activity, ConsumeResult consumeResult, IKafkaConsumerWorkerOptions options); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Models; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Producer.Internal 6 | { 7 | internal static class KafkaProducerExtensions 8 | { 9 | public static void ValidateAndThrow(this IKafkaProducer producer, string suffix) 10 | { 11 | if (producer is null) 12 | { 13 | throw new ArgumentNullException(nameof(producer)); 14 | } 15 | 16 | if (string.IsNullOrWhiteSpace(suffix)) 17 | { 18 | throw new ArgumentException($"{nameof(suffix)} cannot be null or whitespace.", nameof(suffix)); 19 | } 20 | 21 | var producerConfig = producer!.Options!.ProducerConfig; 22 | 23 | if (!string.IsNullOrWhiteSpace(producerConfig!.DefaultTopic) && 24 | !producerConfig!.DefaultTopic.EndsWith(suffix)) 25 | { 26 | throw new KafkaProducerConfigException( 27 | [ 28 | new ValidationResult( 29 | $"{nameof(producerConfig.DefaultTopic)} must end with '{suffix}' suffix.", 30 | [nameof(producerConfig.DefaultTopic)]) 31 | ]); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/ISchemaRegistryJsonSerializerBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json 5 | { 6 | public interface ISchemaRegistryJsonSerializerBuilder 7 | { 8 | ISchemaRegistryJsonSerializerBuilder WithSchemaRegistryClient( 9 | Action configureClient, 10 | object clientKey = null); 11 | 12 | ISchemaRegistryJsonSerializerBuilder WithSchema( 13 | Action configureSchema); 14 | 15 | ISchemaRegistryJsonSerializerBuilder WithSerializerConfiguration( 16 | Action configureSerializer); 17 | 18 | ISchemaRegistryJsonSerializerBuilder WithDeserializerConfiguration( 19 | Action configureDeserializer); 20 | 21 | #if NET8_0_OR_GREATER 22 | ISchemaRegistryJsonSerializerBuilder WithSchemaGeneratorSettings( 23 | Action configureSchemaGenerator); 24 | #else 25 | ISchemaRegistryJsonSerializerBuilder WithSchemaGeneratorSettings( 26 | Action configureSchemaGenerator); 27 | #endif 28 | ISchemaRegistryJsonSerializerBuilder WithRuleRegistry( 29 | RuleRegistry ruleRegistry); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IKafkaConsumerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Consumer 5 | { 6 | public interface IKafkaConsumerConfigBuilder : IConsumerConfigBuilder, IDisposable 7 | { 8 | IKafkaConsumerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IKafkaConsumerConfigBuilder WithTopicSubscriptions(IEnumerable topicSubscriptions); 11 | 12 | IKafkaConsumerConfigBuilder WithPartitionAssignments(IEnumerable partitionAssignments); 13 | 14 | IKafkaConsumerConfigBuilder WithCommitAfterConsuming(bool commitAfterConsuming); 15 | 16 | IKafkaConsumerConfigBuilder WithDefaultTimeout(TimeSpan defaultTimeout); 17 | 18 | IKafkaConsumerConfigBuilder WithDefaultBatchSize(int defaultBatchSize); 19 | 20 | IKafkaConsumerConfigBuilder WithEnableLogging(bool enableLogging); 21 | 22 | IKafkaConsumerConfigBuilder WithEnableDiagnostics(bool enableDiagnostics); 23 | 24 | IKafkaConsumerConfigBuilder WithEnableDeadLetterTopic(bool enableDeadLetterTopic); 25 | 26 | IKafkaConsumerConfigBuilder WithEnableRetryOnFailure(bool enableRetryOnFailure); 27 | 28 | IKafkaConsumerConfigBuilder WithEnableInterceptorExceptionPropagation(bool enableInterceptorExceptionPropagation); 29 | 30 | IKafkaConsumerConfig Build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Confluent.Kafka.Core.Tests/Core/Extensions/KafkaConsumerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Consumer; 2 | using Polly; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace Confluent.Kafka.Core.Tests.Core.Extensions 8 | { 9 | public static class KafkaConsumerExtensions 10 | { 11 | public static ConsumeResult Consume( 12 | this IKafkaConsumer consumer, 13 | TimeSpan timeout, 14 | int retryCount) 15 | { 16 | var consumeResult = Policy> 17 | .HandleResult(result => result is null) 18 | .WaitAndRetry(retryCount, retryAttempt => timeout) 19 | .Execute(consumer.Consume); 20 | 21 | return consumeResult; 22 | } 23 | 24 | public static IEnumerable> ConsumeBatch( 25 | this IKafkaConsumer consumer, 26 | TimeSpan timeout, 27 | int retryCount) 28 | { 29 | var consumeResults = Policy>> 30 | .HandleResult(result => result is null || !result.Any()) 31 | .WaitAndRetry(retryCount, retryAttempt => timeout) 32 | .Execute(consumer.ConsumeBatch); 33 | 34 | return consumeResults; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerOptions``.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Diagnostics; 2 | using Confluent.Kafka.Core.Retry; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Confluent.Kafka.Core.Producer.Internal 8 | { 9 | internal sealed class KafkaProducerOptions : IKafkaProducerOptions 10 | { 11 | public Type ProducerType { get; init; } 12 | public ILoggerFactory LoggerFactory { get; init; } 13 | public IKafkaProducerConfig ProducerConfig { get; init; } 14 | public IKafkaDiagnosticsManager DiagnosticsManager { get; init; } 15 | public ISerializer KeySerializer { get; init; } 16 | public ISerializer ValueSerializer { get; init; } 17 | public Func MessageIdHandler { get; init; } 18 | public IRetryHandler RetryHandler { get; init; } 19 | public IEnumerable> Interceptors { get; init; } 20 | public Action OAuthBearerTokenRefreshHandler { get; init; } 21 | public ICollection, string>> StatisticsHandlers { get; init; } 22 | public ICollection, Error>> ErrorHandlers { get; init; } 23 | public ICollection, LogMessage>> LogHandlers { get; init; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/AsyncLockOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using System; 3 | 4 | namespace Confluent.Kafka.Core.Threading.Internal 5 | { 6 | internal sealed class AsyncLockOptionsBuilder : 7 | FunctionalBuilder, 8 | IAsyncLockOptionsBuilder 9 | { 10 | public IAsyncLockOptionsBuilder WithMaxDegreeOfParallelism(int maxDegreeOfParallelism) 11 | { 12 | AppendAction(options => options.MaxDegreeOfParallelism = maxDegreeOfParallelism); 13 | return this; 14 | } 15 | 16 | public IAsyncLockOptionsBuilder WithHandleLockByKey(bool handleLockByKey) 17 | { 18 | AppendAction(options => options.HandleLockByKey = handleLockByKey); 19 | return this; 20 | } 21 | 22 | public IAsyncLockOptionsBuilder WithLockKeyHandler(Func lockKeyHandler) 23 | { 24 | AppendAction(options => options.LockKeyHandler = lockKeyHandler); 25 | return this; 26 | } 27 | 28 | public static AsyncLockOptions Build(Action configureOptions) 29 | { 30 | using var builder = new AsyncLockOptionsBuilder(); 31 | 32 | configureOptions?.Invoke(builder); 33 | 34 | var options = builder.Build(); 35 | 36 | return options; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/ActivitySourceBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Confluent.Kafka.Core.Diagnostics.Internal 5 | { 6 | internal abstract class ActivitySourceBase 7 | { 8 | protected ActivitySource ActivitySource { get; } 9 | protected string ActivitySourceName => ActivitySource?.Name; 10 | protected string ActivitySourceVersion => ActivitySource?.Version; 11 | 12 | protected ActivitySourceBase(string activitySourceName, string activitySourceVersion = null) 13 | { 14 | if (string.IsNullOrWhiteSpace(activitySourceName)) 15 | { 16 | throw new ArgumentException($"{nameof(activitySourceName)} cannot be null or whitespace.", nameof(activitySourceName)); 17 | } 18 | 19 | ActivitySource = new ActivitySource(activitySourceName, activitySourceVersion); 20 | } 21 | 22 | public Activity StartActivity(string activityName, ActivityKind activityKind, IPropagationContext propagationContext) 23 | { 24 | var activity = propagationContext is null 25 | ? ActivitySource.StartActivity(activityName, activityKind) 26 | : ActivitySource.StartActivity(activityName, activityKind, propagationContext.ActivityContext) 27 | .SetBaggageItems(propagationContext.Baggage); 28 | 29 | return activity; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/IJsonSerializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json 5 | { 6 | public interface IJsonSerializerConfigBuilder 7 | { 8 | IJsonSerializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IJsonSerializerConfigBuilder WithBufferBytes(int? bufferBytes); 11 | 12 | IJsonSerializerConfigBuilder WithAutoRegisterSchemas(bool? autoRegisterSchemas); 13 | 14 | IJsonSerializerConfigBuilder WithNormalizeSchemas(bool? normalizeSchemas); 15 | 16 | IJsonSerializerConfigBuilder WithUseSchemaId(int? useSchemaId); 17 | 18 | IJsonSerializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 19 | 20 | IJsonSerializerConfigBuilder WithLatestCompatibilityStrict(bool? latestCompatibilityStrict); 21 | 22 | IJsonSerializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 23 | 24 | IJsonSerializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 25 | 26 | IJsonSerializerConfigBuilder WithSchemaIdStrategy(SchemaIdSerializerStrategy? schemaIdStrategy); 27 | 28 | IJsonSerializerConfigBuilder WithValidate(bool? validate); 29 | 30 | IJsonSerializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.OpenTelemetry/Confluent.Kafka.Core.OpenTelemetry.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1;net8.0;net9.0 5 | true 6 | true 7 | 1701;1702;1705;1591 8 | true 9 | Confluent.Kafka.Core.OpenTelemetry 10 | Confluent.Kafka.Core.OpenTelemetry 11 | Lima Technology Services;Fernando Luiz de Lima 12 | Copyright © Lima Technology Services;Fernando Luiz de Lima 13 | Provides OpenTelemetry implementation for Apache Kafka .NET client. 14 | https://github.com/ffernandolima/confluent-kafka-core-dotnet 15 | https://github.com/ffernandolima/confluent-kafka-core-dotnet 16 | MIT 17 | git 18 | Apache;Kafka;Confluent;librdkafka;OpenTelemetry 19 | 1.0.6 20 | latest 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IKafkaProducer``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Confluent.Kafka.Core.Producer 6 | { 7 | public interface IKafkaProducer : IProducer 8 | { 9 | IKafkaProducerOptions Options { get; } 10 | 11 | void Produce( 12 | Message message, 13 | Action> deliveryHandler = null); 14 | 15 | void Produce( 16 | Partition partition, 17 | Message message, 18 | Action> deliveryHandler = null); 19 | 20 | void Produce( 21 | string topic, 22 | Partition partition, 23 | Message message, 24 | Action> deliveryHandler = null); 25 | 26 | Task> ProduceAsync( 27 | Message message, 28 | CancellationToken cancellationToken = default); 29 | 30 | Task> ProduceAsync( 31 | Partition partition, 32 | Message message, 33 | CancellationToken cancellationToken = default); 34 | 35 | Task> ProduceAsync( 36 | string topic, 37 | Partition partition, 38 | Message message, 39 | CancellationToken cancellationToken = default); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Retry/RetrySpecification.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Specifications; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace Confluent.Kafka.Core.Retry 6 | { 7 | public sealed class RetrySpecification : ExpressionSpecification 8 | { 9 | private RetrySpecification(Func exceptionFilter, Type[] exceptionTypeFilters) 10 | : base( 11 | exception => 12 | exceptionFilter.Invoke(exception) && !exceptionTypeFilters.Contains(exception.GetType())) 13 | { } 14 | 15 | public static RetrySpecification Create(Func exceptionFilter, string[] exceptionTypeFilters) 16 | { 17 | var retrySpecification = new RetrySpecification( 18 | exceptionFilter is null 19 | ? exception => true 20 | : exceptionFilter, 21 | exceptionTypeFilters is null 22 | ? [] 23 | : exceptionTypeFilters 24 | .Select(typeName => Type.GetType(typeName, throwOnError: false, ignoreCase: true)) 25 | .Where(type => type is not null) 26 | .ToArray()); 27 | 28 | return retrySpecification; 29 | } 30 | 31 | protected override bool Evaluate(Exception source) 32 | { 33 | if (source is null) 34 | { 35 | return false; 36 | } 37 | 38 | return base.Evaluate(source); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/Internal/SchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Internal 6 | { 7 | internal sealed class SchemaBuilder : ISchemaBuilder 8 | { 9 | private readonly IConfiguration _configuration; 10 | 11 | public RegisteredSchema RegisteredSchema { get; private set; } 12 | public Schema UnregisteredSchema { get; private set; } 13 | 14 | public SchemaBuilder(IConfiguration configuration) 15 | { 16 | _configuration = configuration; 17 | } 18 | 19 | public ISchemaBuilder WithRegisteredSchema( 20 | Action configureRegisteredSchema) 21 | { 22 | RegisteredSchema = RegisteredSchemaBuilder.Build(_configuration, configureRegisteredSchema); 23 | return this; 24 | } 25 | 26 | public ISchemaBuilder WithUnregisteredSchema( 27 | Action configureUnregisteredSchema) 28 | { 29 | UnregisteredSchema = UnregisteredSchemaBuilder.Build(_configuration, configureUnregisteredSchema); 30 | return this; 31 | } 32 | 33 | public static SchemaBuilder Configure(IConfiguration configuration, Action configureSchema) 34 | { 35 | var builder = new SchemaBuilder(configuration); 36 | 37 | configureSchema?.Invoke(builder); 38 | 39 | return builder; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Consumer/IConsumerConfig.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Client; 2 | 3 | namespace Confluent.Kafka.Core.Consumer 4 | { 5 | public interface IConsumerConfig : IClientConfig 6 | { 7 | string ConsumeResultFields { get; } 8 | 9 | AutoOffsetReset? AutoOffsetReset { get; } 10 | 11 | string GroupId { get; } 12 | 13 | string GroupInstanceId { get; } 14 | 15 | PartitionAssignmentStrategy? PartitionAssignmentStrategy { get; } 16 | 17 | int? SessionTimeoutMs { get; } 18 | 19 | int? HeartbeatIntervalMs { get; } 20 | 21 | string GroupProtocolType { get; } 22 | 23 | GroupProtocol? GroupProtocol { get; } 24 | 25 | string GroupRemoteAssignor { get; } 26 | 27 | int? CoordinatorQueryIntervalMs { get; } 28 | 29 | int? MaxPollIntervalMs { get; } 30 | 31 | bool? EnableAutoCommit { get; } 32 | 33 | int? AutoCommitIntervalMs { get; } 34 | 35 | bool? EnableAutoOffsetStore { get; } 36 | 37 | int? QueuedMinMessages { get; } 38 | 39 | int? QueuedMaxMessagesKbytes { get; } 40 | 41 | int? FetchWaitMaxMs { get; } 42 | 43 | int? FetchQueueBackoffMs { get; } 44 | 45 | int? MaxPartitionFetchBytes { get; } 46 | 47 | int? FetchMaxBytes { get; } 48 | 49 | int? FetchMinBytes { get; } 50 | 51 | int? FetchErrorBackoffMs { get; } 52 | 53 | IsolationLevel? IsolationLevel { get; } 54 | 55 | bool? EnablePartitionEof { get; } 56 | 57 | bool? CheckCrcs { get; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Retry.Polly/PollyRetryHandlerServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Retry.Polly; 2 | using Confluent.Kafka.Core.Retry.Polly.Internal; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | public static class PollyRetryHandlerServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddPollyRetryHandler( 13 | this IServiceCollection services, 14 | Action configureOptions = null, 15 | object handlerKey = null) 16 | { 17 | if (services is null) 18 | { 19 | throw new ArgumentNullException(nameof(services)); 20 | } 21 | 22 | services.TryAddKeyedSingleton( 23 | handlerKey ?? PollyRetryHandlerConstants.PollyRetryHandlerKey, 24 | (serviceProvider, _) => 25 | { 26 | var retryHandler = PollyRetryHandlerFactory.Instance.CreateRetryHandler( 27 | serviceProvider, 28 | serviceProvider.GetService(), 29 | serviceProvider.GetService(), 30 | configureOptions); 31 | 32 | return retryHandler; 33 | }); 34 | 35 | return services; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Json/Internal/JsonSchemaGeneratorSettingsBuilder.cs: -------------------------------------------------------------------------------- 1 | #if !NET8_0_OR_GREATER 2 | 3 | using Microsoft.Extensions.Configuration; 4 | using NJsonSchema.Generation; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Json.Internal 9 | { 10 | internal sealed class JsonSchemaGeneratorSettingsBuilder : 11 | JsonSchemaGeneratorSettingsBuilder< 12 | JsonSchemaGeneratorSettings, 13 | IJsonSchemaGeneratorSettingsBuilder, 14 | JsonSchemaGeneratorSettingsBuilder>, 15 | IJsonSchemaGeneratorSettingsBuilder 16 | { 17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 18 | protected override IJsonSchemaGeneratorSettingsBuilder BuilderInstance => this; 19 | 20 | public JsonSchemaGeneratorSettingsBuilder(IConfiguration configuration = null) 21 | : base(configuration) 22 | { } 23 | 24 | protected override JsonSchemaGeneratorSettings CreateSubject() => new() 25 | { 26 | FlattenInheritanceHierarchy = true 27 | }; 28 | 29 | public static JsonSchemaGeneratorSettings Build( 30 | IConfiguration configuration, 31 | Action configureSchemaGenerator) 32 | { 33 | using var builder = new JsonSchemaGeneratorSettingsBuilder(configuration); 34 | 35 | configureSchemaGenerator?.Invoke(builder); 36 | 37 | var settings = builder.Build(); 38 | 39 | return settings; 40 | } 41 | } 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Threading/Internal/AsyncLockOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Threading.Internal 6 | { 7 | internal sealed class AsyncLockOptions : IValidatableObject 8 | { 9 | public int MaxDegreeOfParallelism { get; set; } = 1; 10 | public bool HandleLockByKey { get; set; } 11 | public Func LockKeyHandler { get; set; } 12 | 13 | public static IAsyncLockOptionsBuilder CreateBuilder() => new AsyncLockOptionsBuilder(); 14 | 15 | #region IValidatableObject Members 16 | 17 | public IEnumerable Validate(ValidationContext validationContext) 18 | { 19 | var options = validationContext?.ObjectInstance as AsyncLockOptions ?? this; 20 | 21 | if (options.MaxDegreeOfParallelism <= 0) 22 | { 23 | yield return new ValidationResult( 24 | $"{nameof(options.MaxDegreeOfParallelism)} cannot be less than or equal to zero.", 25 | [nameof(options.MaxDegreeOfParallelism)]); 26 | } 27 | 28 | if (options.HandleLockByKey && options.LockKeyHandler is null) 29 | { 30 | yield return new ValidationResult( 31 | $"{nameof(options.LockKeyHandler)} cannot be null when {nameof(options.HandleLockByKey)} is true.", 32 | [nameof(options.LockKeyHandler), nameof(options.HandleLockByKey)]); 33 | } 34 | } 35 | 36 | #endregion IValidatableObject Members 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.JsonCore/Internal/JsonCoreSerializer`.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Encoding; 2 | using System; 3 | using System.Text.Json; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.JsonCore.Internal 6 | { 7 | using System.Text; 8 | 9 | internal sealed class JsonCoreSerializer : ISerializer, IDeserializer 10 | { 11 | private readonly Encoding _encoding; 12 | private readonly JsonSerializerOptions _options; 13 | 14 | public JsonCoreSerializer(JsonSerializerOptions options) 15 | { 16 | _options = options ?? throw new ArgumentNullException(nameof(options)); 17 | _encoding = EncodingFactory.Instance.CreateDefault(); 18 | } 19 | 20 | public byte[] Serialize(T data, SerializationContext context) 21 | { 22 | if (data is null) 23 | { 24 | return null; 25 | } 26 | 27 | var json = JsonSerializer.Serialize(data, data.GetType(), _options); 28 | 29 | var result = _encoding.GetBytes(json); 30 | 31 | return result; 32 | } 33 | 34 | public T Deserialize(ReadOnlySpan data, bool isNull, SerializationContext context) 35 | { 36 | if (isNull) 37 | { 38 | return default; 39 | } 40 | 41 | #if NETSTANDARD2_0 42 | var json = _encoding.GetString(data.ToArray()); 43 | #else 44 | var json = _encoding.GetString(data); 45 | #endif 46 | var result = JsonSerializer.Deserialize(json, _options); 47 | 48 | return result; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Models/Internal/TopicPartitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Confluent.Kafka.Core.Models.Internal 6 | { 7 | internal static class TopicPartitionExtensions 8 | { 9 | public static void ValidateAndThrow(this TopicPartition topicPartition) 10 | { 11 | if (topicPartition is null) 12 | { 13 | throw new ArgumentNullException(nameof(topicPartition)); 14 | } 15 | 16 | List validationResults = null; 17 | 18 | if (string.IsNullOrWhiteSpace(topicPartition.Topic)) 19 | { 20 | validationResults ??= []; 21 | 22 | validationResults.Add( 23 | new ValidationResult( 24 | $"{nameof(topicPartition.Topic)} cannot be null or whitespace.", 25 | [nameof(topicPartition.Topic)])); 26 | } 27 | 28 | if (topicPartition.Partition < Partition.Any) 29 | { 30 | validationResults ??= []; 31 | 32 | validationResults.Add( 33 | new ValidationResult( 34 | $"{nameof(topicPartition.Partition)} cannot be lower than {Partition.Any.Value}.", 35 | [nameof(topicPartition.Partition)])); 36 | } 37 | 38 | if (validationResults is not null && validationResults.Count > 0) 39 | { 40 | throw new TopicPartitionException(validationResults); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IKafkaConsumerWorkerOptions``.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Consumer; 2 | using Confluent.Kafka.Core.Diagnostics; 3 | using Confluent.Kafka.Core.Idempotency; 4 | using Confluent.Kafka.Core.Models; 5 | using Confluent.Kafka.Core.Producer; 6 | using Confluent.Kafka.Core.Retry; 7 | using Microsoft.Extensions.Logging; 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace Confluent.Kafka.Core.Hosting 12 | { 13 | public interface IKafkaConsumerWorkerOptions 14 | { 15 | Type WorkerType { get; } 16 | 17 | ILoggerFactory LoggerFactory { get; } 18 | 19 | IKafkaConsumerWorkerConfig WorkerConfig { get; } 20 | 21 | IKafkaDiagnosticsManager DiagnosticsManager { get; } 22 | 23 | IKafkaConsumer Consumer { get; } 24 | 25 | IRetryHandler RetryHandler { get; } 26 | 27 | IIdempotencyHandler IdempotencyHandler { get; } 28 | 29 | IKafkaProducer RetryProducer { get; } 30 | 31 | IKafkaProducer DeadLetterProducer { get; } 32 | 33 | IKafkaConsumerLifecycleWorker ConsumerLifecycleWorker { get; } 34 | 35 | IEnumerable> ConsumeResultHandlers { get; } 36 | 37 | IConsumeResultErrorHandler ConsumeResultErrorHandler { get; } 38 | 39 | Func, object> MessageOrderGuaranteeKeyHandler { get; } 40 | 41 | ISerializer KeySerializer { get; } 42 | 43 | ISerializer ValueSerializer { get; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/SchemaRegistryClientServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Serialization.SchemaRegistry; 2 | using Confluent.Kafka.Core.Serialization.SchemaRegistry.Internal; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using System; 6 | 7 | namespace Microsoft.Extensions.DependencyInjection 8 | { 9 | public static class SchemaRegistryClientServiceCollectionExtensions 10 | { 11 | public static IServiceCollection AddSchemaRegistryClient( 12 | this IServiceCollection services, 13 | Action configureClient, 14 | object clientKey = null) 15 | { 16 | if (services is null) 17 | { 18 | throw new ArgumentNullException(nameof(services)); 19 | } 20 | 21 | if (configureClient is null) 22 | { 23 | throw new ArgumentNullException(nameof(configureClient)); 24 | } 25 | 26 | services.TryAddKeyedSingleton( 27 | clientKey ?? SchemaRegistryClientConstants.ConfluentSchemaRegistryClientKey, 28 | (serviceProvider, _) => 29 | { 30 | var schemaRegistryClient = SchemaRegistryClientFactory.Instance.CreateSchemaRegistryClient( 31 | serviceProvider, 32 | serviceProvider.GetService(), 33 | configureClient); 34 | 35 | return schemaRegistryClient; 36 | }); 37 | 38 | return services; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.NewtonsoftJson/Internal/NewtonsoftJsonSerializer`.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Encoding; 2 | using Newtonsoft.Json; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.NewtonsoftJson.Internal 6 | { 7 | using System.Text; 8 | 9 | internal sealed class NewtonsoftJsonSerializer : ISerializer, IDeserializer 10 | { 11 | private readonly Encoding _encoding; 12 | private readonly JsonSerializerSettings _settings; 13 | 14 | public NewtonsoftJsonSerializer(JsonSerializerSettings settings) 15 | { 16 | _settings = settings ?? throw new ArgumentNullException(nameof(settings)); 17 | _encoding = EncodingFactory.Instance.CreateDefault(); 18 | } 19 | 20 | public byte[] Serialize(T data, SerializationContext context) 21 | { 22 | if (data is null) 23 | { 24 | return null; 25 | } 26 | 27 | var json = JsonConvert.SerializeObject(data, data.GetType(), _settings); 28 | 29 | var result = _encoding.GetBytes(json); 30 | 31 | return result; 32 | } 33 | 34 | public T Deserialize(ReadOnlySpan data, bool isNull, SerializationContext context) 35 | { 36 | if (isNull) 37 | { 38 | return default; 39 | } 40 | 41 | #if NETSTANDARD2_0 42 | var json = _encoding.GetString(data.ToArray()); 43 | #else 44 | var json = _encoding.GetString(data); 45 | #endif 46 | var result = JsonConvert.DeserializeObject(json, _settings); 47 | 48 | return result; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Diagnostics/Internal/ActivityExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace Confluent.Kafka.Core.Diagnostics.Internal 7 | { 8 | internal static class ActivityExtensions 9 | { 10 | public static ActivityContext GetContextOrDefault(this Activity activity) 11 | { 12 | var activityContext = activity?.Context ?? default; 13 | 14 | return activityContext; 15 | } 16 | 17 | public static IEnumerable> GetBaggageOrEmpty(this Activity activity) 18 | { 19 | var baggage = activity?.Baggage ?? []; 20 | 21 | return baggage; 22 | } 23 | 24 | public static Activity SetBaggageItems(this Activity activity, IEnumerable> baggageItems) 25 | { 26 | if (baggageItems is not null && baggageItems.Any()) 27 | { 28 | foreach (var baggageItem in baggageItems) 29 | { 30 | activity?.SetBaggage(baggageItem.Key, baggageItem.Value); 31 | } 32 | } 33 | 34 | return activity; 35 | } 36 | 37 | public static Activity AddEvent(this Activity activity, string name, DateTimeOffset timestamp = default, ActivityTagsCollection tags = null) 38 | { 39 | if (!string.IsNullOrWhiteSpace(name)) 40 | { 41 | var activityEvent = new ActivityEvent(name, timestamp, tags); 42 | 43 | activity?.AddEvent(activityEvent); 44 | } 45 | 46 | return activity; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisClientServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Idempotency.Redis; 2 | using Confluent.Kafka.Core.Idempotency.Redis.Internal; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | public static class RedisClientServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddRedisClient( 13 | this IServiceCollection services, 14 | Action configureOptions, 15 | object clientKey = null) 16 | { 17 | if (services is null) 18 | { 19 | throw new ArgumentNullException(nameof(services)); 20 | } 21 | 22 | if (configureOptions is null) 23 | { 24 | throw new ArgumentNullException(nameof(configureOptions)); 25 | } 26 | 27 | services.TryAddKeyedSingleton( 28 | clientKey ?? RedisClientConstants.StackExchangeRedisClientKey, 29 | (serviceProvider, _) => 30 | { 31 | var redisClient = RedisClientFactory.Instance.CreateRedisClient( 32 | serviceProvider, 33 | serviceProvider.GetService(), 34 | serviceProvider.GetService(), 35 | configureOptions); 36 | 37 | return redisClient; 38 | }); 39 | 40 | return services; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Models/KafkaMetadataMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Models 4 | { 5 | public class KafkaMetadataMessage : IMessageValue 6 | { 7 | private Guid _id; 8 | public Guid Id 9 | { 10 | get => _id; 11 | set 12 | { 13 | if (_id != Guid.Empty) 14 | { 15 | throw new InvalidOperationException("Id has already been assigned."); 16 | } 17 | _id = value; 18 | } 19 | } 20 | 21 | public object SourceId { get; init; } 22 | public string SourceTopic { get; init; } 23 | public string SourceGroupId { get; init; } 24 | public Partition SourcePartition { get; init; } 25 | public Offset SourceOffset { get; init; } 26 | public byte[] SourceKey { get; init; } 27 | public byte[] SourceValue { get; init; } 28 | public string SourceKeyType { get; init; } 29 | public string SourceValueType { get; init; } 30 | public ErrorCode ErrorCode { get; init; } 31 | public string Reason { get; init; } 32 | 33 | public KafkaMetadataMessage Clone() => new() 34 | { 35 | Id = Guid.NewGuid(), 36 | SourceId = SourceId, 37 | SourceTopic = SourceTopic, 38 | SourceGroupId = SourceGroupId, 39 | SourcePartition = SourcePartition, 40 | SourceOffset = SourceOffset, 41 | SourceKey = SourceKey, 42 | SourceValue = SourceValue, 43 | SourceKeyType = SourceKeyType, 44 | SourceValueType = SourceValueType, 45 | ErrorCode = ErrorCode, 46 | Reason = Reason 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Idempotency.Redis/RedisIdempotencyHandlerServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Idempotency.Redis; 2 | using Confluent.Kafka.Core.Idempotency.Redis.Internal; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | public static class RedisIdempotencyHandlerServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddRedisIdempotencyHandler( 13 | this IServiceCollection services, 14 | Action> configureHandler, 15 | object handlerKey = null) 16 | { 17 | if (services is null) 18 | { 19 | throw new ArgumentNullException(nameof(services)); 20 | } 21 | 22 | if (configureHandler is null) 23 | { 24 | throw new ArgumentNullException(nameof(configureHandler)); 25 | } 26 | 27 | services.TryAddKeyedSingleton( 28 | handlerKey ?? RedisIdempotencyHandlerConstants.RedisIdempotencyHandlerKey, 29 | (serviceProvider, _) => 30 | { 31 | var idempotencyHandler = RedisIdempotencyHandlerFactory.Instance.CreateIdempotencyHandler( 32 | serviceProvider, 33 | serviceProvider.GetService(), 34 | serviceProvider.GetService(), 35 | configureHandler); 36 | 37 | return idempotencyHandler; 38 | }); 39 | 40 | return services; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Confluent.Kafka.Core.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1;net8.0;net9.0 5 | true 6 | true 7 | 1701;1702;1705;1591 8 | true 9 | Confluent.Kafka.Core.Abstractions 10 | Confluent.Kafka.Core.Abstractions 11 | Lima Technology Services;Fernando Luiz de Lima 12 | Copyright © Lima Technology Services;Fernando Luiz de Lima 13 | Provides abstractions for Apache Kafka .NET client. 14 | https://github.com/ffernandolima/confluent-kafka-core-dotnet 15 | https://github.com/ffernandolima/confluent-kafka-core-dotnet 16 | MIT 17 | git 18 | Apache;Kafka;Confluent;librdkafka;Abstractions 19 | 1.0.6 20 | latest 21 | Confluent.Kafka.Core 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf/IProtobufSerializerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using System.Collections.Generic; 3 | 4 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Protobuf 5 | { 6 | public interface IProtobufSerializerConfigBuilder 7 | { 8 | IProtobufSerializerConfigBuilder FromConfiguration(string sectionKey); 9 | 10 | IProtobufSerializerConfigBuilder WithBufferBytes(int? bufferBytes); 11 | 12 | IProtobufSerializerConfigBuilder WithAutoRegisterSchemas(bool? autoRegisterSchemas); 13 | 14 | IProtobufSerializerConfigBuilder WithNormalizeSchemas(bool? normalizeSchemas); 15 | 16 | IProtobufSerializerConfigBuilder WithUseSchemaId(int? useSchemaId); 17 | 18 | IProtobufSerializerConfigBuilder WithUseLatestVersion(bool? useLatestVersion); 19 | 20 | IProtobufSerializerConfigBuilder WithUseLatestWithMetadata(IDictionary useLatestWithMetadata); 21 | 22 | IProtobufSerializerConfigBuilder WithSkipKnownTypes(bool? skipKnownTypes); 23 | 24 | IProtobufSerializerConfigBuilder WithUseDeprecatedFormat(bool? useDeprecatedFormat); 25 | 26 | IProtobufSerializerConfigBuilder WithSubjectNameStrategy(SubjectNameStrategy? subjectNameStrategy); 27 | 28 | IProtobufSerializerConfigBuilder WithSchemaIdStrategy(SchemaIdSerializerStrategy? schemaIdStrategy); 29 | 30 | IProtobufSerializerConfigBuilder WithReferenceSubjectNameStrategy(ReferenceSubjectNameStrategy? referenceSubjectNameStrategy); 31 | 32 | IProtobufSerializerConfigBuilder WithCustomReferenceSubjectNameStrategy(ICustomReferenceSubjectNameStrategy customReferenceSubjectNameStrategy); 33 | 34 | IProtobufSerializerConfigBuilder WithConfigurationProperty(KeyValuePair configurationProperty); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/build-and-tests.yml: -------------------------------------------------------------------------------- 1 | name: build-and-tests 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | pull_request: 7 | types: ["opened", "synchronize", "reopened"] 8 | 9 | jobs: 10 | build-and-test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout the code 15 | uses: actions/checkout@v4 16 | 17 | - name: Check Docker version 18 | run: docker --version 19 | 20 | - name: Start Docker Compose 21 | run: docker compose -f docker-compose.yml up -d 22 | 23 | - name: Setup .NET 8 24 | uses: actions/setup-dotnet@v4 25 | with: 26 | dotnet-version: 8.0.x 27 | 28 | - name: Setup .NET 9 29 | uses: actions/setup-dotnet@v4 30 | with: 31 | dotnet-version: 9.0.x 32 | 33 | - name: Restore dependencies 34 | run: dotnet restore 35 | 36 | - name: Build solution 37 | run: dotnet build --configuration Release --no-restore 38 | 39 | - name: Wait for services availability 40 | run: sleep 35s 41 | shell: bash 42 | 43 | - name: Check containers 44 | run: docker ps -a 45 | 46 | - name: Run tests .NET 8 47 | run: dotnet test --framework net8.0 --configuration Release --no-build --verbosity normal --logger GitHubActions 48 | 49 | - name: Run tests .NET 9 50 | run: dotnet test --framework net9.0 --configuration Release --no-build --verbosity normal --logger GitHubActions 51 | 52 | - name: Stop Docker Compose 53 | run: docker compose -f docker-compose.yml down 54 | 55 | build-and-publish: 56 | needs: build-and-test 57 | if: github.ref == 'refs/heads/main' 58 | uses: ./.github/workflows/build-and-publish.yml 59 | with: 60 | PROJECT: Confluent.Kafka.Core 61 | secrets: 62 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 63 | NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }} -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro/Internal/SchemaRegistryAvroSerializer`.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using Confluent.SchemaRegistry.Serdes; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Avro.Internal 7 | { 8 | internal sealed class SchemaRegistryAvroSerializer : IAsyncSerializer, IAsyncDeserializer 9 | { 10 | private readonly AvroSerializer _serializer; 11 | private readonly AvroDeserializer _deserializer; 12 | 13 | public SchemaRegistryAvroSerializer( 14 | ISchemaRegistryClient schemaRegistryClient, 15 | AvroSerializerConfig serializerConfig = null, 16 | AvroDeserializerConfig deserializerConfig = null, 17 | RuleRegistry ruleRegistry = null) 18 | { 19 | if (schemaRegistryClient is null) 20 | { 21 | throw new ArgumentNullException(nameof(schemaRegistryClient)); 22 | } 23 | 24 | _serializer = new AvroSerializer(schemaRegistryClient, serializerConfig, ruleRegistry); 25 | _deserializer = new AvroDeserializer(schemaRegistryClient, deserializerConfig, ruleRegistry); 26 | } 27 | 28 | public async Task SerializeAsync(T data, SerializationContext context) 29 | { 30 | var result = await _serializer.SerializeAsync(data, context) 31 | .ConfigureAwait(false); 32 | 33 | return result; 34 | } 35 | 36 | public async Task DeserializeAsync(ReadOnlyMemory data, bool isNull, SerializationContext context) 37 | { 38 | var result = await _deserializer.DeserializeAsync(data, isNull, context) 39 | .ConfigureAwait(false); 40 | 41 | return result; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.ProtobufNet/Internal/ProtobufNetSerializerOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.ProtobufNet.Internal 6 | { 7 | internal sealed class ProtobufNetSerializerOptionsBuilder : 8 | FunctionalBuilder, 9 | IProtobufNetSerializerOptionsBuilder 10 | { 11 | public ProtobufNetSerializerOptionsBuilder(IConfiguration configuration = null) 12 | : base(seedSubject: null, configuration) 13 | { } 14 | 15 | public IProtobufNetSerializerOptionsBuilder FromConfiguration(string sectionKey) 16 | { 17 | AppendAction(options => 18 | { 19 | if (!string.IsNullOrWhiteSpace(sectionKey)) 20 | { 21 | options = Bind(options, sectionKey); 22 | } 23 | }); 24 | return this; 25 | } 26 | 27 | public IProtobufNetSerializerOptionsBuilder WithAutomaticRuntimeMap(bool automaticRuntimeMap) 28 | { 29 | AppendAction(options => options.AutomaticRuntimeMap = automaticRuntimeMap); 30 | return this; 31 | } 32 | 33 | public static ProtobufNetSerializerOptions Build( 34 | IServiceProvider serviceProvider, 35 | IConfiguration configuration, 36 | Action configureOptions) 37 | { 38 | using var builder = new ProtobufNetSerializerOptionsBuilder(configuration); 39 | 40 | configureOptions?.Invoke(serviceProvider, builder); 41 | 42 | var options = builder.Build(); 43 | 44 | return options; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/Retry/IKafkaRetryConsumerWorkerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Hosting.Retry 4 | { 5 | public interface IKafkaRetryConsumerWorkerConfigBuilder 6 | { 7 | IKafkaRetryConsumerWorkerConfigBuilder FromConfiguration(string sectionKey); 8 | 9 | IKafkaRetryConsumerWorkerConfigBuilder WithMaxDegreeOfParallelism(int maxDegreeOfParallelism); 10 | 11 | IKafkaRetryConsumerWorkerConfigBuilder WithEnableLogging(bool enableLogging); 12 | 13 | IKafkaRetryConsumerWorkerConfigBuilder WithEnableDiagnostics(bool enableDiagnostics); 14 | 15 | IKafkaRetryConsumerWorkerConfigBuilder WithCommitFaultedMessages(bool commitFaultedMessages); 16 | 17 | IKafkaRetryConsumerWorkerConfigBuilder WithEnableIdempotency(bool enableIdempotency); 18 | 19 | IKafkaRetryConsumerWorkerConfigBuilder WithEnableRetryOnFailure(bool enableRetryOnFailure); 20 | 21 | IKafkaRetryConsumerWorkerConfigBuilder WithEnableDeadLetterTopic(bool enableDeadLetterTopic); 22 | 23 | IKafkaRetryConsumerWorkerConfigBuilder WithRetryCount(int retryCount); 24 | 25 | IKafkaRetryConsumerWorkerConfigBuilder WithEmptyTopicDelay(TimeSpan emptyTopicDelay); 26 | 27 | IKafkaRetryConsumerWorkerConfigBuilder WithNotEmptyTopicDelay(TimeSpan notEmptyTopicDelay); 28 | 29 | IKafkaRetryConsumerWorkerConfigBuilder WithUnavailableProcessingSlotsDelay(TimeSpan unavailableProcessingSlotsDelay); 30 | 31 | IKafkaRetryConsumerWorkerConfigBuilder WithExceptionDelay(TimeSpan exceptionDelay); 32 | 33 | IKafkaRetryConsumerWorkerConfigBuilder WithPendingProcessingDelay(TimeSpan pendingProcessingDelay); 34 | 35 | IKafkaRetryConsumerWorkerConfigBuilder WithRetryTopicDelay(TimeSpan retryTopicDelay); 36 | 37 | IKafkaRetryConsumerWorkerConfig Build(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Producer/IProducerConfigBuilder`.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Client; 2 | 3 | namespace Confluent.Kafka.Core.Producer 4 | { 5 | public interface IProducerConfigBuilder : 6 | IClientConfigBuilder 7 | where TBuilder : IProducerConfigBuilder 8 | { 9 | TBuilder WithEnableBackgroundPoll(bool? enableBackgroundPoll); 10 | 11 | TBuilder WithEnableDeliveryReports(bool? enableDeliveryReports); 12 | 13 | TBuilder WithDeliveryReportFields(string deliveryReportFields); 14 | 15 | TBuilder WithRequestTimeoutMs(int? requestTimeoutMs); 16 | 17 | TBuilder WithMessageTimeoutMs(int? messageTimeoutMs); 18 | 19 | TBuilder WithPartitioner(Partitioner? partitioner); 20 | 21 | TBuilder WithCompressionLevel(int? compressionLevel); 22 | 23 | TBuilder WithTransactionalId(string transactionalId); 24 | 25 | TBuilder WithTransactionTimeoutMs(int? transactionTimeoutMs); 26 | 27 | TBuilder WithEnableIdempotence(bool? enableIdempotence); 28 | 29 | TBuilder WithEnableGaplessGuarantee(bool? enableGaplessGuarantee); 30 | 31 | TBuilder WithQueueBufferingMaxMessages(int? queueBufferingMaxMessages); 32 | 33 | TBuilder WithQueueBufferingMaxKbytes(int? queueBufferingMaxKbytes); 34 | 35 | TBuilder WithLingerMs(double? lingerMs); 36 | 37 | TBuilder WithMessageSendMaxRetries(int? messageSendMaxRetries); 38 | 39 | TBuilder WithQueueBufferingBackpressureThreshold(int? queueBufferingBackpressureThreshold); 40 | 41 | TBuilder WithCompressionType(CompressionType? compressionType); 42 | 43 | TBuilder WithBatchNumMessages(int? batchNumMessages); 44 | 45 | TBuilder WithBatchSize(int? batchSize); 46 | 47 | TBuilder WithStickyPartitioningLingerMs(int? stickyPartitioningLingerMs); 48 | } 49 | } -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Consumer/Internal/KafkaConsumerHandlerFactoryOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Consumer.Internal 6 | { 7 | internal sealed class KafkaConsumerHandlerFactoryOptionsBuilder : 8 | FunctionalBuilder, 9 | IKafkaConsumerHandlerFactoryOptionsBuilder 10 | { 11 | public KafkaConsumerHandlerFactoryOptionsBuilder(IConfiguration configuration = null) 12 | : base(seedSubject: null, configuration) 13 | { } 14 | 15 | public IKafkaConsumerHandlerFactoryOptionsBuilder FromConfiguration(string sectionKey) 16 | { 17 | AppendAction(options => 18 | { 19 | if (!string.IsNullOrWhiteSpace(sectionKey)) 20 | { 21 | options = Bind(options, sectionKey); 22 | } 23 | }); 24 | return this; 25 | } 26 | 27 | public IKafkaConsumerHandlerFactoryOptionsBuilder WithEnableLogging(bool enableLogging) 28 | { 29 | AppendAction(options => options.EnableLogging = enableLogging); 30 | return this; 31 | } 32 | 33 | public static KafkaConsumerHandlerFactoryOptions Build( 34 | IServiceProvider serviceProvider, 35 | IConfiguration configuration, 36 | Action configureOptions) 37 | { 38 | using var builder = new KafkaConsumerHandlerFactoryOptionsBuilder(configuration); 39 | 40 | configureOptions?.Invoke(serviceProvider, builder); 41 | 42 | var options = builder.Build(); 43 | 44 | return options; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Shared/SchemaRegistry/Internal/SchemaRegistryClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.SchemaRegistry; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.SchemaRegistry.Internal 6 | { 7 | internal sealed class SchemaRegistryClientBuilder : ISchemaRegistryClientBuilder 8 | { 9 | private readonly IConfiguration _configuration; 10 | 11 | public SchemaRegistryConfig SchemaRegistryConfig { get; private set; } 12 | public IAuthenticationHeaderValueProvider AuthenticationHeaderValueProvider { get; private set; } 13 | 14 | public SchemaRegistryClientBuilder(IConfiguration configuration) 15 | { 16 | _configuration = configuration; 17 | } 18 | 19 | public ISchemaRegistryClientBuilder WithSchemaRegistryConfiguration( 20 | Action configureSchemaRegistry) 21 | { 22 | SchemaRegistryConfig = SchemaRegistryConfigBuilder.Build(_configuration, configureSchemaRegistry); 23 | return this; 24 | } 25 | 26 | public ISchemaRegistryClientBuilder WithAuthenticationHeaderValueProvider( 27 | IAuthenticationHeaderValueProvider authenticationHeaderValueProvider) 28 | { 29 | AuthenticationHeaderValueProvider = authenticationHeaderValueProvider; 30 | return this; 31 | } 32 | 33 | public static SchemaRegistryClientBuilder Configure( 34 | IServiceProvider serviceProvider, 35 | IConfiguration configuration, 36 | Action configureClient) 37 | { 38 | var builder = new SchemaRegistryClientBuilder(configuration); 39 | 40 | configureClient?.Invoke(serviceProvider, builder); 41 | 42 | return builder; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core/Producer/Internal/KafkaProducerHandlerFactoryOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Confluent.Kafka.Core.Internal; 2 | using Microsoft.Extensions.Configuration; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Producer.Internal 6 | { 7 | internal sealed class KafkaProducerHandlerFactoryOptionsBuilder : 8 | FunctionalBuilder, 9 | IKafkaProducerHandlerFactoryOptionsBuilder 10 | { 11 | public KafkaProducerHandlerFactoryOptionsBuilder(IConfiguration configuration = null) 12 | : base(seedSubject: null, configuration) 13 | { } 14 | 15 | public IKafkaProducerHandlerFactoryOptionsBuilder FromConfiguration(string sectionKey) 16 | { 17 | AppendAction(options => 18 | { 19 | if (!string.IsNullOrWhiteSpace(sectionKey)) 20 | { 21 | options = Bind(options, sectionKey); 22 | } 23 | }); 24 | return this; 25 | } 26 | 27 | public IKafkaProducerHandlerFactoryOptionsBuilder WithEnableLogging(bool enableLogging) 28 | { 29 | AppendAction(options => options.EnableLogging = enableLogging); 30 | return this; 31 | } 32 | 33 | public static KafkaProducerHandlerFactoryOptions Build( 34 | IServiceProvider serviceProvider, 35 | IConfiguration configuration, 36 | Action configureOptions) 37 | { 38 | using var builder = new KafkaProducerHandlerFactoryOptionsBuilder(configuration); 39 | 40 | configureOptions?.Invoke(serviceProvider, builder); 41 | 42 | var options = builder.Build(); 43 | 44 | return options; 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Abstractions/Hosting/IKafkaConsumerWorkerConfigBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Confluent.Kafka.Core.Hosting 4 | { 5 | public interface IKafkaConsumerWorkerConfigBuilder 6 | { 7 | IKafkaConsumerWorkerConfigBuilder FromConfiguration(string sectionKey); 8 | 9 | IKafkaConsumerWorkerConfigBuilder WithMaxDegreeOfParallelism(int maxDegreeOfParallelism); 10 | 11 | IKafkaConsumerWorkerConfigBuilder WithEnableLogging(bool enableLogging); 12 | 13 | IKafkaConsumerWorkerConfigBuilder WithEnableDiagnostics(bool enableDiagnostics); 14 | 15 | IKafkaConsumerWorkerConfigBuilder WithCommitFaultedMessages(bool commitFaultedMessages); 16 | 17 | IKafkaConsumerWorkerConfigBuilder WithEnableIdempotency(bool enableIdempotency); 18 | 19 | IKafkaConsumerWorkerConfigBuilder WithEnableRetryOnFailure(bool enableRetryOnFailure); 20 | 21 | IKafkaConsumerWorkerConfigBuilder WithEnableRetryTopic(bool enableRetryTopic); 22 | 23 | IKafkaConsumerWorkerConfigBuilder WithRetryTopicExceptionTypeFilters(string[] retryTopicExceptionTypeFilters); 24 | 25 | IKafkaConsumerWorkerConfigBuilder WithRetryTopicExceptionFilter(Func retryTopicExceptionFilter); 26 | 27 | IKafkaConsumerWorkerConfigBuilder WithEnableDeadLetterTopic(bool enableDeadLetterTopic); 28 | 29 | IKafkaConsumerWorkerConfigBuilder WithEmptyTopicDelay(TimeSpan emptyTopicDelay); 30 | 31 | IKafkaConsumerWorkerConfigBuilder WithNotEmptyTopicDelay(TimeSpan notEmptyTopicDelay); 32 | 33 | IKafkaConsumerWorkerConfigBuilder WithUnavailableProcessingSlotsDelay(TimeSpan unavailableProcessingSlotsDelay); 34 | 35 | IKafkaConsumerWorkerConfigBuilder WithExceptionDelay(TimeSpan exceptionDelay); 36 | 37 | IKafkaConsumerWorkerConfigBuilder WithPendingProcessingDelay(TimeSpan pendingProcessingDelay); 38 | 39 | IKafkaConsumerWorkerConfig Build(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Confluent.Kafka.Core.Serialization.JsonCore/Internal/JsonCoreSerializerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Confluent.Kafka.Core.Serialization.JsonCore.Internal 6 | { 7 | internal sealed class JsonCoreSerializerFactory : IJsonCoreSerializerFactory 8 | { 9 | private static readonly Lazy Factory = new( 10 | () => new JsonCoreSerializerFactory(), isThreadSafe: true); 11 | 12 | public static JsonCoreSerializerFactory Instance => Factory.Value; 13 | 14 | private JsonCoreSerializerFactory() 15 | { } 16 | 17 | public JsonCoreSerializer GetOrCreateSerializer( 18 | IServiceProvider serviceProvider, 19 | IConfiguration configuration, 20 | Action configureOptions, 21 | object serializerKey) 22 | { 23 | var serializer = serviceProvider?.GetKeyedService>( 24 | serializerKey ?? JsonCoreSerializerConstants.JsonCoreSerializerKey) ?? 25 | CreateSerializer(serviceProvider, configuration, (_, builder) => configureOptions?.Invoke(builder)); 26 | 27 | return serializer; 28 | } 29 | 30 | public JsonCoreSerializer CreateSerializer( 31 | IServiceProvider serviceProvider, 32 | IConfiguration configuration, 33 | Action configureOptions) 34 | { 35 | var options = JsonSerializerOptionsBuilder.Build( 36 | serviceProvider, 37 | configuration ?? serviceProvider?.GetService(), 38 | configureOptions); 39 | 40 | var serializer = new JsonCoreSerializer(options); 41 | 42 | return serializer; 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------