├── .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 DataTest() 10 | { 11 | return new List 12 | { 13 | new object[] 14 | { 15 | new Func(_ => new TimeSpan(1)), -1 16 | }, 17 | new object[] 18 | { 19 | null, 1 20 | } 21 | }; 22 | } 23 | 24 | [Theory] 25 | [MemberData(nameof(DataTest))] 26 | public void RetryDurableRetryPlanBeforeDefinition_Ctor_Validation(Func timeBetweenTriesPlan, 27 | int numberOfRetries) 28 | { 29 | // Act 30 | Action act = () => new RetryDurableRetryPlanBeforeDefinition(timeBetweenTriesPlan, numberOfRetries, false); 31 | 32 | // Assert 33 | act.Should().Throw(); 34 | } 35 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Durable/Encoders/Utf8EncoderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using KafkaFlow.Retry.Durable.Encoders; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Durable.Encoders; 5 | 6 | public class Utf8EncoderTests 7 | { 8 | private readonly Utf8Encoder _utf8Encoder = new(); 9 | 10 | [Fact] 11 | public void Utf8Encoder_Deconde_Success() 12 | { 13 | // Arrange 14 | var data = new byte[0]; 15 | 16 | // Act 17 | var result = _utf8Encoder.Decode(data); 18 | 19 | // Assert 20 | result.Should().BeEquivalentTo(Encoding.UTF8.GetString(data)); 21 | } 22 | 23 | [Fact] 24 | public void Utf8Encoder_Enconde_Success() 25 | { 26 | // Arrange 27 | var data = "new"; 28 | 29 | // Act 30 | var result = _utf8Encoder.Encode(data); 31 | 32 | // Assert 33 | result.Should().BeEquivalentTo(Encoding.UTF8.GetBytes(data)); 34 | } 35 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Durable/RetryDurableMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using KafkaFlow.Retry.Durable; 4 | using KafkaFlow.Retry.Durable.Definitions; 5 | using Moq; 6 | 7 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Durable; 8 | 9 | public class RetryDurableMiddlewareTests 10 | { 11 | public static IEnumerable DataTest() 12 | { 13 | return new List 14 | { 15 | new object[] 16 | { 17 | Mock.Of(), 18 | null 19 | } 20 | }; 21 | } 22 | 23 | [Theory] 24 | [MemberData(nameof(DataTest))] 25 | internal void RetryDurableMiddleware_Ctor_Tests( 26 | ILogHandler logHandler, 27 | RetryDurableDefinition retryDurableDefinition) 28 | { 29 | // Act 30 | Action act = () => new RetryDurableMiddleware( 31 | logHandler, 32 | retryDurableDefinition 33 | ); 34 | 35 | // Assert 36 | act.Should().Throw(); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Forever/RetryForeverDefinitionBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.Forever; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Forever; 5 | 6 | public class RetryForeverDefinitionBuilderTests 7 | { 8 | [Fact] 9 | public void RetryForeverDefinitionBuilder_Build_ReturnsRetryForeverDefinition() 10 | { 11 | // Arrange 12 | var builder = new RetryForeverDefinitionBuilder(); 13 | var exception = new Exception(); 14 | var retryContext = new RetryContext(exception); 15 | 16 | builder.WithTimeBetweenTriesPlan() 17 | .WithTimeBetweenTriesPlan(_ => new TimeSpan()) 18 | .Handle() 19 | .Handle(new Func(_ => true)) 20 | .Handle(d => d == retryContext); 21 | 22 | // Act 23 | var result = builder.Build(); 24 | 25 | // Assert 26 | result.Should().NotBeNull(); 27 | result.Should().BeOfType(typeof(RetryForeverDefinition)); 28 | result.ShouldRetry(retryContext).Should().BeTrue(); 29 | result.TimeBetweenTriesPlan.Should().NotBeNull(); 30 | } 31 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/RetryContextTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry; 4 | 5 | public class RetryContextTests 6 | { 7 | [Fact] 8 | public void RetryContext_Ctor_WithException_Success() 9 | { 10 | //Arrange 11 | var exception = new Exception(); 12 | 13 | // Act 14 | var retryContext = new RetryContext(exception); 15 | 16 | // Assert 17 | retryContext.Should().NotBeNull(); 18 | retryContext.Exception.Should().Be(exception); 19 | } 20 | 21 | [Fact] 22 | public void RetryContext_Ctor_WithNullException_ThrowException() 23 | { 24 | // Act 25 | Action act = () => new RetryContext(null); 26 | 27 | // Assert 28 | act.Should().Throw(); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/KafkaFlow.Retry/Simple/RetrySimpleDefinitionBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.Simple; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.KafkaFlow.Retry.Simple; 5 | 6 | public class RetrySimpleDefinitionBuilderTests 7 | { 8 | [Fact] 9 | public void RetrySimpleDefinitionBuilder_Build_ReturnsRetryForeverDefinition() 10 | { 11 | // Arrange 12 | var builder = new RetrySimpleDefinitionBuilder(); 13 | var exception = new Exception(); 14 | var retryContext = new RetryContext(exception); 15 | var tryTimes = 1; 16 | var pause = false; 17 | 18 | builder.WithTimeBetweenTriesPlan() 19 | .TryTimes(tryTimes) 20 | .ShouldPauseConsumer(pause) 21 | .WithTimeBetweenTriesPlan(_ => new TimeSpan()) 22 | .Handle() 23 | .Handle(new Func(_ => true)) 24 | .Handle(d => d == retryContext); 25 | 26 | // Act 27 | var result = builder.Build(); 28 | 29 | // Assert 30 | result.Should().NotBeNull(); 31 | result.Should().BeOfType(typeof(RetrySimpleDefinition)); 32 | result.ShouldRetry(retryContext).Should().BeTrue(); 33 | result.TimeBetweenTriesPlan.Should().NotBeNull(); 34 | result.NumberOfRetries.Should().Be(tryTimes); 35 | result.PauseConsumer.Should().Be(pause); 36 | } 37 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/MongoDb/Adapters/HeaderAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.Durable.Repository.Model; 3 | using KafkaFlow.Retry.MongoDb.Adapters; 4 | using KafkaFlow.Retry.MongoDb.Model; 5 | 6 | namespace KafkaFlow.Retry.UnitTests.Repositories.MongoDb.Adapters; 7 | 8 | public class HeaderAdapterTests 9 | { 10 | [Fact] 11 | public void HeaderAdapter_Adapt_WithMessageHeader_Success() 12 | { 13 | //Arrange 14 | var adapter = new HeaderAdapter(); 15 | var message = new MessageHeader("key", new byte[2]); 16 | 17 | // Act 18 | var result = adapter.Adapt(message); 19 | 20 | // Assert 21 | adapter.Should().NotBeNull(); 22 | result.Should().BeOfType(typeof(RetryQueueHeaderDbo)); 23 | } 24 | 25 | [Fact] 26 | public void HeaderAdapter_Adapt_WithoutMessageHeader_ThrowException() 27 | { 28 | //Arrange 29 | var adapter = new HeaderAdapter(); 30 | MessageHeader message = null; 31 | 32 | // Act 33 | Action act = () => adapter.Adapt(message); 34 | 35 | // Assert 36 | act.Should().Throw(); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/MongoDb/MongoDbDataProviderFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.MongoDb; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.Repositories.MongoDb; 5 | 6 | public class MongoDbDataProviderFactoryTests 7 | { 8 | private readonly MongoDbDataProviderFactory _mongoDbDataProviderFactory = new(); 9 | 10 | [Fact] 11 | public void MongoDbDataProviderFactory_TryCreate_ReturnsDataProviderCreationResult() 12 | { 13 | // Act 14 | var result = _mongoDbDataProviderFactory.TryCreate(new MongoDbSettings()); 15 | 16 | // Arrange 17 | result.Should().NotBeNull(); 18 | result.Should().BeOfType(typeof(DataProviderCreationResult)); 19 | } 20 | 21 | [Fact] 22 | public void MongoDbDataProviderFactory_TryCreate_WithoutMongoDbSettings_ThrowsException() 23 | { 24 | // Act 25 | Action act = () => _mongoDbDataProviderFactory.TryCreate(null); 26 | 27 | // Arrange 28 | act.Should().Throw(); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/Postgres/Model/Schema/ScriptTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.Postgres.Model.Schema; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.Repositories.Postgres.Model.Schema; 5 | 6 | public class ScriptTests 7 | { 8 | [Fact] 9 | public void Script_Ctor_Success() 10 | { 11 | // Arrange 12 | var query = "test"; 13 | 14 | // Act 15 | var script = new Script(query); 16 | 17 | // Assert 18 | script.Should().NotBeNull(); 19 | script.Value.Should().Be(query); 20 | } 21 | 22 | [Fact] 23 | public void Script_Ctor_WithoutValue_ThrowsException() 24 | { 25 | // Arrange 26 | string query = null; 27 | 28 | // Act 29 | Action act = () => new Script(query); 30 | 31 | // Assert 32 | act.Should().Throw(); 33 | } 34 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/Postgres/RetryDurableDefinitionBuilderExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using KafkaFlow.Retry.Postgres; 2 | 3 | namespace KafkaFlow.Retry.UnitTests.Repositories.Postgres; 4 | 5 | public class RetryDurableDefinitionBuilderExtensionTests 6 | { 7 | [Fact] 8 | public void RetryDurableDefinitionBuilderExtension_WithSqlServerDataProvider_Success() 9 | { 10 | // Arrange 11 | var builder = new RetryDurableDefinitionBuilder(); 12 | 13 | // Act 14 | var result = builder.WithPostgresDataProvider("connectionString", "databaseName"); 15 | 16 | // Arrange 17 | result.Should().NotBeNull(); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/SqlServer/Model/Schema/ScriptTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KafkaFlow.Retry.SqlServer.Model.Schema; 3 | 4 | namespace KafkaFlow.Retry.UnitTests.Repositories.SqlServer.Model.Schema; 5 | 6 | public class ScriptTests 7 | { 8 | [Fact] 9 | public void Script_Ctor_Success() 10 | { 11 | // Arrange 12 | var query = "test"; 13 | 14 | // Act 15 | var script = new Script(query); 16 | 17 | // Assert 18 | script.Should().NotBeNull(); 19 | script.Value.Should().Be(query); 20 | } 21 | 22 | [Fact] 23 | public void Script_Ctor_WithoutValue_ThrowsException() 24 | { 25 | // Arrange 26 | string query = null; 27 | 28 | // Act 29 | Action act = () => new Script(query); 30 | 31 | // Assert 32 | act.Should().Throw(); 33 | } 34 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Repositories/SqlServer/RetryDurableDefinitionBuilderExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using KafkaFlow.Retry.SqlServer; 2 | 3 | namespace KafkaFlow.Retry.UnitTests.Repositories.SqlServer; 4 | 5 | public class RetryDurableDefinitionBuilderExtensionTests 6 | { 7 | [Fact] 8 | public void RetryDurableDefinitionBuilderExtension_WithSqlServerDataProvider_Success() 9 | { 10 | // Arrange 11 | var builder = new RetryDurableDefinitionBuilder(); 12 | 13 | // Act 14 | var result = builder.WithSqlServerDataProvider("connectionString", "databaseName"); 15 | 16 | // Arrange 17 | result.Should().NotBeNull(); 18 | } 19 | 20 | [Fact] 21 | public void RetryDurableDefinitionBuilderExtension_WithSqlServerDataProviderAndSchema_Success() 22 | { 23 | // Arrange 24 | var builder = new RetryDurableDefinitionBuilder(); 25 | 26 | // Act 27 | var result = builder.WithSqlServerDataProvider("connectionString", "databaseName", "schema"); 28 | 29 | // Arrange 30 | result.Should().NotBeNull(); 31 | } 32 | } -------------------------------------------------------------------------------- /tests/KafkaFlow.Retry.UnitTests/Usings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using FluentAssertions; 4 | global using Xunit; -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | $ yarn 8 | 9 | ## Local Development 10 | 11 | $ yarn start 12 | 13 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 14 | 15 | ## Build 16 | 17 | $ yarn build 18 | 19 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 20 | 21 | ## Deployment 22 | 23 | Using SSH: 24 | 25 | $ USE_SSH=true yarn deploy 26 | 27 | Not using SSH: 28 | 29 | $ GIT_USER= yarn deploy 30 | 31 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 32 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /website/docs/getting-started/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Getting Started", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/docs/guides/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Guides", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^2.4.1", 18 | "@docusaurus/preset-classic": "^2.4.1", 19 | "@mdx-js/react": "^1.6.22", 20 | "clsx": "^1.2.1", 21 | "prism-react-renderer": "^1.3.5", 22 | "react": "^17.0.2", 23 | "react-dom": "^17.0.2" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "2.2.0" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "engines": { 41 | "node": ">=16.14" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | module.exports = sidebars; 34 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #949494; 10 | --ifm-color-primary-dark: #dddfdf; 11 | --ifm-color-primary-darker: #949494; 12 | --ifm-color-primary-darkest: #666; 13 | --ifm-color-primary-light: #949494; 14 | --ifm-color-primary-lighter: #dddfdf; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Farfetch/kafkaflow-retry-extensions/1cd76ebb987a10e073b27706bbd36355423831ef/website/static/.nojekyll -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Farfetch/kafkaflow-retry-extensions/1cd76ebb987a10e073b27706bbd36355423831ef/website/static/img/favicon.ico --------------------------------------------------------------------------------