├── .codacy.yml
├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── build.yml
│ ├── deploy-website.yml
│ ├── publish.yml
│ └── test-deploy-website.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Directory.Build.props
├── KafkaFlow.Retry.sln
├── LICENSE.md
├── README.md
├── commitlint.config.js
├── samples
├── KafkaFlow.Retry.API.Sample
│ ├── KafkaFlow.Retry.API.Sample.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── KafkaFlow.Retry.Common.Sample
│ ├── Helpers
│ │ ├── KafkaHelper.cs
│ │ └── SqlServerHelper.cs
│ └── KafkaFlow.Retry.Common.Sample.csproj
├── KafkaFlow.Retry.Sample
│ ├── Exceptions
│ │ ├── RetryDurableTestException.cs
│ │ ├── RetryForeverTestException.cs
│ │ └── RetrySimpleTestException.cs
│ ├── Handlers
│ │ ├── RetryDurableTestHandler.cs
│ │ ├── RetryForeverTestHandler.cs
│ │ └── RetrySimpleTestHandler.cs
│ ├── Helpers
│ │ └── KafkaClusterConfigurationBuilderHelper.cs
│ ├── KafkaFlow.Retry.Sample.csproj
│ ├── Messages
│ │ ├── RetryDurableTestMessage.cs
│ │ ├── RetryForeverTestMessage.cs
│ │ └── RetrySimpleTestMessage.cs
│ └── Program.cs
└── KafkaFlow.Retry.SchemaRegistry.Sample
│ ├── ContractResolvers
│ └── WritablePropertiesOnlyResolver.cs
│ ├── Exceptions
│ └── RetryDurableTestException.cs
│ ├── Handlers
│ └── AvroMessageTestHandler.cs
│ ├── Helpers
│ └── KafkaClusterConfigurationBuilderHelper.cs
│ ├── KafkaFlow.Retry.SchemaRegistry.Sample.csproj
│ ├── Messages
│ └── Avro
│ │ ├── AvroLogMessage.avsc
│ │ ├── AvroLogMessage.cs
│ │ └── LogLevel.cs
│ └── Program.cs
├── src
├── KafkaFlow.Retry.API
│ ├── Adapters
│ │ ├── Common
│ │ │ ├── IRetryQueueItemAdapter.cs
│ │ │ ├── IRetryQueueItemStatusDtoAdapter.cs
│ │ │ ├── Parsers
│ │ │ │ ├── EnumParser.cs
│ │ │ │ └── IQueryParametersParser.cs
│ │ │ ├── RetryQueueItemAdapter.cs
│ │ │ └── RetryQueueItemStatusDtoAdapter.cs
│ │ ├── GetItems
│ │ │ ├── GetItemsInputAdapter.cs
│ │ │ ├── GetItemsRequestDtoReader.cs
│ │ │ ├── GetItemsResponseDtoAdapter.cs
│ │ │ ├── IGetItemsInputAdapter.cs
│ │ │ ├── IGetItemsRequestDtoReader.cs
│ │ │ └── IGetItemsResponseDtoAdapter.cs
│ │ ├── UpdateItems
│ │ │ ├── IUpdateItemsInputAdapter.cs
│ │ │ ├── IUpdateItemsResponseDtoAdapter.cs
│ │ │ ├── UpdateItemsInputAdapter.cs
│ │ │ └── UpdateItemsResponseDtoAdapter.cs
│ │ └── UpdateQueues
│ │ │ ├── IUpdateQueuesInputAdapter.cs
│ │ │ ├── IUpdateQueuesResponseDtoAdapter.cs
│ │ │ ├── UpdateQueuesInputAdapter.cs
│ │ │ └── UpdateQueuesResponseDtoAdapter.cs
│ ├── AppBuilderExtensions.cs
│ ├── Dtos
│ │ ├── Common
│ │ │ ├── RetryQueueItemDto.cs
│ │ │ ├── RetryQueueItemStatusDto.cs
│ │ │ └── RetryQueuetItemMessageInfoDto.cs
│ │ ├── GetItemsRequestDto.cs
│ │ ├── GetItemsResponseDto.cs
│ │ ├── UpdateItemResultDto.cs
│ │ ├── UpdateItemsRequestDto.cs
│ │ ├── UpdateItemsResponseDto.cs
│ │ ├── UpdateQueueResultDto.cs
│ │ ├── UpdateQueuesRequestDto.cs
│ │ └── UpdateQueuesResponseDto.cs
│ ├── Handlers
│ │ ├── GetItemsHandler.cs
│ │ ├── PatchItemsHandler.cs
│ │ └── PatchQueuesHandler.cs
│ ├── HttpExtensions.cs
│ ├── HttpMethod.cs
│ ├── IHttpRequestHandler.cs
│ ├── KafkaFlow.Retry.API.csproj
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── RetryMiddleware.cs
│ └── RetryRequestHandlerBase.cs
├── KafkaFlow.Retry.MongoDb
│ ├── Adapters
│ │ ├── HeaderAdapter.cs
│ │ ├── Interfaces
│ │ │ ├── IHeaderAdapter.cs
│ │ │ ├── IItemAdapter.cs
│ │ │ ├── IMessageAdapter.cs
│ │ │ └── IQueuesAdapter.cs
│ │ ├── ItemAdapter.cs
│ │ ├── MessageAdapter.cs
│ │ └── QueuesAdapter.cs
│ ├── DataProviderCreationException.cs
│ ├── DataProviderCreationResult.cs
│ ├── DbContext.cs
│ ├── KafkaFlow.Retry.MongoDb.csproj
│ ├── Model
│ │ ├── DboConfigurations.cs
│ │ ├── Factories
│ │ │ ├── RetryQueueDboFactory.cs
│ │ │ └── RetryQueueItemDboFactory.cs
│ │ ├── RetryQueueDbo.cs
│ │ ├── RetryQueueHeaderDbo.cs
│ │ ├── RetryQueueItemDbo.cs
│ │ └── RetryQueueItemMessageDbo.cs
│ ├── MongoDbDataProviderFactory.cs
│ ├── MongoDbSettings.cs
│ ├── MongoRepositoryCollectionExtensions.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Repositories
│ │ ├── IRetryQueueItemRepository.cs
│ │ ├── IRetryQueueRepository.cs
│ │ ├── RetryQueueItemRepository.cs
│ │ └── RetryQueueRepository.cs
│ ├── RetryDurableDefinitionBuilderExtension.cs
│ └── RetryQueueDataProvider.cs
├── KafkaFlow.Retry.Postgres
│ ├── ConnectionProvider.cs
│ ├── DbConnectionContext.cs
│ ├── Deploy
│ │ ├── 01 - Create_Tables.sql
│ │ └── 02 - Populate_Tables.sql
│ ├── IConnectionProvider.cs
│ ├── IDbConnection.cs
│ ├── IDbConnectionWithinTransaction.cs
│ ├── IRetrySchemaCreator.cs
│ ├── KafkaFlow.Retry.Postgres.csproj
│ ├── Model
│ │ ├── Factories
│ │ │ ├── IRetryQueueDboFactory.cs
│ │ │ ├── IRetryQueueItemDboFactory.cs
│ │ │ ├── IRetryQueueItemMessageDboFactory.cs
│ │ │ ├── IRetryQueueItemMessageHeaderDboFactory.cs
│ │ │ ├── RetryQueueDboFactory.cs
│ │ │ ├── RetryQueueItemDboFactory.cs
│ │ │ ├── RetryQueueItemMessageDboFactory.cs
│ │ │ └── RetryQueueItemMessageHeaderDboFactory.cs
│ │ ├── RetryQueueDbo.cs
│ │ ├── RetryQueueItemDbo.cs
│ │ ├── RetryQueueItemMessageDbo.cs
│ │ ├── RetryQueueItemMessageHeaderDbo.cs
│ │ ├── RetryQueuesDboWrapper.cs
│ │ └── Schema
│ │ │ └── Script.cs
│ ├── PostgresDbDataProviderFactory.cs
│ ├── PostgresDbSettings.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Readers
│ │ ├── Adapters
│ │ │ ├── IRetryQueueAdapter.cs
│ │ │ ├── IRetryQueueItemAdapter.cs
│ │ │ ├── IRetryQueueItemMessageAdapter.cs
│ │ │ ├── IRetryQueueItemMessageHeaderAdapter.cs
│ │ │ ├── RetryQueueAdapter.cs
│ │ │ ├── RetryQueueItemAdapter.cs
│ │ │ ├── RetryQueueItemMessageAdapter.cs
│ │ │ └── RetryQueueItemMessageHeaderAdapter.cs
│ │ ├── DboCollectionNavigator.cs
│ │ ├── IDboDomainAdapter.cs
│ │ ├── IRetryQueueReader.cs
│ │ └── RetryQueueReader.cs
│ ├── Repositories
│ │ ├── IRetryQueueItemMessageHeaderRepository.cs
│ │ ├── IRetryQueueItemMessageRepository.cs
│ │ ├── IRetryQueueItemRepository.cs
│ │ ├── IRetryQueueRepository.cs
│ │ ├── RetryQueueItemMessageHeaderRepository.cs
│ │ ├── RetryQueueItemMessageRepository.cs
│ │ ├── RetryQueueItemRepository.cs
│ │ └── RetryQueueRepository.cs
│ ├── RetryDurableDefinitionBuilderExtension.cs
│ ├── RetryQueueDataProvider.cs
│ └── RetrySchemaCreator.cs
├── KafkaFlow.Retry.SqlServer
│ ├── ConnectionProvider.cs
│ ├── DbConnectionContext.cs
│ ├── Deploy
│ │ ├── 00 - Create_Database.sql
│ │ ├── 01 - Create_Tables.sql
│ │ └── 02 - Populate_Tables.sql
│ ├── IConnectionProvider.cs
│ ├── IDbConnection.cs
│ ├── IDbConnectionWithinTransaction.cs
│ ├── IRetrySchemaCreator.cs
│ ├── KafkaFlow.Retry.SqlServer.csproj
│ ├── Model
│ │ ├── Factories
│ │ │ ├── IRetryQueueDboFactory.cs
│ │ │ ├── IRetryQueueItemDboFactory.cs
│ │ │ ├── IRetryQueueItemMessageDboFactory.cs
│ │ │ ├── IRetryQueueItemMessageHeaderDboFactory.cs
│ │ │ ├── RetryQueueDboFactory.cs
│ │ │ ├── RetryQueueItemDboFactory.cs
│ │ │ ├── RetryQueueItemMessageDboFactory.cs
│ │ │ └── RetryQueueItemMessageHeaderDboFactory.cs
│ │ ├── RetryQueueDbo.cs
│ │ ├── RetryQueueItemDbo.cs
│ │ ├── RetryQueueItemMessageDbo.cs
│ │ ├── RetryQueueItemMessageHeaderDbo.cs
│ │ ├── RetryQueuesDboWrapper.cs
│ │ └── Schema
│ │ │ └── Script.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Readers
│ │ ├── Adapters
│ │ │ ├── IRetryQueueAdapter.cs
│ │ │ ├── IRetryQueueItemAdapter.cs
│ │ │ ├── IRetryQueueItemMessageAdapter.cs
│ │ │ ├── IRetryQueueItemMessageHeaderAdapter.cs
│ │ │ ├── RetryQueueAdapter.cs
│ │ │ ├── RetryQueueItemAdapter.cs
│ │ │ ├── RetryQueueItemMessageAdapter.cs
│ │ │ └── RetryQueueItemMessageHeaderAdapter.cs
│ │ ├── DboCollectionNavigator.cs
│ │ ├── IDboDomainAdapter.cs
│ │ ├── IRetryQueueReader.cs
│ │ └── RetryQueueReader.cs
│ ├── Repositories
│ │ ├── IRetryQueueItemMessageHeaderRepository.cs
│ │ ├── IRetryQueueItemMessageRepository.cs
│ │ ├── IRetryQueueItemRepository.cs
│ │ ├── IRetryQueueRepository.cs
│ │ ├── RetryQueueItemMessageHeaderRepository.cs
│ │ ├── RetryQueueItemMessageRepository.cs
│ │ ├── RetryQueueItemRepository.cs
│ │ └── RetryQueueRepository.cs
│ ├── RetryDurableDefinitionBuilderExtension.cs
│ ├── RetryQueueDataProvider.cs
│ ├── RetrySchemaCreator.cs
│ ├── SqlServerDbDataProviderFactory.cs
│ └── SqlServerDbSettings.cs
└── KafkaFlow.Retry
│ ├── ConfigurationBuilderExtensions.cs
│ ├── Durable
│ ├── Common
│ │ └── SeverityLevel.cs
│ ├── Compressors
│ │ ├── GzipCompressor.cs
│ │ └── IGzipCompressor.cs
│ ├── Definitions
│ │ ├── Builders
│ │ │ ├── Polling
│ │ │ │ ├── CleanupPollingDefinitionBuilder.cs
│ │ │ │ ├── PollingDefinitionBuilder.cs
│ │ │ │ ├── PollingDefinitionsAggregatorBuilder.cs
│ │ │ │ ├── RetryDurableActiveQueuesCountPollingDefinitionBuilder.cs
│ │ │ │ └── RetryDurablePollingDefinitionBuilder.cs
│ │ │ ├── RetryDurableDefinitionBuilder.cs
│ │ │ ├── RetryDurableEmbeddedClusterDefinitionBuilder.cs
│ │ │ └── RetryDurableRetryPlanBeforeDefinitionBuilder.cs
│ │ ├── Polling
│ │ │ ├── CleanupPollingDefinition.cs
│ │ │ ├── PollingDefinition.cs
│ │ │ ├── PollingDefinitionsAggregator.cs
│ │ │ ├── PollingJobType.cs
│ │ │ ├── RetryDurableActiveQueuesCountPollingDefinition.cs
│ │ │ └── RetryDurablePollingDefinition.cs
│ │ ├── RetryDurableDefinition.cs
│ │ └── RetryDurableRetryPlanBeforeDefinition.cs
│ ├── Encoders
│ │ ├── IUtf8Encoder.cs
│ │ └── Utf8Encoder.cs
│ ├── MessageSerializerStrategy.cs
│ ├── Polling
│ │ ├── Extensions
│ │ │ └── JobDataMapExtensions.cs
│ │ ├── IJobDataProvider.cs
│ │ ├── IJobDataProvidersFactory.cs
│ │ ├── IQueueTrackerCoordinator.cs
│ │ ├── IQueueTrackerFactory.cs
│ │ ├── ITriggerProvider.cs
│ │ ├── JobDataProvidersFactory.cs
│ │ ├── Jobs
│ │ │ ├── CleanupJobDataProvider.cs
│ │ │ ├── CleanupPollingJob.cs
│ │ │ ├── PollingJobConstants.cs
│ │ │ ├── RetryDurableActiveQueuesCountJob.cs
│ │ │ ├── RetryDurableActiveQueuesCountJobDataProvider.cs
│ │ │ ├── RetryDurableJobDataProvider.cs
│ │ │ └── RetryDurablePollingJob.cs
│ │ ├── QueueTracker.cs
│ │ ├── QueueTrackerCoordinator.cs
│ │ ├── QueueTrackerFactory.cs
│ │ └── TriggerProvider.cs
│ ├── Repository
│ │ ├── Actions
│ │ │ ├── Create
│ │ │ │ ├── AddIfQueueExistsResult.cs
│ │ │ │ ├── AddIfQueueExistsResultStatus.cs
│ │ │ │ ├── SaveToQueueInput.cs
│ │ │ │ ├── SaveToQueueResult.cs
│ │ │ │ └── SaveToQueueResultStatus.cs
│ │ │ ├── Delete
│ │ │ │ ├── DeleteQueuesInput.cs
│ │ │ │ └── DeleteQueuesResult.cs
│ │ │ ├── Read
│ │ │ │ ├── CheckQueueInput.cs
│ │ │ │ ├── CheckQueueResult.cs
│ │ │ │ ├── CheckQueueResultStatus.cs
│ │ │ │ ├── CountQueuesInput.cs
│ │ │ │ ├── GetQueuesInput.cs
│ │ │ │ ├── GetQueuesResult.cs
│ │ │ │ ├── GetQueuesSortOption.cs
│ │ │ │ ├── QueueNewestItemsInput.cs
│ │ │ │ ├── QueueNewestItemsResult.cs
│ │ │ │ ├── QueueNewestItemsResultStatus.cs
│ │ │ │ ├── QueuePendingItemsInput.cs
│ │ │ │ ├── QueuePendingItemsResult.cs
│ │ │ │ ├── QueuePendingItemsResultStatus.cs
│ │ │ │ └── StuckStatusFilter.cs
│ │ │ └── Update
│ │ │ │ ├── UpdateItemExecutionInfoInput.cs
│ │ │ │ ├── UpdateItemInput.cs
│ │ │ │ ├── UpdateItemResult.cs
│ │ │ │ ├── UpdateItemResultStatus.cs
│ │ │ │ ├── UpdateItemStatusInput.cs
│ │ │ │ ├── UpdateItemsInQueueInput.cs
│ │ │ │ ├── UpdateItemsInput.cs
│ │ │ │ ├── UpdateItemsResult.cs
│ │ │ │ ├── UpdateQueueResult.cs
│ │ │ │ ├── UpdateQueueResultStatus.cs
│ │ │ │ ├── UpdateQueuesInput.cs
│ │ │ │ └── UpdateQueuesResult.cs
│ │ ├── Adapters
│ │ │ ├── IMessageAdapter.cs
│ │ │ ├── IMessageHeadersAdapter.cs
│ │ │ ├── MessageHeadersAdapter.cs
│ │ │ └── NewtonsoftJsonMessageAdapter.cs
│ │ ├── IRetryDurableQueueRepository.cs
│ │ ├── IRetryDurableQueueRepositoryProvider.cs
│ │ ├── IUpdateRetryQueueItemHandler.cs
│ │ ├── Model
│ │ │ ├── MessageHeader.cs
│ │ │ ├── RetryQueue.cs
│ │ │ ├── RetryQueueItem.cs
│ │ │ ├── RetryQueueItemMessage.cs
│ │ │ ├── RetryQueueItemStatus.cs
│ │ │ └── RetryQueueStatus.cs
│ │ ├── NullRetryDurableQueueRepository.cs
│ │ ├── RetryDurableQueueRepository.cs
│ │ ├── UpdateRetryQueueItemExecutionInfoHandler.cs
│ │ └── UpdateRetryQueueItemStatusHandler.cs
│ ├── RetryConsumerStrategy.cs
│ ├── RetryDurableConstants.cs
│ ├── RetryDurableConsumerCompressorMiddleware.cs
│ ├── RetryDurableConsumerConfigurationBuilderExtensions.cs
│ ├── RetryDurableConsumerGuaranteeOrderedMiddleware.cs
│ ├── RetryDurableConsumerLatestMiddleware.cs
│ ├── RetryDurableConsumerNewtonsoftJsonSerializerMiddleware.cs
│ ├── RetryDurableConsumerUtf8EncoderMiddleware.cs
│ ├── RetryDurableConsumerValidationMiddleware.cs
│ ├── RetryDurableException.cs
│ ├── RetryDurableMiddleware.cs
│ ├── RetryError.cs
│ ├── RetryErrorCode.cs
│ └── Serializers
│ │ ├── INewtonsoftJsonSerializer.cs
│ │ └── NewtonsoftJsonSerializer.cs
│ ├── Forever
│ ├── RetryForeverDefinition.cs
│ ├── RetryForeverDefinitionBuilder.cs
│ └── RetryForeverMiddleware.cs
│ ├── KafkaFlow.Retry.csproj
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── RetryContext.cs
│ └── Simple
│ ├── RetrySimpleDefinition.cs
│ ├── RetrySimpleDefinitionBuilder.cs
│ └── RetrySimpleMiddleware.cs
├── tests
├── CodeCoverage.runsettings
├── KafkaFlow.Retry.IntegrationTests
│ ├── Core
│ │ ├── Bootstrappers
│ │ │ ├── BootstrapperKafka.cs
│ │ │ ├── BootstrapperPostgresSchema.cs
│ │ │ ├── BootstrapperSqlServerSchema.cs
│ │ │ └── Fixtures
│ │ │ │ ├── BootstrapperFixtureTemplate.cs
│ │ │ │ ├── BootstrapperHostFixture.cs
│ │ │ │ └── BootstrapperRepositoryFixture.cs
│ │ ├── Exceptions
│ │ │ ├── RetryDurableTestException.cs
│ │ │ ├── RetryForeverTestException.cs
│ │ │ └── RetrySimpleTestException.cs
│ │ ├── Handlers
│ │ │ ├── RetryDurableTestMessageHandler.cs
│ │ │ ├── RetryForeverTestMessageHandler.cs
│ │ │ └── RetrySimpleTestMessageHandler.cs
│ │ ├── Messages
│ │ │ ├── ITestMessage.cs
│ │ │ ├── RetryDurableTestMessage.cs
│ │ │ ├── RetryForeverTestMessage.cs
│ │ │ └── RetrySimpleTestMessage.cs
│ │ ├── Producers
│ │ │ ├── RetryDurableGuaranteeOrderedConsumptionMongoDbProducer.cs
│ │ │ ├── RetryDurableGuaranteeOrderedConsumptionPostgresProducer.cs
│ │ │ ├── RetryDurableGuaranteeOrderedConsumptionSqlServerProducer.cs
│ │ │ ├── RetryDurableLatestConsumptionMongoDbProducer.cs
│ │ │ ├── RetryDurableLatestConsumptionPostgresProducer.cs
│ │ │ ├── RetryDurableLatestConsumptionSqlServerProducer.cs
│ │ │ ├── RetryForeverProducer.cs
│ │ │ └── RetrySimpleProducer.cs
│ │ ├── Settings
│ │ │ ├── KafkaSettings.cs
│ │ │ ├── MongoDbRepositorySettings.cs
│ │ │ ├── PostgresRepositorySettings.cs
│ │ │ └── SqlServerRepositorySettings.cs
│ │ ├── Storages
│ │ │ ├── Assertion
│ │ │ │ ├── IPhysicalStorageAssert.cs
│ │ │ │ ├── RetryDurableGuaranteeOrderedConsumptionPhysicalStorageAssert.cs
│ │ │ │ └── RetryDurableLatestConsumptionPhysicalStorageAssert.cs
│ │ │ ├── InMemoryAuxiliarStorage.cs
│ │ │ ├── Repositories
│ │ │ │ ├── IRepository.cs
│ │ │ │ ├── IRepositoryProvider.cs
│ │ │ │ ├── MongoDbRepository.cs
│ │ │ │ ├── PostgresRepository.cs
│ │ │ │ ├── RepositoryProvider.cs
│ │ │ │ ├── RepositoryType.cs
│ │ │ │ └── SqlServerRepository.cs
│ │ │ ├── RetryQueueBuilder.cs
│ │ │ └── RetryQueueItemBuilder.cs
│ │ └── TraceLogHandler.cs
│ ├── KafkaFlow.Retry.IntegrationTests.csproj
│ ├── PollingTests
│ │ ├── JobDataProviderSurrogate.cs
│ │ ├── JobSurrogate.cs
│ │ └── QueueTrackerCoordinatorTests.cs
│ ├── Properties
│ │ └── AssemblyMetadata.cs
│ ├── RepositoryTests
│ │ └── RetryQueueDataProviderTests
│ │ │ ├── CheckQueuePendingItemsTests.cs
│ │ │ ├── CheckQueueTests.cs
│ │ │ ├── CountQueuesTests.cs
│ │ │ ├── CreateSchemaCreatorTests.cs
│ │ │ ├── DeleteQueuesTests.cs
│ │ │ ├── GetQueuesTests.cs
│ │ │ ├── RetryQueueDataProviderTestsTemplate.cs
│ │ │ ├── SaveToQueueTests.cs
│ │ │ ├── UpdateItemExecutionInfoTests.cs
│ │ │ ├── UpdateItemStatusTests.cs
│ │ │ ├── UpdateItemsTests.cs
│ │ │ └── UpdateQueuesTests.cs
│ ├── RetryDurableTests.cs
│ ├── RetryForeverTests.cs
│ ├── RetrySimpleTests.cs
│ ├── Usings.cs
│ └── conf
│ │ └── appsettings.json
└── KafkaFlow.Retry.UnitTests
│ ├── API
│ ├── Adapters
│ │ ├── Common
│ │ │ ├── Parses
│ │ │ │ └── EnumParserTests.cs
│ │ │ ├── RetryQueueItemAdapterTests.cs
│ │ │ └── RetryQueueItemStatusDtoAdapterTests.cs
│ │ ├── GetItems
│ │ │ ├── GetItemsInputAdapterTests.cs
│ │ │ ├── GetItemsRequestDtoReaderTests.cs
│ │ │ └── GetItemsResponseDtoAdapterTests.cs
│ │ ├── UpdateItems
│ │ │ ├── UpdateItemsInputAdapterTests.cs
│ │ │ └── UpdateItemsResponseDtoAdapterTests.cs
│ │ └── UpdateQueues
│ │ │ ├── UpdateQueuesInputAdapterTests.cs
│ │ │ └── UpdateQueuesResponseDtoAdapterTests.cs
│ ├── Handlers
│ │ ├── GetItemsHandlerTests.cs
│ │ ├── PatchItemsHandlerTests.cs
│ │ └── PatchQueuesHandlerTests.cs
│ ├── HttpExtensionsTests.cs
│ ├── RetryMiddlewareTests.cs
│ ├── RetryRequestHandlerBaseTests.cs
│ ├── Surrogate
│ │ ├── DtoSurrogate.cs
│ │ └── RetryRequestHandlerSurrogate.cs
│ └── Utilities
│ │ └── HttpContextHelper.cs
│ ├── KafkaFlow.Retry.UnitTests.csproj
│ ├── KafkaFlow.Retry
│ ├── Durable
│ │ ├── Compressors
│ │ │ └── GzipCompressorTests.cs
│ │ ├── Definitions
│ │ │ ├── Polling
│ │ │ │ ├── CleanupPollingDefinitionTests.cs
│ │ │ │ └── RetryDurablePollingDefinitionTests.cs
│ │ │ ├── RetryDurableDefinitionTests.cs
│ │ │ └── RetryDurableRetryPlanBeforeDefinitionTests.cs
│ │ ├── Encoders
│ │ │ └── Utf8EncoderTests.cs
│ │ ├── Polling
│ │ │ ├── JobDataProvidersFactoryTests.cs
│ │ │ ├── Jobs
│ │ │ │ ├── CleanupPollingJobTests.cs
│ │ │ │ └── RetryDurablePollingJobTests.cs
│ │ │ ├── QueueTrackerCoordinatorTests.cs
│ │ │ └── QueueTrackerFactoryTests.cs
│ │ ├── Repository
│ │ │ └── Adapters
│ │ │ │ └── MessageHeadersAdapterTests.cs
│ │ ├── RetryDurableConsumerCompressorMiddlewareTests.cs
│ │ ├── RetryDurableConsumerGuaranteeOrderedMiddlewareTests.cs
│ │ ├── RetryDurableConsumerLatestMiddlewareTests.cs
│ │ ├── RetryDurableConsumerNewtonsoftJsonSerializerMiddlewareTests.cs
│ │ ├── RetryDurableConsumerUtf8EncoderMiddlewareTests.cs
│ │ ├── RetryDurableConsumerValidationMiddlewareTests.cs
│ │ └── RetryDurableMiddlewareTests.cs
│ ├── Forever
│ │ ├── RetryForeverDefinitionBuilderTests.cs
│ │ └── RetryForeverDefinitionTests.cs
│ ├── RetryContextTests.cs
│ └── Simple
│ │ ├── RetrySimpleDefinitionBuilderTests.cs
│ │ ├── RetrySimpleDefinitionTests.cs
│ │ └── RetrySimpleMiddlewareTests.cs
│ ├── Repositories
│ ├── MongoDb
│ │ ├── Adapters
│ │ │ ├── HeaderAdapterTests.cs
│ │ │ ├── ItemAdapterTests.cs
│ │ │ ├── MessageAdapterTests.cs
│ │ │ └── QueuesAdapterTests.cs
│ │ ├── DbContextTests.cs
│ │ ├── Model
│ │ │ └── Factories
│ │ │ │ ├── RetryQueueDboFactoryTests.cs
│ │ │ │ └── RetryQueueItemDboFactoryTests.cs
│ │ ├── MongoDbDataProviderFactoryTests.cs
│ │ ├── MongoRepositoryCollectionExtensionsTests.cs
│ │ ├── Repositories
│ │ │ ├── RetryQueueItemRepositoryTests.cs
│ │ │ └── RetryQueueRepositoryTests.cs
│ │ └── RetryDurableDefinitionBuilderExtensionTests.cs
│ ├── Postgres
│ │ ├── ConnectionProviderTests.cs
│ │ ├── Model
│ │ │ ├── Factories
│ │ │ │ ├── RetryQueueDboFactoryTests.cs
│ │ │ │ ├── RetryQueueItemDboFactoryTests.cs
│ │ │ │ ├── RetryQueueItemMessageDboFactoryTests.cs
│ │ │ │ └── RetryQueueItemMessageHeaderDboFactoryTests.cs
│ │ │ └── Schema
│ │ │ │ └── ScriptTests.cs
│ │ ├── Readers
│ │ │ ├── Adapters
│ │ │ │ ├── RetryQueueAdapterTests.cs
│ │ │ │ ├── RetryQueueItemAdapterTests.cs
│ │ │ │ ├── RetryQueueItemMessageAdapterTests.cs
│ │ │ │ └── RetryQueueItemMessageHeaderAdapterTests.cs
│ │ │ ├── DboCollectionNavigatorTests.cs
│ │ │ └── RetryQueueReaderTests.cs
│ │ └── RetryDurableDefinitionBuilderExtensionTests.cs
│ └── SqlServer
│ │ ├── ConnectionProviderTests.cs
│ │ ├── Model
│ │ ├── Factories
│ │ │ ├── RetryQueueDboFactoryTests.cs
│ │ │ ├── RetryQueueItemDboFactoryTests.cs
│ │ │ ├── RetryQueueItemMessageDboFactoryTests.cs
│ │ │ └── RetryQueueItemMessageHeaderDboFactoryTests.cs
│ │ └── Schema
│ │ │ └── ScriptTests.cs
│ │ ├── Readers
│ │ ├── Adapters
│ │ │ ├── RetryQueueAdapterTests.cs
│ │ │ ├── RetryQueueItemAdapterTests.cs
│ │ │ ├── RetryQueueItemMessageAdapterTests.cs
│ │ │ └── RetryQueueItemMessageHeaderAdapterTests.cs
│ │ ├── DboCollectionNavigatorTests.cs
│ │ └── RetryQueueReaderTests.cs
│ │ └── RetryDurableDefinitionBuilderExtensionTests.cs
│ └── Usings.cs
└── website
├── .gitignore
├── README.md
├── babel.config.js
├── docs
├── getting-started
│ ├── _category_.json
│ ├── installation.md
│ ├── packages.md
│ └── quickstart.md
├── guides
│ ├── _category_.json
│ ├── durable-retries.md
│ ├── exception-handling.md
│ ├── forever-retries.md
│ └── simple-retries.md
└── introduction.md
├── docusaurus.config.js
├── package-lock.json
├── package.json
├── sidebars.js
├── src
└── css
│ └── custom.css
├── static
├── .nojekyll
└── img
│ ├── favicon.ico
│ └── logo.svg
└── yarn.lock
/.codacy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | exclude_paths:
3 | - "website/**"
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Documentation 📚
4 | url: https://github.com/Farfetch/kafkaflow-retry-extensions/wiki
5 | about: Check out the official docs for answers to common questions
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: 💡 Feature Request
2 | description: Suggest a new idea
3 | title: '[Feature Request]:'
4 | labels:
5 | - needs triage
6 | - enhancement
7 | body:
8 | - type: textarea
9 | id: problem
10 | attributes:
11 | label: Is your request related to a problem you have?
12 | description: A description of the problem you are trying to address.
13 | - type: textarea
14 | id: solution
15 | attributes:
16 | label: Describe the solution you'd like
17 | description: A clear and concise description of what you want to happen. Include any alternative solutions or features you've considered.
18 | validations:
19 | required: true
20 | - type: dropdown
21 | id: help
22 | attributes:
23 | label: Are you able to help bring it to life and contribute with a Pull Request?
24 | options:
25 | - 'No'
26 | - 'Yes'
27 | validations:
28 | required: true
29 | - type: textarea
30 | id: context
31 | attributes:
32 | label: Additional context
33 | description: Add any other context or screenshots about the feature request here.
34 |
35 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
4 |
5 | Fixes # (issue)
6 |
7 | ## How Has This Been Tested?
8 |
9 | Please describe the tests that you ran to verify your changes.
10 |
11 | ## Checklist
12 |
13 | - [ ] My code follows the style guidelines of this project
14 | - [ ] I have performed a self-review of my own code
15 | - [ ] I have added tests to cover my changes
16 | - [ ] I have made corresponding changes to the documentation
17 |
18 | ### Disclaimer
19 |
20 | By sending us your contributions, you are agreeing that your contribution is made subject to the terms of our [Contributor Ownership Statement](https://github.com/Farfetch/.github/blob/master/COS.md)
21 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-website.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - release-*
8 |
9 | workflow_dispatch:
10 |
11 | jobs:
12 | deploy:
13 | name: Deploy to GitHub Pages
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - uses: actions/setup-node@v3
20 | with:
21 | node-version: 18
22 | cache: yarn
23 | cache-dependency-path: website/yarn.lock
24 |
25 |
26 | - name: Install dependencies
27 | working-directory: ./website
28 | run: yarn install --frozen-lockfile
29 |
30 | - name: Build website
31 | working-directory: ./website
32 | run: yarn build
33 |
34 | - name: Deploy to GitHub Pages
35 | uses: peaceiris/actions-gh-pages@v3
36 | with:
37 | github_token: ${{ secrets.GITHUB_TOKEN }}
38 | publish_dir: ./website/build
--------------------------------------------------------------------------------
/.github/workflows/test-deploy-website.yml:
--------------------------------------------------------------------------------
1 | name: Test deployment
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | test-deploy:
8 | name: Test deployment
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 |
13 | - uses: actions/setup-node@v3
14 | with:
15 | node-version: 18
16 | cache: yarn
17 | cache-dependency-path: website/yarn.lock
18 |
19 | - name: Install dependencies
20 | working-directory: ./website
21 | run: yarn install --frozen-lockfile
22 |
23 | - name: Test build website
24 | working-directory: ./website
25 | run: yarn build
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 10.0
4 | https://raw.githubusercontent.com/Farfetch/.github/master/images/fuse-logo-128.png
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Farfetch
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 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | const Configuration = {
2 | extends: ['@commitlint/config-conventional'],
3 | rules: {
4 | 'body-max-line-length': [0, 'always'],
5 | },
6 | };
7 | module.exports = Configuration;
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.API.Sample/KafkaFlow.Retry.API.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.API.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace KafkaFlow.Retry.API.Sample;
5 |
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args)
14 | {
15 | return Host.CreateDefaultBuilder(args)
16 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
17 | }
18 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.API.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:14135",
8 | "sslPort": 44320
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "weatherforecast",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "KafkaFlow.Retry.API.Sample": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "weatherforecast",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.API.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.API.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Common.Sample/KafkaFlow.Retry.Common.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | enable
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Exceptions/RetryDurableTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.Sample.Exceptions;
4 |
5 | public class RetryDurableTestException : Exception
6 | {
7 | public RetryDurableTestException(string message) : base(message)
8 | {
9 | }
10 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Exceptions/RetryForeverTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.Sample.Exceptions;
4 |
5 | public class RetrySimpleTestException : Exception
6 | {
7 | public RetrySimpleTestException(string message) : base(message)
8 | { }
9 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Exceptions/RetrySimpleTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.Sample.Exceptions;
4 |
5 | public class RetryForeverTestException : Exception
6 | {
7 | public RetryForeverTestException(string message) : base(message)
8 | { }
9 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Handlers/RetryDurableTestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Sample.Exceptions;
4 | using KafkaFlow.Retry.Sample.Messages;
5 |
6 | namespace KafkaFlow.Retry.Sample.Handlers;
7 |
8 | internal class RetryDurableTestHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, RetryDurableTestMessage message)
11 | {
12 | Console.WriteLine(
13 | "Partition: {0} | Offset: {1} | Message: {2}",
14 | context.ConsumerContext.Partition,
15 | context.ConsumerContext.Offset,
16 | message.Text);
17 |
18 | throw new RetryDurableTestException($"Error: {message.Text}");
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Handlers/RetryForeverTestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Sample.Exceptions;
4 | using KafkaFlow.Retry.Sample.Messages;
5 |
6 | namespace KafkaFlow.Retry.Sample.Handlers;
7 |
8 | internal class RetryForeverTestHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, RetryForeverTestMessage message)
11 | {
12 | Console.WriteLine(
13 | "Partition: {0} | Offset: {1} | Message: {2}",
14 | context.ConsumerContext.Partition,
15 | context.ConsumerContext.Offset,
16 | message.Text);
17 |
18 | throw new RetryForeverTestException($"Error: {message.Text}");
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Handlers/RetrySimpleTestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Sample.Exceptions;
4 | using KafkaFlow.Retry.Sample.Messages;
5 |
6 | namespace KafkaFlow.Retry.Sample.Handlers;
7 |
8 | internal class RetrySimpleTestHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, RetrySimpleTestMessage message)
11 | {
12 | Console.WriteLine(
13 | "Partition: {0} | Offset: {1} | Message: {2}",
14 | context.ConsumerContext.Partition,
15 | context.ConsumerContext.Offset,
16 | message.Text);
17 |
18 | throw new RetrySimpleTestException($"Error: {message.Text}");
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/KafkaFlow.Retry.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Messages/RetryDurableTestMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace KafkaFlow.Retry.Sample.Messages;
4 |
5 | [DataContract]
6 | public class RetryDurableTestMessage
7 | {
8 | [DataMember(Order = 1)] public string Text { get; set; }
9 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Messages/RetryForeverTestMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace KafkaFlow.Retry.Sample.Messages;
4 |
5 | [DataContract]
6 | public class RetryForeverTestMessage
7 | {
8 | [DataMember(Order = 1)] public string Text { get; set; }
9 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.Sample/Messages/RetrySimpleTestMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace KafkaFlow.Retry.Sample.Messages;
4 |
5 | [DataContract]
6 | public class RetrySimpleTestMessage
7 | {
8 | [DataMember(Order = 1)] public string Text { get; set; }
9 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.SchemaRegistry.Sample/ContractResolvers/WritablePropertiesOnlyResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Serialization;
6 |
7 | namespace KafkaFlow.Retry.SchemaRegistry.Sample.ContractResolvers;
8 |
9 | internal class WritablePropertiesOnlyResolver : DefaultContractResolver
10 | {
11 | protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
12 | {
13 | var props = base.CreateProperties(type, memberSerialization);
14 | return props.Where(p => p.Writable).ToList();
15 | }
16 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.SchemaRegistry.Sample/Exceptions/RetryDurableTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.SchemaRegistry.Sample.Exceptions;
4 |
5 | public class RetryDurableTestException : Exception
6 | {
7 | public RetryDurableTestException(string message) : base(message)
8 | {
9 | }
10 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.SchemaRegistry.Sample/Handlers/AvroMessageTestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.SchemaRegistry.Sample.Exceptions;
4 | using SchemaRegistry;
5 |
6 | namespace KafkaFlow.Retry.SchemaRegistry.Sample.Handlers;
7 |
8 | public class AvroMessageTestHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, AvroLogMessage message)
11 | {
12 | Console.WriteLine(
13 | "Partition: {0} | Offset: {1} | Message: {2} | Avro",
14 | context.ConsumerContext.Partition,
15 | context.ConsumerContext.Offset,
16 | message.Severity.ToString());
17 |
18 | throw new RetryDurableTestException($"Error: {message.Severity}");
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.SchemaRegistry.Sample/Messages/Avro/AvroLogMessage.avsc:
--------------------------------------------------------------------------------
1 | {
2 | "type": "record",
3 | "name": "AvroLogMessage",
4 | "namespace": "SchemaRegistry",
5 | "doc": "A simple log message.",
6 | "fields": [
7 | {
8 | "name": "Severity",
9 | "type": {
10 | "type": "enum",
11 | "name": "LogLevel",
12 | "doc": "Enumerates the set of allowable log levels.",
13 | "symbols": [
14 | "None",
15 | "Verbose",
16 | "Info",
17 | "Warning",
18 | "Error"
19 | ]
20 | }
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/samples/KafkaFlow.Retry.SchemaRegistry.Sample/Messages/Avro/LogLevel.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // Generated by avrogen, version 1.11.0.0
4 | // Changes to this file may cause incorrect behavior and will be lost if code
5 | // is regenerated
6 | //
7 | // ------------------------------------------------------------------------------
8 | namespace SchemaRegistry
9 | {
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Text;
13 | using Avro;
14 | using Avro.Specific;
15 |
16 | ///
17 | /// Enumerates the set of allowable log levels.
18 | ///
19 | public enum LogLevel
20 | {
21 | None,
22 | Verbose,
23 | Info,
24 | Warning,
25 | Error,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/IRetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos.Common;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.Common;
5 |
6 | internal interface IRetryQueueItemAdapter
7 | {
8 | RetryQueueItemDto Adapt(RetryQueueItem item, string queueGroupKey);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/IRetryQueueItemStatusDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos.Common;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.Common;
5 |
6 | internal interface IRetryQueueItemStatusDtoAdapter
7 | {
8 | RetryQueueItemStatus Adapt(RetryQueueItemStatusDto dto);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/Parsers/EnumParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Dawn;
5 |
6 | namespace KafkaFlow.Retry.API.Adapters.Common.Parsers;
7 |
8 | internal class EnumParser : IQueryParametersParser where T : struct
9 | {
10 | public IEnumerable Parse(IEnumerable parameters, IEnumerable defaultValue)
11 | {
12 | Guard.Argument(parameters, nameof(parameters)).NotNull();
13 | Guard.Argument(defaultValue, nameof(defaultValue)).NotNull();
14 |
15 | var items = new List();
16 |
17 | if (parameters.Any())
18 | {
19 | foreach (var param in parameters)
20 | {
21 | if (Enum.TryParse(param, out var item))
22 | {
23 | items.Add(item);
24 | }
25 | }
26 |
27 | return items;
28 | }
29 |
30 | return defaultValue;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/Parsers/IQueryParametersParser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace KafkaFlow.Retry.API.Adapters.Common.Parsers;
4 |
5 | internal interface IQueryParametersParser where T : struct
6 | {
7 | IEnumerable Parse(IEnumerable parameters, IEnumerable defaultValue);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/RetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Dtos.Common;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.API.Adapters.Common;
6 |
7 | internal class RetryQueueItemAdapter : IRetryQueueItemAdapter
8 | {
9 | public RetryQueueItemDto Adapt(RetryQueueItem item, string queueGroupKey)
10 | {
11 | Guard.Argument(item, nameof(item)).NotNull();
12 | Guard.Argument(item.Message, nameof(item.Message)).NotNull();
13 |
14 | return new RetryQueueItemDto
15 | {
16 | Id = item.Id,
17 | Status = item.Status,
18 | SeverityLevel = item.SeverityLevel,
19 | AttemptsCount = item.AttemptsCount,
20 | CreationDate = item.CreationDate,
21 | LastExecution = item.LastExecution,
22 | Sort = item.Sort,
23 | MessageInfo = new RetryQueuetItemMessageInfoDto
24 | {
25 | Key = item.Message.Key,
26 | Offset = item.Message.Offset,
27 | Partition = item.Message.Partition,
28 | Topic = item.Message.TopicName,
29 | UtcTimeStamp = item.Message.UtcTimeStamp
30 | },
31 | Description = item.Description,
32 | QueueGroupKey = queueGroupKey
33 | };
34 | }
35 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/Common/RetryQueueItemStatusDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos.Common;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.Common;
5 |
6 | internal class RetryQueueItemStatusDtoAdapter : IRetryQueueItemStatusDtoAdapter
7 | {
8 | public RetryQueueItemStatus Adapt(RetryQueueItemStatusDto dto)
9 | {
10 | switch (dto)
11 | {
12 | case RetryQueueItemStatusDto.Waiting:
13 | return RetryQueueItemStatus.Waiting;
14 |
15 | case RetryQueueItemStatusDto.Done:
16 | return RetryQueueItemStatus.Done;
17 |
18 | case RetryQueueItemStatusDto.InRetry:
19 | return RetryQueueItemStatus.InRetry;
20 |
21 | case RetryQueueItemStatusDto.Cancelled:
22 | return RetryQueueItemStatus.Cancelled;
23 |
24 | default:
25 | return RetryQueueItemStatus.None;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/GetItems/GetItemsInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Dtos;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.API.Adapters.GetItems;
7 |
8 | internal class GetItemsInputAdapter : IGetItemsInputAdapter
9 | {
10 | private readonly GetQueuesSortOption _sortOption = GetQueuesSortOption.ByCreationDateDescending;
11 |
12 | public GetQueuesInput Adapt(GetItemsRequestDto requestDto)
13 | {
14 | Guard.Argument(requestDto, nameof(requestDto)).NotNull();
15 |
16 | return new GetQueuesInput(RetryQueueStatus.Active, requestDto.ItemsStatuses, _sortOption, requestDto.TopQueues)
17 | {
18 | SeverityLevels = requestDto.SeverityLevels,
19 | TopItemsByQueue = requestDto.TopItemsByQueue
20 | };
21 | }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/GetItems/GetItemsResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Dawn;
3 | using KafkaFlow.Retry.API.Adapters.Common;
4 | using KafkaFlow.Retry.API.Dtos;
5 | using KafkaFlow.Retry.API.Dtos.Common;
6 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
7 |
8 | namespace KafkaFlow.Retry.API.Adapters.GetItems;
9 |
10 | internal class GetItemsResponseDtoAdapter : IGetItemsResponseDtoAdapter
11 | {
12 | private readonly IRetryQueueItemAdapter _retryQueueItemAdapter;
13 |
14 | public GetItemsResponseDtoAdapter()
15 | {
16 | _retryQueueItemAdapter = new RetryQueueItemAdapter();
17 | }
18 |
19 | public GetItemsResponseDto Adapt(GetQueuesResult getQueuesResult)
20 | {
21 | Guard.Argument(getQueuesResult, nameof(getQueuesResult)).NotNull();
22 | Guard.Argument(getQueuesResult.RetryQueues, nameof(getQueuesResult.RetryQueues)).NotNull();
23 |
24 | var itemsDto = new List();
25 |
26 | foreach (var queue in getQueuesResult.RetryQueues)
27 | {
28 | foreach (var item in queue.Items)
29 | {
30 | itemsDto.Add(_retryQueueItemAdapter.Adapt(item, queue.QueueGroupKey));
31 | }
32 | }
33 |
34 | return new GetItemsResponseDto(itemsDto);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/GetItems/IGetItemsInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.GetItems;
5 |
6 | public interface IGetItemsInputAdapter
7 | {
8 | GetQueuesInput Adapt(GetItemsRequestDto requestDto);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/GetItems/IGetItemsRequestDtoReader.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.GetItems;
5 |
6 | public interface IGetItemsRequestDtoReader
7 | {
8 | GetItemsRequestDto Read(HttpRequest request);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/GetItems/IGetItemsResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.GetItems;
5 |
6 | public interface IGetItemsResponseDtoAdapter
7 | {
8 | GetItemsResponseDto Adapt(GetQueuesResult getQueuesResult);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateItems/IUpdateItemsInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.UpdateItems;
5 |
6 | public interface IUpdateItemsInputAdapter
7 | {
8 | UpdateItemsInput Adapt(UpdateItemsRequestDto requestDto);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateItems/IUpdateItemsResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.UpdateItems;
5 |
6 | public interface IUpdateItemsResponseDtoAdapter
7 | {
8 | UpdateItemsResponseDto Adapt(UpdateItemsResult updateItemsResult);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateItems/UpdateItemsInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Adapters.Common;
3 | using KafkaFlow.Retry.API.Dtos;
4 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
5 |
6 | namespace KafkaFlow.Retry.API.Adapters.UpdateItems;
7 |
8 | internal class UpdateItemsInputAdapter : IUpdateItemsInputAdapter
9 | {
10 | private readonly IRetryQueueItemStatusDtoAdapter _retryQueueItemStatusDtoAdapter;
11 |
12 | public UpdateItemsInputAdapter()
13 | {
14 | _retryQueueItemStatusDtoAdapter = new RetryQueueItemStatusDtoAdapter();
15 | }
16 |
17 | public UpdateItemsInput Adapt(UpdateItemsRequestDto requestDto)
18 | {
19 | Guard.Argument(requestDto, nameof(requestDto)).NotNull();
20 |
21 | return new UpdateItemsInput(
22 | requestDto.ItemIds,
23 | _retryQueueItemStatusDtoAdapter.Adapt(requestDto.Status)
24 | );
25 | }
26 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateItems/UpdateItemsResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Dtos;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
4 |
5 | namespace KafkaFlow.Retry.API.Adapters.UpdateItems;
6 |
7 | internal class UpdateItemsResponseDtoAdapter : IUpdateItemsResponseDtoAdapter
8 | {
9 | public UpdateItemsResponseDto Adapt(UpdateItemsResult updateItemsResult)
10 | {
11 | Guard.Argument(updateItemsResult, nameof(updateItemsResult)).NotNull();
12 |
13 | var resultDto = new UpdateItemsResponseDto();
14 |
15 | foreach (var result in updateItemsResult.Results)
16 | {
17 | resultDto.UpdateItemsResults.Add(new UpdateItemResultDto(result.Id, result.Status));
18 | }
19 |
20 | return resultDto;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateQueues/IUpdateQueuesInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.UpdateQueues;
5 |
6 | public interface IUpdateQueuesInputAdapter
7 | {
8 | UpdateQueuesInput Adapt(UpdateQueuesRequestDto dto);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateQueues/IUpdateQueuesResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Dtos;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
3 |
4 | namespace KafkaFlow.Retry.API.Adapters.UpdateQueues;
5 |
6 | public interface IUpdateQueuesResponseDtoAdapter
7 | {
8 | UpdateQueuesResponseDto Adapt(UpdateQueuesResult updateQueuesResult);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateQueues/UpdateQueuesInputAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Adapters.Common;
3 | using KafkaFlow.Retry.API.Dtos;
4 | using KafkaFlow.Retry.Durable.Repository;
5 |
6 | namespace KafkaFlow.Retry.API.Adapters.UpdateQueues;
7 |
8 | internal class UpdateQueuesInputAdapter : IUpdateQueuesInputAdapter
9 | {
10 | private readonly IRetryQueueItemStatusDtoAdapter _retryQueueItemStatusDtoAdapter;
11 |
12 | public UpdateQueuesInputAdapter()
13 | {
14 | _retryQueueItemStatusDtoAdapter = new RetryQueueItemStatusDtoAdapter();
15 | }
16 |
17 | public UpdateQueuesInput Adapt(UpdateQueuesRequestDto requestDto)
18 | {
19 | Guard.Argument(requestDto, nameof(requestDto)).NotNull();
20 |
21 | return new UpdateQueuesInput(
22 | requestDto.QueueGroupKeys,
23 | _retryQueueItemStatusDtoAdapter.Adapt(requestDto.ItemStatus)
24 | );
25 | }
26 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Adapters/UpdateQueues/UpdateQueuesResponseDtoAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.API.Dtos;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
4 |
5 | namespace KafkaFlow.Retry.API.Adapters.UpdateQueues;
6 |
7 | internal class UpdateQueuesResponseDtoAdapter : IUpdateQueuesResponseDtoAdapter
8 | {
9 | public UpdateQueuesResponseDto Adapt(UpdateQueuesResult updateQueuesResult)
10 | {
11 | Guard.Argument(updateQueuesResult, nameof(updateQueuesResult)).NotNull();
12 |
13 | var resultDto = new UpdateQueuesResponseDto();
14 |
15 | foreach (var res in updateQueuesResult.Results)
16 | {
17 | resultDto.UpdateQueuesResults.Add(new UpdateQueueResultDto(res.QueueGroupKey, res.Status,
18 | res.RetryQueueStatus));
19 | }
20 |
21 | return resultDto;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/Common/RetryQueueItemDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.Durable.Common;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.API.Dtos.Common;
6 |
7 | public class RetryQueueItemDto
8 | {
9 | public int AttemptsCount { get; set; }
10 |
11 | public DateTime CreationDate { get; set; }
12 |
13 | public string Description { get; set; }
14 |
15 | public Guid Id { get; set; }
16 |
17 | public DateTime? LastExecution { get; set; }
18 |
19 | public RetryQueuetItemMessageInfoDto MessageInfo { get; set; }
20 |
21 | public string QueueGroupKey { get; set; }
22 |
23 | public SeverityLevel SeverityLevel { get; set; }
24 |
25 | public int Sort { get; set; }
26 |
27 | public RetryQueueItemStatus Status { get; set; }
28 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/Common/RetryQueueItemStatusDto.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.API.Dtos.Common;
2 |
3 | public enum RetryQueueItemStatusDto
4 | {
5 | None = 0,
6 | Waiting = 1,
7 | InRetry = 2,
8 | Done = 3,
9 | Cancelled = 4
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/Common/RetryQueuetItemMessageInfoDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.API.Dtos.Common;
4 |
5 | public class RetryQueuetItemMessageInfoDto
6 | {
7 | public byte[] Key { get; set; }
8 |
9 | public long Offset { get; set; }
10 |
11 | public int Partition { get; set; }
12 |
13 | public string Topic { get; set; }
14 |
15 | public DateTimeOffset UtcTimeStamp { get; set; }
16 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/GetItemsRequestDto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Common;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.API.Dtos;
6 |
7 | public class GetItemsRequestDto
8 | {
9 | public IEnumerable ItemsStatuses { get; set; }
10 | public IEnumerable SeverityLevels { get; set; }
11 | public int TopItemsByQueue { get; set; }
12 | public int TopQueues { get; set; }
13 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/GetItemsResponseDto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.API.Dtos.Common;
3 |
4 | namespace KafkaFlow.Retry.API.Dtos;
5 |
6 | public class GetItemsResponseDto
7 | {
8 | public GetItemsResponseDto(IEnumerable queueItemDtos)
9 | {
10 | QueueItems = queueItemDtos;
11 | }
12 |
13 | public IEnumerable QueueItems { get; set; }
14 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateItemResultDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
3 |
4 | namespace KafkaFlow.Retry.API.Dtos;
5 |
6 | public class UpdateItemResultDto
7 | {
8 | public UpdateItemResultDto(Guid itemId, UpdateItemResultStatus value)
9 | {
10 | ItemId = itemId;
11 | Result = value.ToString();
12 | }
13 |
14 | public Guid ItemId { get; set; }
15 |
16 | public string Result { get; set; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateItemsRequestDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using KafkaFlow.Retry.API.Dtos.Common;
4 |
5 | namespace KafkaFlow.Retry.API.Dtos;
6 |
7 | public class UpdateItemsRequestDto
8 | {
9 | public IEnumerable ItemIds { get; set; }
10 |
11 | public RetryQueueItemStatusDto Status { get; set; }
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateItemsResponseDto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace KafkaFlow.Retry.API.Dtos;
4 |
5 | public class UpdateItemsResponseDto
6 | {
7 | public UpdateItemsResponseDto()
8 | {
9 | UpdateItemsResults = new List();
10 | }
11 |
12 | public IList UpdateItemsResults { get; set; }
13 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateQueueResultDto.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.API.Dtos;
5 |
6 | public class UpdateQueueResultDto
7 | {
8 | public UpdateQueueResultDto(string queueGroupKey, UpdateQueueResultStatus status, RetryQueueStatus retryQueueStatus)
9 | {
10 | QueueGroupKey = queueGroupKey;
11 | Result = status.ToString();
12 | QueueStatus = retryQueueStatus.ToString();
13 | }
14 |
15 | public string QueueGroupKey { get; set; }
16 |
17 | public string QueueStatus { get; set; }
18 |
19 | public string Result { get; set; }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateQueuesRequestDto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.API.Dtos.Common;
3 |
4 | namespace KafkaFlow.Retry.API.Dtos;
5 |
6 | public class UpdateQueuesRequestDto
7 | {
8 | public RetryQueueItemStatusDto ItemStatus { get; set; }
9 |
10 | public IEnumerable QueueGroupKeys { get; set; }
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Dtos/UpdateQueuesResponseDto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace KafkaFlow.Retry.API.Dtos;
4 |
5 | public class UpdateQueuesResponseDto
6 | {
7 | public UpdateQueuesResponseDto()
8 | {
9 | UpdateQueuesResults = new List();
10 | }
11 |
12 | public IList UpdateQueuesResults { get; set; }
13 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/HttpExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace KafkaFlow.Retry.API;
5 |
6 | internal static class HttpExtensions
7 | {
8 | private const char QueryStringDelimiter = ',';
9 |
10 | private const char ResourcePathDelimiter = '/';
11 |
12 | public static void AddQueryParams(this HttpRequest httpRequest, string name, string value)
13 | {
14 | httpRequest.QueryString = httpRequest.QueryString.Add(name, value);
15 | }
16 |
17 | public static string ExtendResourcePath(this string resource, string extension)
18 | {
19 | return string.Concat(resource, ResourcePathDelimiter, extension);
20 | }
21 |
22 | public static IEnumerable ReadQueryParams(this HttpRequest httpRequest, string paramKey)
23 | {
24 | var aggregatedParamValues = new List();
25 |
26 | var paramValues = httpRequest.Query[paramKey].ToArray();
27 |
28 | foreach (var value in paramValues)
29 | {
30 | aggregatedParamValues.AddRange(value.Split(QueryStringDelimiter));
31 | }
32 |
33 | return aggregatedParamValues;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/HttpMethod.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.API;
2 |
3 | internal enum HttpMethod
4 | {
5 | None = 0,
6 | GET = 1,
7 | POST = 2,
8 | PUT = 3,
9 | PATCH = 4,
10 | DELETE = 5
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/IHttpRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace KafkaFlow.Retry.API;
5 |
6 | internal interface IHttpRequestHandler
7 | {
8 | Task HandleAsync(HttpRequest httpRequest, HttpResponse response);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.API.Tests")]
4 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.UnitTests")]
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.API/RetryMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace KafkaFlow.Retry.API;
5 |
6 | internal class RetryMiddleware
7 | {
8 | private readonly IHttpRequestHandler _httpRequestHandler;
9 | private readonly RequestDelegate _next;
10 |
11 | public RetryMiddleware(RequestDelegate next, IHttpRequestHandler httpRequestHandler)
12 | {
13 | _next = next;
14 | _httpRequestHandler = httpRequestHandler;
15 | }
16 |
17 | public async Task InvokeAsync(HttpContext httpContext)
18 | {
19 | var handled = await _httpRequestHandler
20 | .HandleAsync(httpContext.Request, httpContext.Response)
21 | .ConfigureAwait(false);
22 |
23 | if (!handled)
24 | {
25 | // Call the next delegate/middleware in the pipeline
26 | await _next(httpContext).ConfigureAwait(false);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/HeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
4 | using KafkaFlow.Retry.MongoDb.Model;
5 |
6 | namespace KafkaFlow.Retry.MongoDb.Adapters;
7 |
8 | internal class HeaderAdapter : IHeaderAdapter
9 | {
10 | public RetryQueueHeaderDbo Adapt(MessageHeader header)
11 | {
12 | Guard.Argument(header, nameof(header)).NotNull();
13 |
14 | return new RetryQueueHeaderDbo
15 | {
16 | Key = header.Key,
17 | Value = header.Value
18 | };
19 | }
20 |
21 | public MessageHeader Adapt(RetryQueueHeaderDbo headerDbo)
22 | {
23 | Guard.Argument(headerDbo, nameof(headerDbo)).NotNull();
24 |
25 | return new MessageHeader(headerDbo.Key, headerDbo.Value);
26 | }
27 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/Interfaces/IHeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.MongoDb.Model;
3 |
4 | namespace KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
5 |
6 | public interface IHeaderAdapter
7 | {
8 | RetryQueueHeaderDbo Adapt(MessageHeader header);
9 |
10 | MessageHeader Adapt(RetryQueueHeaderDbo headerDbo);
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/Interfaces/IItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.MongoDb.Model;
3 |
4 | namespace KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
5 |
6 | public interface IItemAdapter
7 | {
8 | RetryQueueItem Adapt(RetryQueueItemDbo itemDbo);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/Interfaces/IMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.MongoDb.Model;
3 |
4 | namespace KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
5 |
6 | public interface IMessageAdapter
7 | {
8 | RetryQueueItemMessage Adapt(RetryQueueItemMessageDbo messageDbo);
9 |
10 | RetryQueueItemMessageDbo Adapt(RetryQueueItemMessage message);
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/Interfaces/IQueuesAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.MongoDb.Model;
4 |
5 | namespace KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
6 |
7 | internal interface IQueuesAdapter
8 | {
9 | IEnumerable Adapt(IEnumerable queuesDbo, IEnumerable itemsDbo);
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Adapters/ItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
4 | using KafkaFlow.Retry.MongoDb.Model;
5 |
6 | namespace KafkaFlow.Retry.MongoDb.Adapters;
7 |
8 | internal class ItemAdapter : IItemAdapter
9 | {
10 | private readonly IMessageAdapter _messageAdapter;
11 |
12 | public ItemAdapter(IMessageAdapter messageAdater)
13 | {
14 | Guard.Argument(messageAdater, nameof(messageAdater)).NotNull();
15 |
16 | _messageAdapter = messageAdater;
17 | }
18 |
19 | public RetryQueueItem Adapt(RetryQueueItemDbo itemDbo)
20 | {
21 | Guard.Argument(itemDbo, nameof(itemDbo)).NotNull();
22 |
23 | return new RetryQueueItem(
24 | itemDbo.Id,
25 | itemDbo.AttemptsCount,
26 | itemDbo.CreationDate,
27 | itemDbo.Sort,
28 | itemDbo.LastExecution,
29 | itemDbo.ModifiedStatusDate,
30 | itemDbo.Status,
31 | itemDbo.SeverityLevel,
32 | itemDbo.Description
33 | )
34 | {
35 | Message = _messageAdapter.Adapt(itemDbo.Message)
36 | };
37 | }
38 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/DataProviderCreationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.MongoDb;
5 |
6 | [Serializable]
7 | [ExcludeFromCodeCoverage]
8 | public class DataProviderCreationException : Exception
9 | {
10 | public DataProviderCreationException(string message, Exception innerException)
11 | : base(message, innerException)
12 | {
13 | }
14 |
15 | public DataProviderCreationException(string message)
16 | : base(message)
17 | {
18 | }
19 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/DataProviderCreationResult.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository;
2 |
3 | namespace KafkaFlow.Retry.MongoDb;
4 |
5 | public class DataProviderCreationResult
6 | {
7 | internal DataProviderCreationResult(string message, IRetryDurableQueueRepositoryProvider result, bool success)
8 | {
9 | Message = message;
10 | Result = result;
11 | Success = success;
12 | }
13 |
14 | public string Message { get; }
15 | public IRetryDurableQueueRepositoryProvider Result { get; }
16 | public bool Success { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/DbContext.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.MongoDb.Model;
2 | using MongoDB.Driver;
3 |
4 | namespace KafkaFlow.Retry.MongoDb;
5 |
6 | internal sealed class DbContext
7 | {
8 | private readonly IMongoDatabase _database;
9 | private readonly MongoDbSettings _mongoDbSettings;
10 |
11 | public DbContext(MongoDbSettings mongoDbSettings, IMongoClient mongoClient)
12 | {
13 | _mongoDbSettings = mongoDbSettings;
14 | MongoClient = mongoClient;
15 |
16 | _database = mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
17 | }
18 |
19 | public IMongoClient MongoClient { get; }
20 |
21 | public IMongoCollection RetryQueueItems =>
22 | _database.GetCollection(_mongoDbSettings.RetryQueueItemCollectionName);
23 |
24 | public IMongoCollection RetryQueues =>
25 | _database.GetCollection(_mongoDbSettings.RetryQueueCollectionName);
26 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/KafkaFlow.Retry.MongoDb.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | FARFETCH
7 |
8 |
9 |
10 |
11 |
12 | LICENSE.md
13 |
14 | Git
15 | kafka flow kafkaflow extension extensions retry mongo mongodb
16 | A durable persistence adapter for MongoDB for KafkaFlow.Retry extension.
17 | Copyright (c) FARFETCH 2021
18 |
19 |
20 |
21 |
22 | PreserveNewest
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/Factories/RetryQueueDboFactory.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
3 |
4 | namespace KafkaFlow.Retry.MongoDb.Model.Factories;
5 |
6 | internal static class RetryQueueDboFactory
7 | {
8 | internal static RetryQueueDbo Create(SaveToQueueInput input)
9 | {
10 | Guard.Argument(input).NotNull();
11 |
12 | return new RetryQueueDbo
13 | {
14 | SearchGroupKey = input.SearchGroupKey,
15 | QueueGroupKey = input.QueueGroupKey,
16 | CreationDate = input.CreationDate,
17 | LastExecution = input.LastExecution.Value,
18 | Status = input.QueueStatus
19 | };
20 | }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/Factories/RetryQueueItemDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 | using KafkaFlow.Retry.MongoDb.Adapters.Interfaces;
5 |
6 | namespace KafkaFlow.Retry.MongoDb.Model.Factories;
7 |
8 | internal class RetryQueueItemDboFactory
9 | {
10 | private readonly IMessageAdapter _messageAdapter;
11 |
12 | public RetryQueueItemDboFactory(IMessageAdapter messageAdapter)
13 | {
14 | _messageAdapter = messageAdapter;
15 | }
16 |
17 | public RetryQueueItemDbo Create(SaveToQueueInput input, Guid queueId, int sort = 0)
18 | {
19 | Guard.Argument(input, nameof(input)).NotNull();
20 | Guard.Argument(queueId).NotDefault();
21 | Guard.Argument(sort, nameof(sort)).NotNegative();
22 |
23 | return new RetryQueueItemDbo
24 | {
25 | CreationDate = input.CreationDate,
26 | LastExecution = input.LastExecution,
27 | ModifiedStatusDate = input.ModifiedStatusDate,
28 | AttemptsCount = input.AttemptsCount,
29 | Message = _messageAdapter.Adapt(input.Message),
30 | RetryQueueId = queueId,
31 | Sort = sort,
32 | Status = input.ItemStatus,
33 | SeverityLevel = input.SeverityLevel,
34 | Description = input.Description
35 | };
36 | }
37 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/RetryQueueDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.MongoDb.Model;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class RetryQueueDbo
9 | {
10 | public DateTime CreationDate { get; set; }
11 |
12 | public Guid Id { get; set; }
13 |
14 | public DateTime LastExecution { get; set; }
15 |
16 | public string QueueGroupKey { get; set; }
17 |
18 | public string SearchGroupKey { get; set; }
19 |
20 | public RetryQueueStatus Status { get; set; }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/RetryQueueHeaderDbo.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace KafkaFlow.Retry.MongoDb.Model;
4 |
5 | [ExcludeFromCodeCoverage]
6 | public class RetryQueueHeaderDbo
7 | {
8 | public string Key { get; set; }
9 |
10 | public byte[] Value { get; set; }
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/RetryQueueItemDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Common;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.MongoDb.Model;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public class RetryQueueItemDbo
10 | {
11 | public int AttemptsCount { get; set; }
12 |
13 | public DateTime CreationDate { get; set; }
14 |
15 | public string Description { get; set; }
16 | public Guid Id { get; set; }
17 | public DateTime? LastExecution { get; set; }
18 | public RetryQueueItemMessageDbo Message { get; set; }
19 | public DateTime? ModifiedStatusDate { get; set; }
20 | public Guid RetryQueueId { get; set; }
21 |
22 | public SeverityLevel SeverityLevel { get; set; }
23 |
24 | public int Sort { get; set; }
25 |
26 | public RetryQueueItemStatus Status { get; set; }
27 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Model/RetryQueueItemMessageDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 |
5 | namespace KafkaFlow.Retry.MongoDb.Model;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class RetryQueueItemMessageDbo
9 | {
10 | public IEnumerable Headers { get; set; }
11 |
12 | public byte[] Key { get; set; }
13 |
14 | public long Offset { get; set; }
15 |
16 | public int Partition { get; set; }
17 |
18 | public string TopicName { get; set; }
19 |
20 | public DateTime UtcTimeStamp { get; set; }
21 |
22 | public byte[] Value { get; set; }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/MongoDbSettings.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace KafkaFlow.Retry.MongoDb;
4 |
5 | [ExcludeFromCodeCoverage]
6 | public class MongoDbSettings
7 | {
8 | public string ConnectionString { get; set; }
9 |
10 | public string DatabaseName { get; set; }
11 |
12 | public string RetryQueueCollectionName { get; set; }
13 |
14 | public string RetryQueueItemCollectionName { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.IntegrationTests")]
4 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.UnitTests")]
5 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Repositories/IRetryQueueItemRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using KafkaFlow.Retry.Durable.Common;
5 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
7 | using KafkaFlow.Retry.Durable.Repository.Model;
8 | using KafkaFlow.Retry.MongoDb.Model;
9 |
10 | namespace KafkaFlow.Retry.MongoDb.Repositories;
11 |
12 | internal interface IRetryQueueItemRepository
13 | {
14 | Task AnyItemStillActiveAsync(Guid retryQueueId);
15 |
16 | Task DeleteItemsAsync(IEnumerable queueIds);
17 |
18 | Task GetItemAsync(Guid itemId);
19 |
20 | Task> GetItemsAsync(
21 | IEnumerable queueIds,
22 | IEnumerable statuses,
23 | IEnumerable severities = null,
24 | int? top = null,
25 | StuckStatusFilter stuckStatusFilter = null);
26 |
27 | Task IsFirstWaitingInQueue(RetryQueueItemDbo item);
28 |
29 | Task UpdateItemAsync(
30 | Guid itemId,
31 | RetryQueueItemStatus status,
32 | int attemptsCount,
33 | DateTime? lastExecution,
34 | string description);
35 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/Repositories/IRetryQueueRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using KafkaFlow.Retry.Durable.Repository.Actions.Delete;
5 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 | using KafkaFlow.Retry.Durable.Repository.Model;
7 | using KafkaFlow.Retry.MongoDb.Model;
8 | using MongoDB.Driver;
9 |
10 | namespace KafkaFlow.Retry.MongoDb.Repositories;
11 |
12 | internal interface IRetryQueueRepository
13 | {
14 | Task CountQueuesAsync(string searchGroupKey, RetryQueueStatus status);
15 |
16 | Task DeleteQueuesAsync(IEnumerable queueIds);
17 |
18 | Task GetQueueAsync(string queueGroupKey);
19 |
20 | Task> GetQueuesToDeleteAsync(string searchGroupKey, RetryQueueStatus status, DateTime maxLastExecutionDateToBeKept, int maxRowsToDelete);
21 |
22 | Task> GetTopSortedQueuesAsync(RetryQueueStatus status, GetQueuesSortOption sortOption, string searchGroupKey, int top);
23 |
24 | Task UpdateLastExecutionAsync(Guid queueId, DateTime lastExecution);
25 |
26 | Task UpdateStatusAndLastExecutionAsync(Guid queueId, RetryQueueStatus status, DateTime lastExecution);
27 |
28 | Task UpdateStatusAsync(Guid queueId, RetryQueueStatus status);
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.MongoDb/RetryDurableDefinitionBuilderExtension.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.MongoDb;
2 |
3 | public static class RetryDurableDefinitionBuilderExtension
4 | {
5 | public static RetryDurableDefinitionBuilder WithMongoDbDataProvider(
6 | this RetryDurableDefinitionBuilder retryDurableDefinitionBuilder,
7 | string connectionString,
8 | string databaseName,
9 | string mongoDbretryQueueCollectionName,
10 | string mongoDbretryQueueItemCollectionName)
11 | {
12 | var dataProviderCreation = new MongoDbDataProviderFactory()
13 | .TryCreate(
14 | new MongoDbSettings
15 | {
16 | ConnectionString = connectionString,
17 | DatabaseName = databaseName,
18 | RetryQueueCollectionName = mongoDbretryQueueCollectionName,
19 | RetryQueueItemCollectionName = mongoDbretryQueueItemCollectionName
20 | }
21 | );
22 |
23 | if (!dataProviderCreation.Success)
24 | {
25 | throw new DataProviderCreationException(
26 | $"The Retry Queue Data Provider could not be created. Error: {dataProviderCreation.Message}");
27 | }
28 |
29 | retryDurableDefinitionBuilder.WithRepositoryProvider(dataProviderCreation.Result);
30 |
31 | return retryDurableDefinitionBuilder;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/ConnectionProvider.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Postgres;
4 |
5 | internal sealed class ConnectionProvider : IConnectionProvider
6 | {
7 | public IDbConnection Create(PostgresDbSettings postgresDbSettings)
8 | {
9 | Guard.Argument(postgresDbSettings).NotNull();
10 |
11 | return new DbConnectionContext(postgresDbSettings, false);
12 | }
13 |
14 | public IDbConnectionWithinTransaction CreateWithinTransaction(PostgresDbSettings postgresDbSettings)
15 | {
16 | Guard.Argument(postgresDbSettings).NotNull();
17 |
18 | return new DbConnectionContext(postgresDbSettings, true);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Deploy/02 - Populate_Tables.sql:
--------------------------------------------------------------------------------
1 | -- Populate tables
2 |
3 | DO $$ BEGIN
4 | IF NOT EXISTS (SELECT 1 FROM queue_status WHERE Code IN (1, 2)) THEN
5 | INSERT INTO queue_status (Code, Name, Description)
6 | VALUES
7 | (1, 'Active', 'The queue has unprocessed messages'),
8 | (2, 'Done', 'The queue does not have unprocessed messages');
9 | END IF;
10 |
11 | IF NOT EXISTS (SELECT 1 FROM queue_item_status WHERE Code IN (1, 2, 3)) THEN
12 | INSERT INTO queue_item_status (Code, Name, Description)
13 | VALUES
14 | (1, 'Waiting', 'Waiting for retry'),
15 | (2, 'InRetry', 'Retrying'),
16 | (3, 'Done', 'Done'),
17 | (4, 'Cancelled', 'Cancelled');
18 | END IF;
19 |
20 | IF NOT EXISTS (SELECT 1 FROM queue_item_severity WHERE Code IN (1, 2, 3)) THEN
21 | INSERT INTO queue_item_severity (Code, Name, Description)
22 | VALUES
23 | (0, 'Unknown', 'A severity level was not defined.'),
24 | (1, 'Low', 'No loss of service. The software should recover by itself.'),
25 | (2, 'Medium', 'Minor loss of service. The result is an inconvenience, it''s unclear if the software can recover by itself.'),
26 | (3, 'High', 'Partial loss of service with severe impact on the business. Usually needs human intervention to be solved.');
27 | END IF;
28 | END $$;
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/IConnectionProvider.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Postgres;
2 |
3 | internal interface IConnectionProvider
4 | {
5 | IDbConnection Create(PostgresDbSettings postgresDbSettings);
6 |
7 | IDbConnectionWithinTransaction CreateWithinTransaction(PostgresDbSettings postgresDbSettings);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/IDbConnection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Npgsql;
3 |
4 | namespace KafkaFlow.Retry.Postgres;
5 |
6 | internal interface IDbConnection : IDisposable
7 | {
8 | NpgsqlCommand CreateCommand();
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/IDbConnectionWithinTransaction.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Postgres;
2 |
3 | internal interface IDbConnectionWithinTransaction : IDbConnection
4 | {
5 | void Commit();
6 |
7 | void Rollback();
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/IRetrySchemaCreator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace KafkaFlow.Retry.Postgres;
4 |
5 | public interface IRetrySchemaCreator
6 | {
7 | Task CreateOrUpdateSchemaAsync(string databaseName);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/IRetryQueueDboFactory.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
2 |
3 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
4 |
5 | internal interface IRetryQueueDboFactory
6 | {
7 | RetryQueueDbo Create(SaveToQueueInput input);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/IRetryQueueItemDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
5 |
6 | internal interface IRetryQueueItemDboFactory
7 | {
8 | RetryQueueItemDbo Create(SaveToQueueInput input, long retryQueueId, Guid retryQueueDomainId);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/IRetryQueueItemMessageDboFactory.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 |
3 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
4 |
5 | internal interface IRetryQueueItemMessageDboFactory
6 | {
7 | RetryQueueItemMessageDbo Create(RetryQueueItemMessage retryQueueItemMessage, long retryQueueItemId);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/IRetryQueueItemMessageHeaderDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
5 |
6 | internal interface IRetryQueueItemMessageHeaderDboFactory
7 | {
8 | IEnumerable Create(IEnumerable headers, long retryQueueItemId);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/RetryQueueDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
6 |
7 | internal sealed class RetryQueueDboFactory : IRetryQueueDboFactory
8 | {
9 | public RetryQueueDbo Create(SaveToQueueInput input)
10 | {
11 | Guard.Argument(input).NotNull();
12 |
13 | return new RetryQueueDbo
14 | {
15 | IdDomain = Guid.NewGuid(),
16 | SearchGroupKey = input.SearchGroupKey,
17 | QueueGroupKey = input.QueueGroupKey,
18 | CreationDate = input.CreationDate,
19 | LastExecution = input.LastExecution.Value,
20 | Status = input.QueueStatus
21 | };
22 | }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/RetryQueueItemDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
6 |
7 | internal sealed class RetryQueueItemDboFactory : IRetryQueueItemDboFactory
8 | {
9 | public RetryQueueItemDbo Create(SaveToQueueInput input, long retryQueueId, Guid retryQueueDomainId)
10 | {
11 | Guard.Argument(input, nameof(input)).NotNull();
12 | Guard.Argument(retryQueueId, nameof(retryQueueId)).Positive();
13 | Guard.Argument(retryQueueDomainId, nameof(retryQueueDomainId)).NotDefault();
14 |
15 | return new RetryQueueItemDbo
16 | {
17 | IdDomain = Guid.NewGuid(),
18 | CreationDate = input.CreationDate,
19 | LastExecution = input.LastExecution,
20 | ModifiedStatusDate = input.ModifiedStatusDate,
21 | AttemptsCount = input.AttemptsCount,
22 | RetryQueueId = retryQueueId,
23 | DomainRetryQueueId = retryQueueDomainId,
24 | Status = input.ItemStatus,
25 | SeverityLevel = input.SeverityLevel,
26 | Description = input.Description
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/RetryQueueItemMessageDboFactory.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
5 |
6 | internal sealed class RetryQueueItemMessageDboFactory : IRetryQueueItemMessageDboFactory
7 | {
8 | public RetryQueueItemMessageDbo Create(RetryQueueItemMessage retryQueueItemMessage, long retryQueueItemId)
9 | {
10 | Guard.Argument(retryQueueItemMessage, nameof(retryQueueItemMessage)).NotNull();
11 | Guard.Argument(retryQueueItemId, nameof(retryQueueItemId)).Positive();
12 |
13 | return new RetryQueueItemMessageDbo
14 | {
15 | IdRetryQueueItem = retryQueueItemId,
16 | Key = retryQueueItemMessage.Key,
17 | Value = retryQueueItemMessage.Value,
18 | Offset = retryQueueItemMessage.Offset,
19 | Partition = retryQueueItemMessage.Partition,
20 | TopicName = retryQueueItemMessage.TopicName,
21 | UtcTimeStamp = retryQueueItemMessage.UtcTimeStamp
22 | };
23 | }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Factories/RetryQueueItemMessageHeaderDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Postgres.Model.Factories;
7 |
8 | internal sealed class RetryQueueItemMessageHeaderDboFactory : IRetryQueueItemMessageHeaderDboFactory
9 | {
10 | public IEnumerable Create(IEnumerable headers, long retryQueueItemId)
11 | {
12 | Guard.Argument(headers).NotNull();
13 | Guard.Argument(retryQueueItemId, nameof(retryQueueItemId)).Positive();
14 |
15 | return headers.Select(h => Adapt(h, retryQueueItemId));
16 | }
17 |
18 | private RetryQueueItemMessageHeaderDbo Adapt(MessageHeader header, long retryQueueItemId)
19 | {
20 | Guard.Argument(header).NotNull();
21 |
22 | return new RetryQueueItemMessageHeaderDbo
23 | {
24 | Key = header.Key,
25 | Value = header.Value,
26 | RetryQueueItemMessageId = retryQueueItemId
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/RetryQueueDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Model;
6 |
7 | [ExcludeFromCodeCoverage]
8 | internal class RetryQueueDbo
9 | {
10 | public DateTime CreationDate { get; set; }
11 |
12 | public long Id { get; set; }
13 |
14 | public Guid IdDomain { get; set; }
15 |
16 | public DateTime LastExecution { get; set; }
17 |
18 | public string QueueGroupKey { get; set; }
19 |
20 | public string SearchGroupKey { get; set; }
21 |
22 | public RetryQueueStatus Status { get; set; }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/RetryQueueItemDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Common;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Postgres.Model;
7 |
8 | [ExcludeFromCodeCoverage]
9 | internal class RetryQueueItemDbo
10 | {
11 | public int AttemptsCount { get; set; }
12 |
13 | public DateTime CreationDate { get; set; }
14 |
15 | public string Description { get; set; }
16 |
17 | public Guid DomainRetryQueueId { get; set; }
18 |
19 | public long Id { get; set; }
20 |
21 | public Guid IdDomain { get; set; }
22 |
23 | public DateTime? LastExecution { get; set; }
24 |
25 | public DateTime? ModifiedStatusDate { get; set; }
26 |
27 | public long RetryQueueId { get; set; }
28 |
29 | public SeverityLevel SeverityLevel { get; set; }
30 |
31 | public int Sort { get; set; }
32 |
33 | public RetryQueueItemStatus Status { get; set; }
34 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/RetryQueueItemMessageDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Model;
5 |
6 | [ExcludeFromCodeCoverage]
7 | internal class RetryQueueItemMessageDbo
8 | {
9 | public long IdRetryQueueItem { get; set; }
10 |
11 | public byte[] Key { get; set; }
12 |
13 | public long Offset { get; set; }
14 |
15 | public int Partition { get; set; }
16 |
17 | public string TopicName { get; set; }
18 |
19 | public DateTime UtcTimeStamp { get; set; }
20 |
21 | public byte[] Value { get; set; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/RetryQueueItemMessageHeaderDbo.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace KafkaFlow.Retry.Postgres.Model;
4 |
5 | [ExcludeFromCodeCoverage]
6 | internal class RetryQueueItemMessageHeaderDbo
7 | {
8 | public long Id { get; set; }
9 |
10 | public string Key { get; set; }
11 |
12 | public long RetryQueueItemMessageId { get; set; }
13 |
14 | public byte[] Value { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/RetryQueuesDboWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Model;
5 |
6 | [ExcludeFromCodeCoverage]
7 | internal class RetryQueuesDboWrapper
8 | {
9 | public RetryQueuesDboWrapper()
10 | {
11 | QueuesDbos = new RetryQueueDbo[0];
12 | ItemsDbos = new RetryQueueItemDbo[0];
13 | MessagesDbos = new RetryQueueItemMessageDbo[0];
14 | HeadersDbos = new RetryQueueItemMessageHeaderDbo[0];
15 | }
16 |
17 | public IList HeadersDbos { get; set; }
18 | public IList ItemsDbos { get; set; }
19 | public IList MessagesDbos { get; set; }
20 | public IList QueuesDbos { get; set; }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Model/Schema/Script.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Postgres.Model.Schema;
4 |
5 | public class Script
6 | {
7 | public Script(string value)
8 | {
9 | Guard.Argument(value, nameof(value)).NotNull();
10 |
11 | Value = value;
12 | }
13 |
14 | public string Value { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/PostgresDbSettings.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Postgres;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class PostgresDbSettings
8 | {
9 | public PostgresDbSettings(string connectionString, string databaseName)
10 | {
11 | Guard.Argument(connectionString).NotNull().NotEmpty();
12 | Guard.Argument(databaseName).NotNull().NotEmpty();
13 |
14 | ConnectionString = connectionString;
15 | DatabaseName = databaseName;
16 | }
17 |
18 | public string ConnectionString { get; }
19 |
20 | public string DatabaseName { get; }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.IntegrationTests")]
4 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.UnitTests")]
5 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/IRetryQueueAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.Postgres.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
5 |
6 | internal interface IRetryQueueAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/IRetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.Postgres.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/IRetryQueueItemMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.Postgres.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemMessageAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/IRetryQueueItemMessageHeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.Postgres.Model;
3 |
4 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemMessageHeaderAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/RetryQueueAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
6 |
7 | internal class RetryQueueAdapter : IRetryQueueAdapter
8 | {
9 | public RetryQueue Adapt(RetryQueueDbo retryQueueDbo)
10 | {
11 | Guard.Argument(retryQueueDbo).NotNull();
12 |
13 | return new RetryQueue(retryQueueDbo.IdDomain,
14 | retryQueueDbo.SearchGroupKey,
15 | retryQueueDbo.QueueGroupKey,
16 | retryQueueDbo.CreationDate,
17 | retryQueueDbo.LastExecution,
18 | retryQueueDbo.Status);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/RetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
6 |
7 | internal class RetryQueueItemAdapter : IRetryQueueItemAdapter
8 | {
9 | public RetryQueueItem Adapt(RetryQueueItemDbo retryQueueItemDbo)
10 | {
11 | Guard.Argument(retryQueueItemDbo).NotNull();
12 |
13 | return new RetryQueueItem(
14 | retryQueueItemDbo.IdDomain,
15 | retryQueueItemDbo.AttemptsCount,
16 | retryQueueItemDbo.CreationDate,
17 | retryQueueItemDbo.Sort,
18 | retryQueueItemDbo.LastExecution,
19 | retryQueueItemDbo.ModifiedStatusDate,
20 | retryQueueItemDbo.Status,
21 | retryQueueItemDbo.SeverityLevel,
22 | retryQueueItemDbo.Description);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/RetryQueueItemMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
6 |
7 | internal class RetryQueueItemMessageAdapter : IRetryQueueItemMessageAdapter
8 | {
9 | public RetryQueueItemMessage Adapt(RetryQueueItemMessageDbo retryQueueItemMessageDbo)
10 | {
11 | Guard.Argument(retryQueueItemMessageDbo, nameof(retryQueueItemMessageDbo)).NotNull();
12 |
13 | return new RetryQueueItemMessage(
14 | retryQueueItemMessageDbo.TopicName,
15 | retryQueueItemMessageDbo.Key,
16 | retryQueueItemMessageDbo.Value,
17 | retryQueueItemMessageDbo.Partition,
18 | retryQueueItemMessageDbo.Offset,
19 | retryQueueItemMessageDbo.UtcTimeStamp);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/Adapters/RetryQueueItemMessageHeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Readers.Adapters;
6 |
7 | internal class RetryQueueItemMessageHeaderAdapter : IRetryQueueItemMessageHeaderAdapter
8 | {
9 | public MessageHeader Adapt(RetryQueueItemMessageHeaderDbo messageHeaderDbo)
10 | {
11 | Guard.Argument(messageHeaderDbo).NotNull();
12 |
13 | return new MessageHeader(messageHeaderDbo.Key, messageHeaderDbo.Value);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/IDboDomainAdapter.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Postgres.Readers;
2 |
3 | internal interface IDboDomainAdapter
4 | {
5 | TDomain Adapt(TDbo dbo);
6 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Readers/IRetryQueueReader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Readers;
6 |
7 | internal interface IRetryQueueReader
8 | {
9 | ICollection Read(RetryQueuesDboWrapper dboWrapper);
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Repositories/IRetryQueueItemMessageHeaderRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Repositories;
6 |
7 | internal interface IRetryQueueItemMessageHeaderRepository
8 | {
9 | Task AddAsync(IDbConnection dbConnection, IEnumerable retryQueueHeadersDbo);
10 |
11 | Task> GetOrderedAsync(IDbConnection dbConnection, IEnumerable retryQueueItemMessagesDbo);
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/Repositories/IRetryQueueItemMessageRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Postgres.Model;
4 |
5 | namespace KafkaFlow.Retry.Postgres.Repositories;
6 |
7 | internal interface IRetryQueueItemMessageRepository
8 | {
9 | Task AddAsync(IDbConnection dbConnection, RetryQueueItemMessageDbo retryQueueItemMessageDbo);
10 |
11 | Task> GetMessagesOrderedAsync(IDbConnection dbConnection, IEnumerable retryQueueItemsDbo);
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.Postgres/RetryDurableDefinitionBuilderExtension.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Postgres;
2 |
3 | public static class RetryDurableDefinitionBuilderExtension
4 | {
5 | public static RetryDurableDefinitionBuilder WithPostgresDataProvider(
6 | this RetryDurableDefinitionBuilder retryDurableDefinitionBuilder,
7 | string connectionString,
8 | string databaseName)
9 | {
10 | retryDurableDefinitionBuilder.WithRepositoryProvider(
11 | new PostgresDbDataProviderFactory()
12 | .Create(
13 | new PostgresDbSettings(
14 | connectionString,
15 | databaseName)
16 | )
17 | );
18 |
19 | return retryDurableDefinitionBuilder;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/ConnectionProvider.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.SqlServer;
4 |
5 | internal sealed class ConnectionProvider : IConnectionProvider
6 | {
7 | public IDbConnection Create(SqlServerDbSettings sqlServerDbSettings)
8 | {
9 | Guard.Argument(sqlServerDbSettings).NotNull();
10 |
11 | return new DbConnectionContext(sqlServerDbSettings, false);
12 | }
13 |
14 | public IDbConnectionWithinTransaction CreateWithinTransaction(SqlServerDbSettings sqlServerDbSettings)
15 | {
16 | Guard.Argument(sqlServerDbSettings).NotNull();
17 |
18 | return new DbConnectionContext(sqlServerDbSettings, true);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Deploy/00 - Create_Database.sql:
--------------------------------------------------------------------------------
1 | USE master;
2 | IF (NOT EXISTS (SELECT *
3 | FROM sys.databases
4 | WHERE name = '@dbname'))
5 | BEGIN
6 | CREATE DATABASE @dbname;
7 | END
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Deploy/02 - Populate_Tables.sql:
--------------------------------------------------------------------------------
1 | -- Populate tables
2 |
3 | USE @dbname
4 |
5 | IF (NOT EXISTS (SELECT *
6 | FROM [dbo].[QueueStatus]
7 | WHERE [Code] IN (1, 2)))
8 | BEGIN
9 |
10 | INSERT INTO [dbo].[QueueStatus]
11 | VALUES
12 | (1, 'Active', 'The queue has unprocessed messages'),
13 | (2, 'Done', 'The queue does not have unprocessed messages')
14 |
15 | END
16 |
17 | IF (NOT EXISTS (SELECT *
18 | FROM [dbo].[QueueItemStatus]
19 | WHERE [Code] IN (1, 2, 3)))
20 | BEGIN
21 |
22 | INSERT INTO [dbo].[QueueItemStatus]
23 | VALUES
24 | (1, 'Waiting', 'Waiting for retry'),
25 | (2, 'InRetry', 'Retrying'),
26 | (3, 'Done', 'Done'),
27 | (4, 'Cancelled', 'Cancelled')
28 |
29 | END
30 |
31 | IF (NOT EXISTS (SELECT *
32 | FROM [dbo].[QueueItemSeverity]
33 | WHERE [Code] IN (1, 2, 3)))
34 | BEGIN
35 |
36 | INSERT INTO [dbo].[QueueItemSeverity]
37 | VALUES
38 | (0, 'Unknown', 'A severity level was not defined.'),
39 | (1, 'Low', 'No loss of service. The software should recover by itself.'),
40 | (2, 'Medium', 'Minor loss of service. The result is an inconvenience, it''s unclear if the software can recover by itself.'),
41 | (3, 'High', 'Partial loss of service with severe impact on the business. Usually needs human intervention to be solved.')
42 |
43 | END
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/IConnectionProvider.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.SqlServer;
2 |
3 | internal interface IConnectionProvider
4 | {
5 | IDbConnection Create(SqlServerDbSettings sqlServerDbSettings);
6 |
7 | IDbConnectionWithinTransaction CreateWithinTransaction(SqlServerDbSettings sqlServerDbSettings);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/IDbConnection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 |
4 | namespace KafkaFlow.Retry.SqlServer;
5 |
6 | internal interface IDbConnection : IDisposable
7 | {
8 | string Schema { get; }
9 |
10 | SqlCommand CreateCommand();
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/IDbConnectionWithinTransaction.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.SqlServer;
2 |
3 | internal interface IDbConnectionWithinTransaction : IDbConnection
4 | {
5 | void Commit();
6 |
7 | void Rollback();
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/IRetrySchemaCreator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace KafkaFlow.Retry.SqlServer;
4 |
5 | public interface IRetrySchemaCreator
6 | {
7 | Task CreateOrUpdateSchemaAsync(string databaseName);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/IRetryQueueDboFactory.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
2 |
3 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
4 |
5 | internal interface IRetryQueueDboFactory
6 | {
7 | RetryQueueDbo Create(SaveToQueueInput input);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/IRetryQueueItemDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
5 |
6 | internal interface IRetryQueueItemDboFactory
7 | {
8 | RetryQueueItemDbo Create(SaveToQueueInput input, long retryQueueId, Guid retryQueueDomainId);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/IRetryQueueItemMessageDboFactory.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 |
3 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
4 |
5 | internal interface IRetryQueueItemMessageDboFactory
6 | {
7 | RetryQueueItemMessageDbo Create(RetryQueueItemMessage retryQueueItemMessage, long retryQueueItemId);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/IRetryQueueItemMessageHeaderDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
5 |
6 | internal interface IRetryQueueItemMessageHeaderDboFactory
7 | {
8 | IEnumerable Create(IEnumerable headers, long retryQueueItemId);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/RetryQueueDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
6 |
7 | internal sealed class RetryQueueDboFactory : IRetryQueueDboFactory
8 | {
9 | public RetryQueueDbo Create(SaveToQueueInput input)
10 | {
11 | Guard.Argument(input).NotNull();
12 |
13 | return new RetryQueueDbo
14 | {
15 | IdDomain = Guid.NewGuid(),
16 | SearchGroupKey = input.SearchGroupKey,
17 | QueueGroupKey = input.QueueGroupKey,
18 | CreationDate = input.CreationDate,
19 | LastExecution = input.LastExecution.Value,
20 | Status = input.QueueStatus
21 | };
22 | }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/RetryQueueItemDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
6 |
7 | internal sealed class RetryQueueItemDboFactory : IRetryQueueItemDboFactory
8 | {
9 | public RetryQueueItemDbo Create(SaveToQueueInput input, long retryQueueId, Guid retryQueueDomainId)
10 | {
11 | Guard.Argument(input, nameof(input)).NotNull();
12 | Guard.Argument(retryQueueId, nameof(retryQueueId)).Positive();
13 | Guard.Argument(retryQueueDomainId, nameof(retryQueueDomainId)).NotDefault();
14 |
15 | return new RetryQueueItemDbo
16 | {
17 | IdDomain = Guid.NewGuid(),
18 | CreationDate = input.CreationDate,
19 | LastExecution = input.LastExecution,
20 | ModifiedStatusDate = input.ModifiedStatusDate,
21 | AttemptsCount = input.AttemptsCount,
22 | RetryQueueId = retryQueueId,
23 | DomainRetryQueueId = retryQueueDomainId,
24 | Status = input.ItemStatus,
25 | SeverityLevel = input.SeverityLevel,
26 | Description = input.Description
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/RetryQueueItemMessageDboFactory.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
5 |
6 | internal sealed class RetryQueueItemMessageDboFactory : IRetryQueueItemMessageDboFactory
7 | {
8 | public RetryQueueItemMessageDbo Create(RetryQueueItemMessage retryQueueItemMessage, long retryQueueItemId)
9 | {
10 | Guard.Argument(retryQueueItemMessage, nameof(retryQueueItemMessage)).NotNull();
11 | Guard.Argument(retryQueueItemId, nameof(retryQueueItemId)).Positive();
12 |
13 | return new RetryQueueItemMessageDbo
14 | {
15 | IdRetryQueueItem = retryQueueItemId,
16 | Key = retryQueueItemMessage.Key,
17 | Value = retryQueueItemMessage.Value,
18 | Offset = retryQueueItemMessage.Offset,
19 | Partition = retryQueueItemMessage.Partition,
20 | TopicName = retryQueueItemMessage.TopicName,
21 | UtcTimeStamp = retryQueueItemMessage.UtcTimeStamp
22 | };
23 | }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Factories/RetryQueueItemMessageHeaderDboFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.SqlServer.Model.Factories;
7 |
8 | internal sealed class RetryQueueItemMessageHeaderDboFactory : IRetryQueueItemMessageHeaderDboFactory
9 | {
10 | public IEnumerable Create(IEnumerable headers, long retryQueueItemId)
11 | {
12 | Guard.Argument(headers).NotNull();
13 | Guard.Argument(retryQueueItemId, nameof(retryQueueItemId)).Positive();
14 |
15 | return headers.Select(h => Adapt(h, retryQueueItemId));
16 | }
17 |
18 | private RetryQueueItemMessageHeaderDbo Adapt(MessageHeader header, long retryQueueItemId)
19 | {
20 | Guard.Argument(header).NotNull();
21 |
22 | return new RetryQueueItemMessageHeaderDbo
23 | {
24 | Key = header.Key,
25 | Value = header.Value,
26 | RetryQueueItemMessageId = retryQueueItemId
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/RetryQueueDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Model;
6 |
7 | [ExcludeFromCodeCoverage]
8 | internal class RetryQueueDbo
9 | {
10 | public DateTime CreationDate { get; set; }
11 |
12 | public long Id { get; set; }
13 |
14 | public Guid IdDomain { get; set; }
15 |
16 | public DateTime LastExecution { get; set; }
17 |
18 | public string QueueGroupKey { get; set; }
19 |
20 | public string SearchGroupKey { get; set; }
21 |
22 | public RetryQueueStatus Status { get; set; }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/RetryQueueItemDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Common;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.SqlServer.Model;
7 |
8 | [ExcludeFromCodeCoverage]
9 | internal class RetryQueueItemDbo
10 | {
11 | public int AttemptsCount { get; set; }
12 |
13 | public DateTime CreationDate { get; set; }
14 |
15 | public string Description { get; set; }
16 |
17 | public Guid DomainRetryQueueId { get; set; }
18 |
19 | public long Id { get; set; }
20 |
21 | public Guid IdDomain { get; set; }
22 |
23 | public DateTime? LastExecution { get; set; }
24 |
25 | public DateTime? ModifiedStatusDate { get; set; }
26 |
27 | public long RetryQueueId { get; set; }
28 |
29 | public SeverityLevel SeverityLevel { get; set; }
30 |
31 | public int Sort { get; set; }
32 |
33 | public RetryQueueItemStatus Status { get; set; }
34 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/RetryQueueItemMessageDbo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Model;
5 |
6 | [ExcludeFromCodeCoverage]
7 | internal class RetryQueueItemMessageDbo
8 | {
9 | public long IdRetryQueueItem { get; set; }
10 |
11 | public byte[] Key { get; set; }
12 |
13 | public long Offset { get; set; }
14 |
15 | public int Partition { get; set; }
16 |
17 | public string TopicName { get; set; }
18 |
19 | public DateTime UtcTimeStamp { get; set; }
20 |
21 | public byte[] Value { get; set; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/RetryQueueItemMessageHeaderDbo.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace KafkaFlow.Retry.SqlServer.Model;
4 |
5 | [ExcludeFromCodeCoverage]
6 | internal class RetryQueueItemMessageHeaderDbo
7 | {
8 | public long Id { get; set; }
9 |
10 | public string Key { get; set; }
11 |
12 | public long RetryQueueItemMessageId { get; set; }
13 |
14 | public byte[] Value { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/RetryQueuesDboWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Model;
5 |
6 | [ExcludeFromCodeCoverage]
7 | internal class RetryQueuesDboWrapper
8 | {
9 | public RetryQueuesDboWrapper()
10 | {
11 | QueuesDbos = new RetryQueueDbo[0];
12 | ItemsDbos = new RetryQueueItemDbo[0];
13 | MessagesDbos = new RetryQueueItemMessageDbo[0];
14 | HeadersDbos = new RetryQueueItemMessageHeaderDbo[0];
15 | }
16 |
17 | public IList HeadersDbos { get; set; }
18 | public IList ItemsDbos { get; set; }
19 | public IList MessagesDbos { get; set; }
20 | public IList QueuesDbos { get; set; }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Model/Schema/Script.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.SqlServer.Model.Schema;
4 |
5 | public class Script
6 | {
7 | public Script(string value)
8 | {
9 | Guard.Argument(value, nameof(value)).NotNull();
10 |
11 | Value = value;
12 | }
13 |
14 | public string Value { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.IntegrationTests")]
4 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.UnitTests")]
5 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/IRetryQueueAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.SqlServer.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
5 |
6 | internal interface IRetryQueueAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/IRetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.SqlServer.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/IRetryQueueItemMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.SqlServer.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemMessageAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/IRetryQueueItemMessageHeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 | using KafkaFlow.Retry.SqlServer.Model;
3 |
4 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
5 |
6 | internal interface IRetryQueueItemMessageHeaderAdapter : IDboDomainAdapter
7 | {
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/RetryQueueAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
6 |
7 | internal class RetryQueueAdapter : IRetryQueueAdapter
8 | {
9 | public RetryQueue Adapt(RetryQueueDbo retryQueueDbo)
10 | {
11 | Guard.Argument(retryQueueDbo).NotNull();
12 |
13 | return new RetryQueue(retryQueueDbo.IdDomain,
14 | retryQueueDbo.SearchGroupKey,
15 | retryQueueDbo.QueueGroupKey,
16 | retryQueueDbo.CreationDate,
17 | retryQueueDbo.LastExecution,
18 | retryQueueDbo.Status);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/RetryQueueItemAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
6 |
7 | internal class RetryQueueItemAdapter : IRetryQueueItemAdapter
8 | {
9 | public RetryQueueItem Adapt(RetryQueueItemDbo retryQueueItemDbo)
10 | {
11 | Guard.Argument(retryQueueItemDbo).NotNull();
12 |
13 | return new RetryQueueItem(
14 | retryQueueItemDbo.IdDomain,
15 | retryQueueItemDbo.AttemptsCount,
16 | retryQueueItemDbo.CreationDate,
17 | retryQueueItemDbo.Sort,
18 | retryQueueItemDbo.LastExecution,
19 | retryQueueItemDbo.ModifiedStatusDate,
20 | retryQueueItemDbo.Status,
21 | retryQueueItemDbo.SeverityLevel,
22 | retryQueueItemDbo.Description);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/RetryQueueItemMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
6 |
7 | internal class RetryQueueItemMessageAdapter : IRetryQueueItemMessageAdapter
8 | {
9 | public RetryQueueItemMessage Adapt(RetryQueueItemMessageDbo retryQueueItemMessageDbo)
10 | {
11 | Guard.Argument(retryQueueItemMessageDbo, nameof(retryQueueItemMessageDbo)).NotNull();
12 |
13 | return new RetryQueueItemMessage(
14 | retryQueueItemMessageDbo.TopicName,
15 | retryQueueItemMessageDbo.Key,
16 | retryQueueItemMessageDbo.Value,
17 | retryQueueItemMessageDbo.Partition,
18 | retryQueueItemMessageDbo.Offset,
19 | retryQueueItemMessageDbo.UtcTimeStamp);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/Adapters/RetryQueueItemMessageHeaderAdapter.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Readers.Adapters;
6 |
7 | internal class RetryQueueItemMessageHeaderAdapter : IRetryQueueItemMessageHeaderAdapter
8 | {
9 | public MessageHeader Adapt(RetryQueueItemMessageHeaderDbo messageHeaderDbo)
10 | {
11 | Guard.Argument(messageHeaderDbo).NotNull();
12 |
13 | return new MessageHeader(messageHeaderDbo.Key, messageHeaderDbo.Value);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/IDboDomainAdapter.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.SqlServer.Readers;
2 |
3 | internal interface IDboDomainAdapter
4 | {
5 | TDomain Adapt(TDbo dbo);
6 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Readers/IRetryQueueReader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Readers;
6 |
7 | internal interface IRetryQueueReader
8 | {
9 | ICollection Read(RetryQueuesDboWrapper dboWrapper);
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Repositories/IRetryQueueItemMessageHeaderRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Repositories;
6 |
7 | internal interface IRetryQueueItemMessageHeaderRepository
8 | {
9 | Task AddAsync(IDbConnection dbConnection, IEnumerable retryQueueHeadersDbo);
10 |
11 | Task> GetOrderedAsync(IDbConnection dbConnection, IEnumerable retryQueueItemMessagesDbo);
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry.SqlServer/Repositories/IRetryQueueItemMessageRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.SqlServer.Model;
4 |
5 | namespace KafkaFlow.Retry.SqlServer.Repositories;
6 |
7 | internal interface IRetryQueueItemMessageRepository
8 | {
9 | Task AddAsync(IDbConnection dbConnection, RetryQueueItemMessageDbo retryQueueItemMessageDbo);
10 |
11 | Task> GetMessagesOrderedAsync(IDbConnection dbConnection, IEnumerable retryQueueItemsDbo);
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Common/SeverityLevel.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Common;
2 |
3 | ///
4 | /// Severity level indicates the relative impact of a message on our customer's system or
5 | /// business processes
6 | ///
7 | public enum SeverityLevel
8 | {
9 | ///
10 | /// A severity level was not defined.
11 | ///
12 | Unknown = 0,
13 |
14 | ///
15 | /// No loss of service. The software should recover by itself.
16 | ///
17 | Low = 1,
18 |
19 | ///
20 | /// Minor loss of service. The result is an inconvenience, it's unclear if the software can
21 | /// recover by itself.
22 | ///
23 | Medium = 2,
24 |
25 | ///
26 | /// Partial loss of service with severe impact on the business. Usually needs human
27 | /// intervention to be solved.
28 | ///
29 | High = 3
30 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Compressors/GzipCompressor.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using Dawn;
4 |
5 | namespace KafkaFlow.Retry.Durable.Compression;
6 |
7 | internal class GzipCompressor : IGzipCompressor
8 | {
9 | private static readonly int s_bufferSize = 64 * 1024;
10 |
11 | public byte[] Compress(byte[] data)
12 | {
13 | Guard.Argument(data).NotNull();
14 |
15 | using (var compressIntoMs = new MemoryStream())
16 | {
17 | using (var gzs = new BufferedStream(new GZipStream(compressIntoMs, CompressionLevel.Fastest), s_bufferSize))
18 | {
19 | gzs.Write(data, 0, data.Length);
20 | }
21 |
22 | return compressIntoMs.ToArray();
23 | }
24 | }
25 |
26 | public byte[] Decompress(byte[] data)
27 | {
28 | Guard.Argument(data).NotNull();
29 |
30 | using (var compressedMs = new MemoryStream(data))
31 | {
32 | using (var decompressedMs = new MemoryStream())
33 | {
34 | using (var gzs = new BufferedStream(new GZipStream(compressedMs, CompressionMode.Decompress),
35 | s_bufferSize))
36 | {
37 | gzs.CopyTo(decompressedMs);
38 | }
39 |
40 | return decompressedMs.ToArray();
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Compressors/IGzipCompressor.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Compression;
2 |
3 | internal interface IGzipCompressor
4 | {
5 | byte[] Compress(byte[] data);
6 |
7 | byte[] Decompress(byte[] data);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Builders/Polling/CleanupPollingDefinitionBuilder.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Definitions.Polling;
2 |
3 | namespace KafkaFlow.Retry;
4 |
5 | public class CleanupPollingDefinitionBuilder : PollingDefinitionBuilder
6 | {
7 | private int _rowsPerRequest = 256;
8 | private int _timeToLiveInDays = 30;
9 |
10 | internal override bool Required => false;
11 |
12 | public CleanupPollingDefinitionBuilder WithRowsPerRequest(int rowsPerRequest)
13 | {
14 | _rowsPerRequest = rowsPerRequest;
15 | return this;
16 | }
17 |
18 | public CleanupPollingDefinitionBuilder WithTimeToLiveInDays(int timeToLiveInDays)
19 | {
20 | _timeToLiveInDays = timeToLiveInDays;
21 | return this;
22 | }
23 |
24 | internal CleanupPollingDefinition Build()
25 | {
26 | return new CleanupPollingDefinition(
27 | IsEnabled,
28 | CronExpression,
29 | _timeToLiveInDays,
30 | _rowsPerRequest
31 | );
32 | }
33 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Builders/Polling/PollingDefinitionBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry;
2 |
3 | public abstract class PollingDefinitionBuilder where TSelf : PollingDefinitionBuilder
4 | {
5 | protected string CronExpression;
6 | protected bool IsEnabled;
7 |
8 | internal abstract bool Required { get; }
9 |
10 | public TSelf Enabled(bool value)
11 | {
12 | IsEnabled = value;
13 | return (TSelf)this;
14 | }
15 |
16 | public TSelf WithCronExpression(string cronExpression)
17 | {
18 | CronExpression = cronExpression;
19 | return (TSelf)this;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Builders/Polling/RetryDurableActiveQueuesCountPollingDefinitionBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Definitions.Polling;
4 |
5 | namespace KafkaFlow.Retry.Durable.Definitions.Builders.Polling;
6 |
7 | public class RetryDurableActiveQueuesCountPollingDefinitionBuilder
8 | : PollingDefinitionBuilder
9 | {
10 | protected Action ActionToPerform;
11 |
12 | internal override bool Required => false;
13 |
14 | public RetryDurableActiveQueuesCountPollingDefinitionBuilder Do(Action actionToPerform)
15 | {
16 | Guard.Argument(actionToPerform, nameof(actionToPerform)).NotNull();
17 |
18 | ActionToPerform = actionToPerform;
19 | return this;
20 | }
21 |
22 | internal RetryDurableActiveQueuesCountPollingDefinition Build()
23 | {
24 | return new RetryDurableActiveQueuesCountPollingDefinition(
25 | IsEnabled,
26 | CronExpression,
27 | ActionToPerform
28 | );
29 | }
30 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Builders/Polling/RetryDurablePollingDefinitionBuilder.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Definitions.Polling;
2 |
3 | namespace KafkaFlow.Retry;
4 |
5 | public class RetryDurablePollingDefinitionBuilder : PollingDefinitionBuilder
6 | {
7 | protected int ExpirationIntervalFactor = 1;
8 | protected int FetchSize = 256;
9 |
10 | internal override bool Required => true;
11 |
12 | public RetryDurablePollingDefinitionBuilder WithExpirationIntervalFactor(int expirationIntervalFactor)
13 | {
14 | ExpirationIntervalFactor = expirationIntervalFactor;
15 | return this;
16 | }
17 |
18 | public RetryDurablePollingDefinitionBuilder WithFetchSize(int fetchSize)
19 | {
20 | FetchSize = fetchSize;
21 | return this;
22 | }
23 |
24 | internal RetryDurablePollingDefinition Build()
25 | {
26 | return new RetryDurablePollingDefinition(
27 | IsEnabled,
28 | CronExpression,
29 | FetchSize,
30 | ExpirationIntervalFactor
31 | );
32 | }
33 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/CleanupPollingDefinition.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
4 |
5 | internal class CleanupPollingDefinition : PollingDefinition
6 | {
7 | public CleanupPollingDefinition(
8 | bool enabled,
9 | string cronExpression,
10 | int timeToLiveInDays,
11 | int rowsPerRequest)
12 | : base(enabled, cronExpression)
13 | {
14 | if (enabled)
15 | {
16 | Guard.Argument(timeToLiveInDays, nameof(timeToLiveInDays)).Positive();
17 | Guard.Argument(rowsPerRequest, nameof(rowsPerRequest)).Positive();
18 | }
19 |
20 | TimeToLiveInDays = timeToLiveInDays;
21 | RowsPerRequest = rowsPerRequest;
22 | }
23 |
24 | public override PollingJobType PollingJobType => PollingJobType.Cleanup;
25 |
26 | public int RowsPerRequest { get; }
27 |
28 | public int TimeToLiveInDays { get; }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/PollingDefinition.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
4 |
5 | internal abstract class PollingDefinition
6 | {
7 | protected PollingDefinition(bool enabled, string cronExpression)
8 | {
9 | Guard.Argument(PollingJobType, nameof(PollingJobType)).NotDefault();
10 |
11 | if (enabled)
12 | {
13 | Guard.Argument(Quartz.CronExpression.IsValidExpression(cronExpression), nameof(cronExpression))
14 | .True("The cron expression that was defined is not valid");
15 | }
16 |
17 | Enabled = enabled;
18 | CronExpression = cronExpression;
19 | }
20 |
21 | public string CronExpression { get; }
22 |
23 | public bool Enabled { get; }
24 |
25 | public abstract PollingJobType PollingJobType { get; }
26 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/PollingDefinitionsAggregator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Dawn;
4 |
5 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
6 |
7 | internal class PollingDefinitionsAggregator
8 | {
9 | public PollingDefinitionsAggregator(string schedulerId, IEnumerable pollingDefinitions)
10 | {
11 | Guard.Argument(schedulerId, nameof(schedulerId)).NotNull().NotEmpty();
12 | Guard.Argument(pollingDefinitions, nameof(pollingDefinitions)).NotNull().NotEmpty();
13 |
14 | var pollingJobTypes = pollingDefinitions.Select(pd => pd.PollingJobType);
15 | Guard.Argument(pollingJobTypes, nameof(pollingJobTypes)).DoesNotContainDuplicate();
16 |
17 | SchedulerId = schedulerId;
18 | PollingDefinitions = pollingDefinitions.ToDictionary(pd => pd.PollingJobType, pd => pd);
19 | }
20 |
21 | public IDictionary PollingDefinitions { get; }
22 |
23 | public string SchedulerId { get; }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/PollingJobType.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
2 |
3 | internal enum PollingJobType
4 | {
5 | Unknown = 0,
6 |
7 | RetryDurable = 1,
8 | Cleanup = 2,
9 | RetryDurableActiveQueuesCount = 3
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/RetryDurableActiveQueuesCountPollingDefinition.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
5 | internal class RetryDurableActiveQueuesCountPollingDefinition : PollingDefinition
6 | {
7 | public RetryDurableActiveQueuesCountPollingDefinition(
8 | bool enabled,
9 | string cronExpression,
10 | Action activeQueues)
11 | : base(enabled, cronExpression)
12 | {
13 | Guard.Argument(activeQueues, nameof(activeQueues)).NotNull();
14 | ActiveQueues = activeQueues;
15 | }
16 |
17 | public override PollingJobType PollingJobType => PollingJobType.RetryDurableActiveQueuesCount;
18 |
19 | public Action ActiveQueues { get; }
20 | }
21 |
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/Polling/RetryDurablePollingDefinition.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Durable.Definitions.Polling;
4 |
5 | internal class RetryDurablePollingDefinition : PollingDefinition
6 | {
7 | public RetryDurablePollingDefinition(
8 | bool enabled,
9 | string cronExpression,
10 | int fetchSize,
11 | int expirationIntervalFactor)
12 | : base(enabled, cronExpression)
13 | {
14 | Guard.Argument(fetchSize, nameof(fetchSize)).Positive();
15 | Guard.Argument(expirationIntervalFactor, nameof(expirationIntervalFactor)).Positive();
16 |
17 | FetchSize = fetchSize;
18 | ExpirationIntervalFactor = expirationIntervalFactor;
19 | }
20 |
21 | public int ExpirationIntervalFactor { get; }
22 |
23 | public int FetchSize { get; }
24 |
25 | public override PollingJobType PollingJobType => PollingJobType.RetryDurable;
26 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Definitions/RetryDurableRetryPlanBeforeDefinition.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Definitions;
5 |
6 | internal class RetryDurableRetryPlanBeforeDefinition
7 | {
8 | public RetryDurableRetryPlanBeforeDefinition(
9 | Func timeBetweenTriesPlan,
10 | int numberOfRetries,
11 | bool pauseConsumer
12 | )
13 | {
14 | Guard.Argument(numberOfRetries).NotZero()
15 | .NotNegative(value => "The number of retries should be higher than zero");
16 | Guard.Argument(timeBetweenTriesPlan).NotNull("A plan of times betwwen tries should be defined");
17 |
18 | TimeBetweenTriesPlan = timeBetweenTriesPlan;
19 | NumberOfRetries = numberOfRetries;
20 | PauseConsumer = pauseConsumer;
21 | }
22 |
23 | public int NumberOfRetries { get; }
24 |
25 | public bool PauseConsumer { get; }
26 |
27 | public Func TimeBetweenTriesPlan { get; }
28 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Encoders/IUtf8Encoder.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Encoders;
2 |
3 | internal interface IUtf8Encoder
4 | {
5 | string Decode(byte[] data);
6 |
7 | byte[] Encode(string data);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Encoders/Utf8Encoder.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace KafkaFlow.Retry.Durable.Encoders;
4 |
5 | internal class Utf8Encoder : IUtf8Encoder
6 | {
7 | public string Decode(byte[] data) => data is null ? null : Encoding.UTF8.GetString(data);
8 |
9 | public byte[] Encode(string data) => Encoding.UTF8.GetBytes(data);
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/MessageSerializerStrategy.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry;
2 |
3 | public enum MessageSerializerStrategy
4 | {
5 | None = 0,
6 | NewtonsoftJson = 1
7 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/Extensions/JobDataMapExtensions.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using Quartz;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling.Extensions;
5 |
6 | internal static class JobDataMapExtensions
7 | {
8 | public static string GetValidStringValue(this JobDataMap jobDataMap, string key, string jobName)
9 | {
10 | var stringValue = jobDataMap.GetValidValue(key, jobName);
11 |
12 | Guard.Argument(stringValue).NotEmpty($"Argument {key} can't be an empty string for the job {jobName}.");
13 |
14 | return stringValue;
15 | }
16 |
17 | public static T GetValidValue(this JobDataMap jobDataMap, string key, string jobName) where T : class
18 | {
19 | jobDataMap.TryGetValue(key, out var objValue);
20 |
21 | var value = objValue as T;
22 |
23 | Guard.Argument(value).NotNull($"Argument {key} is required for the job {jobName}.");
24 |
25 | return value;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/IJobDataProvider.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Definitions.Polling;
2 | using Quartz;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling;
5 |
6 | internal interface IJobDataProvider
7 | {
8 | IJobDetail JobDetail { get; }
9 | PollingDefinition PollingDefinition { get; }
10 |
11 | ITrigger Trigger { get; }
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/IJobDataProvidersFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace KafkaFlow.Retry.Durable.Polling;
4 |
5 | internal interface IJobDataProvidersFactory
6 | {
7 | IEnumerable Create(IMessageProducer retryDurableMessageProducer, ILogHandler logHandler);
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/IQueueTrackerCoordinator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace KafkaFlow.Retry.Durable.Polling;
4 |
5 | internal interface IQueueTrackerCoordinator
6 | {
7 | Task ScheduleJobsAsync(IMessageProducer retryDurableMessageProducer, ILogHandler logHandler);
8 |
9 | Task UnscheduleJobsAsync();
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/IQueueTrackerFactory.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Polling;
2 |
3 | internal interface IQueueTrackerFactory
4 | {
5 | QueueTracker Create(IMessageProducer retryDurableMessageProducer, ILogHandler logHandler);
6 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/ITriggerProvider.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Definitions.Polling;
2 | using Quartz;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling;
5 |
6 | internal interface ITriggerProvider
7 | {
8 | ITrigger GetPollingTrigger(string schedulerId, PollingDefinition pollingDefinition);
9 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/Jobs/PollingJobConstants.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Polling.Jobs;
2 |
3 | internal static class PollingJobConstants
4 | {
5 | public const string CleanupPollingDefinition = "CleanupPollingDefinition";
6 | public const string LogHandler = "LogHandler";
7 | public const string MessageHeadersAdapter = "MessageHeadersAdapter";
8 | public const string RetryDurableMessageProducer = "RetryDurableProducer";
9 | public const string RetryDurablePollingDefinition = "RetryDurablePollingDefinition";
10 | public const string RetryDurableQueueRepository = "RetryDurableQueueRepository";
11 | public const string SchedulerId = "SchedulerId";
12 | public const string Utf8Encoder = "Utf8Encoder";
13 | public const string RetryDurableActiveQueuesCountPollingDefinition = "RetryDurableActiveQueuesCountPollingDefinition";
14 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/QueueTrackerCoordinator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling;
5 |
6 | internal class QueueTrackerCoordinator : IQueueTrackerCoordinator
7 | {
8 | private readonly IQueueTrackerFactory _queueTrackerFactory;
9 | private QueueTracker _queueTracker;
10 |
11 | public QueueTrackerCoordinator(IQueueTrackerFactory queueTrackerFactory)
12 | {
13 | Guard.Argument(queueTrackerFactory).NotNull();
14 |
15 | _queueTrackerFactory = queueTrackerFactory;
16 | }
17 |
18 | public async Task ScheduleJobsAsync(IMessageProducer retryDurableMessageProducer, ILogHandler logHandler)
19 | {
20 | _queueTracker = _queueTrackerFactory
21 | .Create(retryDurableMessageProducer, logHandler);
22 |
23 | await _queueTracker.ScheduleJobsAsync().ConfigureAwait(false);
24 | }
25 |
26 | public async Task UnscheduleJobsAsync()
27 | {
28 | if (_queueTracker is object)
29 | {
30 | await _queueTracker.UnscheduleJobsAsync().ConfigureAwait(false);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/QueueTrackerFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling;
5 |
6 | internal class QueueTrackerFactory : IQueueTrackerFactory
7 | {
8 | private readonly IJobDataProvidersFactory _jobDataProvidersFactory;
9 | private readonly string _schedulerId;
10 | private IEnumerable _jobDataProviders;
11 |
12 | public QueueTrackerFactory(
13 | string schedulerId,
14 | IJobDataProvidersFactory jobDataProvidersFactory
15 | )
16 | {
17 | Guard.Argument(schedulerId, nameof(schedulerId)).NotNull().NotEmpty();
18 | Guard.Argument(jobDataProvidersFactory, nameof(jobDataProvidersFactory)).NotNull();
19 |
20 | _schedulerId = schedulerId;
21 | _jobDataProvidersFactory = jobDataProvidersFactory;
22 | }
23 |
24 | public QueueTracker Create(IMessageProducer retryDurableMessageProducer, ILogHandler logHandler)
25 | {
26 | if (_jobDataProviders is null)
27 | {
28 | _jobDataProviders = _jobDataProvidersFactory.Create(retryDurableMessageProducer, logHandler);
29 | }
30 |
31 | return new QueueTracker(
32 | _schedulerId,
33 | _jobDataProviders,
34 | logHandler);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Polling/TriggerProvider.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Definitions.Polling;
2 | using Quartz;
3 |
4 | namespace KafkaFlow.Retry.Durable.Polling;
5 |
6 | internal class TriggerProvider : ITriggerProvider
7 | {
8 | public ITrigger GetPollingTrigger(string schedulerId, PollingDefinition pollingDefinition)
9 | {
10 | return TriggerBuilder
11 | .Create()
12 | .WithIdentity($"pollingJobTrigger_{schedulerId}_{pollingDefinition.PollingJobType}", "queueTrackerGroup")
13 | .WithCronSchedule(pollingDefinition.CronExpression,
14 | cronBuilder => cronBuilder.WithMisfireHandlingInstructionDoNothing())
15 | .StartNow()
16 | .WithPriority(1)
17 | .Build();
18 | }
19 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Create/AddIfQueueExistsResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Create;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class AddIfQueueExistsResult
8 | {
9 | public AddIfQueueExistsResult(AddIfQueueExistsResultStatus status)
10 | {
11 | Guard.Argument(status).NotDefault();
12 |
13 | Status = status;
14 | }
15 |
16 | public AddIfQueueExistsResultStatus Status { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Create/AddIfQueueExistsResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Create;
2 |
3 | public enum AddIfQueueExistsResultStatus
4 | {
5 | Unknown = 0,
6 | Added = 1,
7 | NoPendingMembers = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Create/SaveToQueueResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Create;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class SaveToQueueResult
8 | {
9 | public SaveToQueueResult(SaveToQueueResultStatus status)
10 | {
11 | Guard.Argument(status).NotDefault();
12 |
13 | Status = status;
14 | }
15 |
16 | public SaveToQueueResultStatus Status { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Create/SaveToQueueResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Create;
2 |
3 | public enum SaveToQueueResultStatus
4 | {
5 | Unknown = 0,
6 | Added = 1,
7 | Created = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Delete/DeleteQueuesInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Delete;
6 |
7 | public class DeleteQueuesInput
8 | {
9 | public DeleteQueuesInput(
10 | string searchGroupKey,
11 | RetryQueueStatus retryQueueStatus,
12 | DateTime maxLastExecutionDateToBeKept,
13 | int maxRowsToDelete)
14 | {
15 | Guard.Argument(searchGroupKey, nameof(searchGroupKey)).NotNull().NotEmpty();
16 | Guard.Argument(retryQueueStatus, nameof(retryQueueStatus)).NotDefault();
17 | Guard.Argument(maxLastExecutionDateToBeKept, nameof(maxLastExecutionDateToBeKept)).NotDefault();
18 | Guard.Argument(maxRowsToDelete, nameof(maxRowsToDelete)).Positive();
19 |
20 | SearchGroupKey = searchGroupKey;
21 | RetryQueueStatus = retryQueueStatus;
22 | MaxLastExecutionDateToBeKept = maxLastExecutionDateToBeKept;
23 | MaxRowsToDelete = maxRowsToDelete;
24 | }
25 |
26 | public DateTime MaxLastExecutionDateToBeKept { get; }
27 |
28 | public int MaxRowsToDelete { get; }
29 |
30 | public RetryQueueStatus RetryQueueStatus { get; }
31 |
32 | public string SearchGroupKey { get; }
33 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Delete/DeleteQueuesResult.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 |
3 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Delete;
4 |
5 | public class DeleteQueuesResult
6 | {
7 | public DeleteQueuesResult(int totalQueuesDeleted)
8 | {
9 | Guard.Argument(totalQueuesDeleted, nameof(totalQueuesDeleted)).NotNegative();
10 |
11 | TotalQueuesDeleted = totalQueuesDeleted;
12 | }
13 |
14 | public int TotalQueuesDeleted { get; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/CheckQueueInput.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class CheckQueueInput
9 | {
10 | public CheckQueueInput(RetryQueueItemMessage message, string queueGroupKey)
11 | {
12 | Guard.Argument(message).NotNull();
13 | Guard.Argument(queueGroupKey).NotNull().NotEmpty();
14 |
15 | Message = message;
16 | QueueGroupKey = queueGroupKey;
17 | }
18 |
19 | public RetryQueueItemMessage Message { get; }
20 |
21 | public string QueueGroupKey { get; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/CheckQueueResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class CheckQueueResult
8 | {
9 | public CheckQueueResult(CheckQueueResultStatus status)
10 | {
11 | Guard.Argument(status).NotDefault();
12 |
13 | Status = status;
14 | }
15 |
16 | public CheckQueueResultStatus Status { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/CheckQueueResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
2 |
3 | public enum CheckQueueResultStatus
4 | {
5 | Unknown = 0,
6 | DoesNotExist = 1,
7 | Exists = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/CountQueuesInput.cs:
--------------------------------------------------------------------------------
1 | using Dawn;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
5 | public class CountQueuesInput
6 | {
7 | public CountQueuesInput(RetryQueueStatus status)
8 | {
9 | Guard.Argument(status, nameof(status)).NotDefault();
10 |
11 | Status = status;
12 | }
13 | public RetryQueueStatus Status { get; }
14 | public string SearchGroupKey { get; set; }
15 | }
16 |
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/GetQueuesResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public class GetQueuesResult
10 | {
11 | public GetQueuesResult(IEnumerable retryQueues)
12 | {
13 | Guard.Argument(retryQueues, nameof(retryQueues)).NotNull();
14 |
15 | RetryQueues = retryQueues;
16 | }
17 |
18 | public IEnumerable RetryQueues { get; }
19 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/GetQueuesSortOption.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
2 |
3 | public enum GetQueuesSortOption
4 | {
5 | None = 0,
6 | ByLastExecutionAscending = 1,
7 | ByCreationDateDescending = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueueNewestItemsInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class QueueNewestItemsInput
9 | {
10 | public QueueNewestItemsInput(Guid queueId, Guid itemId, int sort)
11 | {
12 | Guard.Argument(queueId, nameof(queueId)).NotDefault();
13 | Guard.Argument(itemId, nameof(itemId)).NotDefault();
14 | Guard.Argument(sort, nameof(sort)).NotNegative();
15 |
16 | QueueId = queueId;
17 | ItemId = itemId;
18 | Sort = sort;
19 | }
20 |
21 | public Guid ItemId { get; }
22 | public Guid QueueId { get; }
23 | public int Sort { get; }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueueNewestItemsResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class QueueNewestItemsResult
8 | {
9 | public QueueNewestItemsResult(QueueNewestItemsResultStatus status)
10 | {
11 | Guard.Argument(status, nameof(status)).NotDefault();
12 |
13 | Status = status;
14 | }
15 |
16 | public QueueNewestItemsResultStatus Status { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueueNewestItemsResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
2 |
3 | public enum QueueNewestItemsResultStatus
4 | {
5 | None = 0,
6 | HasNewestItems = 3,
7 | NoNewestItems = 4
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueuePendingItemsInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class QueuePendingItemsInput
9 | {
10 | public QueuePendingItemsInput(Guid queueId, Guid itemId, int sort)
11 | {
12 | Guard.Argument(queueId, nameof(queueId)).NotDefault();
13 | Guard.Argument(itemId, nameof(itemId)).NotDefault();
14 | Guard.Argument(sort, nameof(sort)).NotNegative();
15 |
16 | QueueId = queueId;
17 | ItemId = itemId;
18 | Sort = sort;
19 | }
20 |
21 | public Guid ItemId { get; }
22 | public Guid QueueId { get; }
23 | public int Sort { get; }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueuePendingItemsResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class QueuePendingItemsResult
8 | {
9 | public QueuePendingItemsResult(QueuePendingItemsResultStatus status)
10 | {
11 | Guard.Argument(status, nameof(status)).NotDefault();
12 |
13 | Status = status;
14 | }
15 |
16 | public QueuePendingItemsResultStatus Status { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/QueuePendingItemsResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
2 |
3 | public enum QueuePendingItemsResultStatus
4 | {
5 | None = 0,
6 | NoPendingItems = 1,
7 | HasPendingItems = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Read/StuckStatusFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 |
7 | public class StuckStatusFilter
8 | {
9 | public StuckStatusFilter(RetryQueueItemStatus itemStatus, TimeSpan expirationInterval)
10 | {
11 | Guard.Argument(itemStatus, nameof(itemStatus)).NotDefault();
12 | Guard.Argument(expirationInterval, nameof(expirationInterval)).NotZero().NotNegative();
13 |
14 | ItemStatus = itemStatus;
15 | ExpirationInterval = expirationInterval;
16 | }
17 |
18 | public TimeSpan ExpirationInterval { get; }
19 | public RetryQueueItemStatus ItemStatus { get; }
20 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemExecutionInfoInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public class UpdateItemExecutionInfoInput : UpdateItemInput
10 | {
11 | public UpdateItemExecutionInfoInput(Guid queueId, Guid itemId, RetryQueueItemStatus status, int attemptCount,
12 | DateTime lastExecution, string description)
13 | : base(itemId, status)
14 | {
15 | Guard.Argument(queueId, nameof(queueId)).NotDefault();
16 | Guard.Argument(attemptCount, nameof(attemptCount)).NotNegative();
17 | Guard.Argument(lastExecution, nameof(lastExecution)).NotDefault();
18 |
19 | QueueId = queueId;
20 | AttemptCount = attemptCount;
21 | LastExecution = lastExecution;
22 | Description = description;
23 | }
24 |
25 | public int AttemptCount { get; }
26 | public string Description { get; }
27 | public DateTime LastExecution { get; }
28 | public Guid QueueId { get; set; }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public abstract class UpdateItemInput
10 | {
11 | protected UpdateItemInput(Guid itemId, RetryQueueItemStatus status)
12 | {
13 | Guard.Argument(itemId, nameof(itemId)).NotDefault();
14 | Guard.Argument(status, nameof(status)).NotDefault();
15 |
16 | ItemId = itemId;
17 | Status = status;
18 | }
19 |
20 | public Guid ItemId { get; }
21 | public RetryQueueItemStatus Status { get; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class UpdateItemResult
9 | {
10 | public UpdateItemResult(Guid id, UpdateItemResultStatus status)
11 | {
12 | Guard.Argument(id, nameof(id)).NotDefault();
13 | Guard.Argument(status, nameof(status)).NotDefault();
14 |
15 | Id = id;
16 | Status = status;
17 | }
18 |
19 | public Guid Id { get; }
20 |
21 | public UpdateItemResultStatus Status { get; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
2 |
3 | public enum UpdateItemResultStatus
4 | {
5 | Unknown = 0,
6 | Updated = 1,
7 | ItemNotFound = 2,
8 | QueueNotFound = 3,
9 | ItemIsNotInWaitingState = 4,
10 | ItemIsNotTheFirstWaitingInQueue = 5,
11 | UpdateIsNotAllowed = 6
12 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemStatusInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class UpdateItemStatusInput : UpdateItemInput
9 | {
10 | public UpdateItemStatusInput(Guid itemId, RetryQueueItemStatus status)
11 | : base(itemId, status)
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemsInQueueInput.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class UpdateItemsInQueueInput
9 | {
10 | public UpdateItemsInQueueInput(string queueGroupKey, RetryQueueItemStatus status)
11 | {
12 | Guard.Argument(queueGroupKey, nameof(queueGroupKey)).NotNull();
13 | Guard.Argument(status, nameof(status)).NotDefault();
14 |
15 | QueueGroupKey = queueGroupKey;
16 | ItemStatus = status;
17 | }
18 |
19 | public RetryQueueItemStatus ItemStatus { get; }
20 |
21 | public string QueueGroupKey { get; }
22 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemsInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using Dawn;
5 | using KafkaFlow.Retry.Durable.Repository.Model;
6 |
7 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
8 |
9 | [ExcludeFromCodeCoverage]
10 | public class UpdateItemsInput
11 | {
12 | public UpdateItemsInput(IEnumerable itemIds, RetryQueueItemStatus status)
13 | {
14 | Guard.Argument(itemIds, nameof(itemIds)).NotNull().NotEmpty();
15 | Guard.Argument(status, nameof(status)).NotDefault();
16 |
17 | ItemIds = itemIds;
18 | Status = status;
19 | }
20 |
21 | public IEnumerable ItemIds { get; }
22 | public RetryQueueItemStatus Status { get; }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateItemsResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class UpdateItemsResult
8 | {
9 | public UpdateItemsResult(IEnumerable results)
10 | {
11 | Results = results ?? new List();
12 | }
13 |
14 | public IEnumerable Results { get; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateQueueResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
6 |
7 | [ExcludeFromCodeCoverage]
8 | public class UpdateQueueResult
9 | {
10 | public UpdateQueueResult(string queueGroupKey, UpdateQueueResultStatus updateStatus,
11 | RetryQueueStatus retryQueueStatus)
12 | {
13 | Guard.Argument(queueGroupKey, nameof(queueGroupKey)).NotNull();
14 | Guard.Argument(updateStatus, nameof(updateStatus)).NotDefault();
15 |
16 | QueueGroupKey = queueGroupKey;
17 | Status = updateStatus;
18 | RetryQueueStatus = retryQueueStatus;
19 | }
20 |
21 | public string QueueGroupKey { get; }
22 | public RetryQueueStatus RetryQueueStatus { get; }
23 | public UpdateQueueResultStatus Status { get; }
24 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateQueueResultStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
2 |
3 | public enum UpdateQueueResultStatus
4 | {
5 | Unknown = 0,
6 | Updated = 1,
7 | QueueNotFound = 2,
8 | QueueIsNotActive = 3,
9 | QueueHasNoItemsWaiting = 4,
10 | NotUpdated = 5,
11 | UpdateIsNotAllowed = 6,
12 | FailedToUpdateAllItems = 7,
13 | FailedToUpdateItems = 8,
14 | AllItemsUpdatedButFailedToUpdateQueue = 9
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateQueuesInput.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Repository.Model;
5 |
6 | namespace KafkaFlow.Retry.Durable.Repository;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public class UpdateQueuesInput
10 | {
11 | public UpdateQueuesInput(IEnumerable queueGroupKeys, RetryQueueItemStatus status)
12 | {
13 | Guard.Argument(queueGroupKeys).NotNull().NotEmpty();
14 | Guard.Argument(status).NotDefault();
15 |
16 | QueueGroupKeys = queueGroupKeys;
17 | ItemStatus = status;
18 | }
19 |
20 | public RetryQueueItemStatus ItemStatus { get; }
21 |
22 | public IEnumerable QueueGroupKeys { get; }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Actions/Update/UpdateQueuesResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Actions.Update;
5 |
6 | [ExcludeFromCodeCoverage]
7 | public class UpdateQueuesResult
8 | {
9 | public UpdateQueuesResult(IEnumerable results)
10 | {
11 | Results = results ?? new List();
12 | }
13 |
14 | public IEnumerable Results { get; }
15 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Adapters/IMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Adapters;
2 |
3 | internal interface IMessageAdapter
4 | {
5 | byte[] AdaptMessageToRepository(object message);
6 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Adapters/IMessageHeadersAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using KafkaFlow.Retry.Durable.Repository.Model;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository.Adapters;
5 |
6 | internal interface IMessageHeadersAdapter
7 | {
8 | IEnumerable AdaptMessageHeadersToRepository(IMessageHeaders messageHeaders);
9 |
10 | IMessageHeaders AdaptMessageHeadersFromRepository(IEnumerable fromMessageHeaders);
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Adapters/MessageHeadersAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Adapters;
6 |
7 | internal class MessageHeadersAdapter : IMessageHeadersAdapter
8 | {
9 | public IMessageHeaders AdaptMessageHeadersFromRepository(IEnumerable fromMessageHeaders)
10 | {
11 | var toMessageHeaders = new MessageHeaders();
12 |
13 | if (fromMessageHeaders is null)
14 | {
15 | return toMessageHeaders;
16 | }
17 |
18 | foreach (var header in fromMessageHeaders)
19 | {
20 | toMessageHeaders.Add(header.Key, header.Value);
21 | }
22 |
23 | return toMessageHeaders;
24 | }
25 |
26 | public IEnumerable AdaptMessageHeadersToRepository(IMessageHeaders messageHeaders)
27 | {
28 | if (messageHeaders is null)
29 | {
30 | return Enumerable.Empty();
31 | }
32 |
33 | return messageHeaders.Select(h => new MessageHeader(h.Key, h.Value)).ToList();
34 | }
35 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Adapters/NewtonsoftJsonMessageAdapter.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Compression;
2 | using KafkaFlow.Retry.Durable.Encoders;
3 | using KafkaFlow.Retry.Durable.Serializers;
4 |
5 | namespace KafkaFlow.Retry.Durable.Repository.Adapters;
6 |
7 | internal class NewtonsoftJsonMessageAdapter : IMessageAdapter
8 | {
9 | private readonly IGzipCompressor _gzipCompressor;
10 | private readonly INewtonsoftJsonSerializer _newtonsoftJsonSerializer;
11 | private readonly IUtf8Encoder _utf8Encoder;
12 |
13 | public NewtonsoftJsonMessageAdapter(
14 | IGzipCompressor gzipCompressor,
15 | INewtonsoftJsonSerializer newtonsoftJsonSerializer,
16 | IUtf8Encoder utf8Encoder)
17 | {
18 | _gzipCompressor = gzipCompressor;
19 | _newtonsoftJsonSerializer = newtonsoftJsonSerializer;
20 | _utf8Encoder = utf8Encoder;
21 | }
22 |
23 | public byte[] AdaptMessageToRepository(object message)
24 | {
25 | var messageSerialized = _newtonsoftJsonSerializer.SerializeObject(message);
26 | var messageEncoded = _utf8Encoder.Encode(messageSerialized);
27 | return _gzipCompressor.Compress(messageEncoded);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/IRetryDurableQueueRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
4 | using KafkaFlow.Retry.Durable.Repository.Actions.Delete;
5 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
6 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
7 | using KafkaFlow.Retry.Durable.Repository.Model;
8 |
9 | namespace KafkaFlow.Retry.Durable.Repository;
10 |
11 | internal interface IRetryDurableQueueRepository
12 | {
13 | Task AddIfQueueExistsAsync(IMessageContext context);
14 |
15 | Task CheckQueueNewestItemsAsync(QueueNewestItemsInput queueNewestItemsInput);
16 |
17 | Task CheckQueuePendingItemsAsync(QueuePendingItemsInput queuePendingItemsInput);
18 |
19 | Task DeleteQueuesAsync(DeleteQueuesInput deleteQueuesInput);
20 |
21 | Task> GetRetryQueuesAsync(GetQueuesInput getQueuesInput);
22 |
23 | Task CountRetryQueuesAsync(CountQueuesInput countQueuesInput);
24 |
25 | Task SaveToQueueAsync(IMessageContext context, string description);
26 |
27 | Task UpdateItemAsync(UpdateItemInput updateItemInput);
28 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/IRetryDurableQueueRepositoryProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Create;
3 | using KafkaFlow.Retry.Durable.Repository.Actions.Delete;
4 | using KafkaFlow.Retry.Durable.Repository.Actions.Read;
5 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
6 |
7 | namespace KafkaFlow.Retry.Durable.Repository;
8 |
9 | public interface IRetryDurableQueueRepositoryProvider
10 | {
11 | Task CheckQueueAsync(CheckQueueInput input);
12 |
13 | Task CheckQueueNewestItemsAsync(QueueNewestItemsInput input);
14 |
15 | Task CheckQueuePendingItemsAsync(QueuePendingItemsInput input);
16 |
17 | Task DeleteQueuesAsync(DeleteQueuesInput input);
18 |
19 | Task GetQueuesAsync(GetQueuesInput input);
20 |
21 | Task CountQueuesAsync(CountQueuesInput input);
22 |
23 | Task SaveToQueueAsync(SaveToQueueInput input);
24 |
25 | Task UpdateItemExecutionInfoAsync(UpdateItemExecutionInfoInput input);
26 |
27 | Task UpdateItemsAsync(UpdateItemsInput input);
28 |
29 | Task UpdateItemStatusAsync(UpdateItemStatusInput input);
30 |
31 | Task UpdateQueuesAsync(UpdateQueuesInput input);
32 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/IUpdateRetryQueueItemHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.Durable.Repository.Actions.Update;
3 |
4 | namespace KafkaFlow.Retry.Durable.Repository;
5 |
6 | internal interface IUpdateRetryQueueItemHandler
7 | {
8 | bool CanHandle(UpdateItemInput input);
9 |
10 | Task UpdateItemAsync(UpdateItemInput input);
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Model/MessageHeader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | [ExcludeFromCodeCoverage]
6 | public class MessageHeader
7 | {
8 | public MessageHeader(string key, byte[] value)
9 | {
10 | Key = key;
11 | Value = value;
12 | }
13 |
14 | public string Key { get; }
15 |
16 | public byte[] Value { get; }
17 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Model/RetryQueueItemStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Model;
2 |
3 | public enum RetryQueueItemStatus
4 | {
5 | None = 0,
6 | Waiting = 1,
7 | InRetry = 2,
8 | Done = 3,
9 | Cancelled = 4
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Repository/Model/RetryQueueStatus.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable.Repository.Model;
2 |
3 | public enum RetryQueueStatus
4 | {
5 | None = 0,
6 | Active = 1,
7 | Done = 2
8 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryConsumerStrategy.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry;
2 |
3 | public enum RetryConsumerStrategy
4 | {
5 | ///
6 | /// The consumed order of messages for the same queue is preserved when the retry runs
7 | ///
8 | GuaranteeOrderedConsumption = 1,
9 |
10 | ///
11 | /// The last consumed message has prevalence over previous consumed messages
12 | ///
13 | LatestConsumption = 2
14 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryDurableConstants.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable;
2 |
3 | internal static class RetryDurableConstants
4 | {
5 | public const string AttemptsCount = "Kafka-Flow-Retry-Durable-Attempts-Count";
6 | public const string EmbeddedConsumerGroupId = "kafka-flow-retry-durable-consumer";
7 | public const string EmbeddedConsumerName = "kafka-flow-retry-durable-consumer";
8 | public const string EmbeddedProducerName = "kafka-flow-retry-durable-producer";
9 | public const string ItemId = "Kafka-Flow-Retry-Durable-Item-Id";
10 | public const string MessageType = "Kafka-Flow-Retry-Durable-Original-Message-Type";
11 | public const string QueueId = "Kafka-Flow-Retry-Durable-Queue-Id";
12 | public const string Sort = "Kafka-Flow-Retry-Durable-Sort";
13 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryDurableConsumerCompressorMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Compression;
4 |
5 | namespace KafkaFlow.Retry.Durable;
6 |
7 | internal class RetryDurableConsumerCompressorMiddleware : IMessageMiddleware
8 | {
9 | private readonly IGzipCompressor _gzipCompressor;
10 |
11 | public RetryDurableConsumerCompressorMiddleware(IGzipCompressor gzipCompressor)
12 | {
13 | Guard.Argument(gzipCompressor).NotNull();
14 |
15 | _gzipCompressor = gzipCompressor;
16 | }
17 |
18 | public async Task Invoke(IMessageContext context, MiddlewareDelegate next)
19 | {
20 | await next(context.SetMessage(context.Message.Key, _gzipCompressor.Decompress((byte[])context.Message.Value)))
21 | .ConfigureAwait(false);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryDurableConsumerNewtonsoftJsonSerializerMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Dawn;
4 | using KafkaFlow.Retry.Durable.Serializers;
5 |
6 | namespace KafkaFlow.Retry.Durable;
7 |
8 | internal class RetryDurableConsumerNewtonsoftJsonSerializerMiddleware : IMessageMiddleware
9 | {
10 | private readonly INewtonsoftJsonSerializer _newtonsoftJsonSerializer;
11 | private readonly Type _type;
12 |
13 | public RetryDurableConsumerNewtonsoftJsonSerializerMiddleware(INewtonsoftJsonSerializer newtonsoftJsonSerializer,
14 | Type type)
15 | {
16 | Guard.Argument(newtonsoftJsonSerializer).NotNull();
17 | Guard.Argument(type).NotNull();
18 |
19 | _newtonsoftJsonSerializer = newtonsoftJsonSerializer;
20 | _type = type;
21 | }
22 |
23 | public async Task Invoke(IMessageContext context, MiddlewareDelegate next)
24 | {
25 | await next(context.SetMessage(context.Message.Key,
26 | _newtonsoftJsonSerializer.DeserializeObject((string)context.Message.Value, _type))).ConfigureAwait(false);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryDurableConsumerUtf8EncoderMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Dawn;
3 | using KafkaFlow.Retry.Durable.Encoders;
4 |
5 | namespace KafkaFlow.Retry.Durable;
6 |
7 | internal class RetryDurableConsumerUtf8EncoderMiddleware : IMessageMiddleware
8 | {
9 | private readonly IUtf8Encoder _utf8Encoder;
10 |
11 | public RetryDurableConsumerUtf8EncoderMiddleware(IUtf8Encoder utf8Encoder)
12 | {
13 | Guard.Argument(utf8Encoder).NotNull();
14 |
15 | _utf8Encoder = utf8Encoder;
16 | }
17 |
18 | public async Task Invoke(IMessageContext context, MiddlewareDelegate next)
19 | {
20 | await next(context.SetMessage(context.Message.Key, _utf8Encoder.Decode((byte[])context.Message.Value)))
21 | .ConfigureAwait(false);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryError.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable;
2 |
3 | public class RetryError
4 | {
5 | public RetryError(RetryErrorCode code)
6 | {
7 | Code = code;
8 | }
9 |
10 | public RetryErrorCode Code { get; set; }
11 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/RetryErrorCode.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.Durable;
2 |
3 | public enum RetryErrorCode
4 | {
5 | Unknown = 0,
6 |
7 | /*
8 | * AABB
9 | * AA -> Module
10 | * BB -> Error number
11 | */
12 |
13 | // MAIN CONSUMER MODULE NUMBER: 00
14 | ConsumerKafkaException = 0001,
15 |
16 | ConsumerUnrecoverableException = 0002,
17 | ConsumerBlockedException = 0003,
18 | ConsumerIgnoredException = 0004,
19 | ConsumerHandledException = 0005,
20 |
21 | // POLLING MODULE NUMBER: 01
22 | PollingUnknownException = 0101,
23 |
24 | PollingProducerException = 0102,
25 |
26 | // DATA PROVIDER MODULE NUMBER: 02
27 | DataProviderUnrecoverableException = 0201,
28 |
29 | DataProviderSaveToQueue = 0202,
30 | DataProviderAddIfQueueExists = 0203,
31 | DataProviderCheckQueuePendingItems = 0204,
32 | DataProviderGetRetryQueues = 0205,
33 | DataProviderUpdateItem = 0206,
34 | DataProviderGetRetryQueuesSameItemSort = 0207,
35 | DataProviderCountRetryQueues = 0208,
36 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Serializers/INewtonsoftJsonSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.Durable.Serializers;
4 |
5 | internal interface INewtonsoftJsonSerializer
6 | {
7 | object DeserializeObject(string data, Type type);
8 |
9 | string SerializeObject(object data);
10 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Durable/Serializers/NewtonsoftJsonSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 |
4 | namespace KafkaFlow.Retry.Durable.Serializers;
5 |
6 | internal class NewtonsoftJsonSerializer : INewtonsoftJsonSerializer
7 | {
8 | private readonly JsonSerializerSettings _jsonSerializerSettings;
9 |
10 | public NewtonsoftJsonSerializer()
11 | : this(new JsonSerializerSettings())
12 | {
13 | }
14 |
15 | public NewtonsoftJsonSerializer(JsonSerializerSettings jsonSerializerSettings)
16 | {
17 | _jsonSerializerSettings = jsonSerializerSettings;
18 | }
19 |
20 | public object DeserializeObject(string data, Type type)
21 | {
22 | return JsonConvert.DeserializeObject(data, type, _jsonSerializerSettings);
23 | }
24 |
25 | public string SerializeObject(object data)
26 | {
27 | return JsonConvert.SerializeObject(data, _jsonSerializerSettings);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Forever/RetryForeverDefinition.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Dawn;
5 |
6 | namespace KafkaFlow.Retry.Forever;
7 |
8 | internal class RetryForeverDefinition
9 | {
10 | private readonly IReadOnlyCollection> _retryWhenExceptions;
11 |
12 | public RetryForeverDefinition(
13 | Func timeBetweenTriesPlan,
14 | IReadOnlyCollection> retryWhenExceptions
15 | )
16 | {
17 | Guard.Argument(retryWhenExceptions).NotNull("At least an exception should be defined");
18 | Guard.Argument(retryWhenExceptions.Count).NotNegative(value => "At least an exception should be defined");
19 | Guard.Argument(timeBetweenTriesPlan).NotNull("A plan of times betwwen tries should be defined");
20 |
21 | TimeBetweenTriesPlan = timeBetweenTriesPlan;
22 | _retryWhenExceptions = retryWhenExceptions;
23 | }
24 |
25 | public Func TimeBetweenTriesPlan { get; }
26 |
27 | public bool ShouldRetry(RetryContext kafkaRetryContext)
28 | {
29 | return _retryWhenExceptions.Any(rule => rule(kafkaRetryContext));
30 | }
31 | }
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
4 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.UnitTests")]
5 | [assembly: InternalsVisibleTo("KafkaFlow.Retry.IntegrationTests")]
6 |
--------------------------------------------------------------------------------
/src/KafkaFlow.Retry/RetryContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dawn;
3 |
4 | namespace KafkaFlow.Retry;
5 |
6 | public class RetryContext
7 | {
8 | public RetryContext(Exception exception)
9 | {
10 | Guard.Argument(exception, nameof(exception)).NotNull();
11 |
12 | Exception = exception;
13 | }
14 |
15 | public Exception Exception { get; }
16 | }
--------------------------------------------------------------------------------
/tests/CodeCoverage.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | .*\.dll$
12 | .*\.exe$
13 |
14 |
15 | .*KafkaFlow.Retry.IntegrationTests.dll
16 | .*KafkaFlow.Retry.UnitTests.dll
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Bootstrappers/Fixtures/BootstrapperRepositoryFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Bootstrappers.Fixtures;
4 |
5 | [CollectionDefinition("BootstrapperRepositoryCollection")]
6 | public class BootstrapperRepositoryCollectionFixture : ICollectionFixture
7 | {
8 | }
9 |
10 | public class BootstrapperRepositoryFixture : BootstrapperFixtureTemplate
11 | {
12 | public BootstrapperRepositoryFixture()
13 | {
14 | var config = new ConfigurationBuilder()
15 | .AddJsonFile(ConfigurationFilePath)
16 | .Build();
17 |
18 | InitializeDatabasesAsync(config).GetAwaiter().GetResult();
19 | }
20 |
21 | public override void Dispose()
22 | {
23 | var repositories = RepositoryProvider.GetAllRepositories();
24 |
25 | foreach (var repository in repositories)
26 | {
27 | repository.CleanDatabaseAsync().GetAwaiter().GetResult();
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Exceptions/RetryDurableTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
4 |
5 | public class RetryDurableTestException : Exception
6 | {
7 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Exceptions/RetryForeverTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
4 |
5 | public class RetryForeverTestException : Exception
6 | {
7 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Exceptions/RetrySimpleTestException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
4 |
5 | public class RetrySimpleTestException : Exception
6 | {
7 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Handlers/RetryDurableTestMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
3 | using KafkaFlow.Retry.IntegrationTests.Core.Messages;
4 | using KafkaFlow.Retry.IntegrationTests.Core.Storages;
5 |
6 | namespace KafkaFlow.Retry.IntegrationTests.Core.Handlers;
7 |
8 | internal class RetryDurableTestMessageHandler : IMessageHandler
9 | {
10 | private readonly ILogHandler _logHandler;
11 |
12 | public RetryDurableTestMessageHandler(ILogHandler logHandler)
13 | {
14 | _logHandler = logHandler;
15 | }
16 |
17 | public Task Handle(IMessageContext context, RetryDurableTestMessage message)
18 | {
19 | InMemoryAuxiliarStorage.Add(message);
20 |
21 | if (InMemoryAuxiliarStorage.ThrowException)
22 | {
23 | throw new RetryDurableTestException();
24 | }
25 |
26 | return Task.CompletedTask;
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Handlers/RetryForeverTestMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
3 | using KafkaFlow.Retry.IntegrationTests.Core.Messages;
4 | using KafkaFlow.Retry.IntegrationTests.Core.Storages;
5 |
6 | namespace KafkaFlow.Retry.IntegrationTests.Core.Handlers;
7 |
8 | internal class RetryForeverTestMessageHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, RetryForeverTestMessage message)
11 | {
12 | InMemoryAuxiliarStorage.Add(message);
13 |
14 | if (InMemoryAuxiliarStorage.ThrowException)
15 | {
16 | throw new RetryForeverTestException();
17 | }
18 |
19 | return Task.CompletedTask;
20 | }
21 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Handlers/RetrySimpleTestMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.IntegrationTests.Core.Exceptions;
3 | using KafkaFlow.Retry.IntegrationTests.Core.Messages;
4 | using KafkaFlow.Retry.IntegrationTests.Core.Storages;
5 |
6 | namespace KafkaFlow.Retry.IntegrationTests.Core.Handlers;
7 |
8 | internal class RetrySimpleTestMessageHandler : IMessageHandler
9 | {
10 | public Task Handle(IMessageContext context, RetrySimpleTestMessage message)
11 | {
12 | InMemoryAuxiliarStorage.Add(message);
13 |
14 | throw new RetrySimpleTestException();
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Messages/ITestMessage.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Messages;
2 |
3 | internal interface ITestMessage
4 | {
5 | string Key { get; set; }
6 | string Value { get; set; }
7 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Messages/RetryDurableTestMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Messages;
4 |
5 | [DataContract]
6 | internal class RetryDurableTestMessage : ITestMessage
7 | {
8 | [DataMember(Order = 1)] public string Key { get; set; }
9 |
10 | [DataMember(Order = 2)] public string Value { get; set; }
11 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Messages/RetryForeverTestMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Messages;
4 |
5 | [DataContract]
6 | internal class RetryForeverTestMessage : ITestMessage
7 | {
8 | [DataMember(Order = 1)] public string Key { get; set; }
9 |
10 | [DataMember(Order = 2)] public string Value { get; set; }
11 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Messages/RetrySimpleTestMessage.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Messages;
2 |
3 | internal class RetrySimpleTestMessage : ITestMessage
4 | {
5 | public string Key { get; set; }
6 | public string Value { get; set; }
7 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableGuaranteeOrderedConsumptionMongoDbProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableGuaranteeOrderedConsumptionMongoDbProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableGuaranteeOrderedConsumptionPostgresProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableGuaranteeOrderedConsumptionPostgresProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableGuaranteeOrderedConsumptionSqlServerProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableGuaranteeOrderedConsumptionSqlServerProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableLatestConsumptionMongoDbProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableLatestConsumptionMongoDbProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableLatestConsumptionPostgresProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableLatestConsumptionPostgresProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryDurableLatestConsumptionSqlServerProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryDurableLatestConsumptionSqlServerProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetryForeverProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetryForeverProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Producers/RetrySimpleProducer.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Producers;
2 |
3 | internal class RetrySimpleProducer
4 | {
5 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Settings/KafkaSettings.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Settings;
2 |
3 | internal class KafkaSettings
4 | {
5 | public string Brokers { get; set; }
6 |
7 | public string SecurityProtocol { get; set; }
8 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Settings/MongoDbRepositorySettings.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Settings;
2 |
3 | internal class MongoDbRepositorySettings
4 | {
5 | public string ConnectionString { get; set; }
6 |
7 | public string DatabaseName { get; set; }
8 |
9 | public string RetryQueueCollectionName { get; set; }
10 |
11 | public string RetryQueueItemCollectionName { get; set; }
12 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Settings/PostgresRepositorySettings.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Settings;
2 |
3 | internal class PostgresRepositorySettings
4 | {
5 | public string ConnectionString { get; set; }
6 |
7 | public string DatabaseName { get; set; }
8 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Settings/SqlServerRepositorySettings.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Settings;
2 |
3 | internal class SqlServerRepositorySettings
4 | {
5 | public string ConnectionString { get; set; }
6 |
7 | public string DatabaseName { get; set; }
8 | public string Schema { get; set; }
9 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/Assertion/IPhysicalStorageAssert.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.IntegrationTests.Core.Messages;
3 | using KafkaFlow.Retry.IntegrationTests.Core.Storages.Repositories;
4 |
5 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages.Assertion;
6 |
7 | internal interface IPhysicalStorageAssert
8 | {
9 | Task AssertRetryDurableMessageCreationAsync(RepositoryType repositoryType, RetryDurableTestMessage message,
10 | int count);
11 |
12 | Task AssertRetryDurableMessageDoneAsync(RepositoryType repositoryType, RetryDurableTestMessage message);
13 |
14 | Task AssertRetryDurableMessageRetryingAsync(RepositoryType repositoryType, RetryDurableTestMessage message,
15 | int retryCount);
16 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/InMemoryAuxiliarStorage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using KafkaFlow.Retry.IntegrationTests.Core.Messages;
7 |
8 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages;
9 |
10 | internal static class InMemoryAuxiliarStorage where T : ITestMessage
11 | {
12 | private const int TimeoutSec = 90;
13 | private static readonly ConcurrentBag s_message = new();
14 |
15 | public static bool ThrowException { get; set; }
16 |
17 | public static void Add(T message)
18 | {
19 | s_message.Add(message);
20 | }
21 |
22 | public static async Task AssertCountMessageAsync(T message, int count)
23 | {
24 | var start = DateTime.Now;
25 |
26 | while (s_message.Count(x => x.Key == message.Key && x.Value == message.Value) != count)
27 | {
28 | if (DateTime.Now.Subtract(start).TotalSeconds > TimeoutSec && !Debugger.IsAttached)
29 | {
30 | Assert.Fail("Message not received.");
31 | return;
32 | }
33 |
34 | await Task.Delay(100);
35 | }
36 | }
37 |
38 | public static void Clear()
39 | {
40 | s_message.Clear();
41 | }
42 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/Repositories/IRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using KafkaFlow.Retry.Durable.Repository;
5 | using KafkaFlow.Retry.Durable.Repository.Model;
6 |
7 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages.Repositories;
8 |
9 | public interface IRepository
10 | {
11 | RepositoryType RepositoryType { get; }
12 |
13 | IRetryDurableQueueRepositoryProvider RetryQueueDataProvider { get; }
14 |
15 | Task CleanDatabaseAsync();
16 |
17 | Task CreateQueueAsync(RetryQueue queue);
18 |
19 | Task GetAllRetryQueueDataAsync(string queueGroupKey);
20 |
21 | Task GetRetryQueueAsync(string queueGroupKey);
22 |
23 | Task> GetRetryQueueItemsAsync(Guid retryQueueId,
24 | Func, bool> stopCondition);
25 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/Repositories/IRepositoryProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages.Repositories;
4 |
5 | internal interface IRepositoryProvider
6 | {
7 | IEnumerable GetAllRepositories();
8 |
9 | IRepository GetRepositoryOfType(RepositoryType repositoryType);
10 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/Repositories/RepositoryProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages.Repositories;
5 |
6 | internal class RepositoryProvider : IRepositoryProvider
7 | {
8 | private readonly IEnumerable _repositories;
9 |
10 | public RepositoryProvider(IEnumerable repositories)
11 | {
12 | _repositories = repositories;
13 | }
14 |
15 | public IEnumerable GetAllRepositories()
16 | {
17 | return _repositories;
18 | }
19 |
20 | public IRepository GetRepositoryOfType(RepositoryType repositoryType)
21 | {
22 | return _repositories.Single(r => r.RepositoryType == repositoryType);
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Core/Storages/Repositories/RepositoryType.cs:
--------------------------------------------------------------------------------
1 | namespace KafkaFlow.Retry.IntegrationTests.Core.Storages.Repositories;
2 |
3 | public enum RepositoryType
4 | {
5 | Unknown = 0,
6 |
7 | SqlServer = 1,
8 | MongoDb = 2,
9 | Postgres = 3
10 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/PollingTests/JobSurrogate.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using Quartz;
4 |
5 | namespace KafkaFlow.Retry.IntegrationTests.PollingTests;
6 |
7 | internal class JobSurrogate : IJob
8 | {
9 | public Task Execute(IJobExecutionContext context)
10 | {
11 | var jobExecutionContexts = context.JobDetail.JobDataMap["JobExecution"] as List;
12 | jobExecutionContexts.Add(context);
13 |
14 | return Task.CompletedTask;
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Properties/AssemblyMetadata.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Runtime.CompilerServices;
3 |
4 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5 | [assembly: ExcludeFromCodeCoverage]
6 | [assembly: CollectionBehavior(DisableTestParallelization = true)]
7 |
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/Usings.cs:
--------------------------------------------------------------------------------
1 | // Global using directives
2 |
3 | global using Xunit;
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.IntegrationTests/conf/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Kafka": {
3 | "Brokers": "localhost:9092",
4 | "SecurityProtocol": "Plaintext"
5 | },
6 | "MongoDbRepository": {
7 | "ConnectionString": "mongodb://localhost:27017",
8 | "DatabaseName": "kafka_flow_retry_durable_test",
9 | "RetryQueueCollectionName": "RetryQueues",
10 | "RetryQueueItemCollectionName": "RetryQueueItems"
11 | },
12 | "SqlServerRepository": {
13 | "ConnectionString": "Server=localhost; User ID=sa; Password=SqlSever123123; Pooling=true; Trusted_Connection=false; TrustServerCertificate=true; Integrated Security=false; Min Pool Size=1; Max Pool Size=100; Application Name=KafkaFlow Retry Tests; Encrypt=false;",
14 | "DatabaseName": "kafka_flow_retry_durable_test",
15 | "Schema": "dbo"
16 | },
17 | "PostgresRepository": {
18 | "ConnectionString": "Server=localhost;Database=postgres;User Id=postgres;Password=Postgres123123;Port=5432;Application Name=KafkaFlow Retry Tests;",
19 | "DatabaseName": "postgres"
20 | }
21 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/Adapters/Common/RetryQueueItemStatusDtoAdapterTests.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.API.Adapters.Common;
2 | using KafkaFlow.Retry.API.Dtos.Common;
3 | using KafkaFlow.Retry.Durable.Repository.Model;
4 |
5 | namespace KafkaFlow.Retry.UnitTests.API.Adapters.Common;
6 |
7 | public class RetryQueueItemStatusDtoAdapterTests
8 | {
9 | [Theory]
10 | [InlineData(RetryQueueItemStatusDto.Cancelled, RetryQueueItemStatus.Cancelled)]
11 | [InlineData(RetryQueueItemStatusDto.Done, RetryQueueItemStatus.Done)]
12 | [InlineData(RetryQueueItemStatusDto.InRetry, RetryQueueItemStatus.InRetry)]
13 | [InlineData(RetryQueueItemStatusDto.Waiting, RetryQueueItemStatus.Waiting)]
14 | [InlineData(RetryQueueItemStatusDto.None, RetryQueueItemStatus.None)]
15 | public void RetryQueueItemStatusDtoAdapter_Adapt_Success(RetryQueueItemStatusDto dto,
16 | RetryQueueItemStatus expectedStatus)
17 | {
18 | // Arrange
19 | var adapter = new RetryQueueItemStatusDtoAdapter();
20 |
21 | // Act
22 | var actualStatus = adapter.Adapt(dto);
23 |
24 | // Assert
25 | actualStatus.Should().Be(expectedStatus);
26 | }
27 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/Adapters/UpdateItems/UpdateItemsInputAdapterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.API.Adapters.UpdateItems;
3 | using KafkaFlow.Retry.API.Dtos;
4 | using KafkaFlow.Retry.API.Dtos.Common;
5 |
6 | namespace KafkaFlow.Retry.UnitTests.API.Adapters.UpdateItems;
7 |
8 | public class UpdateItemsInputAdapterTests
9 | {
10 | private readonly IUpdateItemsInputAdapter _adapter = new UpdateItemsInputAdapter();
11 |
12 | [Fact]
13 | public void UpdateItemsInputAdapter_Adapt_Success()
14 | {
15 | // Arrange
16 | var requestDto = new UpdateItemsRequestDto
17 | {
18 | ItemIds = new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() },
19 | Status = RetryQueueItemStatusDto.Cancelled
20 | };
21 |
22 | // Act
23 | var input = _adapter.Adapt(requestDto);
24 |
25 | // Assert
26 | input.Should().NotBeNull();
27 | input.Should().BeEquivalentTo(requestDto);
28 | }
29 |
30 | [Fact]
31 | public void UpdateItemsInputAdapter_Adapt_WithNullArgs_ThrowsException()
32 | {
33 | // Act
34 | Action act = () => _adapter.Adapt(null);
35 |
36 | // Assert
37 | act.Should().Throw();
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/Adapters/UpdateQueues/UpdateQueuesInputAdapterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.API.Adapters.UpdateQueues;
3 | using KafkaFlow.Retry.API.Dtos;
4 | using KafkaFlow.Retry.API.Dtos.Common;
5 |
6 | namespace KafkaFlow.Retry.UnitTests.API.Adapters.UpdateQueues;
7 |
8 | public class UpdateQueuesInputAdapterTests
9 | {
10 | private readonly IUpdateQueuesInputAdapter _adapter = new UpdateQueuesInputAdapter();
11 |
12 | [Fact]
13 | public void UpdateQueuesInputAdapter_Adapt_Success()
14 | {
15 | // Arrange
16 | var requestDto = new UpdateQueuesRequestDto
17 | {
18 | QueueGroupKeys = new[] { "queueOrderKey1", "queueOrderKey2", "queueOrderKey3" },
19 | ItemStatus = RetryQueueItemStatusDto.Cancelled
20 | };
21 |
22 | // Act
23 | var input = _adapter.Adapt(requestDto);
24 |
25 | // Assert
26 | input.Should().NotBeNull();
27 | input.Should().BeEquivalentTo(requestDto);
28 | }
29 |
30 | [Fact]
31 | public void UpdateQueuesInputAdapter_Adapt_WithNullArgs_ThrowsException()
32 | {
33 | // Act
34 | Action act = () => _adapter.Adapt(null);
35 |
36 | // Assert
37 | act.Should().Throw();
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/RetryMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using KafkaFlow.Retry.API;
3 | using Microsoft.AspNetCore.Http;
4 | using Moq;
5 |
6 | namespace KafkaFlow.Retry.UnitTests.API;
7 |
8 | public class RetryMiddlewareTests
9 | {
10 | [Fact]
11 | public async Task RetryMiddleware_InvokeAsync_CallsHttpRequestHandler()
12 | {
13 | // Arrange
14 | var mockHttpRequestHandler = new Mock();
15 |
16 | var context = new DefaultHttpContext();
17 |
18 | RequestDelegate next = _ => Task.CompletedTask;
19 |
20 | var middleware = new RetryMiddleware(next, mockHttpRequestHandler.Object);
21 |
22 | // Act
23 | await middleware.InvokeAsync(context);
24 |
25 | // Assert
26 | mockHttpRequestHandler.Verify(mock => mock.HandleAsync(context.Request, context.Response), Times.Once());
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/Surrogate/DtoSurrogate.cs:
--------------------------------------------------------------------------------
1 | using KafkaFlow.Retry.Durable.Repository.Model;
2 |
3 | namespace KafkaFlow.Retry.UnitTests.API.Surrogate;
4 |
5 | internal class DtoSurrogate
6 | {
7 | public RetryQueueStatus Text { get; set; }
8 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/API/Surrogate/RetryRequestHandlerSurrogate.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Threading.Tasks;
3 | using KafkaFlow.Retry.API;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace KafkaFlow.Retry.UnitTests.API.Surrogate;
7 |
8 | internal class RetryRequestHandlerSurrogate : RetryRequestHandlerBase
9 | {
10 | public RetryRequestHandlerSurrogate(string endpointPrefix, string resource) : base(endpointPrefix, resource)
11 | {
12 | }
13 |
14 | protected override HttpMethod HttpMethod => HttpMethod.GET;
15 |
16 | protected override async Task HandleRequestAsync(HttpRequest request, HttpResponse response)
17 | {
18 | var requestDto = await ReadRequestDtoAsync(request);
19 |
20 | var responseDto = requestDto;
21 |
22 | await WriteResponseAsync(response, responseDto, (int)HttpStatusCode.OK);
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Durable/Definitions/Polling/RetryDurablePollingDefinitionTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using KafkaFlow.Retry.Durable.Definitions.Polling;
3 |
4 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Durable.Definitions.Polling;
5 |
6 | public class RetryDurablePollingDefinitionTests
7 | {
8 | [Fact]
9 | public void RetryDurablePollingDefinition_Ctor_WithArgumentException_ThrowsException()
10 | {
11 | // Act
12 | Action act = () => new RetryDurablePollingDefinition(
13 | true,
14 | "",
15 | 1,
16 | 2);
17 |
18 | // Assert
19 | act.Should().Throw();
20 | }
21 |
22 | [Theory]
23 | [InlineData(-1, 2)]
24 | [InlineData(1, -2)]
25 | public void RetryDurablePollingDefinition_Ctor_WithArgumentOutOfRangeException_ThrowsException(int fetchSize,
26 | int expirationIntervalFactor)
27 | {
28 | // Act
29 | Action act = () => new RetryDurablePollingDefinition(
30 | false,
31 | "x",
32 | fetchSize,
33 | expirationIntervalFactor);
34 |
35 | // Assert
36 | act.Should().Throw();
37 | }
38 | }
--------------------------------------------------------------------------------
/tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Durable/Definitions/RetryDurableRetryPlanBeforeDefinitionTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using KafkaFlow.Retry.Durable.Definitions;
4 |
5 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Durable.Definitions;
6 |
7 | public class RetryDurableRetryPlanBeforeDefinitionTests
8 | {
9 | public static IEnumerable